openvibers/openvibe.contracts Commits

Mail: an agent answers the mail sent to its own address (0.342.0) (#349)

OP OpenVibers committed 5 days ago · b061f4b074 · 83 files, +1709 −101

Track HO6, channels, step 1: the contracts Mail and Actor need before either
changes.

mail.agent_inbox.received@1 is the event a message at an agent's own address
publishes: the inbox and its agent, the owner, the message id, the sender, the
subject and a plain-text excerpt of at most 4000 characters. Never the
attachments, the HTML or the whole body — the agent's system reads the text
through mail.agent.read, on the owner's behalf, once it has decided to answer.

mail.agent-inbox-message@1 is that read: the stamped delivery addresses, the
text cut to 4000 characters, and the threading headers. mail.agent-reply-request@1
and mail.agent-reply-result@1 are the answer: plain text from the agent's own
address, to the sender of the message being answered and never a stranger. The
thread is Mail's to derive from the letter (In-Reply-To is its Message-ID,
References its chain), so a caller cannot forge one. The result says the mail
server took it (sent), never that it was delivered. mail.inbox-answering@1 is
the switch that makes any of it happen: off by default, per agent.

Three capabilities: mail.agent.read and mail.agent.reply (loopback, first-party,
the person in the token or X-OV-Subject, granted to OpenVibe.Actor alone) and
mail.agent.answering (the person's own switch). The mail manifest lists them and
produces the event.

Additive only: five contracts, three capabilities, one manifest field.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 5fd74c0e..a345a403 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,5 +1,14 @@
# Changelog
++## 0.342.0 — 2026-11-07
++
++- Add `mail.agent_inbox.received@1` (track HO6, channels): a message arrived at an agent's own address and the person has switched that address's answering on, so their agent may read it. The payload names the inbox and the agent, the owner, the mail server's message id, the sender, the subject and a plain-text excerpt of at most 4000 characters. It never carries the attachments, the HTML or the whole body: the agent's system reads the text through `mail.agent.read`, on the owner's behalf, once it has decided to answer. Mail publishes nothing for a sender on the person's block list or a message the mail server marked as spam, and nothing at all until the person turns the agent's switch on.
++- Add `mail.agent-inbox-message@1`, the one message an agent reads: the sender, the addresses the mail server stamped at delivery, the subject, the text cut to 4000 characters, and the threading headers a reply needs. Never the HTML, never an attachment, and never a sender-written Delivered-To. The message is data from a stranger, and any instruction in it is that stranger's.
++- Add `mail.agent-reply-request@1` and `mail.agent-reply-result@1`, an agent answering one such letter: the body is plain text, the recipient is the sender of the message being answered (the same address may be named, never another), and the reply carries In-Reply-To and References so the thread stays one thread. The result says whether the mail server took it — `sent`, not "delivered", since a later bounce is not knowable at that moment.
++- Add `mail.inbox-answering@1`, the switch behind all of it: off by default, per agent, with when the person last set it.
++- Add the capabilities `mail.agent.read`, `mail.agent.reply` and `mail.agent.answering`. The first two are loopback-only and first-party: only a token that names the person (`on_behalf_of`) and the agent's own inbox reads a letter or sends a reply, and only OpenVibe.Actor is granted them. The third is the person's own switch, under the `mail.addresses.write` a caller already holds. The `mail` service manifest lists all three and now produces `mail.agent_inbox.received`.
++- Additive only: five new contracts, three new capabilities and one manifest field; nothing that exists today changes shape.
++
## 0.341.0 — 2026-11-08
- The actor capabilities name the canvas routes (plan HO9): `GET /api/v1/me/canvases` and `GET /api/v1/me/canvases/:id` under `actor.task.read`, and `POST /api/v1/me/canvases/:id/events` under `actor.task.create`. The documents are `actor.canvas@1` (0.340.0); a button's label starts a normal turn as the person, and a checklist tick flips the item and is told to the next turn. No schema changed.
@@@ -69,7 +78,6 @@
A first-party service opens a batch on the network's own account with `ai.batches.create`; a person or a developer app
pays with its own account.
--
## 0.335.0 — 2026-11-07
- Add `actor.voice@1`: an agent's voice as a file, and nothing else. The document carries the agent's name, a short bio, its instructions (the house voice it speaks in), its rules, its default approval level (`ask`, `auto-edits`, `approve-for-me`, `full` — the levels of `actor.agent@1`), the model it prefers and the connected apps it may use (ids only, never a key), an optional face (an avatar ref), an optional author and a `created_at`. It never carries memory, a connector's token or a file.
ddiff --git a/README.md b/README.md
iindex ddff0c25..d0c80f7e 100644
---- a/README.md
++++ b/README.md
@@@ -2,7 +2,7 @@
> Machine-readable contracts for the whole OpenVibe network.
--**Status:** alpha (first released in Wave 1; the release is package.json's `version`, tagged). At v0.317.0: 1061 schemas in `contracts/catalog.json`, 60 service manifests, 479 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin). `node scripts/docs-currency.js <root>` checks every checkout's README and `STATUS.json` against its code: the pins and versions they name, a library's consumer list, the deployed flag against the manifest, README links, and the paths and migrations they name. `node scripts/estate.js` generates `docs/ESTATE.md` and `manifests/repositories/` from the estate checkouts, and `--check` fails on drift (plan T1 step 3).
++**Status:** alpha (first released in Wave 1; the release is package.json's `version`, tagged). At v0.342.0: 1102 schemas in `contracts/catalog.json`, 61 service manifests, 494 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin). `node scripts/docs-currency.js <root>` checks every checkout's README and `STATUS.json` against its code: the pins and versions they name, a library's consumer list, the deployed flag against the manifest, README links, and the paths and migrations they name. `node scripts/estate.js` generates `docs/ESTATE.md` and `manifests/repositories/` from the estate checkouts, and `--check` fails on drift (plan T1 step 3).
**Plan:** OpenVibe Development Roadmap, Wave 1 (implementation plan rev 3, §3.1 and §18.11).
**License:** AGPL-3.0 (same as every OpenVibe service).
ddiff --git a/STATUS.json b/STATUS.json
iindex 5f7a5dc9..1f8b25b6 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -46,7 +46,8 @@
"the Workshop v1 (ADR-054 section 6): Workshop kinds, network.badge, staff review (inventory.review-request@1, inventory.definition.review active) and media_id on equipped slots",
"News, Reviews, Trade, Coupons and OpenRestream go live on their own domains (plan T11); the product at openre.stream is OpenRestream, with OpenVibe Live on by default (live.openre.slot.bind)",
"ADR-055 (proposed): money and the inventory \u2014 primary sales first, gifting next; trading and a market stay closed, and chance is never paid for",
-- "bench.benchmark@1 and bench.run@1 with the capabilities bench.benchmark.write, bench.run.publish and bench.run.read: the benchmark registry and the results API (spec 2026-10-30, stage B1)"
++ "bench.benchmark@1 and bench.run@1 with the capabilities bench.benchmark.write, bench.run.publish and bench.run.read: the benchmark registry and the results API (spec 2026-10-30, stage B1)",
++ "mail.agent_inbox.received@1, mail.agent-inbox-message@1, mail.agent-reply-request@1, mail.agent-reply-result@1 and mail.inbox-answering@1, with the capabilities mail.agent.read, mail.agent.reply and mail.agent.answering (track HO6): an agent answers the mail sent to its own OpenVibe.Mail address, reads it in full text on the owner's behalf, replies from its own address in the thread, and answers only when the person's switch for that address is on"
],
-- "updated": "2026-10-30"
++ "updated": "2026-11-07"
}
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex 60ddea8b..81bf4e16 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -10124,6 +10124,51 @@
"visibility": "public",
"compatibility": "backward",
"status": "active"
++ },
++ {
++ "id": "mail.agent_inbox.received",
++ "version": "1.0.0",
++ "owner": "mail",
++ "schema": "events/payloads/mail.agent_inbox.received.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "mail.agent-inbox-message",
++ "version": "1.0.0",
++ "owner": "mail",
++ "schema": "mail/agent-inbox-message.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "mail.agent-reply-request",
++ "version": "1.0.0",
++ "owner": "mail",
++ "schema": "mail/agent-reply-request.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "mail.agent-reply-result",
++ "version": "1.0.0",
++ "owner": "mail",
++ "schema": "mail/agent-reply-result.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "mail.inbox-answering",
++ "version": "1.0.0",
++ "owner": "mail",
++ "schema": "mail/inbox-answering.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
}
]
}
ddiff --git a/contracts/events/payloads/mail.agent_inbox.received.v1.json b/contracts/events/payloads/mail.agent_inbox.received.v1.json
nnew file mode 100644
iindex 00000000..b504330f
---- /dev/null
++++ b/contracts/events/payloads/mail.agent_inbox.received.v1.json
@@@ -0,0 +1,60 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/events/payloads/mail.agent_inbox.received.v1.json",
++ "title": "MailAgentInboxReceivedPayload",
++ "description": "mail.agent_inbox.received v1 (OpenVibe.Mail server/jobs/agent-mail.js). A message arrived at an agent's own address and the person has switched that address's answering on, so their agent may read it and answer. The payload carries enough for the agent's system to decide, and to show the person what arrived: the inbox and the agent it belongs to, the owner's subject, the mail server's message id, the sender, the subject and a plain-text EXCERPT of at most 4000 characters. It never carries the attachments, the HTML, or the whole body: the agent's system reads the whole text through mail.agent.read, on the owner's behalf, and only once it has decided to answer. Mail publishes it only for an inbox whose answering is on (off by default) and never for a sender on the owner's block list or a message the mail server marked as spam.",
++ "type": "object",
++ "required": ["inbox_id", "inbox", "agent_id", "owner", "message_id", "from", "subject", "excerpt", "received_at"],
++ "additionalProperties": false,
++ "properties": {
++ "inbox_id": {
++ "type": "string",
++ "pattern": "^agi_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The agent inbox's id (mail.agent-inbox@1). Opaque outside OpenVibe.Mail."
++ },
++ "inbox": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's address the message was delivered to, e.g. [email protected]. Matched on the envelope recipient the mail server stamped, never on a sender-written To."
++ },
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address, lowercased and cleaned. It is the id the agent's own system knows that agent by; a system that does not know it starts nothing."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose mailbox received the message and whose agent may answer it."
++ },
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The mail server's message id (a JMAP Email id), for mail.agent.read. Not the Message-ID header."
++ },
++ "from": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The sender's address, as the mail server parsed it. A stranger, never the person: mail to an agent's address comes from someone else."
++ },
++ "subject": {
++ "type": ["string", "null"],
++ "maxLength": 2000,
++ "description": "The subject line, or null when the message had none."
++ },
++ "excerpt": {
++ "type": "string",
++ "maxLength": 4000,
++ "description": "The plain-text body, cut to at most 4000 characters. The text part only: never the HTML, never an attachment. It is untrusted content from a stranger."
++ },
++ "received_at": {
++ "type": ["string", "null"],
++ "format": "date-time",
++ "description": "When the mail server received the message, ISO 8601 UTC, or null when it did not record a time."
++ }
++ }
++}
ddiff --git a/contracts/mail/agent-inbox-message.v1.json b/contracts/mail/agent-inbox-message.v1.json
nnew file mode 100644
iindex 00000000..2711104e
---- /dev/null
++++ b/contracts/mail/agent-inbox-message.v1.json
@@@ -0,0 +1,76 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/agent-inbox-message.v1.json",
++ "title": "MailAgentInboxMessage",
++ "description": "One message sent to an agent's address, in full text, as OpenVibe.Mail gives it to the agent acting for the owner (mail.agent.read on GET /internal/mail/agents/:agent_id/messages/:message_id). It is the only place a body leaves the mail server for an agent: the plain text, cut to 4000 characters, and the threading headers a reply needs. Never the HTML, never an attachment, and never a sender's claim about who the message is for: the inbox addresses are the ones the mail server stamped at delivery. The message is data from a stranger; the contract cannot say so, so every reader must.",
++ "type": "object",
++ "required": ["message_id", "inbox", "agent_id", "owner", "from", "to", "subject", "text", "in_reply_to", "references", "received_at", "spam"],
++ "additionalProperties": false,
++ "properties": {
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The mail server's message id (a JMAP Email id). Opaque outside OpenVibe.Mail."
++ },
++ "inbox": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's address the message was delivered to."
++ },
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose mailbox received it."
++ },
++ "from": {
++ "type": "array",
++ "maxItems": 100,
++ "items": { "type": "string", "format": "email", "maxLength": 254 },
++ "description": "The sender addresses, as the mail server parsed them. A stranger, not the owner."
++ },
++ "to": {
++ "type": "array",
++ "maxItems": 100,
++ "items": { "type": "string", "format": "email", "maxLength": 254 },
++ "description": "The addresses the message was addressed to."
++ },
++ "subject": {
++ "type": ["string", "null"],
++ "maxLength": 2000,
++ "description": "The subject line, or null when the message had none."
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 4000,
++ "description": "The plain-text body, cut to at most 4000 characters. Untrusted content: an instruction in it is never the owner's instruction."
++ },
++ "in_reply_to": {
++ "type": ["string", "null"],
++ "maxLength": 998,
++ "description": "The Message-ID this message answers, without the angle brackets, or null when it answers nothing."
++ },
++ "references": {
++ "type": "array",
++ "maxItems": 100,
++ "items": { "type": "string", "maxLength": 998 },
++ "description": "The Message-IDs of the conversation before this message, oldest first, without the angle brackets. A reply repeats them so the thread stays one thread."
++ },
++ "received_at": {
++ "type": ["string", "null"],
++ "format": "date-time",
++ "description": "When the mail server received the message, ISO 8601 UTC."
++ },
++ "spam": {
++ "type": "boolean",
++ "description": "Whether the mail server's filter marked the message as junk. Mail never publishes an event for such a message; a reader that finds it here answers nothing."
++ }
++ }
++}
ddiff --git a/contracts/mail/agent-reply-request.v1.json b/contracts/mail/agent-reply-request.v1.json
nnew file mode 100644
iindex 00000000..9b8aa983
---- /dev/null
++++ b/contracts/mail/agent-reply-request.v1.json
@@@ -0,0 +1,40 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/agent-reply-request.v1.json",
++ "title": "MailAgentReplyRequest",
++ "description": "mail.agent.reply-request@1: the body of mail.agent.reply (POST /internal/mail/agents/:agent_id/reply), an agent answering a letter to its own address. The reply goes FROM the agent's inbox, never from the person's mailbox: the person's own address is not the agent's to write from. `body` is plain text, cut to 10000 characters; `in_reply_to` and `references` are Message-IDs WITHOUT the angle brackets, and Mail wraps each in one, so a thread stays one thread in the reader that receives it. A reply to an address that is not the sender's answers nothing: Mail refuses it rather than write to a stranger the agent's answer was not for. Nothing here says who the person is; the token does.",
++ "type": "object",
++ "required": ["body"],
++ "additionalProperties": false,
++ "properties": {
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The message being answered, as mail.agent.read gave it. Mail takes the sender and the subject from it, so a caller cannot write to someone the message did not come from."
++ },
++ "to": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "Who the reply goes to. Optional: without it the reply goes to the sender of message_id. When both are given they must be the same address."
++ },
++ "body": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 10000,
++ "description": "The reply, as plain text. Mail builds the message from it and sends no HTML."
++ },
++ "in_reply_to": {
++ "type": ["string", "null"],
++ "maxLength": 998,
++ "description": "The Message-ID this reply answers, without angle brackets. Mail wraps it: In-Reply-To: <…>."
++ },
++ "references": {
++ "type": "array",
++ "maxItems": 100,
++ "items": { "type": "string", "maxLength": 998 },
++ "description": "The Message-IDs of the conversation, oldest first, without angle brackets. Mail wraps each: References: <…> <…>."
++ }
++ }
++}
ddiff --git a/contracts/mail/agent-reply-result.v1.json b/contracts/mail/agent-reply-result.v1.json
nnew file mode 100644
iindex 00000000..32f138b3
---- /dev/null
++++ b/contracts/mail/agent-reply-result.v1.json
@@@ -0,0 +1,30 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/agent-reply-result.v1.json",
++ "title": "MailAgentReplyResult",
++ "description": "mail.agent.reply-result@1: what mail.agent.reply answers once the mail server took the message. `sent` is false when the mail server refused it (the answer's `reason` says why in plain words) and true when it accepted it — a relay's later bounce is not knowable here and is not claimed. `message_id` is the mail server's id for the sent message when it answers with one; `rfc_message_id` is the Message-ID header the reply carries, which a later letter in the thread will name in its In-Reply-To.",
++ "type": "object",
++ "required": ["sent"],
++ "additionalProperties": false,
++ "properties": {
++ "sent": {
++ "type": "boolean",
++ "description": "True when the mail server accepted the reply, false when it did not."
++ },
++ "message_id": {
++ "type": ["string", "null"],
++ "maxLength": 512,
++ "description": "The mail server's id for the sent message."
++ },
++ "rfc_message_id": {
++ "type": ["string", "null"],
++ "maxLength": 998,
++ "description": "The Message-ID header of the reply, without angle brackets."
++ },
++ "reason": {
++ "type": ["string", "null"],
++ "maxLength": 300,
++ "description": "Why the reply was not sent, in plain words. Null when sent is true."
++ }
++ }
++}
ddiff --git a/contracts/mail/inbox-answering.v1.json b/contracts/mail/inbox-answering.v1.json
nnew file mode 100644
iindex 00000000..e6d4528b
---- /dev/null
++++ b/contracts/mail/inbox-answering.v1.json
@@@ -0,0 +1,37 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/inbox-answering.v1.json",
++ "title": "MailInboxAnswering",
++ "description": "mail.inbox-answering@1: whether a person's agent answers the mail sent to its address, as OpenVibe.Mail keeps the switch. It is OFF by default and per agent: an address that exists is not an invitation to answer, and a person turns it on for the agent they trust to. `enabled` is the whole answer; `changed_at` says when the person last set it, so a page can say how long it has been on. Turning it off stops the next message from being published to the agent's system and takes its address out of the answering path; it never deletes what already happened.",
++ "type": "object",
++ "required": ["agent_id", "address", "owner", "enabled", "changed_at"],
++ "additionalProperties": false,
++ "properties": {
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address."
++ },
++ "address": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's inbox address."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose switch it is."
++ },
++ "enabled": {
++ "type": "boolean",
++ "description": "True when the agent answers the mail sent to this address. Off unless the person turned it on."
++ },
++ "changed_at": {
++ "type": ["string", "null"],
++ "format": "date-time",
++ "description": "When the person last turned it on or off; null when they never have (it has been off since the address was made)."
++ }
++ }
++}
ddiff --git a/fixtures/mail.agent-inbox-message/invalid/text-too-long.json b/fixtures/mail.agent-inbox-message/invalid/text-too-long.json
nnew file mode 100644
iindex 00000000..123296f9
---- /dev/null
++++ b/fixtures/mail.agent-inbox-message/invalid/text-too-long.json
@@@ -0,0 +1 @@
++{"message_id": "m-4715", "inbox": "[email protected]", "agent_id": "helper", "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS", "from": ["[email protected]"], "to": ["[email protected]"], "subject": "Too long", "text": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "in_reply_to": null, "references": [], "received_at": "2026-11-07T12:00:00Z", "spam": false}
ddiff --git a/fixtures/mail.agent-inbox-message/invalid/with-html-and-attachments.json b/fixtures/mail.agent-inbox-message/invalid/with-html-and-attachments.json
nnew file mode 100644
iindex 00000000..25f642b5
---- /dev/null
++++ b/fixtures/mail.agent-inbox-message/invalid/with-html-and-attachments.json
@@@ -0,0 +1,16 @@
++{
++ "message_id": "m-4714",
++ "inbox": "[email protected]",
++ "agent_id": "helper",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "from": ["[email protected]"],
++ "to": ["[email protected]"],
++ "subject": "A big one",
++ "text": "the body",
++ "in_reply_to": null,
++ "references": [],
++ "received_at": "2026-11-07T12:00:00Z",
++ "spam": false,
++ "html": "<p>the body</p>",
++ "attachments": [{ "name": "a.pdf" }]
++}
ddiff --git a/fixtures/mail.agent-inbox-message/valid/first-letter.json b/fixtures/mail.agent-inbox-message/valid/first-letter.json
nnew file mode 100644
iindex 00000000..f40b92ea
---- /dev/null
++++ b/fixtures/mail.agent-inbox-message/valid/first-letter.json
@@@ -0,0 +1,14 @@
++{
++ "message_id": "m-4711",
++ "inbox": "[email protected]",
++ "agent_id": "helper",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "from": ["[email protected]"],
++ "to": ["[email protected]"],
++ "subject": "Could you check Tuesday?",
++ "text": "Hi — are we still on for Tuesday at 10? I can move it if that is easier.\n\nThanks,\nDana",
++ "in_reply_to": null,
++ "references": [],
++ "received_at": "2026-11-07T12:00:00Z",
++ "spam": false
++}
ddiff --git a/fixtures/mail.agent-inbox-message/valid/in-a-thread.json b/fixtures/mail.agent-inbox-message/valid/in-a-thread.json
nnew file mode 100644
iindex 00000000..96b1f5f9
---- /dev/null
++++ b/fixtures/mail.agent-inbox-message/valid/in-a-thread.json
@@@ -0,0 +1,14 @@
++{
++ "message_id": "m-4713",
++ "inbox": "[email protected]",
++ "agent_id": "helper",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "from": ["[email protected]"],
++ "to": ["[email protected]"],
++ "subject": "Re: Could you check Tuesday?",
++ "text": "Tuesday at 10 works, thanks for checking back.",
++ "in_reply_to": "[email protected]",
++ "references": ["[email protected]", "[email protected]"],
++ "received_at": "2026-11-07T15:30:00Z",
++ "spam": false
++}
ddiff --git a/fixtures/mail.agent-reply-request/invalid/from-and-html.json b/fixtures/mail.agent-reply-request/invalid/from-and-html.json
nnew file mode 100644
iindex 00000000..6014fe7a
---- /dev/null
++++ b/fixtures/mail.agent-reply-request/invalid/from-and-html.json
@@@ -0,0 +1,6 @@
++{
++ "message_id": "m-4711",
++ "body": "An answer sent from the person's own mailbox, which is not the agent's to write from.",
++ "from": "[email protected]",
++ "html": "<p>An answer</p>"
++}
ddiff --git a/fixtures/mail.agent-reply-request/invalid/no-body.json b/fixtures/mail.agent-reply-request/invalid/no-body.json
nnew file mode 100644
iindex 00000000..c7b47267
---- /dev/null
++++ b/fixtures/mail.agent-reply-request/invalid/no-body.json
@@@ -0,0 +1,5 @@
++{
++ "message_id": "m-4711",
++ "to": "[email protected]",
++ "body": ""
++}
ddiff --git a/fixtures/mail.agent-reply-request/invalid/with-attachments.json b/fixtures/mail.agent-reply-request/invalid/with-attachments.json
nnew file mode 100644
iindex 00000000..258e55bc
---- /dev/null
++++ b/fixtures/mail.agent-reply-request/invalid/with-attachments.json
@@@ -0,0 +1,5 @@
++{
++ "message_id": "m-4711",
++ "body": "At most 10000 characters, said the contract.",
++ "attachments": [{ "name": "a.pdf", "blob": "AAA" }]
++}
ddiff --git a/fixtures/mail.agent-reply-request/valid/threaded.json b/fixtures/mail.agent-reply-request/valid/threaded.json
nnew file mode 100644
iindex 00000000..e203232c
---- /dev/null
++++ b/fixtures/mail.agent-reply-request/valid/threaded.json
@@@ -0,0 +1,7 @@
++{
++ "message_id": "m-4711",
++ "to": "[email protected]",
++ "body": "Tuesday at 10 is still good. See you then.\n\n— Helper, answering for Alice",
++ "in_reply_to": "[email protected]",
++ "references": ["[email protected]"]
++}
ddiff --git a/fixtures/mail.agent-reply-request/valid/to-the-sender.json b/fixtures/mail.agent-reply-request/valid/to-the-sender.json
nnew file mode 100644
iindex 00000000..b31cbf34
---- /dev/null
++++ b/fixtures/mail.agent-reply-request/valid/to-the-sender.json
@@@ -0,0 +1,4 @@
++{
++ "message_id": "m-4711",
++ "body": "Thanks, noted."
++}
ddiff --git a/fixtures/mail.agent-reply-result/invalid/claims-a-bounce.json b/fixtures/mail.agent-reply-result/invalid/claims-a-bounce.json
nnew file mode 100644
iindex 00000000..c396e5c9
---- /dev/null
++++ b/fixtures/mail.agent-reply-result/invalid/claims-a-bounce.json
@@@ -0,0 +1,8 @@
++{
++ "sent": true,
++ "message_id": "m-4720",
++ "rfc_message_id": "[email protected]",
++ "reason": null,
++ "bounced_at": "2026-11-07T13:00:00Z",
++ "delivered": true
++}
ddiff --git a/fixtures/mail.agent-reply-result/invalid/claims-delivery.json b/fixtures/mail.agent-reply-result/invalid/claims-delivery.json
nnew file mode 100644
iindex 00000000..db791278
---- /dev/null
++++ b/fixtures/mail.agent-reply-result/invalid/claims-delivery.json
@@@ -0,0 +1,4 @@
++{
++ "delivered": true,
++ "message_id": "m-4720"
++}
ddiff --git a/fixtures/mail.agent-reply-result/valid/refused.json b/fixtures/mail.agent-reply-result/valid/refused.json
nnew file mode 100644
iindex 00000000..f0c5611a
---- /dev/null
++++ b/fixtures/mail.agent-reply-result/valid/refused.json
@@@ -0,0 +1,6 @@
++{
++ "sent": false,
++ "message_id": null,
++ "rfc_message_id": null,
++ "reason": "The mail server refused the message: the recipient does not exist."
++}
ddiff --git a/fixtures/mail.agent-reply-result/valid/sent.json b/fixtures/mail.agent-reply-result/valid/sent.json
nnew file mode 100644
iindex 00000000..00e55966
---- /dev/null
++++ b/fixtures/mail.agent-reply-result/valid/sent.json
@@@ -0,0 +1,6 @@
++{
++ "sent": true,
++ "message_id": "m-4720",
++ "rfc_message_id": "[email protected]",
++ "reason": null
++}
ddiff --git a/fixtures/mail.agent_inbox.received/invalid/no-owner.json b/fixtures/mail.agent_inbox.received/invalid/no-owner.json
nnew file mode 100644
iindex 00000000..8e77c6ee
---- /dev/null
++++ b/fixtures/mail.agent_inbox.received/invalid/no-owner.json
@@@ -0,0 +1,10 @@
++{
++ "inbox_id": "agi_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "inbox": "[email protected]",
++ "agent_id": "helper",
++ "message_id": "m-4711",
++ "from": "[email protected]",
++ "subject": "Could you check Tuesday?",
++ "excerpt": "Hi — are we still on for Tuesday at 10?",
++ "received_at": "2026-11-07T12:00:00Z"
++}
ddiff --git a/fixtures/mail.agent_inbox.received/invalid/with-html-and-attachments.json b/fixtures/mail.agent_inbox.received/invalid/with-html-and-attachments.json
nnew file mode 100644
iindex 00000000..c16ae7be
---- /dev/null
++++ b/fixtures/mail.agent_inbox.received/invalid/with-html-and-attachments.json
@@@ -0,0 +1,13 @@
++{
++ "inbox_id": "agi_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "inbox": "[email protected]",
++ "agent_id": "helper",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "message_id": "m-4711",
++ "from": "[email protected]",
++ "subject": "Could you check Tuesday?",
++ "excerpt": "Hi — are we still on for Tuesday at 10?",
++ "received_at": "2026-11-07T12:00:00Z",
++ "html": "<p>Hi</p>",
++ "attachments": [{ "name": "invoice.pdf", "type": "application/pdf", "size": 9001 }]
++}
ddiff --git a/fixtures/mail.agent_inbox.received/valid/no-subject-or-time.json b/fixtures/mail.agent_inbox.received/valid/no-subject-or-time.json
nnew file mode 100644
iindex 00000000..b62e97e3
---- /dev/null
++++ b/fixtures/mail.agent_inbox.received/valid/no-subject-or-time.json
@@@ -0,0 +1,11 @@
++{
++ "inbox_id": "agi_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "inbox": "[email protected]",
++ "agent_id": "helper",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "message_id": "m-4712",
++ "from": "[email protected]",
++ "subject": null,
++ "excerpt": "",
++ "received_at": null
++}
ddiff --git a/fixtures/mail.agent_inbox.received/valid/stranger.json b/fixtures/mail.agent_inbox.received/valid/stranger.json
nnew file mode 100644
iindex 00000000..5c05f5a9
---- /dev/null
++++ b/fixtures/mail.agent_inbox.received/valid/stranger.json
@@@ -0,0 +1,11 @@
++{
++ "inbox_id": "agi_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "inbox": "[email protected]",
++ "agent_id": "helper",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "message_id": "m-4711",
++ "from": "[email protected]",
++ "subject": "Could you check Tuesday?",
++ "excerpt": "Hi — are we still on for Tuesday at 10? I can move it if that is easier.\n\nThanks,\nDana",
++ "received_at": "2026-11-07T12:00:00Z"
++}
ddiff --git a/fixtures/mail.inbox-answering/invalid/no-changed-at.json b/fixtures/mail.inbox-answering/invalid/no-changed-at.json
nnew file mode 100644
iindex 00000000..17a8bd4b
---- /dev/null
++++ b/fixtures/mail.inbox-answering/invalid/no-changed-at.json
@@@ -0,0 +1,6 @@
++{
++ "agent_id": "helper",
++ "address": "[email protected]",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "enabled": true
++}
ddiff --git a/fixtures/mail.inbox-answering/valid/off-by-default.json b/fixtures/mail.inbox-answering/valid/off-by-default.json
nnew file mode 100644
iindex 00000000..748ac94d
---- /dev/null
++++ b/fixtures/mail.inbox-answering/valid/off-by-default.json
@@@ -0,0 +1,7 @@
++{
++ "agent_id": "helper",
++ "address": "[email protected]",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "enabled": false,
++ "changed_at": null
++}
ddiff --git a/fixtures/mail.inbox-answering/valid/on.json b/fixtures/mail.inbox-answering/valid/on.json
nnew file mode 100644
iindex 00000000..c3d89fc4
---- /dev/null
++++ b/fixtures/mail.inbox-answering/valid/on.json
@@@ -0,0 +1,7 @@
++{
++ "agent_id": "helper",
++ "address": "[email protected]",
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
++ "enabled": true,
++ "changed_at": "2026-11-07T12:00:00Z"
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 0795fd3e..0c070551 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -111627,6 +111627,351 @@
}
}
}
++ },
++ "mail.agent_inbox.received": {
++ "version": "1.0.0",
++ "owner": "mail",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/events/payloads/mail.agent_inbox.received.v1.json",
++ "title": "MailAgentInboxReceivedPayload",
++ "description": "mail.agent_inbox.received v1 (OpenVibe.Mail server/jobs/agent-mail.js). A message arrived at an agent's own address and the person has switched that address's answering on, so their agent may read it and answer. The payload carries enough for the agent's system to decide, and to show the person what arrived: the inbox and the agent it belongs to, the owner's subject, the mail server's message id, the sender, the subject and a plain-text EXCERPT of at most 4000 characters. It never carries the attachments, the HTML, or the whole body: the agent's system reads the whole text through mail.agent.read, on the owner's behalf, and only once it has decided to answer. Mail publishes it only for an inbox whose answering is on (off by default) and never for a sender on the owner's block list or a message the mail server marked as spam.",
++ "type": "object",
++ "required": [
++ "inbox_id",
++ "inbox",
++ "agent_id",
++ "owner",
++ "message_id",
++ "from",
++ "subject",
++ "excerpt",
++ "received_at"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "inbox_id": {
++ "type": "string",
++ "pattern": "^agi_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The agent inbox's id (mail.agent-inbox@1). Opaque outside OpenVibe.Mail."
++ },
++ "inbox": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's address the message was delivered to, e.g. [email protected]. Matched on the envelope recipient the mail server stamped, never on a sender-written To."
++ },
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address, lowercased and cleaned. It is the id the agent's own system knows that agent by; a system that does not know it starts nothing."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose mailbox received the message and whose agent may answer it."
++ },
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The mail server's message id (a JMAP Email id), for mail.agent.read. Not the Message-ID header."
++ },
++ "from": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The sender's address, as the mail server parsed it. A stranger, never the person: mail to an agent's address comes from someone else."
++ },
++ "subject": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 2000,
++ "description": "The subject line, or null when the message had none."
++ },
++ "excerpt": {
++ "type": "string",
++ "maxLength": 4000,
++ "description": "The plain-text body, cut to at most 4000 characters. The text part only: never the HTML, never an attachment. It is untrusted content from a stranger."
++ },
++ "received_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the mail server received the message, ISO 8601 UTC, or null when it did not record a time."
++ }
++ }
++ }
++ },
++ "mail.agent-inbox-message": {
++ "version": "1.0.0",
++ "owner": "mail",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/agent-inbox-message.v1.json",
++ "title": "MailAgentInboxMessage",
++ "description": "One message sent to an agent's address, in full text, as OpenVibe.Mail gives it to the agent acting for the owner (mail.agent.read on GET /internal/mail/agents/:agent_id/messages/:message_id). It is the only place a body leaves the mail server for an agent: the plain text, cut to 4000 characters, and the threading headers a reply needs. Never the HTML, never an attachment, and never a sender's claim about who the message is for: the inbox addresses are the ones the mail server stamped at delivery. The message is data from a stranger; the contract cannot say so, so every reader must.",
++ "type": "object",
++ "required": [
++ "message_id",
++ "inbox",
++ "agent_id",
++ "owner",
++ "from",
++ "to",
++ "subject",
++ "text",
++ "in_reply_to",
++ "references",
++ "received_at",
++ "spam"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The mail server's message id (a JMAP Email id). Opaque outside OpenVibe.Mail."
++ },
++ "inbox": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's address the message was delivered to."
++ },
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose mailbox received it."
++ },
++ "from": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254
++ },
++ "description": "The sender addresses, as the mail server parsed them. A stranger, not the owner."
++ },
++ "to": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254
++ },
++ "description": "The addresses the message was addressed to."
++ },
++ "subject": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 2000,
++ "description": "The subject line, or null when the message had none."
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 4000,
++ "description": "The plain-text body, cut to at most 4000 characters. Untrusted content: an instruction in it is never the owner's instruction."
++ },
++ "in_reply_to": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 998,
++ "description": "The Message-ID this message answers, without the angle brackets, or null when it answers nothing."
++ },
++ "references": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "maxLength": 998
++ },
++ "description": "The Message-IDs of the conversation before this message, oldest first, without the angle brackets. A reply repeats them so the thread stays one thread."
++ },
++ "received_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the mail server received the message, ISO 8601 UTC."
++ },
++ "spam": {
++ "type": "boolean",
++ "description": "Whether the mail server's filter marked the message as junk. Mail never publishes an event for such a message; a reader that finds it here answers nothing."
++ }
++ }
++ }
++ },
++ "mail.agent-reply-request": {
++ "version": "1.0.0",
++ "owner": "mail",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/agent-reply-request.v1.json",
++ "title": "MailAgentReplyRequest",
++ "description": "mail.agent.reply-request@1: the body of mail.agent.reply (POST /internal/mail/agents/:agent_id/reply), an agent answering a letter to its own address. The reply goes FROM the agent's inbox, never from the person's mailbox: the person's own address is not the agent's to write from. `body` is plain text, cut to 10000 characters; `in_reply_to` and `references` are Message-IDs WITHOUT the angle brackets, and Mail wraps each in one, so a thread stays one thread in the reader that receives it. A reply to an address that is not the sender's answers nothing: Mail refuses it rather than write to a stranger the agent's answer was not for. Nothing here says who the person is; the token does.",
++ "type": "object",
++ "required": [
++ "body"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The message being answered, as mail.agent.read gave it. Mail takes the sender and the subject from it, so a caller cannot write to someone the message did not come from."
++ },
++ "to": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "Who the reply goes to. Optional: without it the reply goes to the sender of message_id. When both are given they must be the same address."
++ },
++ "body": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 10000,
++ "description": "The reply, as plain text. Mail builds the message from it and sends no HTML."
++ },
++ "in_reply_to": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 998,
++ "description": "The Message-ID this reply answers, without angle brackets. Mail wraps it: In-Reply-To: <…>."
++ },
++ "references": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "maxLength": 998
++ },
++ "description": "The Message-IDs of the conversation, oldest first, without angle brackets. Mail wraps each: References: <…> <…>."
++ }
++ }
++ }
++ },
++ "mail.agent-reply-result": {
++ "version": "1.0.0",
++ "owner": "mail",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/agent-reply-result.v1.json",
++ "title": "MailAgentReplyResult",
++ "description": "mail.agent.reply-result@1: what mail.agent.reply answers once the mail server took the message. `sent` is false when the mail server refused it (the answer's `reason` says why in plain words) and true when it accepted it — a relay's later bounce is not knowable here and is not claimed. `message_id` is the mail server's id for the sent message when it answers with one; `rfc_message_id` is the Message-ID header the reply carries, which a later letter in the thread will name in its In-Reply-To.",
++ "type": "object",
++ "required": [
++ "sent"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "sent": {
++ "type": "boolean",
++ "description": "True when the mail server accepted the reply, false when it did not."
++ },
++ "message_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 512,
++ "description": "The mail server's id for the sent message."
++ },
++ "rfc_message_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 998,
++ "description": "The Message-ID header of the reply, without angle brackets."
++ },
++ "reason": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 300,
++ "description": "Why the reply was not sent, in plain words. Null when sent is true."
++ }
++ }
++ }
++ },
++ "mail.inbox-answering": {
++ "version": "1.0.0",
++ "owner": "mail",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/mail/inbox-answering.v1.json",
++ "title": "MailInboxAnswering",
++ "description": "mail.inbox-answering@1: whether a person's agent answers the mail sent to its address, as OpenVibe.Mail keeps the switch. It is OFF by default and per agent: an address that exists is not an invitation to answer, and a person turns it on for the agent they trust to. `enabled` is the whole answer; `changed_at` says when the person last set it, so a page can say how long it has been on. Turning it off stops the next message from being published to the agent's system and takes its address out of the answering path; it never deletes what already happened.",
++ "type": "object",
++ "required": [
++ "agent_id",
++ "address",
++ "owner",
++ "enabled",
++ "changed_at"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address."
++ },
++ "address": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's inbox address."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose switch it is."
++ },
++ "enabled": {
++ "type": "boolean",
++ "description": "True when the agent answers the mail sent to this address. Off unless the person turned it on."
++ },
++ "changed_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the person last turned it on or off; null when they never have (it has been off since the address was made)."
++ }
++ }
++ }
}
}
}
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex 33e84576..a1e35335 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.341.0",
-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 b1cfa4b6..f40a6d59 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.341.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 4fffb449..39dc9bee 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.341.0",
-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 831c47ab..ae425647 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.341.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 ac119cc2..9e04868c 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.341.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 9a7f0732..6179bc96 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.341.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 cebdc5e3..e24259e9 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.341.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 e3a9b510..42374fb5 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.341.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 7242626d..d6c83050 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.341.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 47476402..b41e7245 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.341.0",
-- "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 45f88c46..120ab394 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.341.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 830f87ed..758ed854 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.341.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 90c911c9..ac4fc831 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.341.0",
-- "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 5ca9be6a..eeaba02e 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.341.0",
-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 36407fff..f90e7ea6 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.341.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 67691e10..88d117e7 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.341.0",
-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 d8e162a7..1bc05fc7 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.341.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 099439e9..3277c772 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.341.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 3cbd4cdd..9f592bc2 100644
---- a/generated/openapi/index.json
++++ b/generated/openapi/index.json
@@@ -145,8 +145,8 @@
"service": "mail",
"name": "OpenVibe.Mail",
"origin": "https://openvibe.email",
-- "operations": 12,
-- "capabilities": 2
++ "operations": 16,
++ "capabilities": 5
},
{
"service": "media",
ddiff --git a/generated/openapi/inventory.json b/generated/openapi/inventory.json
iindex 5a9307fb..11a17116 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.341.0",
-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 a91c990e..7f9c0165 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.341.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 429b69cc..acaff337 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.341.0",
-- "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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"
@@@ -92,6 +92,188 @@
}
}
},
++ "/api/v1/agent-inboxes/{id}/answering": {
++ "get": {
++ "operationId": "get_api_v1_agent_inboxes_id_answering",
++ "summary": "Read and set whether an agent answers the mail sent to its own address: GET /api/v1/agent-inboxes/:id/answering and PUT /api/v1/agent-inboxes/:id/answering, both mail.inbox-answering@1.",
++ "description": "**mail.agent.answering** (first-party): Read and set whether an agent answers the mail sent to its own address: GET /api/v1/agent-inboxes/:id/answering and PUT /api/v1/agent-inboxes/:id/answering, both mail.inbox-answering@1. It is OFF by default and per agent; turning it on is what makes Mail publish mail.agent_inbox.received for that address at all. The caller must act for the person (on_behalf_of), and another person's inbox is the same 404 as one that does not exist. Mail still publishes nothing for a sender on the person's block list or for a message the mail server marked as spam.",
++ "tags": [
++ "mail.agent"
++ ],
++ "x-openvibe-capabilities": [
++ "mail.agent.answering"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "mail-addresses-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "mail.agent.answering"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "$ref": "#/components/schemas/mail.inbox-answering.v1"
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "agent_id",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address."
++ },
++ "description": "The agent's name in the address."
++ },
++ {
++ "name": "address",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's inbox address."
++ },
++ "description": "The agent's inbox address."
++ },
++ {
++ "name": "owner",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose switch it is."
++ },
++ "description": "The Network subject of the person whose switch it is."
++ },
++ {
++ "name": "enabled",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "boolean",
++ "description": "True when the agent answers the mail sent to this address. Off unless the person turned it on."
++ },
++ "description": "True when the agent answers the mail sent to this address. Off unless the person turned it on."
++ },
++ {
++ "name": "changed_at",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the person last turned it on or off; null when they never have (it has been off since the address was made)."
++ },
++ "description": "When the person last turned it on or off; null when they never have (it has been off since the address was made)."
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/mail.inbox-answering.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ },
++ "put": {
++ "operationId": "put_api_v1_agent_inboxes_id_answering",
++ "summary": "Read and set whether an agent answers the mail sent to its own address: GET /api/v1/agent-inboxes/:id/answering and PUT /api/v1/agent-inboxes/:id/answering, both mail.inbox-answering@1.",
++ "description": "**mail.agent.answering** (first-party): Read and set whether an agent answers the mail sent to its own address: GET /api/v1/agent-inboxes/:id/answering and PUT /api/v1/agent-inboxes/:id/answering, both mail.inbox-answering@1. It is OFF by default and per agent; turning it on is what makes Mail publish mail.agent_inbox.received for that address at all. The caller must act for the person (on_behalf_of), and another person's inbox is the same 404 as one that does not exist. Mail still publishes nothing for a sender on the person's block list or for a message the mail server marked as spam.",
++ "tags": [
++ "mail.agent"
++ ],
++ "x-openvibe-capabilities": [
++ "mail.agent.answering"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "mail-addresses-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "mail.agent.answering"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/mail.inbox-answering.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/mail.inbox-answering.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/api/v1/agent-inboxes/{id}/messages": {
"get": {
"operationId": "get_api_v1_agent_inboxes_id_messages",
@@@ -537,6 +719,136 @@
}
}
}
++ },
++ "/internal/mail/agents/{agent_id}/messages/{message_id}": {
++ "get": {
++ "operationId": "get_internal_mail_agents_agent_id_messages_message_id",
++ "summary": "Read one message sent to an agent's own address, in full plain text, as that agent acting for the person whose mailbox received it.",
++ "description": "**mail.agent.read** (first-party): Read one message sent to an agent's own address, in full plain text, as that agent acting for the person whose mailbox received it. GET /internal/mail/agents/:agent_id/messages/:message_id answers mail.agent-inbox-message@1: the sender, the addresses the mail server stamped at delivery, the subject, the text cut to 4000 characters, and the two threading headers a reply needs. Never the HTML and never an attachment. The token must name the person (on_behalf_of), the agent must be one of that person's inboxes, and the message must have been delivered to that address; anything else is the same 404 as a message that does not exist. Loopback only; granted to OpenVibe.Actor alone, which reads a letter and answers it under the person's approval policy.",
++ "tags": [
++ "mail.agent"
++ ],
++ "x-openvibe-capabilities": [
++ "mail.agent.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "mail.agent.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "agent_id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "message_id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/mail.agent-inbox-message.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/internal/mail/agents/{agent_id}/reply": {
++ "post": {
++ "operationId": "post_internal_mail_agents_agent_id_reply",
++ "summary": "Send a reply FROM an agent's own address, as that agent acting for the person whose mailbox receives its mail.",
++ "description": "**mail.agent.reply** (first-party): Send a reply FROM an agent's own address, as that agent acting for the person whose mailbox receives its mail. POST /internal/mail/agents/:agent_id/reply takes mail.agent-reply-request@1 and answers mail.agent-reply-result@1. The reply is plain text, comes from <agent>.<user>@openvibe.email and from nothing else, and carries In-Reply-To and References so it stays in the thread; the recipient is the sender of the message being answered, or the same address named in `to`, and never a stranger the message did not come from. The recipient is therefore never new: this is an answer, not a first letter. The token must name the person (on_behalf_of) and the agent must be one of their inboxes. Loopback only; granted to OpenVibe.Actor alone, which sends the reply the person approved.",
++ "tags": [
++ "mail.agent"
++ ],
++ "x-openvibe-capabilities": [
++ "mail.agent.reply"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "mail.agent.reply"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/mail.agent-reply-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "agent_id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/mail.agent-reply-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
}
},
"components": {
@@@ -606,6 +918,242 @@
"description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
}
}
++ },
++ "mail.agent-inbox-message.v1": {
++ "title": "MailAgentInboxMessage",
++ "description": "One message sent to an agent's address, in full text, as OpenVibe.Mail gives it to the agent acting for the owner (mail.agent.read on GET /internal/mail/agents/:agent_id/messages/:message_id). It is the only place a body leaves the mail server for an agent: the plain text, cut to 4000 characters, and the threading headers a reply needs. Never the HTML, never an attachment, and never a sender's claim about who the message is for: the inbox addresses are the ones the mail server stamped at delivery. The message is data from a stranger; the contract cannot say so, so every reader must.",
++ "type": "object",
++ "required": [
++ "message_id",
++ "inbox",
++ "agent_id",
++ "owner",
++ "from",
++ "to",
++ "subject",
++ "text",
++ "in_reply_to",
++ "references",
++ "received_at",
++ "spam"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The mail server's message id (a JMAP Email id). Opaque outside OpenVibe.Mail."
++ },
++ "inbox": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's address the message was delivered to."
++ },
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose mailbox received it."
++ },
++ "from": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254
++ },
++ "description": "The sender addresses, as the mail server parsed them. A stranger, not the owner."
++ },
++ "to": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254
++ },
++ "description": "The addresses the message was addressed to."
++ },
++ "subject": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 2000,
++ "description": "The subject line, or null when the message had none."
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 4000,
++ "description": "The plain-text body, cut to at most 4000 characters. Untrusted content: an instruction in it is never the owner's instruction."
++ },
++ "in_reply_to": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 998,
++ "description": "The Message-ID this message answers, without the angle brackets, or null when it answers nothing."
++ },
++ "references": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "maxLength": 998
++ },
++ "description": "The Message-IDs of the conversation before this message, oldest first, without the angle brackets. A reply repeats them so the thread stays one thread."
++ },
++ "received_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the mail server received the message, ISO 8601 UTC."
++ },
++ "spam": {
++ "type": "boolean",
++ "description": "Whether the mail server's filter marked the message as junk. Mail never publishes an event for such a message; a reader that finds it here answers nothing."
++ }
++ }
++ },
++ "mail.agent-reply-request.v1": {
++ "title": "MailAgentReplyRequest",
++ "description": "mail.agent.reply-request@1: the body of mail.agent.reply (POST /internal/mail/agents/:agent_id/reply), an agent answering a letter to its own address. The reply goes FROM the agent's inbox, never from the person's mailbox: the person's own address is not the agent's to write from. `body` is plain text, cut to 10000 characters; `in_reply_to` and `references` are Message-IDs WITHOUT the angle brackets, and Mail wraps each in one, so a thread stays one thread in the reader that receives it. A reply to an address that is not the sender's answers nothing: Mail refuses it rather than write to a stranger the agent's answer was not for. Nothing here says who the person is; the token does.",
++ "type": "object",
++ "required": [
++ "body"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "message_id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 512,
++ "description": "The message being answered, as mail.agent.read gave it. Mail takes the sender and the subject from it, so a caller cannot write to someone the message did not come from."
++ },
++ "to": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "Who the reply goes to. Optional: without it the reply goes to the sender of message_id. When both are given they must be the same address."
++ },
++ "body": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 10000,
++ "description": "The reply, as plain text. Mail builds the message from it and sends no HTML."
++ },
++ "in_reply_to": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 998,
++ "description": "The Message-ID this reply answers, without angle brackets. Mail wraps it: In-Reply-To: <…>."
++ },
++ "references": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "string",
++ "maxLength": 998
++ },
++ "description": "The Message-IDs of the conversation, oldest first, without angle brackets. Mail wraps each: References: <…> <…>."
++ }
++ }
++ },
++ "mail.agent-reply-result.v1": {
++ "title": "MailAgentReplyResult",
++ "description": "mail.agent.reply-result@1: what mail.agent.reply answers once the mail server took the message. `sent` is false when the mail server refused it (the answer's `reason` says why in plain words) and true when it accepted it — a relay's later bounce is not knowable here and is not claimed. `message_id` is the mail server's id for the sent message when it answers with one; `rfc_message_id` is the Message-ID header the reply carries, which a later letter in the thread will name in its In-Reply-To.",
++ "type": "object",
++ "required": [
++ "sent"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "sent": {
++ "type": "boolean",
++ "description": "True when the mail server accepted the reply, false when it did not."
++ },
++ "message_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 512,
++ "description": "The mail server's id for the sent message."
++ },
++ "rfc_message_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 998,
++ "description": "The Message-ID header of the reply, without angle brackets."
++ },
++ "reason": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 300,
++ "description": "Why the reply was not sent, in plain words. Null when sent is true."
++ }
++ }
++ },
++ "mail.inbox-answering.v1": {
++ "title": "MailInboxAnswering",
++ "description": "mail.inbox-answering@1: whether a person's agent answers the mail sent to its address, as OpenVibe.Mail keeps the switch. It is OFF by default and per agent: an address that exists is not an invitation to answer, and a person turns it on for the agent they trust to. `enabled` is the whole answer; `changed_at` says when the person last set it, so a page can say how long it has been on. Turning it off stops the next message from being published to the agent's system and takes its address out of the answering path; it never deletes what already happened.",
++ "type": "object",
++ "required": [
++ "agent_id",
++ "address",
++ "owner",
++ "enabled",
++ "changed_at"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "agent_id": {
++ "type": "string",
++ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$",
++ "maxLength": 32,
++ "description": "The agent's name in the address."
++ },
++ "address": {
++ "type": "string",
++ "format": "email",
++ "maxLength": 254,
++ "description": "The agent's inbox address."
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The Network subject of the person whose switch it is."
++ },
++ "enabled": {
++ "type": "boolean",
++ "description": "True when the agent answers the mail sent to this address. Off unless the person turned it on."
++ },
++ "changed_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the person last turned it on or off; null when they never have (it has been off since the address was made)."
++ }
++ }
}
},
"securitySchemes": {
ddiff --git a/generated/openapi/media-hub.json b/generated/openapi/media-hub.json
iindex b29baf34..51a712d4 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.341.0",
-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 c275909b..7c808e0a 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.341.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 5b2209d9..1dfd4dec 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.341.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 75b34160..ab2173f4 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.341.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 99937ff2..11b9a45c 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.341.0",
-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 889ccda1..c9a08fcb 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.341.0",
-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 d28002b6..5f07414c 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.341.0",
-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 61792921..ac3dd1eb 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.341.0",
-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 2dc24528..1244ad2b 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.341.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 3eb18df1..a02cbf1a 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.341.0",
-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 de723f9d..81dec099 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.341.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 cc2bae95..2b364333 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.341.0",
-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 0664246f..172c85ea 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.341.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 bbed16f2..be5bea1e 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.341.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 ecc5d714..a3e08e98 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.341.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 83b387e4..5f6e73a4 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.341.0",
-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 dee45949..2fdff7cf 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.341.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 13142785..97998b0b 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.341.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 8b30dc51..30444069 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.341.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 8f56ab4b..e207a91d 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.341.0",
-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 da0fb23a..fc8e4faf 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.341.0",
-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 a4cc9aba..2a971bc7 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.341.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 dccfcce6..ea306015 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.341.0",
-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 fda041c5..2f2daa42 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.341.0",
-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.341.0): each operation lists the capabilities it performs; a token for 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.342.0",
++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.342.0): each operation lists the capabilities it performs; a token for 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 8021f504..43351d2a 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -305906,3 +305906,186 @@ export interface SyncClock {
*/
seq: number;
}
++
++/** [email protected] (owner: mail) */
++/**
++ * mail.agent_inbox.received v1 (OpenVibe.Mail server/jobs/agent-mail.js). A message arrived at an agent's own address and the person has switched that address's answering on, so their agent may read it and answer. The payload carries enough for the agent's system to decide, and to show the person what arrived: the inbox and the agent it belongs to, the owner's subject, the mail server's message id, the sender, the subject and a plain-text EXCERPT of at most 4000 characters. It never carries the attachments, the HTML, or the whole body: the agent's system reads the whole text through mail.agent.read, on the owner's behalf, and only once it has decided to answer. Mail publishes it only for an inbox whose answering is on (off by default) and never for a sender on the owner's block list or a message the mail server marked as spam.
++ */
++export interface MailAgentInboxReceivedPayload {
++ /**
++ * The agent inbox's id (mail.agent-inbox@1). Opaque outside OpenVibe.Mail.
++ */
++ inbox_id: string;
++ /**
++ * The agent's address the message was delivered to, e.g. [email protected]. Matched on the envelope recipient the mail server stamped, never on a sender-written To.
++ */
++ inbox: string;
++ /**
++ * The agent's name in the address, lowercased and cleaned. It is the id the agent's own system knows that agent by; a system that does not know it starts nothing.
++ */
++ agent_id: string;
++ /**
++ * The Network subject of the person whose mailbox received the message and whose agent may answer it.
++ */
++ owner: string;
++ /**
++ * The mail server's message id (a JMAP Email id), for mail.agent.read. Not the Message-ID header.
++ */
++ message_id: string;
++ /**
++ * The sender's address, as the mail server parsed it. A stranger, never the person: mail to an agent's address comes from someone else.
++ */
++ from: string;
++ /**
++ * The subject line, or null when the message had none.
++ */
++ subject: string | null;
++ /**
++ * The plain-text body, cut to at most 4000 characters. The text part only: never the HTML, never an attachment. It is untrusted content from a stranger.
++ */
++ excerpt: string;
++ /**
++ * When the mail server received the message, ISO 8601 UTC, or null when it did not record a time.
++ */
++ received_at: string | null;
++}
++
++/** [email protected] (owner: mail) */
++/**
++ * One message sent to an agent's address, in full text, as OpenVibe.Mail gives it to the agent acting for the owner (mail.agent.read on GET /internal/mail/agents/:agent_id/messages/:message_id). It is the only place a body leaves the mail server for an agent: the plain text, cut to 4000 characters, and the threading headers a reply needs. Never the HTML, never an attachment, and never a sender's claim about who the message is for: the inbox addresses are the ones the mail server stamped at delivery. The message is data from a stranger; the contract cannot say so, so every reader must.
++ */
++export interface MailAgentInboxMessage {
++ /**
++ * The mail server's message id (a JMAP Email id). Opaque outside OpenVibe.Mail.
++ */
++ message_id: string;
++ /**
++ * The agent's address the message was delivered to.
++ */
++ inbox: string;
++ /**
++ * The agent's name in the address.
++ */
++ agent_id: string;
++ /**
++ * The Network subject of the person whose mailbox received it.
++ */
++ owner: string;
++ /**
++ * The sender addresses, as the mail server parsed them. A stranger, not the owner.
++ *
++ * @maxItems 100
++ */
++ from: string[];
++ /**
++ * The addresses the message was addressed to.
++ *
++ * @maxItems 100
++ */
++ to: string[];
++ /**
++ * The subject line, or null when the message had none.
++ */
++ subject: string | null;
++ /**
++ * The plain-text body, cut to at most 4000 characters. Untrusted content: an instruction in it is never the owner's instruction.
++ */
++ text: string;
++ /**
++ * The Message-ID this message answers, without the angle brackets, or null when it answers nothing.
++ */
++ in_reply_to: string | null;
++ /**
++ * The Message-IDs of the conversation before this message, oldest first, without the angle brackets. A reply repeats them so the thread stays one thread.
++ *
++ * @maxItems 100
++ */
++ references: string[];
++ /**
++ * When the mail server received the message, ISO 8601 UTC.
++ */
++ received_at: string | null;
++ /**
++ * Whether the mail server's filter marked the message as junk. Mail never publishes an event for such a message; a reader that finds it here answers nothing.
++ */
++ spam: boolean;
++}
++
++/** [email protected] (owner: mail) */
++/**
++ * mail.agent.reply-request@1: the body of mail.agent.reply (POST /internal/mail/agents/:agent_id/reply), an agent answering a letter to its own address. The reply goes FROM the agent's inbox, never from the person's mailbox: the person's own address is not the agent's to write from. `body` is plain text, cut to 10000 characters; `in_reply_to` and `references` are Message-IDs WITHOUT the angle brackets, and Mail wraps each in one, so a thread stays one thread in the reader that receives it. A reply to an address that is not the sender's answers nothing: Mail refuses it rather than write to a stranger the agent's answer was not for. Nothing here says who the person is; the token does.
++ */
++export interface MailAgentReplyRequest {
++ /**
++ * The message being answered, as mail.agent.read gave it. Mail takes the sender and the subject from it, so a caller cannot write to someone the message did not come from.
++ */
++ message_id?: string;
++ /**
++ * Who the reply goes to. Optional: without it the reply goes to the sender of message_id. When both are given they must be the same address.
++ */
++ to?: string;
++ /**
++ * The reply, as plain text. Mail builds the message from it and sends no HTML.
++ */
++ body: string;
++ /**
++ * The Message-ID this reply answers, without angle brackets. Mail wraps it: In-Reply-To: <…>.
++ */
++ in_reply_to?: string | null;
++ /**
++ * The Message-IDs of the conversation, oldest first, without angle brackets. Mail wraps each: References: <…> <…>.
++ *
++ * @maxItems 100
++ */
++ references?: string[];
++}
++
++/** [email protected] (owner: mail) */
++/**
++ * mail.agent.reply-result@1: what mail.agent.reply answers once the mail server took the message. `sent` is false when the mail server refused it (the answer's `reason` says why in plain words) and true when it accepted it — a relay's later bounce is not knowable here and is not claimed. `message_id` is the mail server's id for the sent message when it answers with one; `rfc_message_id` is the Message-ID header the reply carries, which a later letter in the thread will name in its In-Reply-To.
++ */
++export interface MailAgentReplyResult {
++ /**
++ * True when the mail server accepted the reply, false when it did not.
++ */
++ sent: boolean;
++ /**
++ * The mail server's id for the sent message.
++ */
++ message_id?: string | null;
++ /**
++ * The Message-ID header of the reply, without angle brackets.
++ */
++ rfc_message_id?: string | null;
++ /**
++ * Why the reply was not sent, in plain words. Null when sent is true.
++ */
++ reason?: string | null;
++}
++
++/** [email protected] (owner: mail) */
++/**
++ * mail.inbox-answering@1: whether a person's agent answers the mail sent to its address, as OpenVibe.Mail keeps the switch. It is OFF by default and per agent: an address that exists is not an invitation to answer, and a person turns it on for the agent they trust to. `enabled` is the whole answer; `changed_at` says when the person last set it, so a page can say how long it has been on. Turning it off stops the next message from being published to the agent's system and takes its address out of the answering path; it never deletes what already happened.
++ */
++export interface MailInboxAnswering {
++ /**
++ * The agent's name in the address.
++ */
++ agent_id: string;
++ /**
++ * The agent's inbox address.
++ */
++ address: string;
++ /**
++ * The Network subject of the person whose switch it is.
++ */
++ owner: string;
++ /**
++ * True when the agent answers the mail sent to this address. Off unless the person turned it on.
++ */
++ enabled: boolean;
++ /**
++ * When the person last turned it on or off; null when they never have (it has been off since the address was made).
++ */
++ changed_at: string | null;
++}
ddiff --git a/manifests/capabilities/mail.agent.answering.json b/manifests/capabilities/mail.agent.answering.json
nnew file mode 100644
iindex 00000000..c3bec40e
---- /dev/null
++++ b/manifests/capabilities/mail.agent.answering.json
@@@ -0,0 +1,23 @@
++{
++ "id": "mail.agent.answering",
++ "version": "1.0",
++ "owner": "mail",
++ "status": "active",
++ "visibility": "first-party",
++ "sensitive": true,
++ "description": "Read and set whether an agent answers the mail sent to its own address: GET /api/v1/agent-inboxes/:id/answering and PUT /api/v1/agent-inboxes/:id/answering, both mail.inbox-answering@1. It is OFF by default and per agent; turning it on is what makes Mail publish mail.agent_inbox.received for that address at all. The caller must act for the person (on_behalf_of), and another person's inbox is the same 404 as one that does not exist. Mail still publishes nothing for a sender on the person's block list or for a message the mail server marked as spam.",
++ "permissions": [
++ "mail:addresses:write"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "mail-addresses-write",
++ "events": [],
++ "implementedBy": [
++ "GET /api/v1/agent-inboxes/:id/answering",
++ "PUT /api/v1/agent-inboxes/:id/answering"
++ ],
++ "inputSchema": "mail.inbox-answering@1",
++ "outputSchema": "mail.inbox-answering@1"
++}
ddiff --git a/manifests/capabilities/mail.agent.read.json b/manifests/capabilities/mail.agent.read.json
nnew file mode 100644
iindex 00000000..65c37b5e
---- /dev/null
++++ b/manifests/capabilities/mail.agent.read.json
@@@ -0,0 +1,24 @@
++{
++ "id": "mail.agent.read",
++ "version": "1.0",
++ "owner": "mail",
++ "status": "active",
++ "visibility": "first-party",
++ "sensitive": true,
++ "description": "Read one message sent to an agent's own address, in full plain text, as that agent acting for the person whose mailbox received it. GET /internal/mail/agents/:agent_id/messages/:message_id answers mail.agent-inbox-message@1: the sender, the addresses the mail server stamped at delivery, the subject, the text cut to 4000 characters, and the two threading headers a reply needs. Never the HTML and never an attachment. The token must name the person (on_behalf_of), the agent must be one of that person's inboxes, and the message must have been delivered to that address; anything else is the same 404 as a message that does not exist. Loopback only; granted to OpenVibe.Actor alone, which reads a letter and answers it under the person's approval policy.",
++ "permissions": [
++ "mail:messages:read"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "internal-read",
++ "events": [
++ "mail.agent_inbox.received"
++ ],
++ "implementedBy": [
++ "GET /internal/mail/agents/:agent_id/messages/:message_id"
++ ],
++ "inputSchema": "common.no-body@1",
++ "outputSchema": "mail.agent-inbox-message@1"
++}
ddiff --git a/manifests/capabilities/mail.agent.reply.json b/manifests/capabilities/mail.agent.reply.json
nnew file mode 100644
iindex 00000000..6dbf1a09
---- /dev/null
++++ b/manifests/capabilities/mail.agent.reply.json
@@@ -0,0 +1,22 @@
++{
++ "id": "mail.agent.reply",
++ "version": "1.0",
++ "owner": "mail",
++ "status": "active",
++ "visibility": "first-party",
++ "sensitive": true,
++ "description": "Send a reply FROM an agent's own address, as that agent acting for the person whose mailbox receives its mail. POST /internal/mail/agents/:agent_id/reply takes mail.agent-reply-request@1 and answers mail.agent-reply-result@1. The reply is plain text, comes from <agent>.<user>@openvibe.email and from nothing else, and carries In-Reply-To and References so it stays in the thread; the recipient is the sender of the message being answered, or the same address named in `to`, and never a stranger the message did not come from. The recipient is therefore never new: this is an answer, not a first letter. The token must name the person (on_behalf_of) and the agent must be one of their inboxes. Loopback only; granted to OpenVibe.Actor alone, which sends the reply the person approved.",
++ "permissions": [
++ "mail:messages:send"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "internal-write",
++ "events": [],
++ "implementedBy": [
++ "POST /internal/mail/agents/:agent_id/reply"
++ ],
++ "inputSchema": "mail.agent-reply-request@1",
++ "outputSchema": "mail.agent-reply-result@1"
++}
ddiff --git a/manifests/services/mail.json b/manifests/services/mail.json
iindex 8173e49f..85876d77 100644
---- a/manifests/services/mail.json
++++ b/manifests/services/mail.json
@@@ -18,9 +18,14 @@
},
"capabilities": [
"mail.addresses.write",
-- "mail.messages.read"
++ "mail.messages.read",
++ "mail.agent.read",
++ "mail.agent.reply",
++ "mail.agent.answering"
++ ],
++ "eventsProduced": [
++ "mail.agent_inbox.received"
],
-- "eventsProduced": [],
"eventsConsumed": [],
"namespacesOwned": [],
"lifecycle": {
ddiff --git a/package-lock.json b/package-lock.json
iindex 17313599..a8a2f23d 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.341.0",
++ "version": "0.336.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.341.0",
++ "version": "0.336.0",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 49983911..9728984f 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.341.0",
++ "version": "0.342.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",