openvibers/openvibe.contracts Commits

chat.vibe.deliver: Actor posts each finished run of a person's OpenVibe Agent into their own Vibe chat named after it (each run is a chat they can open); chat.vibe-deliver-request/result@1; agents gain send_to_vibe (0.214.0) (#218)

OP OpenVibers committed 19 days ago · 964095f5ef · 18 files, +522 −7

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex a8739342..065f972a 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -6,6 +6,13 @@ the notes were in the tag and commit messages (`git tag -n1`).
## 0.214.0 — 2026-10-26
++- **`chat.vibe.deliver`** (first-party, sensitive, owned by `chat`; consumer plan §4: "each run is a chat you can
++ open"): a first-party service puts a result of the person's own work into their Vibe chats (`POST
++ /internal/chat/vibe/deliver`). OpenVibe.Actor posts each finished run of one of their OpenVibe Agents into one chat per
++ agent, named after it, as Vibe's message. No AI answer is made and nothing counts against the allowance. Schemas
++ `chat.vibe-deliver-request@1` (subject, source { kind agent, id, name }, text in Markdown, url, idempotency_key) and
++ `chat.vibe-deliver-result@1` (chat, url, repeated), with fixtures. `actor.agent-manage-request@1` and `-result@1`
++ gain `send_to_vibe` (boolean, default true), the agent's switch for it.
- **`actor.agent.manage`** lists the suggestions routes (`GET /api/v1/me/agents/:id/suggestions`, `POST
…/suggestions/:pid/:action` with apply | dismiss | undo): a personal agent may suggest a line for its instructions after a
run that passed its check, which waits for the person (OpenVibe.Actor#115).
ddiff --git a/contracts/actor/agent-manage-request.v1.json b/contracts/actor/agent-manage-request.v1.json
iindex 0ab5a9f3..417a55db 100644
---- a/contracts/actor/agent-manage-request.v1.json
++++ b/contracts/actor/agent-manage-request.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/actor/agent-manage-request.v1.json",
"title": "ActorAgentManageRequest",
-- "description": "actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.",
++ "description": "actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, send_to_vibe?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.",
"anyOf": [
{
"type": "object",
@@@ -78,6 +78,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
ddiff --git a/contracts/actor/agent-manage-result.v1.json b/contracts/actor/agent-manage-result.v1.json
iindex efd6146b..61fc9ac2 100644
---- a/contracts/actor/agent-manage-result.v1.json
++++ b/contracts/actor/agent-manage-result.v1.json
@@@ -100,6 +100,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
@@@ -267,6 +271,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex f579f7c1..f2a0da6c 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -5096,6 +5096,24 @@
"compatibility": "backward",
"status": "active"
},
++ {
++ "id": "chat.vibe-deliver-request",
++ "version": "1.0.0",
++ "owner": "chat",
++ "schema": "chat/vibe-deliver-request.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "chat.vibe-deliver-result",
++ "version": "1.0.0",
++ "owner": "chat",
++ "schema": "chat/vibe-deliver-result.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
{
"id": "live.moderation.action",
"version": "1.0.0",
ddiff --git a/contracts/chat/vibe-deliver-request.v1.json b/contracts/chat/vibe-deliver-request.v1.json
nnew file mode 100644
iindex 00000000..a0a9dfd3
---- /dev/null
++++ b/contracts/chat/vibe-deliver-request.v1.json
@@@ -0,0 +1,63 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/vibe-deliver-request.v1.json",
++ "title": "ChatVibeDeliverRequest",
++ "description": "chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.",
++ "type": "object",
++ "required": [
++ "subject",
++ "source",
++ "text",
++ "idempotency_key"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "subject": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The person whose work it is, as the calling service vouches (Actor: the agent's owner)."
++ },
++ "source": {
++ "type": "object",
++ "required": [
++ "kind",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "enum": [
++ "agent"
++ ]
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
++ "description": "The source's stable id (the agent's agt_ id): one chat per source."
++ },
++ "name": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "What to call it in the chat's name (the agent's name)."
++ }
++ }
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 8000,
++ "description": "The result, in Markdown (as Vibe's answers are shown)."
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https://",
++ "description": "Where the whole run is (its steps, sources and cost)."
++ },
++ "idempotency_key": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
++ "description": "The caller's id for this result (Actor: the run's task id): sent again, it is posted once."
++ }
++ }
++}
ddiff --git a/contracts/chat/vibe-deliver-result.v1.json b/contracts/chat/vibe-deliver-result.v1.json
nnew file mode 100644
iindex 00000000..966e578c
---- /dev/null
++++ b/contracts/chat/vibe-deliver-result.v1.json
@@@ -0,0 +1,28 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/vibe-deliver-result.v1.json",
++ "title": "ChatVibeDeliverResult",
++ "description": "chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).",
++ "type": "object",
++ "required": [
++ "chat",
++ "url",
++ "repeated"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "chat": {
++ "type": "string",
++ "pattern": "^v-[0-9a-f]{12}$",
++ "description": "The Vibe chat's slug."
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https://"
++ },
++ "repeated": {
++ "type": "boolean"
++ }
++ }
++}
ddiff --git a/fixtures/chat.vibe-deliver-request/invalid/no-key.json b/fixtures/chat.vibe-deliver-request/invalid/no-key.json
nnew file mode 100644
iindex 00000000..64dada54
---- /dev/null
++++ b/fixtures/chat.vibe-deliver-request/invalid/no-key.json
@@@ -0,0 +1,8 @@
++{
++ "subject": "usr_01JABCDEFGHJKMNPQRSTVWXYZ0",
++ "source": {
++ "kind": "agent",
++ "id": "agt_01JABCDEFGHJKMNPQRSTVWXYZ1"
++ },
++ "text": "Done."
++}
ddiff --git a/fixtures/chat.vibe-deliver-request/invalid/voice-source.json b/fixtures/chat.vibe-deliver-request/invalid/voice-source.json
nnew file mode 100644
iindex 00000000..1a2c5374
---- /dev/null
++++ b/fixtures/chat.vibe-deliver-request/invalid/voice-source.json
@@@ -0,0 +1,9 @@
++{
++ "subject": "usr_01JABCDEFGHJKMNPQRSTVWXYZ0",
++ "source": {
++ "kind": "voice",
++ "id": "dev_1"
++ },
++ "text": "Done.",
++ "idempotency_key": "k1"
++}
ddiff --git a/fixtures/chat.vibe-deliver-request/valid/agent-run.json b/fixtures/chat.vibe-deliver-request/valid/agent-run.json
nnew file mode 100644
iindex 00000000..3b22ec39
---- /dev/null
++++ b/fixtures/chat.vibe-deliver-request/valid/agent-run.json
@@@ -0,0 +1,11 @@
++{
++ "subject": "usr_01JABCDEFGHJKMNPQRSTVWXYZ0",
++ "source": {
++ "kind": "agent",
++ "id": "agt_01JABCDEFGHJKMNPQRSTVWXYZ1",
++ "name": "Remote jobs"
++ },
++ "text": "**3 new remote video editing jobs** today:\n\n- [Editor at Example](https://example.com/jobs/1)",
++ "url": "https://openvibe.actor/tasks/tsk_01JABCDEFGHJKMNPQRSTVWXYZ2",
++ "idempotency_key": "agent-run-0001"
++}
ddiff --git a/fixtures/chat.vibe-deliver-result/invalid/no-repeated.json b/fixtures/chat.vibe-deliver-result/invalid/no-repeated.json
nnew file mode 100644
iindex 00000000..dd227435
---- /dev/null
++++ b/fixtures/chat.vibe-deliver-result/invalid/no-repeated.json
@@@ -0,0 +1,4 @@
++{
++ "chat": "v-0123456789ab",
++ "url": "https://openvibe.chat/vibe/v-0123456789ab"
++}
ddiff --git a/fixtures/chat.vibe-deliver-result/valid/posted.json b/fixtures/chat.vibe-deliver-result/valid/posted.json
nnew file mode 100644
iindex 00000000..7f091b5e
---- /dev/null
++++ b/fixtures/chat.vibe-deliver-result/valid/posted.json
@@@ -0,0 +1,5 @@
++{
++ "chat": "v-0123456789ab",
++ "url": "https://openvibe.chat/vibe/v-0123456789ab",
++ "repeated": false
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex e79f5599..3c2fda62 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -56475,6 +56475,107 @@
}
}
},
++ "chat.vibe-deliver-request": {
++ "version": "1.0.0",
++ "owner": "chat",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/vibe-deliver-request.v1.json",
++ "title": "ChatVibeDeliverRequest",
++ "description": "chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.",
++ "type": "object",
++ "required": [
++ "subject",
++ "source",
++ "text",
++ "idempotency_key"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "subject": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The person whose work it is, as the calling service vouches (Actor: the agent's owner)."
++ },
++ "source": {
++ "type": "object",
++ "required": [
++ "kind",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "enum": [
++ "agent"
++ ]
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
++ "description": "The source's stable id (the agent's agt_ id): one chat per source."
++ },
++ "name": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "What to call it in the chat's name (the agent's name)."
++ }
++ }
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 8000,
++ "description": "The result, in Markdown (as Vibe's answers are shown)."
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https://",
++ "description": "Where the whole run is (its steps, sources and cost)."
++ },
++ "idempotency_key": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
++ "description": "The caller's id for this result (Actor: the run's task id): sent again, it is posted once."
++ }
++ }
++ }
++ },
++ "chat.vibe-deliver-result": {
++ "version": "1.0.0",
++ "owner": "chat",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/vibe-deliver-result.v1.json",
++ "title": "ChatVibeDeliverResult",
++ "description": "chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).",
++ "type": "object",
++ "required": [
++ "chat",
++ "url",
++ "repeated"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "chat": {
++ "type": "string",
++ "pattern": "^v-[0-9a-f]{12}$",
++ "description": "The Vibe chat's slug."
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https://"
++ },
++ "repeated": {
++ "type": "boolean"
++ }
++ }
++ }
++ },
"live.moderation.action": {
"version": "1.0.0",
"owner": "live",
@@@ -76945,7 +77046,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/actor/agent-manage-request.v1.json",
"title": "ActorAgentManageRequest",
-- "description": "actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.",
++ "description": "actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, send_to_vibe?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.",
"anyOf": [
{
"type": "object",
@@@ -77021,6 +77122,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
@@@ -77152,6 +77257,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
@@@ -77319,6 +77428,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex 13d1a35d..596dcf78 100644
---- a/generated/openapi/actor.json
++++ b/generated/openapi/actor.json
@@@ -1547,7 +1547,7 @@
},
"actor.agent-manage-request.v1": {
"title": "ActorAgentManageRequest",
-- "description": "actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.",
++ "description": "actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, send_to_vibe?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.",
"anyOf": [
{
"type": "object",
@@@ -1623,6 +1623,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
@@@ -1747,6 +1751,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
@@@ -1914,6 +1922,10 @@
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
"description": "HH:MM UTC: quiet hours end."
},
++ "send_to_vibe": {
++ "type": "boolean",
++ "description": "Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true."
++ },
"status": {
"enum": [
"active",
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex 3c32de3d..d2e9b59a 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -4139,6 +4139,62 @@
}
}
},
++ "/internal/chat/vibe/deliver": {
++ "post": {
++ "operationId": "post_internal_chat_vibe_deliver",
++ "summary": "Put a result of a person's own work into their Vibe chats: OpenVibe.Actor posts each finished run of one of their OpenVibe Agents (one chat per agent, named after it) as Vibe's message, so each run is",
++ "description": "**chat.vibe.deliver** (first-party): Put a result of a person's own work into their Vibe chats: OpenVibe.Actor posts each finished run of one of their OpenVibe Agents (one chat per agent, named after it) as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. Loopback only, first-party service token; never granted to an app or delegated to an agent.",
++ "tags": [
++ "chat.vibe"
++ ],
++ "x-openvibe-capabilities": [
++ "chat.vibe.deliver"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "chat.vibe.deliver"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.vibe-deliver-request.v1"
++ }
++ }
++ }
++ },
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.vibe-deliver-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/internal/live/calls": {
"post": {
"operationId": "post_internal_live_calls",
@@@ -8303,6 +8359,93 @@
}
}
},
++ "chat.vibe-deliver-request.v1": {
++ "title": "ChatVibeDeliverRequest",
++ "description": "chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.",
++ "type": "object",
++ "required": [
++ "subject",
++ "source",
++ "text",
++ "idempotency_key"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "subject": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The person whose work it is, as the calling service vouches (Actor: the agent's owner)."
++ },
++ "source": {
++ "type": "object",
++ "required": [
++ "kind",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "enum": [
++ "agent"
++ ]
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
++ "description": "The source's stable id (the agent's agt_ id): one chat per source."
++ },
++ "name": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "What to call it in the chat's name (the agent's name)."
++ }
++ }
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 8000,
++ "description": "The result, in Markdown (as Vibe's answers are shown)."
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https://",
++ "description": "Where the whole run is (its steps, sources and cost)."
++ },
++ "idempotency_key": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
++ "description": "The caller's id for this result (Actor: the run's task id): sent again, it is posted once."
++ }
++ }
++ },
++ "chat.vibe-deliver-result.v1": {
++ "title": "ChatVibeDeliverResult",
++ "description": "chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).",
++ "type": "object",
++ "required": [
++ "chat",
++ "url",
++ "repeated"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "chat": {
++ "type": "string",
++ "pattern": "^v-[0-9a-f]{12}$",
++ "description": "The Vibe chat's slug."
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https://"
++ },
++ "repeated": {
++ "type": "boolean"
++ }
++ }
++ },
"common.owned-resource-control-request.v1": {
"title": "OwnedResourceControlRequest",
"description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",
ddiff --git a/generated/openapi/index.json b/generated/openapi/index.json
iindex 5194cd65..8c1b40e5 100644
---- a/generated/openapi/index.json
++++ b/generated/openapi/index.json
@@@ -40,8 +40,8 @@
"service": "chat",
"name": "OpenVibe.Chat",
"origin": "https://openvibe.chat",
-- "operations": 53,
-- "capabilities": 18
++ "operations": 54,
++ "capabilities": 19
},
{
"service": "codes",
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 8e1ab5e2..406511f5 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -191023,6 +191023,53 @@ export interface ChatVibeAskResult {
code?: string;
}
++/** [email protected] (owner: chat) */
++/**
++ * chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.
++ */
++export interface ChatVibeDeliverRequest {
++ /**
++ * The person whose work it is, as the calling service vouches (Actor: the agent's owner).
++ */
++ subject: string;
++ source: {
++ kind: "agent";
++ /**
++ * The source's stable id (the agent's agt_ id): one chat per source.
++ */
++ id: string;
++ /**
++ * What to call it in the chat's name (the agent's name).
++ */
++ name?: string;
++ };
++ /**
++ * The result, in Markdown (as Vibe's answers are shown).
++ */
++ text: string;
++ /**
++ * Where the whole run is (its steps, sources and cost).
++ */
++ url?: string;
++ /**
++ * The caller's id for this result (Actor: the run's task id): sent again, it is posted once.
++ */
++ idempotency_key: string;
++}
++
++/** [email protected] (owner: chat) */
++/**
++ * chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).
++ */
++export interface ChatVibeDeliverResult {
++ /**
++ * The Vibe chat's slug.
++ */
++ chat: string;
++ url: string;
++ repeated: boolean;
++}
++
/** [email protected] (owner: live) */
/**
* live.moderation.action v1 (OpenVibe.Live server/db/database.js logModerationAction; ADR-022). A staff or channel moderator action taken on OpenVibe.Live outside chat: site and global bans, IP bans, message deletes and purges from Live's admin panel, a stream force-ended, relay users hidden, channel moderators added or removed. Not a person tidying their own messages or configuring their own channel. Written to Live's outbox in the transaction that records the action. OpenVibe.Network keeps it in the moderation audit log. Envelope: subject { type: moderation_action, id: <action_id> }, visibility internal, actor the acting person when known. details is free-form per action (never secrets or message text beyond what moderators saw).
@@@ -200433,7 +200480,7 @@ export interface ActorTaskCreateRequest {
/** [email protected] (owner: actor) */
/**
-- * actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.
++ * actor.agent-manage-request@1: bodies of actor.agent.manage on OpenVibe.Actor. POST /api/v1/me/agents { name, instructions, mode?, budget_per_run_usd?, daily_runs_max?, schedule?, schedule_at?, schedule_dow?, quiet_from?, quiet_to?, send_to_vibe?, status? } creates a personal agent (10 per person: 429 actor.agent.limit); PATCH /api/v1/me/agents/:id takes any subset. POST /api/v1/me/agents/:id/memory { text } adds a note (1–1000 characters; 50 notes, the oldest agent-written note goes first, a person's note never automatically) and PATCH /api/v1/me/agents/:id/memory/:mem { text } edits one. Unknown fields are 422 actor.agent.invalid; another person's agent is 404 actor.agent.not_found.
*/
export type ActorAgentManageRequest =
| {
@@@ -200462,6 +200509,10 @@ export type ActorAgentManageRequest =
* HH:MM UTC: quiet hours end.
*/
quiet_to?: string | null;
++ /**
++ * Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true.
++ */
++ send_to_vibe?: boolean;
status?: "active" | "paused";
}
| {
@@@ -200502,6 +200553,10 @@ export type ActorAgentManageResult =
* HH:MM UTC: quiet hours end.
*/
quiet_to?: string | null;
++ /**
++ * Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true.
++ */
++ send_to_vibe?: boolean;
status: "active" | "paused";
next_run_at?: string | null;
last_run_at?: string | null;
@@@ -200548,6 +200603,10 @@ export type ActorAgentManageResult =
* HH:MM UTC: quiet hours end.
*/
quiet_to?: string | null;
++ /**
++ * Post each finished run into the person's own Vibe chat named after the agent (chat.vibe.deliver), so each run is a chat they can open and continue. Default true.
++ */
++ send_to_vibe?: boolean;
status: "active" | "paused";
next_run_at?: string | null;
last_run_at?: string | null;
ddiff --git a/manifests/capabilities/chat.vibe.deliver.json b/manifests/capabilities/chat.vibe.deliver.json
nnew file mode 100644
iindex 00000000..f8000b2e
---- /dev/null
++++ b/manifests/capabilities/chat.vibe.deliver.json
@@@ -0,0 +1,22 @@
++{
++ "id": "chat.vibe.deliver",
++ "version": "1.0",
++ "owner": "chat",
++ "status": "active",
++ "visibility": "first-party",
++ "sensitive": true,
++ "description": "Put a result of a person's own work into their Vibe chats: OpenVibe.Actor posts each finished run of one of their OpenVibe Agents (one chat per agent, named after it) as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. Loopback only, first-party service token; never granted to an app or delegated to an agent.",
++ "permissions": [
++ "chat:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "internal-write",
++ "events": [],
++ "implementedBy": [
++ "POST /internal/chat/vibe/deliver"
++ ],
++ "inputSchema": "chat.vibe-deliver-request@1",
++ "outputSchema": "chat.vibe-deliver-result@1"
++}
ddiff --git a/manifests/services/chat.json b/manifests/services/chat.json
iindex 5a444c32..af2d289d 100644
---- a/manifests/services/chat.json
++++ b/manifests/services/chat.json
@@@ -31,7 +31,8 @@
"chat.inbox.read",
"chat.inbox.reply",
"chat.inbox.manage",
-- "chat.vibe.ask"
++ "chat.vibe.ask",
++ "chat.vibe.deliver"
],
"eventsProduced": [
"chat.message.created",