openvibers/openvibe.contracts Commits
Capability schemas (WS-C task 4) for AI and Events: ai provider and model records, provider-manage request/result, usage result, definition-version request/result (templates, workflows, routes); events publish result, pull read request/result (pages, one event, checkpoints), subscription record/request/result, delivery-admin request/result. 11 capabilities complete; gaps 144 -> 133 (unreleased)
Changes
@@@ -1,33 +1,6 @@{"_about": "Active capabilities still without an input or output schema (roadmap WS-C task 4). test/capability-schemas.test.js fails on any active capability missing a schema that is not listed here, and on a listed one that has since gained it (remove it from this list). The list only shrinks: fill a capability's schemas, then delete its row. A request with no body names common.no-body@1; a response of bytes names common.binary@1.","gaps": [-- {-- "id": "ai.provider.manage",-- "missing": [-- "inputSchema",-- "outputSchema"-- ]-- },-- {-- "id": "ai.run.read",-- "missing": [-- "inputSchema"-- ]-- },-- {-- "id": "ai.usage.read",-- "missing": [-- "inputSchema",-- "outputSchema"-- ]-- },-- {-- "id": "ai.workflow.manage",-- "missing": [-- "inputSchema",-- "outputSchema"-- ]-- },{"id": "blog.blog.configure","missing": [@@@ -250,52 +223,6 @@"outputSchema"]},-- {-- "id": "events.app.publish",-- "missing": [-- "outputSchema"-- ]-- },-- {-- "id": "events.app.read",-- "missing": [-- "inputSchema",-- "outputSchema"-- ]-- },-- {-- "id": "events.app.subscribe",-- "missing": [-- "inputSchema",-- "outputSchema"-- ]-- },-- {-- "id": "events.delivery.admin",-- "missing": [-- "inputSchema",-- "outputSchema"-- ]-- },-- {-- "id": "events.event.publish",-- "missing": [-- "outputSchema"-- ]-- },-- {-- "id": "events.event.read",-- "missing": [-- "inputSchema"-- ]-- },-- {-- "id": "events.subscription.manage",-- "missing": [-- "inputSchema",-- "outputSchema"-- ]-- },{"id": "games.mod.manage","missing": [nnew file mode 100644@@@ -0,0 +1,216 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/definition-version-request.v1.json",++ "title": "AiDefinitionVersionRequest",++ "description": "ai.definition-version-request@1: bodies of ai.workflow.manage on OpenVibe.AI. An edit is always a new version (the previous one stays readable); fields left out are taken from the newest existing version. POST /api/v1/templates/:key/versions: a prompt template (input_schema and output_schema must compile as JSON Schema; visibility public, first-party or internal). POST /api/v1/workflows/:key/versions (key <namespace>.<name>): steps (at least one; kind llm (naming an existing template), passthrough, transcribe or embed), schemas, default route, cache mode. POST /api/v1/routes/:key/versions: primary { provider, model } and fallbacks (every provider must exist), or alias_of another route. POST …/:key/versions/:version/status: { status } (templates and workflows draft|active|deprecated|archived; routes active|disabled). Refusals are 422 ai.invalid.",++ "anyOf": [++ {++ "type": "object",++ "properties": {++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "input_schema": {++ "type": "object",++ "description": "A JSON Schema the run input must match."++ },++ "output_schema": {++ "type": "object",++ "description": "A JSON Schema the output must match."++ },++ "system_prompt": {++ "type": "string"++ },++ "user_prompt": {++ "type": "string"++ },++ "default_route": {++ "type": [++ "string",++ "null"++ ]++ },++ "owner": {++ "type": "string"++ },++ "visibility": {++ "enum": [++ "public",++ "first-party",++ "internal"++ ]++ },++ "status": {++ "enum": [++ "draft",++ "active",++ "deprecated",++ "archived"++ ]++ },++ "metadata": {++ "type": "object"++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "namespace": {++ "type": "string"++ },++ "input_schema": {++ "type": "object",++ "description": "A JSON Schema."++ },++ "output_schema": {++ "type": "object",++ "description": "A JSON Schema."++ },++ "steps": {++ "type": "array",++ "minItems": 1,++ "items": {++ "type": "object",++ "required": [++ "kind"++ ],++ "properties": {++ "kind": {++ "enum": [++ "llm",++ "passthrough",++ "transcribe",++ "embed"++ ]++ },++ "template": {++ "type": "string"++ }++ }++ }++ },++ "default_route": {++ "type": [++ "string",++ "null"++ ]++ },++ "cache_mode": {++ "enum": [++ "none",++ "private",++ "service"++ ]++ },++ "cache_ttl_sec": {++ "type": [++ "integer",++ "null"++ ]++ },++ "status": {++ "enum": [++ "draft",++ "active",++ "deprecated",++ "archived"++ ]++ },++ "metadata": {++ "type": "object"++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "primary": {++ "type": "object",++ "required": [++ "provider"++ ],++ "properties": {++ "provider": {++ "type": "string"++ },++ "model": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ "fallbacks": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "provider"++ ],++ "properties": {++ "provider": {++ "type": "string"++ },++ "model": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ }++ },++ "options": {++ "type": "object"++ },++ "max_output_tokens": {++ "type": [++ "integer",++ "null"++ ]++ },++ "response_format": {++ "enum": [++ "text",++ "json"++ ]++ },++ "timeout_ms": {++ "type": [++ "integer",++ "null"++ ]++ },++ "alias_of": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,92 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/definition-version-result.v1.json",++ "title": "AiDefinitionVersionResult",++ "description": "ai.definition-version-result@1: answers of ai.workflow.manage on OpenVibe.AI. POST …/:key/versions → 201 { template | workflow | route } (the new version, whole: prompts and schemas for a template, steps for a workflow, primary/fallbacks for a route); POST …/versions/:version/status → { template | workflow | route: { key, version, status } }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "template"++ ],++ "properties": {++ "template": {++ "type": "object",++ "required": [++ "key",++ "version",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ },++ "status": {++ "type": "string"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "workflow"++ ],++ "properties": {++ "workflow": {++ "type": "object",++ "required": [++ "key",++ "version",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ },++ "status": {++ "type": "string"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "route"++ ],++ "properties": {++ "route": {++ "type": "object",++ "required": [++ "key",++ "version",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ },++ "status": {++ "type": "string"++ }++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,111 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/model.v1.json",++ "title": "AiModel",++ "description": "ai.model@1: one model of a provider in OpenVibe.AI's registry (server/registry.js decodeModel): type, status, limits, per-million-token costs and what it supports.",++ "type": "object",++ "required": [++ "provider_key",++ "model_key",++ "type",++ "status",++ "cost",++ "supports"++ ],++ "properties": {++ "provider_key": {++ "type": "string"++ },++ "model_key": {++ "type": "string",++ "maxLength": 200++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "type": {++ "enum": [++ "chat",++ "vision",++ "embedding",++ "stt"++ ]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]++ },++ "context_window": {++ "type": [++ "integer",++ "null"++ ]++ },++ "max_output": {++ "type": [++ "integer",++ "null"++ ]++ },++ "cost": {++ "type": "object",++ "properties": {++ "in_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "out_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "cached_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ }++ }++ },++ "supports": {++ "type": "object",++ "properties": {++ "json": {++ "type": "boolean"++ },++ "tools": {++ "type": "boolean"++ },++ "streaming": {++ "type": "boolean"++ },++ "vision": {++ "type": "boolean"++ }++ }++ },++ "metadata": {++ "type": "object"++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ]++ }++ }++}nnew file mode 100644@@@ -0,0 +1,262 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/provider-manage-request.v1.json",++ "title": "AiProviderManageRequest",++ "description": "ai.provider-manage-request@1: bodies of ai.provider.manage on OpenVibe.AI. POST /api/v1/providers creates or replaces a provider (key required) and PATCH /api/v1/providers/:key changes one (fields left out keep their value); a body carrying api_key or secret is refused (422: send secret_ref env:NAME, never a secret value). POST /api/v1/models (provider_key and model_key required) and PATCH /api/v1/models/:provider/:model upsert a model. POST /api/v1/quotas upserts a quota (scope_type and window required; scope_id too unless global; the same scope, window and workflow_prefix is one quota). POST /api/v1/providers/:key/disable|enable|reset and DELETE /api/v1/cache[?workflow=] take no body. Unknown fields are ignored.",++ "anyOf": [++ {++ "type": "object",++ "not": {++ "anyOf": [++ {++ "required": [++ "api_key"++ ]++ },++ {++ "required": [++ "secret"++ ]++ }++ ]++ },++ "properties": {++ "key": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"++ },++ "display_name": {++ "type": "string"++ },++ "kind": {++ "enum": [++ "stub",++ "openai",++ "anthropic",++ "http",++ "whisper"++ ]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]++ },++ "base_url": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^https?://\\S+$"++ },++ "auth_mode": {++ "enum": [++ "none",++ "bearer",++ "x-api-key"++ ]++ },++ "secret_ref": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^(env:[A-Z][A-Z0-9_]{0,63})?$"++ },++ "default_model": {++ "type": [++ "string",++ "null"++ ]++ },++ "capabilities": {++ "type": "array",++ "items": {++ "enum": [++ "chat",++ "generate",++ "summarize",++ "classify",++ "extract",++ "enrich",++ "embed",++ "vision",++ "json",++ "transcribe"++ ]++ }++ },++ "timeout_ms": {++ "type": [++ "integer",++ "string"++ ],++ "description": "Clamped to 1000..600000."++ },++ "priority": {++ "type": [++ "number",++ "string"++ ]++ },++ "metadata": {++ "type": "object"++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "provider_key": {++ "type": "string"++ },++ "model_key": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "display_name": {++ "type": "string"++ },++ "type": {++ "enum": [++ "chat",++ "vision",++ "embedding",++ "stt"++ ]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]++ },++ "context_window": {++ "type": [++ "integer",++ "null"++ ]++ },++ "max_output": {++ "type": [++ "integer",++ "null"++ ]++ },++ "cost": {++ "type": "object",++ "properties": {++ "in_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "out_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "cached_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ }++ }++ },++ "supports": {++ "type": "object",++ "properties": {++ "json": {++ "type": "boolean"++ },++ "tools": {++ "type": "boolean"++ },++ "streaming": {++ "type": "boolean"++ },++ "vision": {++ "type": "boolean"++ }++ }++ },++ "metadata": {++ "type": "object"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "scope_type",++ "window"++ ],++ "properties": {++ "scope_type": {++ "enum": [++ "global",++ "service",++ "actor",++ "attribution"++ ]++ },++ "scope_id": {++ "type": "string",++ "maxLength": 200,++ "description": "* on a non-global scope = each one separately."++ },++ "window": {++ "enum": [++ "minute",++ "hour",++ "day"++ ]++ },++ "max_requests": {++ "type": [++ "number",++ "string",++ "null"++ ],++ "minimum": 0++ },++ "max_tokens": {++ "type": [++ "number",++ "string",++ "null"++ ],++ "minimum": 0++ },++ "max_cost_usd": {++ "type": [++ "number",++ "string",++ "null"++ ],++ "minimum": 0++ },++ "workflow_prefix": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,130 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/provider-manage-result.v1.json",++ "title": "AiProviderManageResult",++ "description": "ai.provider-manage-result@1: answers of ai.provider.manage on OpenVibe.AI. Provider create/update/disable/enable → { provider } (201 on POST); POST …/reset → { health } (the circuit breaker, closed again); model upserts → { model }; POST /api/v1/quotas → 201 { quota } (the stored row); DELETE /api/v1/cache → { removed } (entries purged). Every change is audited.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "provider"++ ],++ "properties": {++ "provider": {++ "$ref": "provider.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "health"++ ],++ "properties": {++ "health": {++ "type": "object",++ "required": [++ "state"++ ],++ "properties": {++ "provider_key": {++ "type": "string"++ },++ "state": {++ "enum": [++ "closed",++ "open",++ "half_open"++ ]++ },++ "consecutive_failures": {++ "type": "integer"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "model"++ ],++ "properties": {++ "model": {++ "$ref": "model.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "quota"++ ],++ "properties": {++ "quota": {++ "type": "object",++ "required": [++ "id",++ "scope_type",++ "scope_id",++ "window",++ "status"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "scope_type": {++ "type": "string"++ },++ "scope_id": {++ "type": "string"++ },++ "window": {++ "type": "string"++ },++ "max_requests": {++ "type": [++ "number",++ "null"++ ]++ },++ "max_tokens": {++ "type": [++ "number",++ "null"++ ]++ },++ "max_cost_usd": {++ "type": [++ "number",++ "null"++ ]++ },++ "workflow_prefix": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "type": "string"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "removed"++ ],++ "properties": {++ "removed": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,146 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/provider.v1.json",++ "title": "AiProvider",++ "description": "ai.provider@1: one model provider as OpenVibe.AI's registry API shows it (server/registry.js publicProvider): never a secret value, only the secret reference name (env:NAME) and whether it resolves (credentials), plus the circuit-breaker health.",++ "type": "object",++ "required": [++ "key",++ "kind",++ "status",++ "auth_mode",++ "capabilities",++ "credentials"++ ],++ "properties": {++ "key": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "kind": {++ "enum": [++ "stub",++ "openai",++ "anthropic",++ "http",++ "whisper"++ ]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]++ },++ "base_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "auth_mode": {++ "enum": [++ "none",++ "bearer",++ "x-api-key"++ ]++ },++ "secret_ref": {++ "type": [++ "string",++ "null"++ ],++ "description": "env:NAME, a reference; never the secret."++ },++ "default_model": {++ "type": [++ "string",++ "null"++ ]++ },++ "capabilities": {++ "type": "array",++ "items": {++ "enum": [++ "chat",++ "generate",++ "summarize",++ "classify",++ "extract",++ "enrich",++ "embed",++ "vision",++ "json",++ "transcribe"++ ]++ }++ },++ "timeout_ms": {++ "type": [++ "integer",++ "null"++ ]++ },++ "priority": {++ "type": [++ "number",++ "null"++ ]++ },++ "metadata": {++ "type": "object"++ },++ "origin": {++ "type": [++ "string",++ "null"++ ]++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "credentials": {++ "enum": [++ "not_required",++ "configured",++ "missing"++ ]++ },++ "health": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "provider_key": {++ "type": "string"++ },++ "state": {++ "enum": [++ "closed",++ "open",++ "half_open"++ ]++ },++ "consecutive_failures": {++ "type": "integer"++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,223 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/usage-result.v1.json",++ "title": "AiUsageResult",++ "description": "ai.usage-result@1: answers of ai.usage.read on OpenVibe.AI (all GETs, parameters in the query). GET /api/v1/status → { providers, runs, today, workflows, templates, routes, inflight, quotas }; GET /api/v1/usage?from=&to=&requester= → { usage } (daily rows); GET /api/v1/quotas → { quotas, current }; GET /api/v1/requests?provider=&status=&run=&fallback=&limit= → { requests } (request-log metadata: hashes, tokens, cost, latency; raw debug fields only for provider managers who ask); GET /api/v1/audit → { audit }; GET /api/v1/cache → { cache }; GET /api/v1/providers → { providers }; GET /api/v1/models?provider= → { models }. Never raw prompts, never secrets.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "providers",++ "runs",++ "today"++ ],++ "properties": {++ "providers": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "key"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "kind": {++ "type": "string"++ },++ "status": {++ "type": "string"++ },++ "credentials": {++ "type": "string"++ },++ "health": {++ "type": "string"++ }++ }++ }++ },++ "runs": {++ "type": "object",++ "additionalProperties": {++ "type": "integer"++ },++ "description": "Run counts by status."++ },++ "today": {++ "type": "object",++ "required": [++ "day"++ ],++ "properties": {++ "day": {++ "type": "string"++ },++ "requests": {++ "type": "number"++ },++ "cost_usd": {++ "type": "number"++ }++ }++ },++ "workflows": {++ "type": "integer"++ },++ "templates": {++ "type": "integer"++ },++ "routes": {++ "type": "integer"++ },++ "inflight": {++ "type": "integer"++ },++ "quotas": {++ "type": "array"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "providers"++ ],++ "properties": {++ "providers": {++ "type": "array",++ "items": {++ "$ref": "provider.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "models"++ ],++ "properties": {++ "models": {++ "type": "array",++ "items": {++ "$ref": "model.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "quotas",++ "current"++ ],++ "properties": {++ "quotas": {++ "type": "array",++ "items": {++ "type": "object"++ }++ },++ "current": {++ "type": "array",++ "items": {++ "type": "object",++ "properties": {++ "quota": {++ "type": "object"++ },++ "scope_id": {++ "type": "string"++ },++ "window_start": {++ "type": "string"++ },++ "resets_in_seconds": {++ "type": "number"++ },++ "used": {++ "type": "object"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "usage"++ ],++ "properties": {++ "usage": {++ "type": "array",++ "items": {++ "type": "object"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "requests"++ ],++ "properties": {++ "requests": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "type": "object"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "audit"++ ],++ "properties": {++ "audit": {++ "type": "array",++ "items": {++ "type": "object"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "cache"++ ],++ "properties": {++ "cache": {++ "type": "array",++ "items": {++ "type": "object",++ "properties": {++ "workflow_key": {++ "type": "string"++ },++ "privacy": {++ "type": "string"++ },++ "entries": {++ "type": "integer"++ },++ "hits": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ }++ }++ }++ ]++}@@@ -270,6 +270,78 @@"status": "active","adr": "ADR-026"},++ {++ "id": "events.delivery-admin-request",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/delivery-admin-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "events.delivery-admin-result",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/delivery-admin-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "events.publish-result",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/publish-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "events.read-request",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/read-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "events.read-result",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "events.subscription-request",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/subscription-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "events.subscription-result",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/subscription-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "events.subscription",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/subscription.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "chat.message.deleted","version": "1.0.0",@@@ -627,6 +699,69 @@"status": "planned","adr": "ADR-015"},++ {++ "id": "ai.definition-version-request",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/definition-version-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.definition-version-result",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/definition-version-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.model",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/model.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.provider-manage-request",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/provider-manage-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.provider-manage-result",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/provider-manage-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.provider",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/provider.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.usage-result",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/usage-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "tips.interaction.ready","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,45 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/delivery-admin-request.v1.json",++ "title": "EventsDeliveryAdminRequest",++ "description": "events.delivery-admin-request@1: the body of POST /api/v1/deliveries/replay on OpenVibe.Events (events.delivery.admin, operators): { subscription_id } and exactly one of from_seq (requeue everything the subscription matches from that seq) or event_ids (at most 1000). GET /api/v1/deliveries?status=&subscription_id=&after_seq=&limit= takes no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "subscription_id"++ ],++ "oneOf": [++ {++ "required": [++ "from_seq"++ ]++ },++ {++ "required": [++ "event_ids"++ ]++ }++ ],++ "properties": {++ "subscription_id": {++ "type": "string"++ },++ "from_seq": {++ "type": "integer",++ "minimum": 0++ },++ "event_ids": {++ "type": "array",++ "maxItems": 1000,++ "items": {++ "type": "string"++ }++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,118 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/delivery-admin-result.v1.json",++ "title": "EventsDeliveryAdminResult",++ "description": "events.delivery-admin-result@1: answers of events.delivery.admin on OpenVibe.Events. GET /api/v1/deliveries → { deliveries, counts } (per delivery: event, subscription, seq, status pending|delivered|failed|dead, attempt, next_attempt_at, the last HTTP status and error, delivered_at; counts by status); POST /api/v1/deliveries/replay → { subscription_id, queued }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "deliveries",++ "counts"++ ],++ "properties": {++ "deliveries": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "event_id",++ "subscription_id",++ "seq",++ "status",++ "attempt"++ ],++ "properties": {++ "event_id": {++ "type": "string"++ },++ "subscription_id": {++ "type": "string"++ },++ "seq": {++ "type": "integer"++ },++ "status": {++ "enum": [++ "pending",++ "delivered",++ "failed",++ "dead"++ ]++ },++ "attempt": {++ "type": "integer",++ "minimum": 0++ },++ "next_attempt_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_status": {++ "type": [++ "integer",++ "null"++ ]++ },++ "last_error": {++ "type": [++ "string",++ "null"++ ]++ },++ "delivered_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "counts": {++ "type": "object",++ "required": [++ "pending",++ "delivered",++ "failed",++ "dead"++ ],++ "properties": {++ "pending": {++ "type": "integer"++ },++ "delivered": {++ "type": "integer"++ },++ "failed": {++ "type": "integer"++ },++ "dead": {++ "type": "integer"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "subscription_id",++ "queued"++ ],++ "properties": {++ "subscription_id": {++ "type": "string"++ },++ "queued": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,65 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/publish-result.v1.json",++ "title": "EventsPublishResult",++ "description": "events.publish-result@1: the answer of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps). One envelope → { event_id, seq, duplicate } (201 when stored, 200 when every event was a repeat); a batch { events: [...] } (at most 100, atomic) → { results } in the same order. Refusals are problem+json: 422 events.invalid_envelope / events.invalid_redaction, 403 events.source_mismatch / events.type_not_allowed / events.actor_mismatch, 413 events.payload_too_large / events.batch_too_large, 429 events.quota_exceeded (apps).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "event_id",++ "seq",++ "duplicate"++ ],++ "properties": {++ "event_id": {++ "type": "string",++ "pattern": "^evt_"++ },++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "duplicate": {++ "type": "boolean",++ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "results"++ ],++ "properties": {++ "results": {++ "type": "array",++ "minItems": 1,++ "maxItems": 100,++ "items": {++ "type": "object",++ "required": [++ "event_id",++ "seq",++ "duplicate"++ ],++ "properties": {++ "event_id": {++ "type": "string",++ "pattern": "^evt_"++ },++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "duplicate": {++ "type": "boolean",++ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."++ }++ }++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,31 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/read-request.v1.json",++ "title": "EventsReadRequest",++ "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "topic",++ "cursor"++ ],++ "properties": {++ "topic": {++ "type": "string",++ "maxLength": 200,++ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",++ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."++ },++ "cursor": {++ "type": "integer",++ "minimum": 0,++ "description": "A seq (next_after_seq of the last page handled)."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,105 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/read-result.v1.json",++ "title": "EventsReadResult",++ "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, event }], next_after_seq, latest_seq, gap? } (keep next_after_seq as the cursor: it moves past events that did not match; gap { from_seq, to_seq } says events after after_seq were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). An app sees only its project's events in its token's environment plus public first-party events.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "events",++ "next_after_seq",++ "latest_seq"++ ],++ "properties": {++ "events": {++ "type": "array",++ "maxItems": 1000,++ "items": {++ "type": "object",++ "required": [++ "seq",++ "event"++ ],++ "properties": {++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "event": {++ "$ref": "event-envelope.v1.json"++ }++ }++ }++ },++ "next_after_seq": {++ "type": "integer",++ "minimum": 0++ },++ "latest_seq": {++ "type": "integer",++ "minimum": 0++ },++ "gap": {++ "type": "object",++ "required": [++ "from_seq",++ "to_seq"++ ],++ "properties": {++ "from_seq": {++ "type": "integer"++ },++ "to_seq": {++ "type": "integer"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "seq",++ "event"++ ],++ "properties": {++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "event": {++ "$ref": "event-envelope.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "consumer",++ "topic",++ "cursor",++ "updated_at"++ ],++ "properties": {++ "consumer": {++ "type": "string"++ },++ "topic": {++ "type": "string"++ },++ "cursor": {++ "type": "integer",++ "minimum": 0++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,76 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/subscription-request.v1.json",++ "title": "EventsSubscriptionRequest",++ "description": "events.subscription-request@1: the body of POST /api/v1/subscriptions on OpenVibe.Events (events.subscription.manage for services, events.app.subscribe for developer apps; the consumer is always the caller). topic_pattern (topic is accepted for it) and endpoint are required: a service's endpoint must be http(s) on the allow-list (127.0.0.1, *.openvibe.*), an app's must be https resolving only to public addresses. secret, when given, is 32 to 256 characters (else one is generated); retry_policy accepts max_attempts (1..20) and backoff_ms (1..20 delays of 0..86400000 ms) and nothing else. The same pattern and endpoint twice is 409 events.subscription_exists. GET /api/v1/subscriptions[/:id] and POST /api/v1/subscriptions/:id/disable|enable take no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "endpoint"++ ],++ "anyOf": [++ {++ "required": [++ "topic_pattern"++ ]++ },++ {++ "required": [++ "topic"++ ]++ }++ ],++ "properties": {++ "topic_pattern": {++ "type": "string",++ "maxLength": 200,++ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",++ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."++ },++ "topic": {++ "type": "string",++ "maxLength": 200,++ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",++ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."++ },++ "endpoint": {++ "type": "string",++ "pattern": "^https?://"++ },++ "secret": {++ "type": "string",++ "minLength": 32,++ "maxLength": 256++ },++ "retry_policy": {++ "type": [++ "object",++ "null"++ ],++ "additionalProperties": false,++ "properties": {++ "max_attempts": {++ "type": "integer",++ "minimum": 1,++ "maximum": 20++ },++ "backoff_ms": {++ "type": "array",++ "minItems": 1,++ "maxItems": 20,++ "items": {++ "type": "integer",++ "minimum": 0,++ "maximum": 86400000++ }++ }++ }++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/subscription-result.v1.json",++ "title": "EventsSubscriptionResult",++ "description": "events.subscription-result@1: answers of the subscription routes on OpenVibe.Events. POST /api/v1/subscriptions → 201 events.subscription@1 with its secret (shown once); GET /api/v1/subscriptions → { subscriptions } (the caller's own, no secrets); GET /api/v1/subscriptions/:id and POST …/disable|enable → events.subscription@1. Another consumer's subscription is 404.",++ "anyOf": [++ {++ "$ref": "subscription.v1.json"++ },++ {++ "type": "object",++ "required": [++ "subscriptions"++ ],++ "properties": {++ "subscriptions": {++ "type": "array",++ "items": {++ "$ref": "subscription.v1.json"++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,71 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/subscription.v1.json",++ "title": "EventsSubscription",++ "description": "events.subscription@1: one push subscription as OpenVibe.Events shows it to its consumer (server/store.js subscriptionView). The signing secret appears only in the answer that creates it (and in a rotation's). A developer app's subscription also names its project and environment.",++ "type": "object",++ "required": [++ "id",++ "consumer",++ "topic_pattern",++ "endpoint",++ "enabled",++ "retry_policy",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^sub_"++ },++ "consumer": {++ "type": "string",++ "description": "The calling service, or app:app_<ULID>."++ },++ "topic_pattern": {++ "type": "string"++ },++ "endpoint": {++ "type": "string"++ },++ "enabled": {++ "type": "boolean"++ },++ "retry_policy": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "max_attempts": {++ "type": "integer"++ },++ "backoff_ms": {++ "type": "array",++ "items": {++ "type": "integer"++ }++ }++ }++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "project_id": {++ "type": "string"++ },++ "env": {++ "type": "string"++ },++ "secret": {++ "type": "string",++ "description": "Only when the subscription is created: shown once."++ }++ }++}nnew file mode 100644@@@ -0,0 +1 @@++{ "status": "live" }nnew file mode 100644@@@ -0,0 +1 @@++{ "primary": { "provider": "anthropic", "model": "claude-sonnet-4-5" }, "fallbacks": [ { "provider": "openai", "model": "gpt-4.1-mini" } ], "response_format": "json" }nnew file mode 100644@@@ -0,0 +1 @@++{ "name": "Clip title", "system_prompt": "You title stream clips.", "user_prompt": "Transcript: {{transcript}}", "input_schema": { "type": "object", "required": ["transcript"] }, "output_schema": { "type": "object", "required": ["title"] }, "visibility": "first-party", "status": "draft" }nnew file mode 100644@@@ -0,0 +1 @@++{ "steps": [ { "kind": "llm", "template": "live.clip_title" } ], "cache_mode": "service", "cache_ttl_sec": 86400 }nnew file mode 100644@@@ -0,0 +1 @@++{ "workflow": { "key": "live.clip_title", "status": "active" } }nnew file mode 100644@@@ -0,0 +1 @@++{ "route": { "key": "default.chat", "version": 3, "status": "disabled" } }nnew file mode 100644@@@ -0,0 +1 @@++{ "template": { "key": "live.clip_title", "version": 4, "status": "active", "name": "Clip title" } }nnew file mode 100644@@@ -0,0 +1 @@++{ "provider_key": "anthropic", "model_key": "x", "type": "image", "status": "active", "cost": {}, "supports": {} }nnew file mode 100644@@@ -0,0 +1 @@++{ "provider_key": "anthropic", "model_key": "claude-sonnet-4-5", "display_name": "Claude Sonnet 4.5", "type": "chat", "status": "active", "context_window": 200000, "max_output": 64000, "cost": { "in_per_mtok": 3, "out_per_mtok": 15, "cached_per_mtok": 0.3 }, "supports": { "json": true, "tools": true, "streaming": true, "vision": true }, "metadata": {}, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-20T10:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "openai", "kind": "openai", "api_key": "sk-live-123", "status": "paused" }nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "openai", "kind": "openai", "auth_mode": "bearer", "secret_ref": "env:OPENAI_API_KEY", "capabilities": ["chat", "embed"], "priority": 20 }nnew file mode 100644@@@ -0,0 +1 @@++{ "scope_type": "service", "scope_id": "live", "window": "day", "max_cost_usd": 25 }nnew file mode 100644@@@ -0,0 +1 @@++{ "removed": -1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "removed": 42 }nnew file mode 100644@@@ -0,0 +1 @@++{ "provider": { "key": "anthropic", "display_name": "Anthropic", "kind": "anthropic", "status": "active", "base_url": null, "auth_mode": "x-api-key", "secret_ref": "env:ANTHROPIC_API_KEY", "default_model": "claude-sonnet-4-5", "capabilities": ["chat", "generate", "summarize", "json"], "timeout_ms": 30000, "priority": 10, "metadata": {}, "origin": "admin", "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "credentials": "configured", "health": { "provider_key": "anthropic", "state": "closed", "consecutive_failures": 0, "opened_at": null, "last_error": null } } }nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "anthropic", "kind": "anthropic", "status": "active", "auth_mode": "x-api-key", "capabilities": ["chat"], "credentials": "sk-ant-secret" }nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "anthropic", "display_name": "Anthropic", "kind": "anthropic", "status": "active", "base_url": null, "auth_mode": "x-api-key", "secret_ref": "env:ANTHROPIC_API_KEY", "default_model": "claude-sonnet-4-5", "capabilities": ["chat", "generate", "summarize", "json"], "timeout_ms": 30000, "priority": 10, "metadata": {}, "origin": "admin", "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "credentials": "configured", "health": { "provider_key": "anthropic", "state": "closed", "consecutive_failures": 0, "opened_at": null, "last_error": null } }nnew file mode 100644@@@ -0,0 +1 @@++{ "runs": { "succeeded": 1 } }nnew file mode 100644@@@ -0,0 +1 @@++{ "models": [ { "provider_key": "anthropic", "model_key": "claude-sonnet-4-5", "display_name": "Claude Sonnet 4.5", "type": "chat", "status": "active", "context_window": 200000, "max_output": 64000, "cost": { "in_per_mtok": 3, "out_per_mtok": 15, "cached_per_mtok": 0.3 }, "supports": { "json": true, "tools": true, "streaming": true, "vision": true }, "metadata": {}, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-20T10:00:00.000Z" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "providers": [ { "key": "anthropic", "kind": "anthropic", "status": "active", "credentials": "configured", "health": "closed" } ], "runs": { "succeeded": 1203, "failed": 7 }, "today": { "day": "2026-09-25", "requests": 311, "cost_usd": 1.84 }, "workflows": 23, "templates": 19, "routes": 6, "inflight": 2, "quotas": [] }nnew file mode 100644@@@ -0,0 +1 @@++{ "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "from_seq": 1, "event_ids": ["evt_01JAB2C3D4E5F6G7H8J9K0MNPQ"] }nnew file mode 100644@@@ -0,0 +1 @@++{ "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "from_seq": 88000 }nnew file mode 100644@@@ -0,0 +1 @@++{ "deliveries": [], "counts": { "pending": 0 } }nnew file mode 100644@@@ -0,0 +1 @@++{ "deliveries": [ { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 88213, "status": "dead", "attempt": 8, "next_attempt_at": null, "last_status": 502, "last_error": "HTTP 502", "delivered_at": null } ], "counts": { "pending": 3, "delivered": 91822, "failed": 1, "dead": 1 } }nnew file mode 100644@@@ -0,0 +1 @@++{ "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "queued": 213 }nnew file mode 100644@@@ -0,0 +1 @@++{ "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 0 }nnew file mode 100644@@@ -0,0 +1 @@++{ "results": [ { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 88213, "duplicate": false }, { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPR", "seq": 70001, "duplicate": true } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 88213, "duplicate": false }nnew file mode 100644@@@ -0,0 +1 @@++{ "topic": "media..vod", "cursor": -1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "topic": "media.vod.*", "cursor": 88213 }nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1 @@++{ "events": [ { "seq": 1, "event": { "event_type": "x" } } ], "next_after_seq": 1, "latest_seq": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "consumer": "live", "topic": "media.vod.*", "cursor": 0, "updated_at": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "events": [ { "seq": 88213, "event": { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPR", "event_type": "app.p01jab2c3d4e5f6g7h8j9k0mnpq.order.created", "version": 1, "source": "app-01jab2c3d4e5f6g7h8j9k0mnps", "actor": { "type": "app", "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPS" }, "timestamp": "2026-09-23T12:00:00Z", "visibility": "internal", "subject": { "type": "order", "id": "42" }, "payload": { "total": 3 }} } ], "next_after_seq": 88240, "latest_seq": 88240 }nnew file mode 100644@@@ -0,0 +1 @@++{ "topic_pattern": "media.vod.*", "endpoint": "https://hooks.example.com/ov", "retry_policy": { "max_attempts": 8, "jitter": true } }nnew file mode 100644@@@ -0,0 +1 @@++{ "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/internal/media-events", "retry_policy": { "max_attempts": 8, "backoff_ms": [1000, 5000, 30000] } }nnew file mode 100644@@@ -0,0 +1 @@++{ "subscriptions": [ { "id": "sub_1" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{"id":"sub_01JAB2C3D4E5F6G7H8J9K0MNPQ","consumer":"live","topic_pattern":"media.vod.*","endpoint":"http://127.0.0.1:3000/internal/media-events","enabled":true,"retry_policy":null,"created_at":"2026-09-25T18:00:00.000Z","updated_at":"2026-09-25T18:00:00.000Z","secret":"whsec_4f1c0b8e2a9d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b"}nnew file mode 100644@@@ -0,0 +1 @@++{ "subscriptions": [ { "id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "consumer": "live", "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/internal/media-events", "enabled": true, "retry_policy": null, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "consumer": "live", "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/x", "enabled": "yes", "retry_policy": null, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "consumer": "live", "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/internal/media-events", "enabled": true, "retry_policy": null, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }@@@ -3347,372 +3347,1703 @@}}},++ "events.delivery-admin-request": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/delivery-admin-request.v1.json",++ "title": "EventsDeliveryAdminRequest",++ "description": "events.delivery-admin-request@1: the body of POST /api/v1/deliveries/replay on OpenVibe.Events (events.delivery.admin, operators): { subscription_id } and exactly one of from_seq (requeue everything the subscription matches from that seq) or event_ids (at most 1000). GET /api/v1/deliveries?status=&subscription_id=&after_seq=&limit= takes no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "subscription_id"++ ],++ "oneOf": [++ {++ "required": [++ "from_seq"++ ]++ },++ {++ "required": [++ "event_ids"++ ]++ }++ ],++ "properties": {++ "subscription_id": {++ "type": "string"++ },++ "from_seq": {++ "type": "integer",++ "minimum": 0++ },++ "event_ids": {++ "type": "array",++ "maxItems": 1000,++ "items": {++ "type": "string"++ }++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "events.delivery-admin-result": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/delivery-admin-result.v1.json",++ "title": "EventsDeliveryAdminResult",++ "description": "events.delivery-admin-result@1: answers of events.delivery.admin on OpenVibe.Events. GET /api/v1/deliveries → { deliveries, counts } (per delivery: event, subscription, seq, status pending|delivered|failed|dead, attempt, next_attempt_at, the last HTTP status and error, delivered_at; counts by status); POST /api/v1/deliveries/replay → { subscription_id, queued }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "deliveries",++ "counts"++ ],++ "properties": {++ "deliveries": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "event_id",++ "subscription_id",++ "seq",++ "status",++ "attempt"++ ],++ "properties": {++ "event_id": {++ "type": "string"++ },++ "subscription_id": {++ "type": "string"++ },++ "seq": {++ "type": "integer"++ },++ "status": {++ "enum": [++ "pending",++ "delivered",++ "failed",++ "dead"++ ]++ },++ "attempt": {++ "type": "integer",++ "minimum": 0++ },++ "next_attempt_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_status": {++ "type": [++ "integer",++ "null"++ ]++ },++ "last_error": {++ "type": [++ "string",++ "null"++ ]++ },++ "delivered_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "counts": {++ "type": "object",++ "required": [++ "pending",++ "delivered",++ "failed",++ "dead"++ ],++ "properties": {++ "pending": {++ "type": "integer"++ },++ "delivered": {++ "type": "integer"++ },++ "failed": {++ "type": "integer"++ },++ "dead": {++ "type": "integer"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "subscription_id",++ "queued"++ ],++ "properties": {++ "subscription_id": {++ "type": "string"++ },++ "queued": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ ]++ }++ },++ "events.publish-result": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/publish-result.v1.json",++ "title": "EventsPublishResult",++ "description": "events.publish-result@1: the answer of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps). One envelope → { event_id, seq, duplicate } (201 when stored, 200 when every event was a repeat); a batch { events: [...] } (at most 100, atomic) → { results } in the same order. Refusals are problem+json: 422 events.invalid_envelope / events.invalid_redaction, 403 events.source_mismatch / events.type_not_allowed / events.actor_mismatch, 413 events.payload_too_large / events.batch_too_large, 429 events.quota_exceeded (apps).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "event_id",++ "seq",++ "duplicate"++ ],++ "properties": {++ "event_id": {++ "type": "string",++ "pattern": "^evt_"++ },++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "duplicate": {++ "type": "boolean",++ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "results"++ ],++ "properties": {++ "results": {++ "type": "array",++ "minItems": 1,++ "maxItems": 100,++ "items": {++ "type": "object",++ "required": [++ "event_id",++ "seq",++ "duplicate"++ ],++ "properties": {++ "event_id": {++ "type": "string",++ "pattern": "^evt_"++ },++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "duplicate": {++ "type": "boolean",++ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."++ }++ }++ }++ }++ }++ }++ ]++ }++ },++ "events.read-request": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/read-request.v1.json",++ "title": "EventsReadRequest",++ "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "topic",++ "cursor"++ ],++ "properties": {++ "topic": {++ "type": "string",++ "maxLength": 200,++ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",++ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."++ },++ "cursor": {++ "type": "integer",++ "minimum": 0,++ "description": "A seq (next_after_seq of the last page handled)."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "events.read-result": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/read-result.v1.json",++ "title": "EventsReadResult",++ "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, event }], next_after_seq, latest_seq, gap? } (keep next_after_seq as the cursor: it moves past events that did not match; gap { from_seq, to_seq } says events after after_seq were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). An app sees only its project's events in its token's environment plus public first-party events.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "events",++ "next_after_seq",++ "latest_seq"++ ],++ "properties": {++ "events": {++ "type": "array",++ "maxItems": 1000,++ "items": {++ "type": "object",++ "required": [++ "seq",++ "event"++ ],++ "properties": {++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "event": {++ "$ref": "event-envelope.v1.json"++ }++ }++ }++ },++ "next_after_seq": {++ "type": "integer",++ "minimum": 0++ },++ "latest_seq": {++ "type": "integer",++ "minimum": 0++ },++ "gap": {++ "type": "object",++ "required": [++ "from_seq",++ "to_seq"++ ],++ "properties": {++ "from_seq": {++ "type": "integer"++ },++ "to_seq": {++ "type": "integer"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "seq",++ "event"++ ],++ "properties": {++ "seq": {++ "type": "integer",++ "minimum": 1++ },++ "event": {++ "$ref": "event-envelope.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "consumer",++ "topic",++ "cursor",++ "updated_at"++ ],++ "properties": {++ "consumer": {++ "type": "string"++ },++ "topic": {++ "type": "string"++ },++ "cursor": {++ "type": "integer",++ "minimum": 0++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ ]++ }++ },++ "events.subscription-request": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/subscription-request.v1.json",++ "title": "EventsSubscriptionRequest",++ "description": "events.subscription-request@1: the body of POST /api/v1/subscriptions on OpenVibe.Events (events.subscription.manage for services, events.app.subscribe for developer apps; the consumer is always the caller). topic_pattern (topic is accepted for it) and endpoint are required: a service's endpoint must be http(s) on the allow-list (127.0.0.1, *.openvibe.*), an app's must be https resolving only to public addresses. secret, when given, is 32 to 256 characters (else one is generated); retry_policy accepts max_attempts (1..20) and backoff_ms (1..20 delays of 0..86400000 ms) and nothing else. The same pattern and endpoint twice is 409 events.subscription_exists. GET /api/v1/subscriptions[/:id] and POST /api/v1/subscriptions/:id/disable|enable take no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "endpoint"++ ],++ "anyOf": [++ {++ "required": [++ "topic_pattern"++ ]++ },++ {++ "required": [++ "topic"++ ]++ }++ ],++ "properties": {++ "topic_pattern": {++ "type": "string",++ "maxLength": 200,++ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",++ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."++ },++ "topic": {++ "type": "string",++ "maxLength": 200,++ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",++ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."++ },++ "endpoint": {++ "type": "string",++ "pattern": "^https?://"++ },++ "secret": {++ "type": "string",++ "minLength": 32,++ "maxLength": 256++ },++ "retry_policy": {++ "type": [++ "object",++ "null"++ ],++ "additionalProperties": false,++ "properties": {++ "max_attempts": {++ "type": "integer",++ "minimum": 1,++ "maximum": 20++ },++ "backoff_ms": {++ "type": "array",++ "minItems": 1,++ "maxItems": 20,++ "items": {++ "type": "integer",++ "minimum": 0,++ "maximum": 86400000++ }++ }++ }++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "events.subscription-result": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/subscription-result.v1.json",++ "title": "EventsSubscriptionResult",++ "description": "events.subscription-result@1: answers of the subscription routes on OpenVibe.Events. POST /api/v1/subscriptions → 201 events.subscription@1 with its secret (shown once); GET /api/v1/subscriptions → { subscriptions } (the caller's own, no secrets); GET /api/v1/subscriptions/:id and POST …/disable|enable → events.subscription@1. Another consumer's subscription is 404.",++ "anyOf": [++ {++ "$ref": "subscription.v1.json"++ },++ {++ "type": "object",++ "required": [++ "subscriptions"++ ],++ "properties": {++ "subscriptions": {++ "type": "array",++ "items": {++ "$ref": "subscription.v1.json"++ }++ }++ }++ }++ ]++ }++ },++ "events.subscription": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/subscription.v1.json",++ "title": "EventsSubscription",++ "description": "events.subscription@1: one push subscription as OpenVibe.Events shows it to its consumer (server/store.js subscriptionView). The signing secret appears only in the answer that creates it (and in a rotation's). A developer app's subscription also names its project and environment.",++ "type": "object",++ "required": [++ "id",++ "consumer",++ "topic_pattern",++ "endpoint",++ "enabled",++ "retry_policy",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^sub_"++ },++ "consumer": {++ "type": "string",++ "description": "The calling service, or app:app_<ULID>."++ },++ "topic_pattern": {++ "type": "string"++ },++ "endpoint": {++ "type": "string"++ },++ "enabled": {++ "type": "boolean"++ },++ "retry_policy": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "max_attempts": {++ "type": "integer"++ },++ "backoff_ms": {++ "type": "array",++ "items": {++ "type": "integer"++ }++ }++ }++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "project_id": {++ "type": "string"++ },++ "env": {++ "type": "string"++ },++ "secret": {++ "type": "string",++ "description": "Only when the subscription is created: shown once."++ }++ }++ }++ },"chat.message.deleted": {"version": "1.0.0",-- "owner": "chat",++ "owner": "chat",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/chat.message.deleted.v1.json",++ "title": "ChatMessageDeletedPayload",++ "description": "chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.",++ "type": "object",++ "required": [++ "message_ids",++ "redacts"++ ],++ "additionalProperties": false,++ "properties": {++ "message_ids": {++ "type": "array",++ "minItems": 1,++ "maxItems": 500,++ "uniqueItems": true,++ "items": {++ "type": "integer",++ "minimum": 1++ },++ "description": "Chat message ids (the id in chat.message.created)."++ },++ "redacts": {++ "$ref": "../redaction-directive.v1.json",++ "description": "{ subject_type: chat_message, subject_ids: message_ids as strings }."++ }++ }++ }++ },++ "sources.item.created": {++ "version": "1.0.0",++ "owner": "sources",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/sources.item.created.v1.json",++ "title": "SourcesItemCreatedPayload",++ "description": "sources.item.created v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch, or a manual entry, produced an item Sources had not seen (revision 1). A summary only: consumers fetch the full sources.item@1 with sources.item.read. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",++ "type": "object",++ "required": [++ "item_id",++ "source_key",++ "category",++ "kind",++ "canonical_url",++ "title",++ "revision",++ "content_hash",++ "retrieved_at"++ ],++ "additionalProperties": false,++ "properties": {++ "item_id": {++ "type": "string",++ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "source_key": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"++ },++ "category": {++ "enum": [++ "news",++ "blog",++ "reviews",++ "deals",++ "coupons",++ "trade"++ ]++ },++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,39}$",++ "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ],++ "format": "uri"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "content_hash": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$"++ },++ "retrieved_at": {++ "type": "string",++ "format": "date-time",++ "description": "Last successful fetch (or manual entry) that contained the item."++ }++ }++ }++ },++ "sources.item.updated": {++ "version": "1.0.0",++ "owner": "sources",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/sources.item.updated.v1.json",++ "title": "SourcesItemUpdatedPayload",++ "description": "sources.item.updated v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch or manual entry changed an existing item's content hash, which made a new revision. A removed item is never updated (removal is sticky). Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",++ "type": "object",++ "required": [++ "item_id",++ "source_key",++ "category",++ "kind",++ "canonical_url",++ "title",++ "revision",++ "content_hash",++ "retrieved_at",++ "previous_content_hash"++ ],++ "additionalProperties": false,++ "properties": {++ "item_id": {++ "type": "string",++ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "source_key": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"++ },++ "category": {++ "enum": [++ "news",++ "blog",++ "reviews",++ "deals",++ "coupons",++ "trade"++ ]++ },++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,39}$",++ "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ],++ "format": "uri"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "content_hash": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$"++ },++ "retrieved_at": {++ "type": "string",++ "format": "date-time",++ "description": "Last successful fetch (or manual entry) that contained the item."++ },++ "previous_content_hash": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$"++ }++ }++ }++ },++ "sources.item.removed": {++ "version": "1.0.0",++ "owner": "sources",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/sources.item.removed.v1.json",++ "title": "SourcesItemRemovedPayload",++ "description": "sources.item.removed v1 (OpenVibe.Sources server/items.js remove → itemEvent, from DELETE /api/v1/items/:id). Staff removed an item (takedown, licence, error). Sticky: a later fetch that still lists it does not bring it back. The removal bumps the revision; content_hash is the last content's. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",++ "type": "object",++ "required": [++ "item_id",++ "source_key",++ "category",++ "kind",++ "canonical_url",++ "title",++ "revision",++ "content_hash",++ "retrieved_at",++ "reason"++ ],++ "additionalProperties": false,++ "properties": {++ "item_id": {++ "type": "string",++ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "source_key": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"++ },++ "category": {++ "enum": [++ "news",++ "blog",++ "reviews",++ "deals",++ "coupons",++ "trade"++ ]++ },++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,39}$",++ "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ],++ "format": "uri"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "content_hash": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$"++ },++ "retrieved_at": {++ "type": "string",++ "format": "date-time",++ "description": "Last successful fetch (or manual entry) that contained the item."++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 300++ }++ }++ }++ },++ "sources.fetch.failed": {++ "version": "1.0.0",++ "owner": "sources",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/sources.fetch.failed.v1.json",++ "title": "SourcesFetchFailedPayload",++ "description": "sources.fetch.failed v1 (OpenVibe.Sources server/ingest.js failedEvent). One endpoint fetch of a source failed and counts as a failure: robots denied or unreachable, an HTTP error, the site's 429, a parse error or an internal error. Not sent for 304, a disabled source, a missing credential or the local rate limit. No item is created or changed. Envelope: subject { type: source, id: <source_key> }, visibility internal, priority important, actor service:sources.",++ "type": "object",++ "required": [++ "source_key",++ "category",++ "run_id",++ "endpoint_url",++ "state",++ "error_code",++ "http_status",++ "consecutive_failures"++ ],++ "additionalProperties": false,++ "properties": {++ "source_key": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"++ },++ "category": {++ "enum": [++ "news",++ "blog",++ "reviews",++ "deals",++ "coupons",++ "trade"++ ]++ },++ "run_id": {++ "type": "string",++ "pattern": "^frn_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The recorded fetch run (GET /api/v1/sources/:key/runs)."++ },++ "endpoint_url": {++ "type": "string",++ "minLength": 1,++ "maxLength": 2048++ },++ "state": {++ "enum": [++ "robots_denied",++ "http_error",++ "rate_limited",++ "parse_error"++ ]++ },++ "error_code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,39}$",++ "description": "robots | address_refused | port_refused | bad_url | upstream_429 | http_<status> | unreadable | adapter_error | internal."++ },++ "http_status": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 100,++ "maximum": 599++ },++ "consecutive_failures": {++ "type": "integer",++ "minimum": 1,++ "description": "The source's consecutive failed runs including this one."++ }++ }++ }++ },++ "sources.index_document.upserted": {++ "version": "1.0.0",++ "owner": "sources","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/chat.message.deleted.v1.json",-- "title": "ChatMessageDeletedPayload",-- "description": "chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.",++ "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.upserted.v1.json",++ "title": "SourcesIndexDocumentUpsertedPayload",++ "description": "sources.index_document.upserted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was created or updated; the payload is its search.index-document@1 (owner sources, type item, visibility members for role:admin and role:global_mod, authorship imported, indexability noindex third_party_content). Consumed by OpenVibe.Search (*.index_document.*). Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.",++ "$ref": "../../search/index-document.v1.json"++ }++ },++ "sources.index_document.deleted": {++ "version": "1.0.0",++ "owner": "sources",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.deleted.v1.json",++ "title": "SourcesIndexDocumentDeletedPayload",++ "description": "sources.index_document.deleted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was removed; OpenVibe.Search stores a tombstone at this revision. Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.","type": "object","required": [-- "message_ids",-- "redacts"++ "type",++ "id",++ "revision"],"additionalProperties": false,"properties": {-- "message_ids": {-- "type": "array",-- "minItems": 1,-- "maxItems": 500,-- "uniqueItems": true,-- "items": {-- "type": "integer",-- "minimum": 1-- },-- "description": "Chat message ids (the id in chat.message.created)."++ "type": {++ "const": "item"},-- "redacts": {-- "$ref": "../redaction-directive.v1.json",-- "description": "{ subject_type: chat_message, subject_ids: message_ids as strings }."++ "id": {++ "type": "string",++ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ }++ }++ }++ },++ "search.document.indexed": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/search.document.indexed.v1.json",++ "title": "SearchDocumentIndexedPayload",++ "description": "search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",++ "type": "object",++ "required": [++ "owner",++ "type",++ "id",++ "revision",++ "exposure",++ "reindexed",++ "canonical_url"++ ],++ "additionalProperties": false,++ "properties": {++ "owner": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "type": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"++ },++ "revision": {++ "type": "integer",++ "minimum": 0,++ "maximum": 9007199254740991++ },++ "exposure": {++ "enum": [++ "restricted",++ "public_unlisted",++ "public_listed"++ ]++ },++ "reindexed": {++ "type": "boolean",++ "description": "true when Search already held an earlier revision of the document."++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ],++ "description": "Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none."++ }++ }++ }++ },++ "search.document.removed": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/search.document.removed.v1.json",++ "title": "SearchDocumentRemovedPayload",++ "description": "search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",++ "type": "object",++ "required": [++ "owner",++ "type",++ "id",++ "revision",++ "reason",++ "previous_exposure",++ "exposure",++ "canonical_url"++ ],++ "additionalProperties": false,++ "properties": {++ "owner": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "type": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"++ },++ "revision": {++ "type": "integer",++ "minimum": 0,++ "maximum": 9007199254740991++ },++ "reason": {++ "enum": [++ "deleted",++ "draft",++ "not_published",++ "visibility_changed",++ "noindex"++ ]++ },++ "previous_exposure": {++ "enum": [++ "restricted",++ "public_unlisted",++ "public_listed"++ ]++ },++ "exposure": {++ "enum": [++ "none",++ "restricted",++ "public_unlisted"++ ],++ "description": "Lower than previous_exposure. none: no longer served to anyone."++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ],++ "description": "The URL only when the document was public before (so it must be purged); null otherwise."++ }++ }++ }++ },++ "wiki.space.updated": {++ "version": "1.0.0",++ "owner": "wiki",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.space.updated.v1.json",++ "title": "WikiSpaceUpdatedPayload",++ "description": "wiki.space.updated v1 (OpenVibe.Wiki server/wiki/service.js spaceEvent). Emitted when a space is created, renamed, changes visibility or settings, has its roles changed, or is deleted, in the transaction of the change. Envelope: subject { type: space, id: spc_… }, visibility public for a public space that is not deleted, otherwise internal, priority important, actor the person (user) or service.",++ "type": "object",++ "required": [++ "slug",++ "name",++ "kind",++ "visibility",++ "deleted",++ "change",++ "url"++ ],++ "additionalProperties": false,++ "properties": {++ "slug": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$"++ },++ "name": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "kind": {++ "enum": [++ "official",++ "user"++ ]++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "private"++ ]++ },++ "deleted": {++ "type": "boolean"++ },++ "change": {++ "enum": [++ "created",++ "renamed",++ "visibility",++ "settings",++ "roles",++ "deleted"++ ],++ "description": "What changed. renamed = a new slug."++ },++ "url": {++ "type": "string",++ "format": "uri",++ "pattern": "^https?://",++ "maxLength": 2048,++ "description": "Canonical URL of the space."}}}},-- "sources.item.created": {++ "wiki.revision.created": {"version": "1.0.0",-- "owner": "sources",++ "owner": "wiki","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/sources.item.created.v1.json",-- "title": "SourcesItemCreatedPayload",-- "description": "sources.item.created v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch, or a manual entry, produced an item Sources had not seen (revision 1). A summary only: consumers fetch the full sources.item@1 with sources.item.read. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.revision.created.v1.json",++ "title": "WikiRevisionCreatedPayload",++ "description": "wiki.revision.created v1 (OpenVibe.Wiki server/wiki/service.js revisionEvent). Emitted when a page gets a new immutable revision: an edit, a revert or an AI proposal. A new revision is a draft until it is published (then wiki.page.published|updated follows). Envelope: subject { type: page, id: pg_…, revision: <number> }, visibility internal, priority important, actor the person or service.","type": "object","required": [-- "item_id",-- "source_key",-- "category",++ "space_id",++ "space",++ "slug",++ "number","kind",-- "canonical_url",-- "title",-- "revision",-- "content_hash",-- "retrieved_at"++ "parent_number",++ "reverted_to",++ "authorship",++ "author",++ "message"],"additionalProperties": false,"properties": {-- "item_id": {++ "space_id": {"type": "string",-- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^spc_[0-9A-HJKMNP-TV-Z]{26}$"},-- "source_key": {++ "space": {"type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"++ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",++ "description": "Space slug."},-- "category": {++ "slug": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80,++ "description": "Page slug."++ },++ "number": {++ "type": "integer",++ "minimum": 1,++ "description": "The new revision number."++ },++ "kind": {"enum": [-- "news",-- "blog",-- "reviews",-- "deals",-- "coupons",-- "trade"++ "edit",++ "revert",++ "import"]},-- "kind": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,39}$",-- "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."++ "parent_number": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1},-- "canonical_url": {++ "reverted_to": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision a revert restored; null otherwise."++ },++ "authorship": {++ "enum": [++ "human",++ "ai_assisted",++ "ai_generated",++ "imported",++ null++ ],++ "description": "Search's authorship vocabulary; null when the revision has no authorship record."++ },++ "author": {"type": ["string","null"],-- "format": "uri"++ "maxLength": 200,++ "description": "Who wrote it: a subject id (usr_…) or a service (svc:<id>)."},-- "title": {++ "message": {"type": ["string","null"-- ]++ ],++ "maxLength": 2000++ },++ "proposal_id": {++ "type": "string",++ "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Present when the revision is an AI proposal awaiting a person's approval."++ }++ }++ }++ },++ "wiki.watch.triggered": {++ "version": "1.0.0",++ "owner": "wiki",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.watch.triggered.v1.json",++ "title": "WikiWatchTriggeredPayload",++ "description": "wiki.watch.triggered v1 (OpenVibe.Wiki server/wiki/service.js notifyWatchers). One event per change to a watched page, listing the watchers who may read the page (never the actor); not emitted when nobody is left. Envelope: subject { type: page, id: pg_…, revision: <revision or 0> }, visibility internal, priority important, actor the person or service.",++ "type": "object",++ "required": [++ "action",++ "space",++ "slug",++ "title",++ "revision",++ "url",++ "recipients"++ ],++ "additionalProperties": false,++ "properties": {++ "action": {++ "enum": [++ "published",++ "updated",++ "unpublished",++ "deleted",++ "revised",++ "proposed"++ ],++ "description": "revised = a new draft revision; proposed = an AI proposal awaits approval; the rest follow wiki.page.*."++ },++ "space": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",++ "description": "Space slug."++ },++ "slug": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80,++ "description": "Page slug."++ },++ "title": {++ "type": "string",++ "maxLength": 200},"revision": {-- "type": "integer",++ "type": [++ "integer",++ "null"++ ],"minimum": 1},-- "content_hash": {++ "url": {"type": "string",-- "pattern": "^[0-9a-f]{64}$"++ "format": "uri",++ "pattern": "^https?://",++ "maxLength": 2048},-- "retrieved_at": {++ "recipients": {++ "type": "array",++ "minItems": 1,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "description": "Watchers to notify."++ }++ }++ }++ },++ "wiki.page.published": {++ "version": "1.0.0",++ "owner": "wiki",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.published.v1.json",++ "title": "WikiPagePublishedPayload",++ "description": "wiki.page.published v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page became published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).",++ "type": "object",++ "required": [++ "canonical_url",++ "publication_state",++ "indexability",++ "space",++ "slug",++ "visibility"++ ],++ "additionalProperties": false,++ "properties": {++ "canonical_url": {"type": "string",-- "format": "date-time",-- "description": "Last successful fetch (or manual entry) that contained the item."++ "format": "uri",++ "pattern": "^https?://",++ "maxLength": 2048++ },++ "publication_state": {++ "const": "published"++ },++ "indexability": {++ "type": "object",++ "required": [++ "decision",++ "reasons"++ ],++ "additionalProperties": false,++ "properties": {++ "decision": {++ "enum": [++ "index",++ "noindex"++ ]++ },++ "reasons": {++ "type": "array",++ "maxItems": 20,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,63}$"++ }++ }++ }++ },++ "space": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",++ "description": "Space slug."++ },++ "slug": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80,++ "description": "Page slug inside the space."++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "private"++ ],++ "description": "Effective visibility (the page's, narrowed by its space's)."}}}},-- "sources.item.updated": {++ "wiki.page.updated": {"version": "1.0.0",-- "owner": "sources",++ "owner": "wiki","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/sources.item.updated.v1.json",-- "title": "SourcesItemUpdatedPayload",-- "description": "sources.item.updated v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch or manual entry changed an existing item's content hash, which made a new revision. A removed item is never updated (removal is sticky). Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.updated.v1.json",++ "title": "WikiPageUpdatedPayload",++ "description": "wiki.page.updated v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page got a new published revision or visibility, or a review changed what Search holds, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).","type": "object","required": [-- "item_id",-- "source_key",-- "category",-- "kind","canonical_url",-- "title",-- "revision",-- "content_hash",-- "retrieved_at",-- "previous_content_hash"++ "publication_state",++ "indexability",++ "space",++ "slug",++ "visibility"],"additionalProperties": false,"properties": {-- "item_id": {-- "type": "string",-- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "source_key": {++ "canonical_url": {"type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"-- },-- "category": {-- "enum": [-- "news",-- "blog",-- "reviews",-- "deals",-- "coupons",-- "trade"-- ]++ "format": "uri",++ "pattern": "^https?://",++ "maxLength": 2048},-- "kind": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,39}$",-- "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."++ "publication_state": {++ "const": "published"},-- "canonical_url": {-- "type": [-- "string",-- "null"++ "indexability": {++ "type": "object",++ "required": [++ "decision",++ "reasons"],-- "format": "uri"-- },-- "title": {-- "type": [-- "string",-- "null"-- ]-- },-- "revision": {-- "type": "integer",-- "minimum": 1++ "additionalProperties": false,++ "properties": {++ "decision": {++ "enum": [++ "index",++ "noindex"++ ]++ },++ "reasons": {++ "type": "array",++ "maxItems": 20,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,63}$"++ }++ }++ }},-- "content_hash": {++ "space": {"type": "string",-- "pattern": "^[0-9a-f]{64}$"++ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",++ "description": "Space slug."},-- "retrieved_at": {++ "slug": {"type": "string",-- "format": "date-time",-- "description": "Last successful fetch (or manual entry) that contained the item."++ "minLength": 1,++ "maxLength": 80,++ "description": "Page slug inside the space."},-- "previous_content_hash": {-- "type": "string",-- "pattern": "^[0-9a-f]{64}$"++ "visibility": {++ "enum": [++ "public",++ "members",++ "private"++ ],++ "description": "Effective visibility (the page's, narrowed by its space's)."}}}},-- "sources.item.removed": {++ "wiki.page.unpublished": {"version": "1.0.0",-- "owner": "sources",++ "owner": "wiki","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/sources.item.removed.v1.json",-- "title": "SourcesItemRemovedPayload",-- "description": "sources.item.removed v1 (OpenVibe.Sources server/items.js remove → itemEvent, from DELETE /api/v1/items/:id). Staff removed an item (takedown, licence, error). Sticky: a later fetch that still lists it does not bring it back. The removal bumps the revision; content_hash is the last content's. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.unpublished.v1.json",++ "title": "WikiPageUnpublishedPayload",++ "description": "wiki.page.unpublished v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).","type": "object","required": [-- "item_id",-- "source_key",-- "category",-- "kind","canonical_url",-- "title",-- "revision",-- "content_hash",-- "retrieved_at",-- "reason"++ "publication_state",++ "indexability",++ "space",++ "slug",++ "visibility"],"additionalProperties": false,"properties": {-- "item_id": {-- "type": "string",-- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "source_key": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"-- },-- "category": {-- "enum": [-- "news",-- "blog",-- "reviews",-- "deals",-- "coupons",-- "trade"-- ]-- },-- "kind": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,39}$",-- "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."-- },"canonical_url": {"type": ["string","null"],-- "format": "uri"++ "description": "null: the event carries a tombstone, which has no URL."},-- "title": {++ "publication_state": {++ "const": "unpublished"++ },++ "indexability": {"type": [-- "string",++ "object","null"-- ]-- },-- "revision": {-- "type": "integer",-- "minimum": 1-- },-- "content_hash": {-- "type": "string",-- "pattern": "^[0-9a-f]{64}$"++ ],++ "required": [++ "decision",++ "reasons"++ ],++ "additionalProperties": false,++ "properties": {++ "decision": {++ "enum": [++ "index",++ "noindex"++ ]++ },++ "reasons": {++ "type": "array",++ "maxItems": 20,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,63}$"++ }++ }++ },++ "description": "The last gate decision, or null when there is none."},-- "retrieved_at": {++ "space": {"type": "string",-- "format": "date-time",-- "description": "Last successful fetch (or manual entry) that contained the item."++ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",++ "description": "Space slug."},-- "reason": {++ "slug": {"type": "string","minLength": 1,-- "maxLength": 300++ "maxLength": 80,++ "description": "Page slug inside the space."++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "private"++ ],++ "description": "Effective visibility (the page's, narrowed by its space's)."}}}},-- "sources.fetch.failed": {++ "wiki.page.deleted": {"version": "1.0.0",-- "owner": "sources",++ "owner": "wiki","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/sources.fetch.failed.v1.json",-- "title": "SourcesFetchFailedPayload",-- "description": "sources.fetch.failed v1 (OpenVibe.Sources server/ingest.js failedEvent). One endpoint fetch of a source failed and counts as a failure: robots denied or unreachable, an HTTP error, the site's 429, a parse error or an internal error. Not sent for 304, a disabled source, a missing credential or the local rate limit. No item is created or changed. Envelope: subject { type: source, id: <source_key> }, visibility internal, priority important, actor service:sources.",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.deleted.v1.json",++ "title": "WikiPageDeletedPayload",++ "description": "wiki.page.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page (or its whole space) was deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).","type": "object","required": [-- "source_key",-- "category",-- "run_id",-- "endpoint_url",-- "state",-- "error_code",-- "http_status",-- "consecutive_failures"++ "canonical_url",++ "publication_state",++ "indexability",++ "space",++ "slug",++ "visibility"],"additionalProperties": false,"properties": {-- "source_key": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"-- },-- "category": {-- "enum": [-- "news",-- "blog",-- "reviews",-- "deals",-- "coupons",-- "trade"-- ]++ "canonical_url": {++ "type": [++ "string",++ "null"++ ],++ "description": "null: the event carries a tombstone, which has no URL."},-- "run_id": {++ "publication_state": {++ "const": "deleted"++ },++ "indexability": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "decision",++ "reasons"++ ],++ "additionalProperties": false,++ "properties": {++ "decision": {++ "enum": [++ "index",++ "noindex"++ ]++ },++ "reasons": {++ "type": "array",++ "maxItems": 20,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,63}$"++ }++ }++ },++ "description": "The last gate decision, or null when there is none."++ },++ "space": {"type": "string",-- "pattern": "^frn_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The recorded fetch run (GET /api/v1/sources/:key/runs)."++ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",++ "description": "Space slug."},-- "endpoint_url": {++ "slug": {"type": "string","minLength": 1,-- "maxLength": 2048++ "maxLength": 80,++ "description": "Page slug inside the space."},-- "state": {++ "visibility": {"enum": [-- "robots_denied",-- "http_error",-- "rate_limited",-- "parse_error"-- ]-- },-- "error_code": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,39}$",-- "description": "robots | address_refused | port_refused | bad_url | upstream_429 | http_<status> | unreadable | adapter_error | internal."-- },-- "http_status": {-- "type": [-- "integer",-- "null"++ "public",++ "members",++ "private"],-- "minimum": 100,-- "maximum": 599-- },-- "consecutive_failures": {-- "type": "integer",-- "minimum": 1,-- "description": "The source's consecutive failed runs including this one."++ "description": "Effective visibility (the page's, narrowed by its space's)."}}}},-- "sources.index_document.upserted": {++ "wiki.index_document.upserted": {"version": "1.0.0",-- "owner": "sources",++ "owner": "wiki","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.upserted.v1.json",-- "title": "SourcesIndexDocumentUpsertedPayload",-- "description": "sources.index_document.upserted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was created or updated; the payload is its search.index-document@1 (owner sources, type item, visibility members for role:admin and role:global_mod, authorship imported, indexability noindex third_party_content). Consumed by OpenVibe.Search (*.index_document.*). Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.",-- "$ref": "../../search/index-document.v1.json"++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.upserted.v1.json",++ "title": "WikiIndexDocumentUpsertedPayload",++ "description": "wiki.index_document.upserted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable page, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision: <document revision> }, visibility internal, priority important, actor service:wiki.",++ "allOf": [++ {++ "$ref": "../../search/index-document.v1.json"++ },++ {++ "type": "object",++ "required": [++ "owner",++ "type",++ "deleted"++ ],++ "properties": {++ "owner": {++ "const": "wiki"++ },++ "type": {++ "const": "page"++ },++ "id": {++ "type": "string",++ "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "deleted": {++ "const": false++ }++ }++ }++ ]}},-- "sources.index_document.deleted": {++ "wiki.index_document.deleted": {"version": "1.0.0",-- "owner": "sources",++ "owner": "wiki","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.deleted.v1.json",-- "title": "SourcesIndexDocumentDeletedPayload",-- "description": "sources.index_document.deleted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was removed; OpenVibe.Search stores a tombstone at this revision. Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.",++ "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.deleted.v1.json",++ "title": "WikiIndexDocumentDeletedPayload",++ "description": "wiki.index_document.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the page is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a page Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision }, visibility internal, priority important, actor service:wiki.","type": "object","required": ["type",@@@ -3722,406 +5053,359 @@"additionalProperties": false,"properties": {"type": {-- "const": "item"++ "const": "page"},"id": {"type": "string",-- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"},"revision": {"type": "integer",-- "minimum": 1++ "minimum": 1,++ "maximum": 9007199254740991,++ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."}}}},-- "search.document.indexed": {++ "live.index_document.upserted": {"version": "1.0.0",-- "owner": "search",++ "owner": "live","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/search.document.indexed.v1.json",-- "title": "SearchDocumentIndexedPayload",-- "description": "search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",++ "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.upserted.v1.json",++ "title": "LiveIndexDocumentUpsertedPayload",++ "description": "live.index_document.upserted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). The OpenVibe.Search document of one of Live's canonical pages: a streamer's channel (type channel, openvibe.live/@username: its name, bio, what it streams and whether it is live), or a public VOD or clip of a Live stream (types vod and clip, openvibe.live/vod/<id> and /clip/<id>: title, channel, AI overview and transcript; the id is the Media id; AI clips are noindex). Sent whenever what Search should hold changed; a banned channel, or a VOD or clip that is deleted, made private or failed, gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:live.",++ "allOf": [++ {++ "$ref": "../../search/index-document.v1.json"++ },++ {++ "type": "object",++ "required": [++ "owner",++ "type",++ "deleted"++ ],++ "properties": {++ "owner": {++ "const": "live"++ },++ "type": {++ "enum": [++ "channel",++ "vod",++ "clip"++ ]++ },++ "id": {++ "type": "string",++ "pattern": "^[0-9]{1,15}$"++ },++ "deleted": {++ "const": false++ }++ }++ }++ ]++ }++ },++ "live.index_document.deleted": {++ "version": "1.0.0",++ "owner": "live",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.deleted.v1.json",++ "title": "LiveIndexDocumentDeletedPayload",++ "description": "live.index_document.deleted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:live.","type": "object","required": [-- "owner","type","id",-- "revision",-- "exposure",-- "reindexed",-- "canonical_url"++ "revision"],"additionalProperties": false,"properties": {-- "owner": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9-]{1,39}$"-- },"type": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,39}$"++ "enum": [++ "channel",++ "vod",++ "clip"++ ]},"id": {"type": "string",-- "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"++ "pattern": "^[0-9]{1,15}$"},"revision": {"type": "integer",-- "minimum": 0,-- "maximum": 9007199254740991-- },-- "exposure": {-- "enum": [-- "restricted",-- "public_unlisted",-- "public_listed"-- ]-- },-- "reindexed": {-- "type": "boolean",-- "description": "true when Search already held an earlier revision of the document."++ "minimum": 1,++ "maximum": 9007199254740991,++ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."++ }++ }++ }++ },++ "media.index_document.upserted": {++ "version": "1.0.0",++ "owner": "media",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.upserted.v1.json",++ "title": "MediaIndexDocumentUpsertedPayload",++ "description": "media.index_document.upserted v1 (OpenVibe.Media; roadmap WS-O task 10). The OpenVibe.Search document of a public, playable VOD or clip whose canonical page is on openvibe.media (openvibe.media/v/<id>, /c/<id>): readiness playable, visibility public, made by a person, and owned by an app other than Live (Live's own VODs and clips are live.index_document, type vod or clip, on Live's pages). Sent whenever what Search should hold changed; a VOD or clip that is deleted, made private or stops being playable gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:media.",++ "allOf": [++ {++ "$ref": "../../search/index-document.v1.json"},-- "canonical_url": {-- "type": [-- "string",-- "null"++ {++ "type": "object",++ "required": [++ "owner",++ "type",++ "deleted"],-- "description": "Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none."++ "properties": {++ "owner": {++ "const": "media"++ },++ "type": {++ "enum": [++ "vod",++ "clip"++ ]++ },++ "id": {++ "type": "string",++ "pattern": "^[0-9]{1,15}$"++ },++ "deleted": {++ "const": false++ }++ }}-- }++ ]}},-- "search.document.removed": {++ "media.index_document.deleted": {"version": "1.0.0",-- "owner": "search",++ "owner": "media","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/search.document.removed.v1.json",-- "title": "SearchDocumentRemovedPayload",-- "description": "search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",++ "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.deleted.v1.json",++ "title": "MediaIndexDocumentDeletedPayload",++ "description": "media.index_document.deleted v1 (OpenVibe.Media; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:media.","type": "object","required": [-- "owner","type","id",-- "revision",-- "reason",-- "previous_exposure",-- "exposure",-- "canonical_url"++ "revision"],"additionalProperties": false,"properties": {-- "owner": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9-]{1,39}$"-- },"type": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,39}$"++ "enum": [++ "vod",++ "clip"++ ]},"id": {"type": "string",-- "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"++ "pattern": "^[0-9]{1,15}$"},"revision": {"type": "integer",-- "minimum": 0,-- "maximum": 9007199254740991-- },-- "reason": {-- "enum": [-- "deleted",-- "draft",-- "not_published",-- "visibility_changed",-- "noindex"-- ]-- },-- "previous_exposure": {-- "enum": [-- "restricted",-- "public_unlisted",-- "public_listed"-- ]-- },-- "exposure": {-- "enum": [-- "none",-- "restricted",-- "public_unlisted"-- ],-- "description": "Lower than previous_exposure. none: no longer served to anyone."-- },-- "canonical_url": {-- "type": [-- "string",-- "null"-- ],-- "description": "The URL only when the document was public before (so it must be purged); null otherwise."++ "minimum": 1,++ "maximum": 9007199254740991,++ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."}}}},-- "wiki.space.updated": {++ "community.index_document.upserted": {"version": "1.0.0",-- "owner": "wiki",++ "owner": "community","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.space.updated.v1.json",-- "title": "WikiSpaceUpdatedPayload",-- "description": "wiki.space.updated v1 (OpenVibe.Wiki server/wiki/service.js spaceEvent). Emitted when a space is created, renamed, changes visibility or settings, has its roles changed, or is deleted, in the transaction of the change. Envelope: subject { type: space, id: spc_… }, visibility public for a public space that is not deleted, otherwise internal, priority important, actor the person (user) or service.",-- "type": "object",-- "required": [-- "slug",-- "name",-- "kind",-- "visibility",-- "deleted",-- "change",-- "url"-- ],-- "additionalProperties": false,-- "properties": {-- "slug": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$"-- },-- "name": {-- "type": "string",-- "minLength": 1,-- "maxLength": 200-- },-- "kind": {-- "enum": [-- "official",-- "user"-- ]-- },-- "visibility": {-- "enum": [-- "public",-- "members",-- "private"-- ]-- },-- "deleted": {-- "type": "boolean"++ "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.upserted.v1.json",++ "title": "CommunityIndexDocumentUpsertedPayload",++ "description": "community.index_document.upserted v1 (OpenVibe.Community; roadmap WS-O task 10). The OpenVibe.Search document of A public forum thread (in a public, open space) or a public paste on openvibe.community. Sent whenever what Search should hold changed; a thread or paste that is deleted, made private or members-only, or burns after reading gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:community.",++ "allOf": [++ {++ "$ref": "../../search/index-document.v1.json"},-- "change": {-- "enum": [-- "created",-- "renamed",-- "visibility",-- "settings",-- "roles",++ {++ "type": "object",++ "required": [++ "owner",++ "type","deleted"],-- "description": "What changed. renamed = a new slug."-- },-- "url": {-- "type": "string",-- "format": "uri",-- "pattern": "^https?://",-- "maxLength": 2048,-- "description": "Canonical URL of the space."++ "properties": {++ "owner": {++ "const": "community"++ },++ "type": {++ "enum": [++ "thread",++ "paste"++ ]++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_-]{1,80}$"++ },++ "deleted": {++ "const": false++ }++ }}-- }++ ]}},-- "wiki.revision.created": {++ "community.index_document.deleted": {"version": "1.0.0",-- "owner": "wiki",++ "owner": "community","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.revision.created.v1.json",-- "title": "WikiRevisionCreatedPayload",-- "description": "wiki.revision.created v1 (OpenVibe.Wiki server/wiki/service.js revisionEvent). Emitted when a page gets a new immutable revision: an edit, a revert or an AI proposal. A new revision is a draft until it is published (then wiki.page.published|updated follows). Envelope: subject { type: page, id: pg_…, revision: <number> }, visibility internal, priority important, actor the person or service.",++ "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.deleted.v1.json",++ "title": "CommunityIndexDocumentDeletedPayload",++ "description": "community.index_document.deleted v1 (OpenVibe.Community; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:community.","type": "object","required": [-- "space_id",-- "space",-- "slug",-- "number",-- "kind",-- "parent_number",-- "reverted_to",-- "authorship",-- "author",-- "message"++ "type",++ "id",++ "revision"],"additionalProperties": false,"properties": {-- "space_id": {-- "type": "string",-- "pattern": "^spc_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "space": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",-- "description": "Space slug."-- },-- "slug": {-- "type": "string",-- "minLength": 1,-- "maxLength": 80,-- "description": "Page slug."-- },-- "number": {-- "type": "integer",-- "minimum": 1,-- "description": "The new revision number."-- },-- "kind": {-- "enum": [-- "edit",-- "revert",-- "import"-- ]-- },-- "parent_number": {-- "type": [-- "integer",-- "null"-- ],-- "minimum": 1-- },-- "reverted_to": {-- "type": [-- "integer",-- "null"-- ],-- "minimum": 1,-- "description": "The revision a revert restored; null otherwise."-- },-- "authorship": {++ "type": {"enum": [-- "human",-- "ai_assisted",-- "ai_generated",-- "imported",-- null-- ],-- "description": "Search's authorship vocabulary; null when the revision has no authorship record."-- },-- "author": {-- "type": [-- "string",-- "null"-- ],-- "maxLength": 200,-- "description": "Who wrote it: a subject id (usr_…) or a service (svc:<id>)."-- },-- "message": {-- "type": [-- "string",-- "null"-- ],-- "maxLength": 2000++ "thread",++ "paste"++ ]},-- "proposal_id": {++ "id": {"type": "string",-- "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "Present when the revision is an AI proposal awaiting a person's approval."++ "pattern": "^[A-Za-z0-9_-]{1,80}$"++ },++ "revision": {++ "type": "integer",++ "minimum": 1,++ "maximum": 9007199254740991,++ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."}}}},-- "wiki.watch.triggered": {++ "blog.post.created": {"version": "1.0.0",-- "owner": "wiki",++ "owner": "blog","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.watch.triggered.v1.json",-- "title": "WikiWatchTriggeredPayload",-- "description": "wiki.watch.triggered v1 (OpenVibe.Wiki server/wiki/service.js notifyWatchers). One event per change to a watched page, listing the watchers who may read the page (never the actor); not emitted when nobody is left. Envelope: subject { type: page, id: pg_…, revision: <revision or 0> }, visibility internal, priority important, actor the person or service.",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.created.v1.json",++ "title": "BlogPostCreatedPayload",++ "description": "blog.post.created v1 (OpenVibe.Blog server/domain/posts.js create). A post exists as a draft (its first revision). Envelope: subject { type: post, id: pst_…, revision: 1 }, visibility internal, priority low, actor the person or the calling service.","type": "object","required": [-- "action",-- "space",-- "slug",-- "title",-- "revision",-- "url",-- "recipients"++ "blog",++ "state",++ "visibility",++ "authorship"],"additionalProperties": false,"properties": {-- "action": {-- "enum": [-- "published",-- "updated",-- "unpublished",-- "deleted",-- "revised",-- "proposed"++ "blog": {++ "type": "object",++ "required": [++ "id",++ "handle"],-- "description": "revised = a new draft revision; proposed = an AI proposal awaits approval; the rest follow wiki.page.*."-- },-- "space": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",-- "description": "Space slug."++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "handle": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"++ }++ }},-- "slug": {-- "type": "string",-- "minLength": 1,-- "maxLength": 80,-- "description": "Page slug."++ "state": {++ "const": "draft"},-- "title": {-- "type": "string",-- "maxLength": 200++ "visibility": {++ "enum": [++ "public",++ "unlisted",++ "members",++ "private"++ ]},-- "revision": {-- "type": [-- "integer",-- "null"++ "authorship": {++ "enum": [++ "human",++ "ai",++ "hybrid",++ "imported"],-- "minimum": 1-- },-- "url": {-- "type": "string",-- "format": "uri",-- "pattern": "^https?://",-- "maxLength": 2048++ "description": "openvibe-publishing authorship mode (not Search's vocabulary)."},-- "recipients": {-- "type": "array",-- "minItems": 1,-- "uniqueItems": true,-- "items": {-- "type": "string",-- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "description": "Watchers to notify."++ "workflow": {++ "type": "object",++ "required": [++ "id",++ "runId"++ ],++ "additionalProperties": false,++ "description": "The OpenVibe.AI workflow and run behind ai and hybrid posts; absent for human posts.",++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"++ },++ "runId": {++ "type": "string",++ "minLength": 1++ },++ "version": {++ "type": [++ "integer",++ "string"++ ]++ },++ "model": {++ "type": "string"++ }++ }}}}},-- "wiki.page.published": {++ "blog.post.published": {"version": "1.0.0",-- "owner": "wiki",++ "owner": "blog","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.published.v1.json",-- "title": "WikiPagePublishedPayload",-- "description": "wiki.page.published v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page became published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.published.v1.json",++ "title": "BlogPostPublishedPayload",++ "description": "blog.post.published v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post became published (now or by its schedule), in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).","type": "object","required": ["canonical_url","publication_state","indexability",-- "space",-- "slug",++ "blog","visibility"],"additionalProperties": false,@@@ -4160,44 +5444,51 @@}}},-- "space": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",-- "description": "Space slug."-- },-- "slug": {-- "type": "string",-- "minLength": 1,-- "maxLength": 80,-- "description": "Page slug inside the space."++ "blog": {++ "type": "object",++ "required": [++ "id",++ "handle"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "handle": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"++ }++ }},"visibility": {"enum": ["public",++ "unlisted","members","private"],-- "description": "Effective visibility (the page's, narrowed by its space's)."++ "description": "The post's own visibility (members = entitlement-gated)."}}}},-- "wiki.page.updated": {++ "blog.post.updated": {"version": "1.0.0",-- "owner": "wiki",++ "owner": "blog","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.updated.v1.json",-- "title": "WikiPageUpdatedPayload",-- "description": "wiki.page.updated v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page got a new published revision or visibility, or a review changed what Search holds, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.updated.v1.json",++ "title": "BlogPostUpdatedPayload",++ "description": "blog.post.updated v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post got a new published revision, visibility or URL, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).","type": "object","required": ["canonical_url","publication_state","indexability",-- "space",-- "slug",++ "blog","visibility"],"additionalProperties": false,@@@ -4236,44 +5527,51 @@}}},-- "space": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",-- "description": "Space slug."-- },-- "slug": {-- "type": "string",-- "minLength": 1,-- "maxLength": 80,-- "description": "Page slug inside the space."++ "blog": {++ "type": "object",++ "required": [++ "id",++ "handle"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "handle": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"++ }++ }},"visibility": {"enum": ["public",++ "unlisted","members","private"],-- "description": "Effective visibility (the page's, narrowed by its space's)."++ "description": "The post's own visibility (members = entitlement-gated)."}}}},-- "wiki.page.unpublished": {++ "blog.post.unpublished": {"version": "1.0.0",-- "owner": "wiki",++ "owner": "blog","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.unpublished.v1.json",-- "title": "WikiPageUnpublishedPayload",-- "description": "wiki.page.unpublished v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.unpublished.v1.json",++ "title": "BlogPostUnpublishedPayload",++ "description": "blog.post.unpublished v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).","type": "object","required": ["canonical_url","publication_state","indexability",-- "space",-- "slug",++ "blog","visibility"],"additionalProperties": false,@@@ -4305,56 +5603,63 @@"noindex"]},-- "reasons": {-- "type": "array",-- "maxItems": 20,-- "uniqueItems": true,-- "items": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,63}$"-- }++ "reasons": {++ "type": "array",++ "maxItems": 20,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,63}$"++ }++ }++ },++ "description": "The last gate decision, or null when there is none."++ },++ "blog": {++ "type": "object",++ "required": [++ "id",++ "handle"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "handle": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"}-- },-- "description": "The last gate decision, or null when there is none."-- },-- "space": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",-- "description": "Space slug."-- },-- "slug": {-- "type": "string",-- "minLength": 1,-- "maxLength": 80,-- "description": "Page slug inside the space."++ }},"visibility": {"enum": ["public",++ "unlisted","members","private"],-- "description": "Effective visibility (the page's, narrowed by its space's)."++ "description": "The post's own visibility (members = entitlement-gated)."}}}},-- "wiki.page.deleted": {++ "blog.post.deleted": {"version": "1.0.0",-- "owner": "wiki",++ "owner": "blog","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.deleted.v1.json",-- "title": "WikiPageDeletedPayload",-- "description": "wiki.page.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page (or its whole space) was deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.deleted.v1.json",++ "title": "BlogPostDeletedPayload",++ "description": "blog.post.deleted v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post was soft-deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).","type": "object","required": ["canonical_url","publication_state","indexability",-- "space",-- "slug",++ "blog","visibility"],"additionalProperties": false,@@@ -4398,189 +5703,112 @@},"description": "The last gate decision, or null when there is none."},-- "space": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",-- "description": "Space slug."-- },-- "slug": {-- "type": "string",-- "minLength": 1,-- "maxLength": 80,-- "description": "Page slug inside the space."-- },-- "visibility": {-- "enum": [-- "public",-- "members",-- "private"-- ],-- "description": "Effective visibility (the page's, narrowed by its space's)."-- }-- }-- }-- },-- "wiki.index_document.upserted": {-- "version": "1.0.0",-- "owner": "wiki",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.upserted.v1.json",-- "title": "WikiIndexDocumentUpsertedPayload",-- "description": "wiki.index_document.upserted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable page, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision: <document revision> }, visibility internal, priority important, actor service:wiki.",-- "allOf": [-- {-- "$ref": "../../search/index-document.v1.json"-- },-- {++ "blog": {"type": "object","required": [-- "owner",-- "type",-- "deleted"++ "id",++ "handle"],++ "additionalProperties": false,"properties": {-- "owner": {-- "const": "wiki"-- },-- "type": {-- "const": "page"-- },"id": {"type": "string",-- "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"},-- "deleted": {-- "const": false++ "handle": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"}}-- }-- ]-- }-- },-- "wiki.index_document.deleted": {-- "version": "1.0.0",-- "owner": "wiki",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.deleted.v1.json",-- "title": "WikiIndexDocumentDeletedPayload",-- "description": "wiki.index_document.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the page is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a page Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision }, visibility internal, priority important, actor service:wiki.",-- "type": "object",-- "required": [-- "type",-- "id",-- "revision"-- ],-- "additionalProperties": false,-- "properties": {-- "type": {-- "const": "page"-- },-- "id": {-- "type": "string",-- "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "revision": {-- "type": "integer",-- "minimum": 1,-- "maximum": 9007199254740991,-- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."-- }-- }-- }-- },-- "live.index_document.upserted": {-- "version": "1.0.0",-- "owner": "live",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.upserted.v1.json",-- "title": "LiveIndexDocumentUpsertedPayload",-- "description": "live.index_document.upserted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). The OpenVibe.Search document of one of Live's canonical pages: a streamer's channel (type channel, openvibe.live/@username: its name, bio, what it streams and whether it is live), or a public VOD or clip of a Live stream (types vod and clip, openvibe.live/vod/<id> and /clip/<id>: title, channel, AI overview and transcript; the id is the Media id; AI clips are noindex). Sent whenever what Search should hold changed; a banned channel, or a VOD or clip that is deleted, made private or failed, gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:live.",-- "allOf": [-- {-- "$ref": "../../search/index-document.v1.json"},-- {-- "type": "object",-- "required": [-- "owner",-- "type",-- "deleted"++ "visibility": {++ "enum": [++ "public",++ "unlisted",++ "members",++ "private"],-- "properties": {-- "owner": {-- "const": "live"-- },-- "type": {-- "enum": [-- "channel",-- "vod",-- "clip"-- ]-- },-- "id": {-- "type": "string",-- "pattern": "^[0-9]{1,15}$"-- },-- "deleted": {-- "const": false-- }-- }++ "description": "The post's own visibility (members = entitlement-gated)."}-- ]++ }}},-- "live.index_document.deleted": {++ "blog.schedule.failed": {"version": "1.0.0",-- "owner": "live",++ "owner": "blog","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.deleted.v1.json",-- "title": "LiveIndexDocumentDeletedPayload",-- "description": "live.index_document.deleted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:live.",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.schedule.failed.v1.json",++ "title": "BlogScheduleFailedPayload",++ "description": "blog.schedule.failed v1 (OpenVibe.Blog server/domain/posts.js runScheduled). A scheduled publish or unpublish gave up after its retries; a scheduled post goes back to draft. Envelope: subject { type: post, id: pst_…, revision?: <scheduled revision> }, visibility internal, priority important, actor service:blog.","type": "object","required": [-- "type",-- "id",-- "revision"++ "job_id",++ "action",++ "revision",++ "run_at",++ "attempts",++ "error",++ "blog_id"],"additionalProperties": false,"properties": {-- "type": {++ "job_id": {++ "type": "string",++ "pattern": "^job_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "action": {"enum": [-- "channel",-- "vod",-- "clip"++ "publish",++ "unpublish"]},-- "id": {++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision a publish was to make live; null for an unpublish."++ },++ "run_at": {"type": "string",-- "pattern": "^[0-9]{1,15}$"++ "format": "date-time",++ "description": "When the job was last due."},-- "revision": {++ "attempts": {"type": "integer",-- "minimum": 1,-- "maximum": 9007199254740991,-- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."++ "minimum": 0++ },++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 1000,++ "description": "The last error."++ },++ "blog_id": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "null when the post no longer exists."}}}},-- "media.index_document.upserted": {++ "blog.index_document.upserted": {"version": "1.0.0",-- "owner": "media",++ "owner": "blog","status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.upserted.v1.json",-- "title": "MediaIndexDocumentUpsertedPayload",-- "description": "media.index_document.upserted v1 (OpenVibe.Media; roadmap WS-O task 10). The OpenVibe.Search document of a public, playable VOD or clip whose canonical page is on openvibe.media (openvibe.media/v/<id>, /c/<id>): readiness playable, visibility public, made by a person, and owned by an app other than Live (Live's own VODs and clips are live.index_document, type vod or clip, on Live's pages). Sent whenever what Search should hold changed; a VOD or clip that is deleted, made private or stops being playable gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:media.",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.upserted.v1.json",++ "title": "BlogIndexDocumentUpsertedPayload",++ "description": "blog.index_document.upserted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable post, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision: <document revision> }, visibility internal, priority important, actor service:blog.","allOf": [{"$ref": "../../search/index-document.v1.json"@@@ -4594,17 +5822,14 @@],"properties": {"owner": {-- "const": "media"++ "const": "blog"},"type": {-- "enum": [-- "vod",-- "clip"-- ]++ "const": "post"},"id": {"type": "string",-- "pattern": "^[0-9]{1,15}$"++ "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"},"deleted": {"const": false@@@ -4614,15 +5839,15 @@]}},-- "media.index_document.deleted": {++ "blog.index_document.deleted": {"version": "1.0.0",-- "owner": "media",++ "owner": "blog","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.deleted.v1.json",-- "title": "MediaIndexDocumentDeletedPayload",-- "description": "media.index_document.deleted v1 (OpenVibe.Media; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:media.",++ "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.deleted.v1.json",++ "title": "BlogIndexDocumentDeletedPayload",++ "description": "blog.index_document.deleted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the post is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a post Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision }, visibility internal, priority important, actor service:blog.","type": "object","required": ["type",@@@ -4632,14 +5857,11 @@"additionalProperties": false,"properties": {"type": {-- "enum": [-- "vod",-- "clip"-- ]++ "const": "post"},"id": {"type": "string",-- "pattern": "^[0-9]{1,15}$"++ "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"},"revision": {"type": "integer",@@@ -4650,766 +5872,964 @@}}},-- "community.index_document.upserted": {-- "version": "1.0.0",-- "owner": "community",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.upserted.v1.json",-- "title": "CommunityIndexDocumentUpsertedPayload",-- "description": "community.index_document.upserted v1 (OpenVibe.Community; roadmap WS-O task 10). The OpenVibe.Search document of A public forum thread (in a public, open space) or a public paste on openvibe.community. Sent whenever what Search should hold changed; a thread or paste that is deleted, made private or members-only, or burns after reading gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:community.",-- "allOf": [-- {-- "$ref": "../../search/index-document.v1.json"-- },-- {-- "type": "object",-- "required": [-- "owner",-- "type",-- "deleted"-- ],-- "properties": {-- "owner": {-- "const": "community"-- },-- "type": {-- "enum": [-- "thread",-- "paste"-- ]-- },-- "id": {-- "type": "string",-- "pattern": "^[A-Za-z0-9_-]{1,80}$"-- },-- "deleted": {-- "const": false-- }-- }-- }-- ]-- }-- },-- "community.index_document.deleted": {++ "ai.run-request": {"version": "1.0.0",-- "owner": "community",++ "owner": "ai","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.deleted.v1.json",-- "title": "CommunityIndexDocumentDeletedPayload",-- "description": "community.index_document.deleted v1 (OpenVibe.Community; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:community.",++ "$id": "https://openvibe.network/contracts/ai/run-request.v1.json",++ "title": "AiRunRequest",++ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.","type": "object","required": [-- "type",-- "id",-- "revision"++ "workflow"],"additionalProperties": false,"properties": {-- "type": {-- "enum": [-- "thread",-- "paste"-- ]-- },-- "id": {++ "workflow": {"type": "string",-- "pattern": "^[A-Za-z0-9_-]{1,80}$"++ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)+$",++ "maxLength": 200,++ "description": "Workflow key, <namespace>.<name> (live.chat_reply, news.summarize_story, network.site_copy…)."},-- "revision": {++ "version": {"type": "integer","minimum": 1,-- "maximum": 9007199254740991,-- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."++ "description": "Pin a workflow version (active or deprecated). Omitted: the newest active version."++ },++ "input": {++ "type": "object",++ "description": "Workflow input; validated against the workflow version's input_schema. Default {}."++ },++ "target": {++ "$ref": "../common/entity-ref.v1.json"++ },++ "attribution": {++ "$ref": "../common/entity-ref.v1.json"++ },++ "on_behalf_of": {++ "$ref": "../identity/subject-ref.v1.json"++ },++ "idempotency_key": {++ "type": "string",++ "pattern": "^[A-Za-z0-9._:-]{8,128}$",++ "description": "Same (requester, key) returns the existing run; a different workflow or input under it is 409 idempotency.conflict. The Idempotency-Key header is used when this is absent."++ },++ "options": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "cache": {++ "type": "boolean",++ "default": true,++ "description": "false skips the scoped cache lookup and store."++ },++ "debug": {++ "type": "boolean",++ "default": false,++ "description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."++ }++ }}}}},-- "blog.post.created": {++ "ai.run": {"version": "1.0.0",-- "owner": "blog",++ "owner": "ai","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.created.v1.json",-- "title": "BlogPostCreatedPayload",-- "description": "blog.post.created v1 (OpenVibe.Blog server/domain/posts.js create). A post exists as a draft (its first revision). Envelope: subject { type: post, id: pst_…, revision: 1 }, visibility internal, priority low, actor the person or the calling service.",++ "$id": "https://openvibe.network/contracts/ai/run.v1.json",++ "title": "AiRun",++ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted.","type": "object","required": [-- "blog",-- "state",-- "visibility",-- "authorship"++ "id",++ "status",++ "workflow",++ "template",++ "route",++ "requester",++ "on_behalf_of",++ "attribution",++ "source_service",++ "target",++ "input",++ "output",++ "error",++ "synthetic",++ "provenance",++ "usage",++ "citations_count",++ "retry_of",++ "idempotency_key",++ "trace_id",++ "created_at",++ "started_at",++ "finished_at"],"additionalProperties": false,"properties": {-- "blog": {++ "id": {++ "type": "string",++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "status": {++ "enum": [++ "queued",++ "running",++ "succeeded",++ "failed",++ "cancelled",++ "cached"++ ],++ "description": "Terminal: succeeded, failed, cancelled, cached (a scoped cache hit, no provider call; provenance.cached_from names the run it reused)."++ },++ "workflow": {"type": "object","required": [-- "id",-- "handle"++ "key",++ "version"],"additionalProperties": false,"properties": {-- "id": {-- "type": "string",-- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ "key": {++ "type": "string"},-- "handle": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"++ "version": {++ "type": "integer",++ "minimum": 1}}},-- "state": {-- "const": "draft"-- },-- "visibility": {-- "enum": [-- "public",-- "unlisted",-- "members",-- "private"-- ]-- },-- "authorship": {-- "enum": [-- "human",-- "ai",-- "hybrid",-- "imported"++ "template": {++ "type": [++ "object",++ "null"],-- "description": "openvibe-publishing authorship mode (not Search's vocabulary)."++ "required": [++ "key",++ "version"++ ],++ "additionalProperties": false,++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": [++ "integer",++ "null"++ ]++ }++ },++ "description": "The prompt template of the workflow's llm step; null for workflows without one."},-- "workflow": {-- "type": "object",++ "route": {++ "type": [++ "object",++ "null"++ ],"required": [-- "id",-- "runId"++ "key",++ "version"],"additionalProperties": false,-- "description": "The OpenVibe.AI workflow and run behind ai and hybrid posts; absent for human posts.","properties": {-- "id": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"-- },-- "runId": {-- "type": "string",-- "minLength": 1++ "key": {++ "type": "string"},"version": {"type": ["integer",-- "string"++ "null"]++ }++ },++ "description": "Provider route; version null while the route is unresolved or disabled."++ },++ "requester": {++ "$ref": "../identity/subject-ref.v1.json"++ },++ "on_behalf_of": {++ "anyOf": [++ {++ "$ref": "../identity/subject-ref.v1.json"},-- "model": {-- "type": "string"++ {++ "type": "null"}-- }-- }-- }-- }-- },-- "blog.post.published": {-- "version": "1.0.0",-- "owner": "blog",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.published.v1.json",-- "title": "BlogPostPublishedPayload",-- "description": "blog.post.published v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post became published (now or by its schedule), in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).",-- "type": "object",-- "required": [-- "canonical_url",-- "publication_state",-- "indexability",-- "blog",-- "visibility"-- ],-- "additionalProperties": false,-- "properties": {-- "canonical_url": {-- "type": "string",-- "format": "uri",-- "pattern": "^https?://",-- "maxLength": 2048++ ]},-- "publication_state": {-- "const": "published"++ "attribution": {++ "anyOf": [++ {++ "$ref": "../common/entity-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]},-- "indexability": {-- "type": "object",-- "required": [-- "decision",-- "reasons"++ "source_service": {++ "type": [++ "string",++ "null"],-- "additionalProperties": false,-- "properties": {-- "decision": {-- "enum": [-- "index",-- "noindex"-- ]++ "description": "The requester's service id when the requester is a service."++ },++ "target": {++ "anyOf": [++ {++ "$ref": "../common/entity-ref.v1.json"},-- "reasons": {-- "type": "array",-- "maxItems": 20,-- "uniqueItems": true,-- "items": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,63}$"-- }++ {++ "type": "null"}-- }++ ]},-- "blog": {++ "input": {"type": "object",++ "description": "The retained input (see the description)."++ },++ "output": {++ "type": [++ "object",++ "null"++ ],++ "description": "Workflow output, or null."++ },++ "error": {++ "type": [++ "object",++ "null"++ ],"required": [-- "id",-- "handle"++ "code",++ "detail"],"additionalProperties": false,"properties": {-- "id": {++ "code": {"type": "string",-- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",++ "description": "run.cancelled, run.interrupted, run.internal, output.invalid, output.empty, provider.unavailable, route.unavailable, source.unavailable, media.unresolvable…"},-- "handle": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"++ "detail": {++ "type": [++ "string",++ "null"++ ]}}},-- "visibility": {-- "enum": [-- "public",-- "unlisted",-- "members",-- "private"-- ],-- "description": "The post's own visibility (members = entitlement-gated)."-- }-- }-- }-- },-- "blog.post.updated": {-- "version": "1.0.0",-- "owner": "blog",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.updated.v1.json",-- "title": "BlogPostUpdatedPayload",-- "description": "blog.post.updated v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post got a new published revision, visibility or URL, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).",-- "type": "object",-- "required": [-- "canonical_url",-- "publication_state",-- "indexability",-- "blog",-- "visibility"-- ],-- "additionalProperties": false,-- "properties": {-- "canonical_url": {-- "type": "string",-- "format": "uri",-- "pattern": "^https?://",-- "maxLength": 2048-- },-- "publication_state": {-- "const": "published"++ "synthetic": {++ "type": "boolean",++ "description": "Produced by the stub provider; never served to production callers and never cached."},-- "indexability": {++ "provenance": {"type": "object",++ "additionalProperties": false,"required": [-- "decision",-- "reasons"++ "origin",++ "workflow",++ "workflow_version",++ "template_version",++ "route",++ "route_version",++ "provider",++ "model",++ "fallback_used",++ "run_id",++ "cached_from",++ "synthetic"],-- "additionalProperties": false,"properties": {-- "decision": {-- "enum": [-- "index",-- "noindex"++ "origin": {++ "const": "ai"++ },++ "workflow": {++ "type": "string"++ },++ "workflow_version": {++ "type": "integer",++ "minimum": 1++ },++ "template_version": {++ "type": [++ "integer",++ "null"]},-- "reasons": {-- "type": "array",-- "maxItems": 20,-- "uniqueItems": true,-- "items": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,63}$"-- }++ "route": {++ "type": [++ "string",++ "null"++ ]++ },++ "route_version": {++ "type": [++ "integer",++ "null"++ ]++ },++ "provider": {++ "type": [++ "string",++ "null"++ ]++ },++ "model": {++ "type": [++ "string",++ "null"++ ]++ },++ "fallback_used": {++ "type": "boolean",++ "description": "The primary provider was degraded and the run fell back (ADR-015: fallbacks are recorded)."++ },++ "run_id": {++ "type": "string",++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "cached_from": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "synthetic": {++ "type": "boolean"}}},-- "blog": {++ "usage": {"type": "object","required": [-- "id",-- "handle"++ "tokens_in",++ "tokens_out",++ "cost_usd",++ "attempts"],"additionalProperties": false,"properties": {-- "id": {-- "type": "string",-- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ "tokens_in": {++ "type": "integer",++ "minimum": 0},-- "handle": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"++ "tokens_out": {++ "type": "integer",++ "minimum": 0++ },++ "cost_usd": {++ "type": "number",++ "minimum": 0++ },++ "attempts": {++ "type": "integer",++ "minimum": 0,++ "description": "Provider calls made (skipped routes not counted)."}}},-- "visibility": {-- "enum": [-- "public",-- "unlisted",-- "members",-- "private"-- ],-- "description": "The post's own visibility (members = entitlement-gated)."-- }-- }-- }-- },-- "blog.post.unpublished": {-- "version": "1.0.0",-- "owner": "blog",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.unpublished.v1.json",-- "title": "BlogPostUnpublishedPayload",-- "description": "blog.post.unpublished v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).",-- "type": "object",-- "required": [-- "canonical_url",-- "publication_state",-- "indexability",-- "blog",-- "visibility"-- ],-- "additionalProperties": false,-- "properties": {-- "canonical_url": {++ "citations_count": {++ "type": "integer",++ "minimum": 0,++ "description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."++ },++ "retry_of": {"type": ["string","null"],-- "description": "null: the event carries a tombstone, which has no URL."++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"},-- "publication_state": {-- "const": "unpublished"++ "idempotency_key": {++ "type": [++ "string",++ "null"++ ]},-- "indexability": {++ "trace_id": {"type": [-- "object",++ "string","null"-- ],-- "required": [-- "decision",-- "reasons"-- ],-- "additionalProperties": false,-- "properties": {-- "decision": {-- "enum": [-- "index",-- "noindex"-- ]-- },-- "reasons": {-- "type": "array",-- "maxItems": 20,-- "uniqueItems": true,-- "items": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,63}$"-- }-- }-- },-- "description": "The last gate decision, or null when there is none."++ ]},-- "blog": {-- "type": "object",-- "required": [-- "id",-- "handle"++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "started_at": {++ "type": [++ "string",++ "null"],-- "additionalProperties": false,-- "properties": {-- "id": {-- "type": "string",-- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "handle": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"-- }-- }++ "format": "date-time"},-- "visibility": {-- "enum": [-- "public",-- "unlisted",-- "members",-- "private"++ "finished_at": {++ "type": [++ "string",++ "null"],-- "description": "The post's own visibility (members = entitlement-gated)."++ "format": "date-time"}}}},-- "blog.post.deleted": {++ "ai.run.queued": {"version": "1.0.0",-- "owner": "blog",-- "status": "active",++ "owner": "ai",++ "status": "planned","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.deleted.v1.json",-- "title": "BlogPostDeletedPayload",-- "description": "blog.post.deleted v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post was soft-deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).",++ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.queued.v1.json",++ "title": "AiRunQueuedPayload",++ "description": "ai.run.queued v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run was stored queued (it then runs inline within ?wait or on the worker). Not published for a cache hit (ai.run.cached) or a create refused by quota or a full queue. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.","type": "object","required": [-- "canonical_url",-- "publication_state",-- "indexability",-- "blog",-- "visibility"++ "run_id",++ "status",++ "workflow",++ "requester",++ "on_behalf_of",++ "target",++ "attribution",++ "provider",++ "model",++ "fallback_used",++ "synthetic",++ "cached_from",++ "usage",++ "error",++ "citations_count",++ "retry_of",++ "created_at",++ "finished_at"],"additionalProperties": false,"properties": {-- "canonical_url": {-- "type": [-- "string",-- "null"-- ],-- "description": "null: the event carries a tombstone, which has no URL."++ "run_id": {++ "type": "string",++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"},-- "publication_state": {-- "const": "deleted"++ "status": {++ "const": "queued"},-- "indexability": {-- "type": [-- "object",-- "null"-- ],++ "workflow": {++ "type": "object","required": [-- "decision",-- "reasons"++ "key",++ "version"],"additionalProperties": false,"properties": {-- "decision": {-- "enum": [-- "index",-- "noindex"-- ]++ "key": {++ "type": "string"},-- "reasons": {-- "type": "array",-- "maxItems": 20,-- "uniqueItems": true,-- "items": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,63}$"-- }++ "version": {++ "type": "integer",++ "minimum": 1++ }++ }++ },++ "requester": {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ "on_behalf_of": {++ "anyOf": [++ {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "target": {++ "anyOf": [++ {++ "$ref": "../../common/entity-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "attribution": {++ "anyOf": [++ {++ "$ref": "../../common/entity-ref.v1.json"++ },++ {++ "type": "null"}-- },-- "description": "The last gate decision, or null when there is none."++ ]},-- "blog": {++ "provider": {++ "type": [++ "string",++ "null"++ ],++ "description": "Provider that produced the output; null before a provider call and for cached runs."++ },++ "model": {++ "type": [++ "string",++ "null"++ ]++ },++ "fallback_used": {++ "type": "boolean"++ },++ "synthetic": {++ "type": "boolean",++ "description": "Stub provider output."++ },++ "cached_from": {++ "type": "null"++ },++ "usage": {"type": "object","required": [-- "id",-- "handle"++ "tokens_in",++ "tokens_out",++ "cost_usd",++ "attempts"],"additionalProperties": false,"properties": {-- "id": {-- "type": "string",-- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"++ "tokens_in": {++ "type": "integer",++ "minimum": 0},-- "handle": {-- "type": "string",-- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"++ "tokens_out": {++ "type": "integer",++ "minimum": 0++ },++ "cost_usd": {++ "type": "number",++ "minimum": 0++ },++ "attempts": {++ "type": "integer",++ "minimum": 0}}},-- "visibility": {-- "enum": [-- "public",-- "unlisted",-- "members",-- "private"++ "error": {++ "type": "null"++ },++ "citations_count": {++ "type": "integer",++ "minimum": 0++ },++ "retry_of": {++ "type": [++ "string",++ "null"],-- "description": "The post's own visibility (members = entitlement-gated)."++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "finished_at": {++ "type": "null"}}}},-- "blog.schedule.failed": {++ "ai.run.succeeded": {"version": "1.0.0",-- "owner": "blog",-- "status": "active",++ "owner": "ai",++ "status": "planned","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.schedule.failed.v1.json",-- "title": "BlogScheduleFailedPayload",-- "description": "blog.schedule.failed v1 (OpenVibe.Blog server/domain/posts.js runScheduled). A scheduled publish or unpublish gave up after its retries; a scheduled post goes back to draft. Envelope: subject { type: post, id: pst_…, revision?: <scheduled revision> }, visibility internal, priority important, actor service:blog.",++ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.succeeded.v1.json",++ "title": "AiRunSucceededPayload",++ "description": "ai.run.succeeded v1 (OpenVibe.AI server/runs.js execute). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run finished with output that matched the workflow output schema. The output itself is not in the event: a consumer that needs it reads GET /api/v1/runs/:id with ai.run.read (own runs only). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.","type": "object","required": [-- "job_id",-- "action",-- "revision",-- "run_at",-- "attempts",++ "run_id",++ "status",++ "workflow",++ "requester",++ "on_behalf_of",++ "target",++ "attribution",++ "provider",++ "model",++ "fallback_used",++ "synthetic",++ "cached_from",++ "usage","error",-- "blog_id"++ "citations_count",++ "retry_of",++ "created_at",++ "finished_at"],"additionalProperties": false,"properties": {-- "job_id": {++ "run_id": {"type": "string",-- "pattern": "^job_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"},-- "action": {-- "enum": [-- "publish",-- "unpublish"-- ]++ "status": {++ "const": "succeeded"},-- "revision": {-- "type": [-- "integer",-- "null"++ "workflow": {++ "type": "object",++ "required": [++ "key",++ "version"],-- "minimum": 1,-- "description": "The revision a publish was to make live; null for an unpublish."++ "additionalProperties": false,++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ }++ }},-- "run_at": {-- "type": "string",-- "format": "date-time",-- "description": "When the job was last due."++ "requester": {++ "$ref": "../../identity/subject-ref.v1.json"},-- "attempts": {-- "type": "integer",-- "minimum": 0++ "on_behalf_of": {++ "anyOf": [++ {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]},-- "error": {++ "target": {++ "anyOf": [++ {++ "$ref": "../../common/entity-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "attribution": {++ "anyOf": [++ {++ "$ref": "../../common/entity-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "provider": {"type": ["string","null"],-- "maxLength": 1000,-- "description": "The last error."++ "description": "Provider that produced the output; null before a provider call and for cached runs."},-- "blog_id": {++ "model": {"type": ["string","null"-- ],-- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "null when the post no longer exists."-- }-- }-- }-- },-- "blog.index_document.upserted": {-- "version": "1.0.0",-- "owner": "blog",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.upserted.v1.json",-- "title": "BlogIndexDocumentUpsertedPayload",-- "description": "blog.index_document.upserted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable post, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision: <document revision> }, visibility internal, priority important, actor service:blog.",-- "allOf": [-- {-- "$ref": "../../search/index-document.v1.json"++ ]},-- {++ "fallback_used": {++ "type": "boolean"++ },++ "synthetic": {++ "type": "boolean",++ "description": "Stub provider output."++ },++ "cached_from": {++ "type": "null"++ },++ "usage": {"type": "object","required": [-- "owner",-- "type",-- "deleted"++ "tokens_in",++ "tokens_out",++ "cost_usd",++ "attempts"],++ "additionalProperties": false,"properties": {-- "owner": {-- "const": "blog"++ "tokens_in": {++ "type": "integer",++ "minimum": 0},-- "type": {-- "const": "post"++ "tokens_out": {++ "type": "integer",++ "minimum": 0},-- "id": {-- "type": "string",-- "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"++ "cost_usd": {++ "type": "number",++ "minimum": 0},-- "deleted": {-- "const": false++ "attempts": {++ "type": "integer",++ "minimum": 0}}-- }-- ]-- }-- },-- "blog.index_document.deleted": {-- "version": "1.0.0",-- "owner": "blog",-- "status": "active",-- "schema": {-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.deleted.v1.json",-- "title": "BlogIndexDocumentDeletedPayload",-- "description": "blog.index_document.deleted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the post is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a post Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision }, visibility internal, priority important, actor service:blog.",-- "type": "object",-- "required": [-- "type",-- "id",-- "revision"-- ],-- "additionalProperties": false,-- "properties": {-- "type": {-- "const": "post"},-- "id": {-- "type": "string",-- "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"++ "error": {++ "type": "null"},-- "revision": {++ "citations_count": {"type": "integer",-- "minimum": 1,-- "maximum": 9007199254740991,-- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."++ "minimum": 0++ },++ "retry_of": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "finished_at": {++ "type": "string",++ "format": "date-time"}}}},-- "ai.run-request": {++ "ai.run.failed": {"version": "1.0.0","owner": "ai",-- "status": "active",++ "status": "planned","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/ai/run-request.v1.json",-- "title": "AiRunRequest",-- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",++ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.failed.v1.json",++ "title": "AiRunFailedPayload",++ "description": "ai.run.failed v1 (OpenVibe.AI server/runs.js execute / recoverInterrupted). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run ended failed: a provider, route, source or output error, run.internal, or run.interrupted after a restart or shutdown (retryable with POST /api/v1/runs/:id/retry unless its input was not retained). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.","type": "object","required": [-- "workflow"++ "run_id",++ "status",++ "workflow",++ "requester",++ "on_behalf_of",++ "target",++ "attribution",++ "provider",++ "model",++ "fallback_used",++ "synthetic",++ "cached_from",++ "usage",++ "error",++ "citations_count",++ "retry_of",++ "created_at",++ "finished_at"],"additionalProperties": false,"properties": {-- "workflow": {++ "run_id": {"type": "string",-- "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)+$",-- "maxLength": 200,-- "description": "Workflow key, <namespace>.<name> (live.chat_reply, news.summarize_story, network.site_copy…)."++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"},-- "version": {-- "type": "integer",-- "minimum": 1,-- "description": "Pin a workflow version (active or deprecated). Omitted: the newest active version."++ "status": {++ "const": "failed"},-- "input": {++ "workflow": {"type": "object",-- "description": "Workflow input; validated against the workflow version's input_schema. Default {}."++ "required": [++ "key",++ "version"++ ],++ "additionalProperties": false,++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ }++ }++ },++ "requester": {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ "on_behalf_of": {++ "anyOf": [++ {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]},"target": {-- "$ref": "../common/entity-ref.v1.json"++ "anyOf": [++ {++ "$ref": "../../common/entity-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]},"attribution": {-- "$ref": "../common/entity-ref.v1.json"++ "anyOf": [++ {++ "$ref": "../../common/entity-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]},-- "on_behalf_of": {-- "$ref": "../identity/subject-ref.v1.json"++ "provider": {++ "type": [++ "string",++ "null"++ ],++ "description": "Provider that produced the output; null before a provider call and for cached runs."},-- "idempotency_key": {-- "type": "string",-- "pattern": "^[A-Za-z0-9._:-]{8,128}$",-- "description": "Same (requester, key) returns the existing run; a different workflow or input under it is 409 idempotency.conflict. The Idempotency-Key header is used when this is absent."++ "model": {++ "type": [++ "string",++ "null"++ ]},-- "options": {++ "fallback_used": {++ "type": "boolean"++ },++ "synthetic": {++ "type": "boolean",++ "description": "Stub provider output."++ },++ "cached_from": {++ "type": "null"++ },++ "usage": {"type": "object",++ "required": [++ "tokens_in",++ "tokens_out",++ "cost_usd",++ "attempts"++ ],"additionalProperties": false,"properties": {-- "cache": {-- "type": "boolean",-- "default": true,-- "description": "false skips the scoped cache lookup and store."++ "tokens_in": {++ "type": "integer",++ "minimum": 0},-- "debug": {-- "type": "boolean",-- "default": false,-- "description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."++ "tokens_out": {++ "type": "integer",++ "minimum": 0++ },++ "cost_usd": {++ "type": "number",++ "minimum": 0++ },++ "attempts": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "error": {++ "type": "object",++ "required": [++ "code",++ "detail"++ ],++ "additionalProperties": false,++ "properties": {++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"++ },++ "detail": {++ "type": [++ "string",++ "null"++ ]}}++ },++ "citations_count": {++ "type": "integer",++ "minimum": 0++ },++ "retry_of": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "finished_at": {++ "type": "string",++ "format": "date-time"}}}},-- "ai.run": {++ "ai.run.cached": {"version": "1.0.0","owner": "ai",-- "status": "active",++ "status": "planned","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/ai/run.v1.json",-- "title": "AiRun",-- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted.",++ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.cached.v1.json",++ "title": "AiRunCachedPayload",++ "description": "ai.run.cached v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A create was served from the scoped cache: no provider call, cached_from names the run whose output was reused. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.","type": "object","required": [-- "id",++ "run_id","status","workflow",-- "template",-- "route","requester","on_behalf_of",-- "attribution",-- "source_service","target",-- "input",-- "output",-- "error",++ "attribution",++ "provider",++ "model",++ "fallback_used","synthetic",-- "provenance",++ "cached_from","usage",++ "error","citations_count","retry_of",-- "idempotency_key",-- "trace_id","created_at",-- "started_at","finished_at"],"additionalProperties": false,"properties": {-- "id": {++ "run_id": {"type": "string","pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"},"status": {-- "enum": [-- "queued",-- "running",-- "succeeded",-- "failed",-- "cancelled",-- "cached"-- ],-- "description": "Terminal: succeeded, failed, cancelled, cached (a scoped cache hit, no provider call; provenance.cached_from names the run it reused)."++ "const": "cached"},"workflow": {"type": "object",@@@ -5428,958 +6848,1329 @@}}},-- "template": {-- "type": [-- "object",-- "null"-- ],-- "required": [-- "key",-- "version"-- ],-- "additionalProperties": false,-- "properties": {-- "key": {-- "type": "string"-- },-- "version": {-- "type": [-- "integer",-- "null"-- ]-- }-- },-- "description": "The prompt template of the workflow's llm step; null for workflows without one."-- },-- "route": {-- "type": [-- "object",-- "null"-- ],-- "required": [-- "key",-- "version"-- ],-- "additionalProperties": false,-- "properties": {-- "key": {-- "type": "string"-- },-- "version": {-- "type": [-- "integer",-- "null"-- ]-- }-- },-- "description": "Provider route; version null while the route is unresolved or disabled."-- },"requester": {-- "$ref": "../identity/subject-ref.v1.json"++ "$ref": "../../identity/subject-ref.v1.json"},"on_behalf_of": {"anyOf": [{-- "$ref": "../identity/subject-ref.v1.json"++ "$ref": "../../identity/subject-ref.v1.json"},{"type": "null"}]},-- "attribution": {++ "target": {"anyOf": [{-- "$ref": "../common/entity-ref.v1.json"++ "$ref": "../../common/entity-ref.v1.json"},{"type": "null"}]},-- "source_service": {-- "type": [-- "string",-- "null"-- ],-- "description": "The requester's service id when the requester is a service."-- },-- "target": {++ "attribution": {"anyOf": [{-- "$ref": "../common/entity-ref.v1.json"++ "$ref": "../../common/entity-ref.v1.json"},{"type": "null"}]},-- "input": {-- "type": "object",-- "description": "The retained input (see the description)."-- },-- "output": {++ "provider": {"type": [-- "object",++ "string","null"],-- "description": "Workflow output, or null."++ "description": "Provider that produced the output; null before a provider call and for cached runs."},-- "error": {++ "model": {"type": [-- "object",++ "string","null"-- ],++ ]++ },++ "fallback_used": {++ "type": "boolean"++ },++ "synthetic": {++ "type": "boolean",++ "description": "Stub provider output."++ },++ "cached_from": {++ "type": "string",++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "usage": {++ "type": "object","required": [-- "code",-- "detail"++ "tokens_in",++ "tokens_out",++ "cost_usd",++ "attempts"],"additionalProperties": false,"properties": {-- "code": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",-- "description": "run.cancelled, run.interrupted, run.internal, output.invalid, output.empty, provider.unavailable, route.unavailable, source.unavailable, media.unresolvable…"++ "tokens_in": {++ "type": "integer",++ "minimum": 0},-- "detail": {++ "tokens_out": {++ "type": "integer",++ "minimum": 0++ },++ "cost_usd": {++ "type": "number",++ "minimum": 0++ },++ "attempts": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "error": {++ "type": "null"++ },++ "citations_count": {++ "type": "integer",++ "minimum": 0++ },++ "retry_of": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "finished_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },++ "ai.definition-version-request": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/definition-version-request.v1.json",++ "title": "AiDefinitionVersionRequest",++ "description": "ai.definition-version-request@1: bodies of ai.workflow.manage on OpenVibe.AI. An edit is always a new version (the previous one stays readable); fields left out are taken from the newest existing version. POST /api/v1/templates/:key/versions: a prompt template (input_schema and output_schema must compile as JSON Schema; visibility public, first-party or internal). POST /api/v1/workflows/:key/versions (key <namespace>.<name>): steps (at least one; kind llm (naming an existing template), passthrough, transcribe or embed), schemas, default route, cache mode. POST /api/v1/routes/:key/versions: primary { provider, model } and fallbacks (every provider must exist), or alias_of another route. POST …/:key/versions/:version/status: { status } (templates and workflows draft|active|deprecated|archived; routes active|disabled). Refusals are 422 ai.invalid.",++ "anyOf": [++ {++ "type": "object",++ "properties": {++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "input_schema": {++ "type": "object",++ "description": "A JSON Schema the run input must match."++ },++ "output_schema": {++ "type": "object",++ "description": "A JSON Schema the output must match."++ },++ "system_prompt": {++ "type": "string"++ },++ "user_prompt": {++ "type": "string"++ },++ "default_route": {"type": ["string","null"]++ },++ "owner": {++ "type": "string"++ },++ "visibility": {++ "enum": [++ "public",++ "first-party",++ "internal"++ ]++ },++ "status": {++ "enum": [++ "draft",++ "active",++ "deprecated",++ "archived"++ ]++ },++ "metadata": {++ "type": "object"}}},-- "synthetic": {-- "type": "boolean",-- "description": "Produced by the stub provider; never served to production callers and never cached."-- },-- "provenance": {++ {"type": "object",-- "additionalProperties": false,-- "required": [-- "origin",-- "workflow",-- "workflow_version",-- "template_version",-- "route",-- "route_version",-- "provider",-- "model",-- "fallback_used",-- "run_id",-- "cached_from",-- "synthetic"-- ],"properties": {-- "origin": {-- "const": "ai"++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "namespace": {++ "type": "string"++ },++ "input_schema": {++ "type": "object",++ "description": "A JSON Schema."++ },++ "output_schema": {++ "type": "object",++ "description": "A JSON Schema."++ },++ "steps": {++ "type": "array",++ "minItems": 1,++ "items": {++ "type": "object",++ "required": [++ "kind"++ ],++ "properties": {++ "kind": {++ "enum": [++ "llm",++ "passthrough",++ "transcribe",++ "embed"++ ]++ },++ "template": {++ "type": "string"++ }++ }++ }},-- "workflow": {-- "type": "string"++ "default_route": {++ "type": [++ "string",++ "null"++ ]},-- "workflow_version": {-- "type": "integer",-- "minimum": 1++ "cache_mode": {++ "enum": [++ "none",++ "private",++ "service"++ ]},-- "template_version": {++ "cache_ttl_sec": {"type": ["integer","null"]},-- "route": {-- "type": [-- "string",-- "null"++ "status": {++ "enum": [++ "draft",++ "active",++ "deprecated",++ "archived"]},-- "route_version": {++ "metadata": {++ "type": "object"++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "primary": {++ "type": "object",++ "required": [++ "provider"++ ],++ "properties": {++ "provider": {++ "type": "string"++ },++ "model": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ "fallbacks": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "provider"++ ],++ "properties": {++ "provider": {++ "type": "string"++ },++ "model": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ }++ },++ "options": {++ "type": "object"++ },++ "max_output_tokens": {"type": ["integer","null"]},-- "provider": {-- "type": [-- "string",-- "null"++ "response_format": {++ "enum": [++ "text",++ "json"]},-- "model": {++ "timeout_ms": {"type": [-- "string",++ "integer","null"]},-- "fallback_used": {-- "type": "boolean",-- "description": "The primary provider was degraded and the run fell back (ADR-015: fallbacks are recorded)."-- },-- "run_id": {-- "type": "string",-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "cached_from": {++ "alias_of": {"type": ["string","null"-- ],-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "synthetic": {-- "type": "boolean"-- }-- }-- },-- "usage": {-- "type": "object",-- "required": [-- "tokens_in",-- "tokens_out",-- "cost_usd",-- "attempts"-- ],-- "additionalProperties": false,-- "properties": {-- "tokens_in": {-- "type": "integer",-- "minimum": 0-- },-- "tokens_out": {-- "type": "integer",-- "minimum": 0-- },-- "cost_usd": {-- "type": "number",-- "minimum": 0++ ]},-- "attempts": {-- "type": "integer",-- "minimum": 0,-- "description": "Provider calls made (skipped routes not counted)."++ "status": {++ "enum": [++ "active",++ "disabled"++ ]}}-- },-- "citations_count": {-- "type": "integer",-- "minimum": 0,-- "description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."-- },-- "retry_of": {-- "type": [-- "string",-- "null"-- ],-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "idempotency_key": {-- "type": [-- "string",-- "null"-- ]-- },-- "trace_id": {-- "type": [-- "string",-- "null"-- ]-- },-- "created_at": {-- "type": "string",-- "format": "date-time"-- },-- "started_at": {-- "type": [-- "string",-- "null"-- ],-- "format": "date-time"-- },-- "finished_at": {-- "type": [-- "string",-- "null"-- ],-- "format": "date-time"}-- }++ ]}},-- "ai.run.queued": {++ "ai.definition-version-result": {"version": "1.0.0","owner": "ai",-- "status": "planned",++ "status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.queued.v1.json",-- "title": "AiRunQueuedPayload",-- "description": "ai.run.queued v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run was stored queued (it then runs inline within ?wait or on the worker). Not published for a cache hit (ai.run.cached) or a create refused by quota or a full queue. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.",-- "type": "object",-- "required": [-- "run_id",-- "status",-- "workflow",-- "requester",-- "on_behalf_of",-- "target",-- "attribution",-- "provider",-- "model",-- "fallback_used",-- "synthetic",-- "cached_from",-- "usage",-- "error",-- "citations_count",-- "retry_of",-- "created_at",-- "finished_at"-- ],-- "additionalProperties": false,-- "properties": {-- "run_id": {-- "type": "string",-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "status": {-- "const": "queued"-- },-- "workflow": {++ "$id": "https://openvibe.network/contracts/ai/definition-version-result.v1.json",++ "title": "AiDefinitionVersionResult",++ "description": "ai.definition-version-result@1: answers of ai.workflow.manage on OpenVibe.AI. POST …/:key/versions → 201 { template | workflow | route } (the new version, whole: prompts and schemas for a template, steps for a workflow, primary/fallbacks for a route); POST …/versions/:version/status → { template | workflow | route: { key, version, status } }.",++ "anyOf": [++ {"type": "object","required": [-- "key",-- "version"++ "template"],-- "additionalProperties": false,"properties": {-- "key": {-- "type": "string"-- },-- "version": {-- "type": "integer",-- "minimum": 1-- }-- }-- },-- "requester": {-- "$ref": "../../identity/subject-ref.v1.json"-- },-- "on_behalf_of": {-- "anyOf": [-- {-- "$ref": "../../identity/subject-ref.v1.json"-- },-- {-- "type": "null"-- }-- ]-- },-- "target": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"-- },-- {-- "type": "null"-- }-- ]-- },-- "attribution": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"-- },-- {-- "type": "null"++ "template": {++ "type": "object",++ "required": [++ "key",++ "version",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ },++ "status": {++ "type": "string"++ }++ }}-- ]-- },-- "provider": {-- "type": [-- "string",-- "null"-- ],-- "description": "Provider that produced the output; null before a provider call and for cached runs."-- },-- "model": {-- "type": [-- "string",-- "null"-- ]-- },-- "fallback_used": {-- "type": "boolean"-- },-- "synthetic": {-- "type": "boolean",-- "description": "Stub provider output."-- },-- "cached_from": {-- "type": "null"++ }},-- "usage": {++ {"type": "object","required": [-- "tokens_in",-- "tokens_out",-- "cost_usd",-- "attempts"++ "workflow"],-- "additionalProperties": false,"properties": {-- "tokens_in": {-- "type": "integer",-- "minimum": 0-- },-- "tokens_out": {-- "type": "integer",-- "minimum": 0-- },-- "cost_usd": {-- "type": "number",-- "minimum": 0-- },-- "attempts": {-- "type": "integer",-- "minimum": 0++ "workflow": {++ "type": "object",++ "required": [++ "key",++ "version",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ },++ "status": {++ "type": "string"++ }++ }}}},-- "error": {-- "type": "null"-- },-- "citations_count": {-- "type": "integer",-- "minimum": 0-- },-- "retry_of": {-- "type": [-- "string",-- "null"++ {++ "type": "object",++ "required": [++ "route"],-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "created_at": {-- "type": "string",-- "format": "date-time"-- },-- "finished_at": {-- "type": "null"++ "properties": {++ "route": {++ "type": "object",++ "required": [++ "key",++ "version",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "version": {++ "type": "integer",++ "minimum": 1++ },++ "status": {++ "type": "string"++ }++ }++ }++ }}-- }++ ]}},-- "ai.run.succeeded": {++ "ai.model": {"version": "1.0.0","owner": "ai",-- "status": "planned",++ "status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.succeeded.v1.json",-- "title": "AiRunSucceededPayload",-- "description": "ai.run.succeeded v1 (OpenVibe.AI server/runs.js execute). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run finished with output that matched the workflow output schema. The output itself is not in the event: a consumer that needs it reads GET /api/v1/runs/:id with ai.run.read (own runs only). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.",++ "$id": "https://openvibe.network/contracts/ai/model.v1.json",++ "title": "AiModel",++ "description": "ai.model@1: one model of a provider in OpenVibe.AI's registry (server/registry.js decodeModel): type, status, limits, per-million-token costs and what it supports.","type": "object","required": [-- "run_id",++ "provider_key",++ "model_key",++ "type","status",-- "workflow",-- "requester",-- "on_behalf_of",-- "target",-- "attribution",-- "provider",-- "model",-- "fallback_used",-- "synthetic",-- "cached_from",-- "usage",-- "error",-- "citations_count",-- "retry_of",-- "created_at",-- "finished_at"++ "cost",++ "supports"],-- "additionalProperties": false,"properties": {-- "run_id": {-- "type": "string",-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "status": {-- "const": "succeeded"-- },-- "workflow": {-- "type": "object",-- "required": [-- "key",-- "version"-- ],-- "additionalProperties": false,-- "properties": {-- "key": {-- "type": "string"-- },-- "version": {-- "type": "integer",-- "minimum": 1-- }-- }++ "provider_key": {++ "type": "string"},-- "requester": {-- "$ref": "../../identity/subject-ref.v1.json"++ "model_key": {++ "type": "string",++ "maxLength": 200},-- "on_behalf_of": {-- "anyOf": [-- {-- "$ref": "../../identity/subject-ref.v1.json"-- },-- {-- "type": "null"-- }++ "display_name": {++ "type": [++ "string",++ "null"]},-- "target": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"-- },-- {-- "type": "null"-- }++ "type": {++ "enum": [++ "chat",++ "vision",++ "embedding",++ "stt"]},-- "attribution": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"-- },-- {-- "type": "null"-- }++ "status": {++ "enum": [++ "active",++ "disabled"]},-- "provider": {++ "context_window": {"type": [-- "string",++ "integer","null"-- ],-- "description": "Provider that produced the output; null before a provider call and for cached runs."++ ]},-- "model": {++ "max_output": {"type": [-- "string",++ "integer","null"]},-- "fallback_used": {-- "type": "boolean"-- },-- "synthetic": {-- "type": "boolean",-- "description": "Stub provider output."-- },-- "cached_from": {-- "type": "null"++ "cost": {++ "type": "object",++ "properties": {++ "in_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "out_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "cached_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ }++ }},-- "usage": {++ "supports": {"type": "object",-- "required": [-- "tokens_in",-- "tokens_out",-- "cost_usd",-- "attempts"-- ],-- "additionalProperties": false,"properties": {-- "tokens_in": {-- "type": "integer",-- "minimum": 0++ "json": {++ "type": "boolean"},-- "tokens_out": {-- "type": "integer",-- "minimum": 0++ "tools": {++ "type": "boolean"},-- "cost_usd": {-- "type": "number",-- "minimum": 0++ "streaming": {++ "type": "boolean"},-- "attempts": {-- "type": "integer",-- "minimum": 0++ "vision": {++ "type": "boolean"}}},-- "error": {-- "type": "null"-- },-- "citations_count": {-- "type": "integer",-- "minimum": 0++ "metadata": {++ "type": "object"},-- "retry_of": {++ "created_at": {"type": ["string","null"-- ],-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "created_at": {-- "type": "string",-- "format": "date-time"++ ]},-- "finished_at": {-- "type": "string",-- "format": "date-time"++ "updated_at": {++ "type": [++ "string",++ "null"++ ]}}}},-- "ai.run.failed": {++ "ai.provider-manage-request": {"version": "1.0.0","owner": "ai",-- "status": "planned",++ "status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.failed.v1.json",-- "title": "AiRunFailedPayload",-- "description": "ai.run.failed v1 (OpenVibe.AI server/runs.js execute / recoverInterrupted). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run ended failed: a provider, route, source or output error, run.internal, or run.interrupted after a restart or shutdown (retryable with POST /api/v1/runs/:id/retry unless its input was not retained). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.",-- "type": "object",-- "required": [-- "run_id",-- "status",-- "workflow",-- "requester",-- "on_behalf_of",-- "target",-- "attribution",-- "provider",-- "model",-- "fallback_used",-- "synthetic",-- "cached_from",-- "usage",-- "error",-- "citations_count",-- "retry_of",-- "created_at",-- "finished_at"-- ],-- "additionalProperties": false,-- "properties": {-- "run_id": {-- "type": "string",-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "status": {-- "const": "failed"-- },-- "workflow": {++ "$id": "https://openvibe.network/contracts/ai/provider-manage-request.v1.json",++ "title": "AiProviderManageRequest",++ "description": "ai.provider-manage-request@1: bodies of ai.provider.manage on OpenVibe.AI. POST /api/v1/providers creates or replaces a provider (key required) and PATCH /api/v1/providers/:key changes one (fields left out keep their value); a body carrying api_key or secret is refused (422: send secret_ref env:NAME, never a secret value). POST /api/v1/models (provider_key and model_key required) and PATCH /api/v1/models/:provider/:model upsert a model. POST /api/v1/quotas upserts a quota (scope_type and window required; scope_id too unless global; the same scope, window and workflow_prefix is one quota). POST /api/v1/providers/:key/disable|enable|reset and DELETE /api/v1/cache[?workflow=] take no body. Unknown fields are ignored.",++ "anyOf": [++ {"type": "object",-- "required": [-- "key",-- "version"-- ],-- "additionalProperties": false,++ "not": {++ "anyOf": [++ {++ "required": [++ "api_key"++ ]++ },++ {++ "required": [++ "secret"++ ]++ }++ ]++ },"properties": {"key": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"++ },++ "display_name": {"type": "string"},-- "version": {-- "type": "integer",-- "minimum": 1-- }-- }-- },-- "requester": {-- "$ref": "../../identity/subject-ref.v1.json"-- },-- "on_behalf_of": {-- "anyOf": [-- {-- "$ref": "../../identity/subject-ref.v1.json"++ "kind": {++ "enum": [++ "stub",++ "openai",++ "anthropic",++ "http",++ "whisper"++ ]},-- {-- "type": "null"-- }-- ]-- },-- "target": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"++ "status": {++ "enum": [++ "active",++ "disabled"++ ]},-- {-- "type": "null"-- }-- ]-- },-- "attribution": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"++ "base_url": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^https?://\\S+$"},-- {-- "type": "null"++ "auth_mode": {++ "enum": [++ "none",++ "bearer",++ "x-api-key"++ ]++ },++ "secret_ref": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^(env:[A-Z][A-Z0-9_]{0,63})?$"++ },++ "default_model": {++ "type": [++ "string",++ "null"++ ]++ },++ "capabilities": {++ "type": "array",++ "items": {++ "enum": [++ "chat",++ "generate",++ "summarize",++ "classify",++ "extract",++ "enrich",++ "embed",++ "vision",++ "json",++ "transcribe"++ ]++ }++ },++ "timeout_ms": {++ "type": [++ "integer",++ "string"++ ],++ "description": "Clamped to 1000..600000."++ },++ "priority": {++ "type": [++ "number",++ "string"++ ]++ },++ "metadata": {++ "type": "object"}-- ]-- },-- "provider": {-- "type": [-- "string",-- "null"-- ],-- "description": "Provider that produced the output; null before a provider call and for cached runs."-- },-- "model": {-- "type": [-- "string",-- "null"-- ]-- },-- "fallback_used": {-- "type": "boolean"-- },-- "synthetic": {-- "type": "boolean",-- "description": "Stub provider output."-- },-- "cached_from": {-- "type": "null"++ }},-- "usage": {++ {"type": "object",-- "required": [-- "tokens_in",-- "tokens_out",-- "cost_usd",-- "attempts"-- ],-- "additionalProperties": false,"properties": {-- "tokens_in": {-- "type": "integer",-- "minimum": 0++ "provider_key": {++ "type": "string"},-- "tokens_out": {-- "type": "integer",-- "minimum": 0++ "model_key": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200},-- "cost_usd": {-- "type": "number",-- "minimum": 0++ "display_name": {++ "type": "string"},-- "attempts": {-- "type": "integer",-- "minimum": 0++ "type": {++ "enum": [++ "chat",++ "vision",++ "embedding",++ "stt"++ ]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]++ },++ "context_window": {++ "type": [++ "integer",++ "null"++ ]++ },++ "max_output": {++ "type": [++ "integer",++ "null"++ ]++ },++ "cost": {++ "type": "object",++ "properties": {++ "in_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "out_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ },++ "cached_per_mtok": {++ "type": [++ "number",++ "null"++ ]++ }++ }++ },++ "supports": {++ "type": "object",++ "properties": {++ "json": {++ "type": "boolean"++ },++ "tools": {++ "type": "boolean"++ },++ "streaming": {++ "type": "boolean"++ },++ "vision": {++ "type": "boolean"++ }++ }++ },++ "metadata": {++ "type": "object"}}},-- "error": {++ {"type": "object","required": [-- "code",-- "detail"++ "scope_type",++ "window"],-- "additionalProperties": false,"properties": {-- "code": {++ "scope_type": {++ "enum": [++ "global",++ "service",++ "actor",++ "attribution"++ ]++ },++ "scope_id": {"type": "string",-- "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"++ "maxLength": 200,++ "description": "* on a non-global scope = each one separately."},-- "detail": {++ "window": {++ "enum": [++ "minute",++ "hour",++ "day"++ ]++ },++ "max_requests": {++ "type": [++ "number",++ "string",++ "null"++ ],++ "minimum": 0++ },++ "max_tokens": {++ "type": [++ "number",++ "string",++ "null"++ ],++ "minimum": 0++ },++ "max_cost_usd": {++ "type": [++ "number",++ "string",++ "null"++ ],++ "minimum": 0++ },++ "workflow_prefix": {"type": ["string","null"]++ },++ "status": {++ "enum": [++ "active",++ "disabled"++ ]}}},-- "citations_count": {-- "type": "integer",-- "minimum": 0++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "ai.provider-manage-result": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/provider-manage-result.v1.json",++ "title": "AiProviderManageResult",++ "description": "ai.provider-manage-result@1: answers of ai.provider.manage on OpenVibe.AI. Provider create/update/disable/enable → { provider } (201 on POST); POST …/reset → { health } (the circuit breaker, closed again); model upserts → { model }; POST /api/v1/quotas → 201 { quota } (the stored row); DELETE /api/v1/cache → { removed } (entries purged). Every change is audited.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "provider"++ ],++ "properties": {++ "provider": {++ "$ref": "provider.v1.json"++ }++ }},-- "retry_of": {-- "type": [-- "string",-- "null"++ {++ "type": "object",++ "required": [++ "health"],-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ "properties": {++ "health": {++ "type": "object",++ "required": [++ "state"++ ],++ "properties": {++ "provider_key": {++ "type": "string"++ },++ "state": {++ "enum": [++ "closed",++ "open",++ "half_open"++ ]++ },++ "consecutive_failures": {++ "type": "integer"++ }++ }++ }++ }},-- "created_at": {-- "type": "string",-- "format": "date-time"++ {++ "type": "object",++ "required": [++ "model"++ ],++ "properties": {++ "model": {++ "$ref": "model.v1.json"++ }++ }},-- "finished_at": {-- "type": "string",-- "format": "date-time"++ {++ "type": "object",++ "required": [++ "quota"++ ],++ "properties": {++ "quota": {++ "type": "object",++ "required": [++ "id",++ "scope_type",++ "scope_id",++ "window",++ "status"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "scope_type": {++ "type": "string"++ },++ "scope_id": {++ "type": "string"++ },++ "window": {++ "type": "string"++ },++ "max_requests": {++ "type": [++ "number",++ "null"++ ]++ },++ "max_tokens": {++ "type": [++ "number",++ "null"++ ]++ },++ "max_cost_usd": {++ "type": [++ "number",++ "null"++ ]++ },++ "workflow_prefix": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "type": "string"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "removed"++ ],++ "properties": {++ "removed": {++ "type": "integer",++ "minimum": 0++ }++ }}-- }++ ]}},-- "ai.run.cached": {++ "ai.provider": {"version": "1.0.0","owner": "ai",-- "status": "planned",++ "status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.cached.v1.json",-- "title": "AiRunCachedPayload",-- "description": "ai.run.cached v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A create was served from the scoped cache: no provider call, cached_from names the run whose output was reused. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.",++ "$id": "https://openvibe.network/contracts/ai/provider.v1.json",++ "title": "AiProvider",++ "description": "ai.provider@1: one model provider as OpenVibe.AI's registry API shows it (server/registry.js publicProvider): never a secret value, only the secret reference name (env:NAME) and whether it resolves (credentials), plus the circuit-breaker health.","type": "object","required": [-- "run_id",++ "key",++ "kind","status",-- "workflow",-- "requester",-- "on_behalf_of",-- "target",-- "attribution",-- "provider",-- "model",-- "fallback_used",-- "synthetic",-- "cached_from",-- "usage",-- "error",-- "citations_count",-- "retry_of",-- "created_at",-- "finished_at"++ "auth_mode",++ "capabilities",++ "credentials"],-- "additionalProperties": false,"properties": {-- "run_id": {++ "key": {"type": "string",-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "kind": {++ "enum": [++ "stub",++ "openai",++ "anthropic",++ "http",++ "whisper"++ ]},"status": {-- "const": "cached"++ "enum": [++ "active",++ "disabled"++ ]},-- "workflow": {-- "type": "object",-- "required": [-- "key",-- "version"-- ],-- "additionalProperties": false,-- "properties": {-- "key": {-- "type": "string"-- },-- "version": {-- "type": "integer",-- "minimum": 1-- }-- }++ "base_url": {++ "type": [++ "string",++ "null"++ ]},-- "requester": {-- "$ref": "../../identity/subject-ref.v1.json"++ "auth_mode": {++ "enum": [++ "none",++ "bearer",++ "x-api-key"++ ]},-- "on_behalf_of": {-- "anyOf": [-- {-- "$ref": "../../identity/subject-ref.v1.json"-- },-- {-- "type": "null"-- }++ "secret_ref": {++ "type": [++ "string",++ "null"++ ],++ "description": "env:NAME, a reference; never the secret."++ },++ "default_model": {++ "type": [++ "string",++ "null"]},-- "target": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"-- },-- {-- "type": "null"-- }++ "capabilities": {++ "type": "array",++ "items": {++ "enum": [++ "chat",++ "generate",++ "summarize",++ "classify",++ "extract",++ "enrich",++ "embed",++ "vision",++ "json",++ "transcribe"++ ]++ }++ },++ "timeout_ms": {++ "type": [++ "integer",++ "null"]},-- "attribution": {-- "anyOf": [-- {-- "$ref": "../../common/entity-ref.v1.json"-- },-- {-- "type": "null"-- }++ "priority": {++ "type": [++ "number",++ "null"]},-- "provider": {++ "metadata": {++ "type": "object"++ },++ "origin": {"type": ["string","null"-- ],-- "description": "Provider that produced the output; null before a provider call and for cached runs."++ ]},-- "model": {++ "created_at": {"type": ["string","null"]},-- "fallback_used": {-- "type": "boolean"-- },-- "synthetic": {-- "type": "boolean",-- "description": "Stub provider output."++ "updated_at": {++ "type": [++ "string",++ "null"++ ]},-- "cached_from": {-- "type": "string",-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ "credentials": {++ "enum": [++ "not_required",++ "configured",++ "missing"++ ]},-- "usage": {++ "health": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "provider_key": {++ "type": "string"++ },++ "state": {++ "enum": [++ "closed",++ "open",++ "half_open"++ ]++ },++ "consecutive_failures": {++ "type": "integer"++ }++ }++ }++ }++ }++ },++ "ai.usage-result": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/usage-result.v1.json",++ "title": "AiUsageResult",++ "description": "ai.usage-result@1: answers of ai.usage.read on OpenVibe.AI (all GETs, parameters in the query). GET /api/v1/status → { providers, runs, today, workflows, templates, routes, inflight, quotas }; GET /api/v1/usage?from=&to=&requester= → { usage } (daily rows); GET /api/v1/quotas → { quotas, current }; GET /api/v1/requests?provider=&status=&run=&fallback=&limit= → { requests } (request-log metadata: hashes, tokens, cost, latency; raw debug fields only for provider managers who ask); GET /api/v1/audit → { audit }; GET /api/v1/cache → { cache }; GET /api/v1/providers → { providers }; GET /api/v1/models?provider= → { models }. Never raw prompts, never secrets.",++ "anyOf": [++ {"type": "object","required": [-- "tokens_in",-- "tokens_out",-- "cost_usd",-- "attempts"++ "providers",++ "runs",++ "today"],-- "additionalProperties": false,"properties": {-- "tokens_in": {-- "type": "integer",-- "minimum": 0++ "providers": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "key"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "kind": {++ "type": "string"++ },++ "status": {++ "type": "string"++ },++ "credentials": {++ "type": "string"++ },++ "health": {++ "type": "string"++ }++ }++ }},-- "tokens_out": {-- "type": "integer",-- "minimum": 0++ "runs": {++ "type": "object",++ "additionalProperties": {++ "type": "integer"++ },++ "description": "Run counts by status."},-- "cost_usd": {-- "type": "number",-- "minimum": 0++ "today": {++ "type": "object",++ "required": [++ "day"++ ],++ "properties": {++ "day": {++ "type": "string"++ },++ "requests": {++ "type": "number"++ },++ "cost_usd": {++ "type": "number"++ }++ }},-- "attempts": {-- "type": "integer",-- "minimum": 0++ "workflows": {++ "type": "integer"++ },++ "templates": {++ "type": "integer"++ },++ "routes": {++ "type": "integer"++ },++ "inflight": {++ "type": "integer"++ },++ "quotas": {++ "type": "array"}}},-- "error": {-- "type": "null"++ {++ "type": "object",++ "required": [++ "providers"++ ],++ "properties": {++ "providers": {++ "type": "array",++ "items": {++ "$ref": "provider.v1.json"++ }++ }++ }},-- "citations_count": {-- "type": "integer",-- "minimum": 0++ {++ "type": "object",++ "required": [++ "models"++ ],++ "properties": {++ "models": {++ "type": "array",++ "items": {++ "$ref": "model.v1.json"++ }++ }++ }},-- "retry_of": {-- "type": [-- "string",-- "null"++ {++ "type": "object",++ "required": [++ "quotas",++ "current"],-- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"++ "properties": {++ "quotas": {++ "type": "array",++ "items": {++ "type": "object"++ }++ },++ "current": {++ "type": "array",++ "items": {++ "type": "object",++ "properties": {++ "quota": {++ "type": "object"++ },++ "scope_id": {++ "type": "string"++ },++ "window_start": {++ "type": "string"++ },++ "resets_in_seconds": {++ "type": "number"++ },++ "used": {++ "type": "object"++ }++ }++ }++ }++ }},-- "created_at": {-- "type": "string",-- "format": "date-time"++ {++ "type": "object",++ "required": [++ "usage"++ ],++ "properties": {++ "usage": {++ "type": "array",++ "items": {++ "type": "object"++ }++ }++ }},-- "finished_at": {-- "type": "string",-- "format": "date-time"++ {++ "type": "object",++ "required": [++ "requests"++ ],++ "properties": {++ "requests": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "type": "object"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "audit"++ ],++ "properties": {++ "audit": {++ "type": "array",++ "items": {++ "type": "object"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "cache"++ ],++ "properties": {++ "cache": {++ "type": "array",++ "items": {++ "type": "object",++ "properties": {++ "workflow_key": {++ "type": "string"++ },++ "privacy": {++ "type": "string"++ },++ "entries": {++ "type": "integer"++ },++ "hits": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ }++ }}-- }++ ]}},"tips.interaction.ready": {@@@ -5793,6 +5793,178 @@ export interface TombstonePayload {redacted_by: string;}++/** [email protected] (owner: events) */++/**++ * events.delivery-admin-request@1: the body of POST /api/v1/deliveries/replay on OpenVibe.Events (events.delivery.admin, operators): { subscription_id } and exactly one of from_seq (requeue everything the subscription matches from that seq) or event_ids (at most 1000). GET /api/v1/deliveries?status=&subscription_id=&after_seq=&limit= takes no body.++ */++export type EventsDeliveryAdminRequest =++ | {++ [k: string]: unknown | undefined;++ }++ | NoBody;++++/** [email protected] (owner: events) */++/**++ * events.delivery-admin-result@1: answers of events.delivery.admin on OpenVibe.Events. GET /api/v1/deliveries → { deliveries, counts } (per delivery: event, subscription, seq, status pending|delivered|failed|dead, attempt, next_attempt_at, the last HTTP status and error, delivered_at; counts by status); POST /api/v1/deliveries/replay → { subscription_id, queued }.++ */++export type EventsDeliveryAdminResult =++ | {++ deliveries: {++ event_id: string;++ subscription_id: string;++ seq: number;++ status: "pending" | "delivered" | "failed" | "dead";++ attempt: number;++ next_attempt_at?: string | null;++ last_status?: number | null;++ last_error?: string | null;++ delivered_at?: string | null;++ }[];++ counts: {++ pending: number;++ delivered: number;++ failed: number;++ dead: number;++ };++ }++ | {++ subscription_id: string;++ queued: number;++ };++++/** [email protected] (owner: events) */++/**++ * events.publish-result@1: the answer of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps). One envelope → { event_id, seq, duplicate } (201 when stored, 200 when every event was a repeat); a batch { events: [...] } (at most 100, atomic) → { results } in the same order. Refusals are problem+json: 422 events.invalid_envelope / events.invalid_redaction, 403 events.source_mismatch / events.type_not_allowed / events.actor_mismatch, 413 events.payload_too_large / events.batch_too_large, 429 events.quota_exceeded (apps).++ */++export type EventsPublishResult =++ | {++ event_id: string;++ seq: number;++ /**++ * true: this event_id was stored before; the stored seq is returned and nothing is stored twice.++ */++ duplicate: boolean;++ }++ | {++ /**++ * @minItems 1++ * @maxItems 100++ */++ results: [++ {++ event_id: string;++ seq: number;++ /**++ * true: this event_id was stored before; the stored seq is returned and nothing is stored twice.++ */++ duplicate: boolean;++ },++ ...{++ event_id: string;++ seq: number;++ /**++ * true: this event_id was stored before; the stored seq is returned and nothing is stored twice.++ */++ duplicate: boolean;++ }[]++ ];++ };++++/** [email protected] (owner: events) */++/**++ * events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.++ */++export type EventsReadRequest =++ | {++ /**++ * Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused).++ */++ topic: string;++ /**++ * A seq (next_after_seq of the last page handled).++ */++ cursor: number;++ }++ | NoBody;++++/** [email protected] (owner: events) */++/**++ * events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, event }], next_after_seq, latest_seq, gap? } (keep next_after_seq as the cursor: it moves past events that did not match; gap { from_seq, to_seq } says events after after_seq were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). An app sees only its project's events in its token's environment plus public first-party events.++ */++export type EventsReadResult =++ | {++ /**++ * @maxItems 1000++ */++ events: {++ seq: number;++ event: EventEnvelope;++ }[];++ next_after_seq: number;++ latest_seq: number;++ gap?: {++ from_seq: number;++ to_seq: number;++ };++ }++ | {++ seq: number;++ event: EventEnvelope;++ }++ | {++ consumer: string;++ topic: string;++ cursor: number;++ updated_at: string | null;++ };++++/** [email protected] (owner: events) */++/**++ * events.subscription-request@1: the body of POST /api/v1/subscriptions on OpenVibe.Events (events.subscription.manage for services, events.app.subscribe for developer apps; the consumer is always the caller). topic_pattern (topic is accepted for it) and endpoint are required: a service's endpoint must be http(s) on the allow-list (127.0.0.1, *.openvibe.*), an app's must be https resolving only to public addresses. secret, when given, is 32 to 256 characters (else one is generated); retry_policy accepts max_attempts (1..20) and backoff_ms (1..20 delays of 0..86400000 ms) and nothing else. The same pattern and endpoint twice is 409 events.subscription_exists. GET /api/v1/subscriptions[/:id] and POST /api/v1/subscriptions/:id/disable|enable take no body.++ */++export type EventsSubscriptionRequest =++ | {++ [k: string]: unknown | undefined;++ }++ | NoBody;++++/** [email protected] (owner: events) */++/**++ * events.subscription-result@1: answers of the subscription routes on OpenVibe.Events. POST /api/v1/subscriptions → 201 events.subscription@1 with its secret (shown once); GET /api/v1/subscriptions → { subscriptions } (the caller's own, no secrets); GET /api/v1/subscriptions/:id and POST …/disable|enable → events.subscription@1. Another consumer's subscription is 404.++ */++export type EventsSubscriptionResult =++ | EventsSubscription++ | {++ subscriptions: EventsSubscription[];++ };++++/** [email protected] (owner: events) */++/**++ * events.subscription@1: one push subscription as OpenVibe.Events shows it to its consumer (server/store.js subscriptionView). The signing secret appears only in the answer that creates it (and in a rotation's). A developer app's subscription also names its project and environment.++ */++export interface EventsSubscription {++ id: string;++ /**++ * The calling service, or app:app_<ULID>.++ */++ consumer: string;++ topic_pattern: string;++ endpoint: string;++ enabled: boolean;++ retry_policy: {++ max_attempts?: number;++ backoff_ms?: number[];++ } | null;++ created_at: string;++ updated_at: string;++ project_id?: string;++ env?: string;++ /**++ * Only when the subscription is created: shown once.++ */++ secret?: string;++}++/** [email protected] (owner: chat) *//*** chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.@@@ -8024,6 +8196,337 @@ export interface AiRunCachedPayload {finished_at: string;}++/** [email protected] (owner: ai) */++/**++ * ai.definition-version-request@1: bodies of ai.workflow.manage on OpenVibe.AI. An edit is always a new version (the previous one stays readable); fields left out are taken from the newest existing version. POST /api/v1/templates/:key/versions: a prompt template (input_schema and output_schema must compile as JSON Schema; visibility public, first-party or internal). POST /api/v1/workflows/:key/versions (key <namespace>.<name>): steps (at least one; kind llm (naming an existing template), passthrough, transcribe or embed), schemas, default route, cache mode. POST /api/v1/routes/:key/versions: primary { provider, model } and fallbacks (every provider must exist), or alias_of another route. POST …/:key/versions/:version/status: { status } (templates and workflows draft|active|deprecated|archived; routes active|disabled). Refusals are 422 ai.invalid.++ */++export type AiDefinitionVersionRequest =++ | {++ name?: string;++ description?: string | null;++ /**++ * A JSON Schema the run input must match.++ */++ input_schema?: {};++ /**++ * A JSON Schema the output must match.++ */++ output_schema?: {};++ system_prompt?: string;++ user_prompt?: string;++ default_route?: string | null;++ owner?: string;++ visibility?: "public" | "first-party" | "internal";++ status?: "draft" | "active" | "deprecated" | "archived";++ metadata?: {};++ }++ | {++ name?: string;++ description?: string | null;++ namespace?: string;++ /**++ * A JSON Schema.++ */++ input_schema?: {};++ /**++ * A JSON Schema.++ */++ output_schema?: {};++ /**++ * @minItems 1++ */++ steps?: [++ {++ kind: "llm" | "passthrough" | "transcribe" | "embed";++ template?: string;++ },++ ...{++ kind: "llm" | "passthrough" | "transcribe" | "embed";++ template?: string;++ }[]++ ];++ default_route?: string | null;++ cache_mode?: "none" | "private" | "service";++ cache_ttl_sec?: number | null;++ status?: "draft" | "active" | "deprecated" | "archived";++ metadata?: {};++ }++ | {++ primary?: {++ provider: string;++ model?: string | null;++ };++ fallbacks?: {++ provider: string;++ model?: string | null;++ }[];++ options?: {};++ max_output_tokens?: number | null;++ response_format?: "text" | "json";++ timeout_ms?: number | null;++ alias_of?: string | null;++ status?: "active" | "disabled";++ };++++/** [email protected] (owner: ai) */++/**++ * ai.definition-version-result@1: answers of ai.workflow.manage on OpenVibe.AI. POST …/:key/versions → 201 { template | workflow | route } (the new version, whole: prompts and schemas for a template, steps for a workflow, primary/fallbacks for a route); POST …/versions/:version/status → { template | workflow | route: { key, version, status } }.++ */++export type AiDefinitionVersionResult =++ | {++ template: {++ key: string;++ version: number;++ status: string;++ };++ }++ | {++ workflow: {++ key: string;++ version: number;++ status: string;++ };++ }++ | {++ route: {++ key: string;++ version: number;++ status: string;++ };++ };++++/** [email protected] (owner: ai) */++/**++ * ai.model@1: one model of a provider in OpenVibe.AI's registry (server/registry.js decodeModel): type, status, limits, per-million-token costs and what it supports.++ */++export interface AiModel {++ provider_key: string;++ model_key: string;++ display_name?: string | null;++ type: "chat" | "vision" | "embedding" | "stt";++ status: "active" | "disabled";++ context_window?: number | null;++ max_output?: number | null;++ cost: {++ in_per_mtok?: number | null;++ out_per_mtok?: number | null;++ cached_per_mtok?: number | null;++ };++ supports: {++ json?: boolean;++ tools?: boolean;++ streaming?: boolean;++ vision?: boolean;++ };++ metadata?: {};++ created_at?: string | null;++ updated_at?: string | null;++}++++/** [email protected] (owner: ai) */++/**++ * ai.provider-manage-request@1: bodies of ai.provider.manage on OpenVibe.AI. POST /api/v1/providers creates or replaces a provider (key required) and PATCH /api/v1/providers/:key changes one (fields left out keep their value); a body carrying api_key or secret is refused (422: send secret_ref env:NAME, never a secret value). POST /api/v1/models (provider_key and model_key required) and PATCH /api/v1/models/:provider/:model upsert a model. POST /api/v1/quotas upserts a quota (scope_type and window required; scope_id too unless global; the same scope, window and workflow_prefix is one quota). POST /api/v1/providers/:key/disable|enable|reset and DELETE /api/v1/cache[?workflow=] take no body. Unknown fields are ignored.++ */++export type AiProviderManageRequest =++ | {++ key?: string;++ display_name?: string;++ kind?: "stub" | "openai" | "anthropic" | "http" | "whisper";++ status?: "active" | "disabled";++ base_url?: string | null;++ auth_mode?: "none" | "bearer" | "x-api-key";++ secret_ref?: string | null;++ default_model?: string | null;++ capabilities?: (++ | "chat"++ | "generate"++ | "summarize"++ | "classify"++ | "extract"++ | "enrich"++ | "embed"++ | "vision"++ | "json"++ | "transcribe"++ )[];++ /**++ * Clamped to 1000..600000.++ */++ timeout_ms?: number | string;++ priority?: number | string;++ metadata?: {};++ }++ | {++ provider_key?: string;++ model_key?: string;++ display_name?: string;++ type?: "chat" | "vision" | "embedding" | "stt";++ status?: "active" | "disabled";++ context_window?: number | null;++ max_output?: number | null;++ cost?: {++ in_per_mtok?: number | null;++ out_per_mtok?: number | null;++ cached_per_mtok?: number | null;++ };++ supports?: {++ json?: boolean;++ tools?: boolean;++ streaming?: boolean;++ vision?: boolean;++ };++ metadata?: {};++ }++ | {++ scope_type: "global" | "service" | "actor" | "attribution";++ /**++ * * on a non-global scope = each one separately.++ */++ scope_id?: string;++ window: "minute" | "hour" | "day";++ max_requests?: number | string | null;++ max_tokens?: number | string | null;++ max_cost_usd?: number | string | null;++ workflow_prefix?: string | null;++ status?: "active" | "disabled";++ }++ | NoBody;++++/** [email protected] (owner: ai) */++/**++ * ai.provider-manage-result@1: answers of ai.provider.manage on OpenVibe.AI. Provider create/update/disable/enable → { provider } (201 on POST); POST …/reset → { health } (the circuit breaker, closed again); model upserts → { model }; POST /api/v1/quotas → 201 { quota } (the stored row); DELETE /api/v1/cache → { removed } (entries purged). Every change is audited.++ */++export type AiProviderManageResult =++ | {++ provider: AiProvider;++ }++ | {++ health: {++ provider_key?: string;++ state: "closed" | "open" | "half_open";++ consecutive_failures?: number;++ };++ }++ | {++ model: AiModel;++ }++ | {++ quota: {++ id: number;++ scope_type: string;++ scope_id: string;++ window: string;++ max_requests?: number | null;++ max_tokens?: number | null;++ max_cost_usd?: number | null;++ workflow_prefix?: string | null;++ status: string;++ };++ }++ | {++ removed: number;++ };++++/** [email protected] (owner: ai) */++/**++ * ai.provider@1: one model provider as OpenVibe.AI's registry API shows it (server/registry.js publicProvider): never a secret value, only the secret reference name (env:NAME) and whether it resolves (credentials), plus the circuit-breaker health.++ */++export interface AiProvider {++ key: string;++ display_name?: string | null;++ kind: "stub" | "openai" | "anthropic" | "http" | "whisper";++ status: "active" | "disabled";++ base_url?: string | null;++ auth_mode: "none" | "bearer" | "x-api-key";++ /**++ * env:NAME, a reference; never the secret.++ */++ secret_ref?: string | null;++ default_model?: string | null;++ capabilities: (++ "chat" | "generate" | "summarize" | "classify" | "extract" | "enrich" | "embed" | "vision" | "json" | "transcribe"++ )[];++ timeout_ms?: number | null;++ priority?: number | null;++ metadata?: {};++ origin?: string | null;++ created_at?: string | null;++ updated_at?: string | null;++ credentials: "not_required" | "configured" | "missing";++ health?: {++ provider_key?: string;++ state?: "closed" | "open" | "half_open";++ consecutive_failures?: number;++ } | null;++}++++/** [email protected] (owner: ai) */++/**++ * ai.usage-result@1: answers of ai.usage.read on OpenVibe.AI (all GETs, parameters in the query). GET /api/v1/status → { providers, runs, today, workflows, templates, routes, inflight, quotas }; GET /api/v1/usage?from=&to=&requester= → { usage } (daily rows); GET /api/v1/quotas → { quotas, current }; GET /api/v1/requests?provider=&status=&run=&fallback=&limit= → { requests } (request-log metadata: hashes, tokens, cost, latency; raw debug fields only for provider managers who ask); GET /api/v1/audit → { audit }; GET /api/v1/cache → { cache }; GET /api/v1/providers → { providers }; GET /api/v1/models?provider= → { models }. Never raw prompts, never secrets.++ */++export type AiUsageResult =++ | {++ providers: {++ key: string;++ kind?: string;++ status?: string;++ credentials?: string;++ health?: string;++ }[];++ /**++ * Run counts by status.++ */++ runs: {++ [k: string]: number | undefined;++ };++ today: {++ day: string;++ requests?: number;++ cost_usd?: number;++ };++ workflows?: number;++ templates?: number;++ routes?: number;++ inflight?: number;++ quotas?: unknown[];++ }++ | {++ providers: AiProvider[];++ }++ | {++ models: AiModel[];++ }++ | {++ quotas: {}[];++ current: {++ quota?: {};++ scope_id?: string;++ window_start?: string;++ resets_in_seconds?: number;++ used?: {};++ }[];++ }++ | {++ usage: {}[];++ }++ | {++ /**++ * @maxItems 500++ */++ requests: {}[];++ }++ | {++ audit: {}[];++ }++ | {++ cache: {++ workflow_key?: string;++ privacy?: string;++ entries?: number;++ hits?: number | null;++ }[];++ };++/** [email protected] (owner: tips) *//*** tips.interaction.ready v1 (OpenVibe.Tips server/domain/interactions.js settle, summary()). The interaction settled: it is active and its effects (overlay alert, chat line, paid message, TTS, media request) are queued. Envelope: subject { type: interaction, id: tint_… }, visibility internal, priority important, actor service:tips. Never emitted for tests or imports.@@@ -23,5 +23,7 @@"PATCH /api/v1/models/:provider/:model","POST /api/v1/quotas","DELETE /api/v1/cache"-- ]++ ],++ "inputSchema": "ai.provider-manage-request@1",++ "outputSchema": "ai.provider-manage-result@1"}@@@ -18,5 +18,6 @@"GET /api/v1/runs","GET /api/v1/runs/:id","GET /api/v1/runs/:id/citations"-- ]++ ],++ "inputSchema": "common.no-body@1"}@@@ -22,5 +22,7 @@"GET /api/v1/cache","GET /api/v1/providers","GET /api/v1/models"-- ]++ ],++ "inputSchema": "common.no-body@1",++ "outputSchema": "ai.usage-result@1"}@@@ -20,5 +20,7 @@"POST /api/v1/workflows/:key/versions/:version/status","POST /api/v1/routes/:key/versions","POST /api/v1/routes/:key/versions/:version/status"-- ]++ ],++ "inputSchema": "ai.definition-version-request@1",++ "outputSchema": "ai.definition-version-result@1"}@@@ -16,5 +16,6 @@"events": [],"implementedBy": ["POST /api/v1/events"-- ]++ ],++ "outputSchema": "events.publish-result@1"}@@@ -18,5 +18,7 @@"GET /api/v1/events/:event_id","GET /api/v1/checkpoints","PUT /api/v1/checkpoints"-- ]++ ],++ "inputSchema": "events.read-request@1",++ "outputSchema": "events.read-result@1"}@@@ -19,5 +19,7 @@"GET /api/v1/subscriptions/:id","POST /api/v1/subscriptions/:id/disable","POST /api/v1/subscriptions/:id/enable"-- ]++ ],++ "inputSchema": "events.subscription-request@1",++ "outputSchema": "events.subscription-result@1"}@@@ -16,5 +16,7 @@"implementedBy": ["GET /api/v1/deliveries","POST /api/v1/deliveries/replay"-- ]++ ],++ "inputSchema": "events.delivery-admin-request@1",++ "outputSchema": "events.delivery-admin-result@1"}@@@ -16,5 +16,6 @@"events": [],"implementedBy": ["POST /api/v1/events"-- ]++ ],++ "outputSchema": "events.publish-result@1"}@@@ -20,5 +20,6 @@"GET /api/v1/checkpoints","PUT /api/v1/checkpoints","GET /realtime/stream"-- ]++ ],++ "inputSchema": "events.read-request@1"}@@@ -19,5 +19,7 @@"GET /api/v1/subscriptions/:id","POST /api/v1/subscriptions/:id/disable","POST /api/v1/subscriptions/:id/enable"-- ]++ ],++ "inputSchema": "events.subscription-request@1",++ "outputSchema": "events.subscription-result@1"}
@@ -1,33 +1,6 @@
@@ -1,33 +1,6 @@
{
{
"_about": "Active capabilities still without an input or output schema (roadmap WS-C task 4). test/capability-schemas.test.js fails on any active capability missing a schema that is not listed here, and on a listed one that has since gained it (remove it from this list). The list only shrinks: fill a capability's schemas, then delete its row. A request with no body names common.no-body@1; a response of bytes names common.binary@1.",
"_about": "Active capabilities still without an input or output schema (roadmap WS-C task 4). test/capability-schemas.test.js fails on any active capability missing a schema that is not listed here, and on a listed one that has since gained it (remove it from this list). The list only shrinks: fill a capability's schemas, then delete its row. A request with no body names common.no-body@1; a response of bytes names common.binary@1.",
"gaps": [
"gaps": [
- {
- "id": "ai.provider.manage",
- "missing": [
- "inputSchema",
- "outputSchema"
- ]
- },
- {
- "id": "ai.run.read",
- "missing": [
- "inputSchema"
- ]
- },
- {
- "id": "ai.usage.read",
- "missing": [
- "inputSchema",
- "outputSchema"
- ]
- },
- {
- "id": "ai.workflow.manage",
- "missing": [
- "inputSchema",
- "outputSchema"
- ]
- },
{
{
"id": "blog.blog.configure",
"id": "blog.blog.configure",
"missing": [
"missing": [
@@ -250,52 +223,6 @@
@@ -250,52 +223,6 @@
"outputSchema"
"outputSchema"
]
]
},
},
- {
- "id": "events.app.publish",
- "missing": [
- "outputSchema"
- ]
- },
- {
- "id": "events.app.read",
- "missing": [
- "inputSchema",
- "outputSchema"
- ]
- },
- {
- "id": "events.app.subscribe",
- "missing": [
- "inputSchema",
- "outputSchema"
- ]
- },
- {
- "id": "events.delivery.admin",
- "missing": [
- "inputSchema",
- "outputSchema"
- ]
- },
- {
- "id": "events.event.publish",
- "missing": [
- "outputSchema"
- ]
- },
- {
- "id": "events.event.read",
- "missing": [
- "inputSchema"
- ]
- },
- {
- "id": "events.subscription.manage",
- "missing": [
- "inputSchema",
- "outputSchema"
- ]
- },
{
{
"id": "games.mod.manage",
"id": "games.mod.manage",
"missing": [
"missing": [
new file mode 100644
new file mode 100644
@@ -0,0 +1,216 @@
@@ -0,0 +1,216 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/definition-version-request.v1.json",
+ "title": "AiDefinitionVersionRequest",
+ "description": "ai.definition-version-request@1: bodies of ai.workflow.manage on OpenVibe.AI. An edit is always a new version (the previous one stays readable); fields left out are taken from the newest existing version. POST /api/v1/templates/:key/versions: a prompt template (input_schema and output_schema must compile as JSON Schema; visibility public, first-party or internal). POST /api/v1/workflows/:key/versions (key <namespace>.<name>): steps (at least one; kind llm (naming an existing template), passthrough, transcribe or embed), schemas, default route, cache mode. POST /api/v1/routes/:key/versions: primary { provider, model } and fallbacks (every provider must exist), or alias_of another route. POST …/:key/versions/:version/status: { status } (templates and workflows draft|active|deprecated|archived; routes active|disabled). Refusals are 422 ai.invalid.",
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "input_schema": {
+ "type": "object",
+ "description": "A JSON Schema the run input must match."
+ },
+ "output_schema": {
+ "type": "object",
+ "description": "A JSON Schema the output must match."
+ },
+ "system_prompt": {
+ "type": "string"
+ },
+ "user_prompt": {
+ "type": "string"
+ },
+ "default_route": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "owner": {
+ "type": "string"
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "first-party",
+ "internal"
+ ]
+ },
+ "status": {
+ "enum": [
+ "draft",
+ "active",
+ "deprecated",
+ "archived"
+ ]
+ },
+ "metadata": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "type": "string"
+ },
+ "input_schema": {
+ "type": "object",
+ "description": "A JSON Schema."
+ },
+ "output_schema": {
+ "type": "object",
+ "description": "A JSON Schema."
+ },
+ "steps": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "required": [
+ "kind"
+ ],
+ "properties": {
+ "kind": {
+ "enum": [
+ "llm",
+ "passthrough",
+ "transcribe",
+ "embed"
+ ]
+ },
+ "template": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "default_route": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "cache_mode": {
+ "enum": [
+ "none",
+ "private",
+ "service"
+ ]
+ },
+ "cache_ttl_sec": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "status": {
+ "enum": [
+ "draft",
+ "active",
+ "deprecated",
+ "archived"
+ ]
+ },
+ "metadata": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "primary": {
+ "type": "object",
+ "required": [
+ "provider"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "fallbacks": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "provider"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ }
+ },
+ "options": {
+ "type": "object"
+ },
+ "max_output_tokens": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "response_format": {
+ "enum": [
+ "text",
+ "json"
+ ]
+ },
+ "timeout_ms": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "alias_of": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/definition-version-result.v1.json",
+ "title": "AiDefinitionVersionResult",
+ "description": "ai.definition-version-result@1: answers of ai.workflow.manage on OpenVibe.AI. POST …/:key/versions → 201 { template | workflow | route } (the new version, whole: prompts and schemas for a template, steps for a workflow, primary/fallbacks for a route); POST …/versions/:version/status → { template | workflow | route: { key, version, status } }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "template"
+ ],
+ "properties": {
+ "template": {
+ "type": "object",
+ "required": [
+ "key",
+ "version",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "workflow"
+ ],
+ "properties": {
+ "workflow": {
+ "type": "object",
+ "required": [
+ "key",
+ "version",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "route"
+ ],
+ "properties": {
+ "route": {
+ "type": "object",
+ "required": [
+ "key",
+ "version",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/model.v1.json",
+ "title": "AiModel",
+ "description": "ai.model@1: one model of a provider in OpenVibe.AI's registry (server/registry.js decodeModel): type, status, limits, per-million-token costs and what it supports.",
+ "type": "object",
+ "required": [
+ "provider_key",
+ "model_key",
+ "type",
+ "status",
+ "cost",
+ "supports"
+ ],
+ "properties": {
+ "provider_key": {
+ "type": "string"
+ },
+ "model_key": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "enum": [
+ "chat",
+ "vision",
+ "embedding",
+ "stt"
+ ]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
+ },
+ "context_window": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "max_output": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "cost": {
+ "type": "object",
+ "properties": {
+ "in_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "out_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "cached_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ }
+ },
+ "supports": {
+ "type": "object",
+ "properties": {
+ "json": {
+ "type": "boolean"
+ },
+ "tools": {
+ "type": "boolean"
+ },
+ "streaming": {
+ "type": "boolean"
+ },
+ "vision": {
+ "type": "boolean"
+ }
+ }
+ },
+ "metadata": {
+ "type": "object"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,262 @@
@@ -0,0 +1,262 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/provider-manage-request.v1.json",
+ "title": "AiProviderManageRequest",
+ "description": "ai.provider-manage-request@1: bodies of ai.provider.manage on OpenVibe.AI. POST /api/v1/providers creates or replaces a provider (key required) and PATCH /api/v1/providers/:key changes one (fields left out keep their value); a body carrying api_key or secret is refused (422: send secret_ref env:NAME, never a secret value). POST /api/v1/models (provider_key and model_key required) and PATCH /api/v1/models/:provider/:model upsert a model. POST /api/v1/quotas upserts a quota (scope_type and window required; scope_id too unless global; the same scope, window and workflow_prefix is one quota). POST /api/v1/providers/:key/disable|enable|reset and DELETE /api/v1/cache[?workflow=] take no body. Unknown fields are ignored.",
+ "anyOf": [
+ {
+ "type": "object",
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "api_key"
+ ]
+ },
+ {
+ "required": [
+ "secret"
+ ]
+ }
+ ]
+ },
+ "properties": {
+ "key": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"
+ },
+ "display_name": {
+ "type": "string"
+ },
+ "kind": {
+ "enum": [
+ "stub",
+ "openai",
+ "anthropic",
+ "http",
+ "whisper"
+ ]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
+ },
+ "base_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^https?://\\S+$"
+ },
+ "auth_mode": {
+ "enum": [
+ "none",
+ "bearer",
+ "x-api-key"
+ ]
+ },
+ "secret_ref": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^(env:[A-Z][A-Z0-9_]{0,63})?$"
+ },
+ "default_model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "type": "array",
+ "items": {
+ "enum": [
+ "chat",
+ "generate",
+ "summarize",
+ "classify",
+ "extract",
+ "enrich",
+ "embed",
+ "vision",
+ "json",
+ "transcribe"
+ ]
+ }
+ },
+ "timeout_ms": {
+ "type": [
+ "integer",
+ "string"
+ ],
+ "description": "Clamped to 1000..600000."
+ },
+ "priority": {
+ "type": [
+ "number",
+ "string"
+ ]
+ },
+ "metadata": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "provider_key": {
+ "type": "string"
+ },
+ "model_key": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "display_name": {
+ "type": "string"
+ },
+ "type": {
+ "enum": [
+ "chat",
+ "vision",
+ "embedding",
+ "stt"
+ ]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
+ },
+ "context_window": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "max_output": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "cost": {
+ "type": "object",
+ "properties": {
+ "in_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "out_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "cached_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ }
+ },
+ "supports": {
+ "type": "object",
+ "properties": {
+ "json": {
+ "type": "boolean"
+ },
+ "tools": {
+ "type": "boolean"
+ },
+ "streaming": {
+ "type": "boolean"
+ },
+ "vision": {
+ "type": "boolean"
+ }
+ }
+ },
+ "metadata": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "scope_type",
+ "window"
+ ],
+ "properties": {
+ "scope_type": {
+ "enum": [
+ "global",
+ "service",
+ "actor",
+ "attribution"
+ ]
+ },
+ "scope_id": {
+ "type": "string",
+ "maxLength": 200,
+ "description": "* on a non-global scope = each one separately."
+ },
+ "window": {
+ "enum": [
+ "minute",
+ "hour",
+ "day"
+ ]
+ },
+ "max_requests": {
+ "type": [
+ "number",
+ "string",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "max_tokens": {
+ "type": [
+ "number",
+ "string",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "max_cost_usd": {
+ "type": [
+ "number",
+ "string",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "workflow_prefix": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/provider-manage-result.v1.json",
+ "title": "AiProviderManageResult",
+ "description": "ai.provider-manage-result@1: answers of ai.provider.manage on OpenVibe.AI. Provider create/update/disable/enable → { provider } (201 on POST); POST …/reset → { health } (the circuit breaker, closed again); model upserts → { model }; POST /api/v1/quotas → 201 { quota } (the stored row); DELETE /api/v1/cache → { removed } (entries purged). Every change is audited.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "provider"
+ ],
+ "properties": {
+ "provider": {
+ "$ref": "provider.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "health"
+ ],
+ "properties": {
+ "health": {
+ "type": "object",
+ "required": [
+ "state"
+ ],
+ "properties": {
+ "provider_key": {
+ "type": "string"
+ },
+ "state": {
+ "enum": [
+ "closed",
+ "open",
+ "half_open"
+ ]
+ },
+ "consecutive_failures": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "model"
+ ],
+ "properties": {
+ "model": {
+ "$ref": "model.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "quota"
+ ],
+ "properties": {
+ "quota": {
+ "type": "object",
+ "required": [
+ "id",
+ "scope_type",
+ "scope_id",
+ "window",
+ "status"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "scope_type": {
+ "type": "string"
+ },
+ "scope_id": {
+ "type": "string"
+ },
+ "window": {
+ "type": "string"
+ },
+ "max_requests": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "max_tokens": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "max_cost_usd": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "workflow_prefix": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "removed"
+ ],
+ "properties": {
+ "removed": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,146 @@
@@ -0,0 +1,146 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/provider.v1.json",
+ "title": "AiProvider",
+ "description": "ai.provider@1: one model provider as OpenVibe.AI's registry API shows it (server/registry.js publicProvider): never a secret value, only the secret reference name (env:NAME) and whether it resolves (credentials), plus the circuit-breaker health.",
+ "type": "object",
+ "required": [
+ "key",
+ "kind",
+ "status",
+ "auth_mode",
+ "capabilities",
+ "credentials"
+ ],
+ "properties": {
+ "key": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "enum": [
+ "stub",
+ "openai",
+ "anthropic",
+ "http",
+ "whisper"
+ ]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
+ },
+ "base_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "auth_mode": {
+ "enum": [
+ "none",
+ "bearer",
+ "x-api-key"
+ ]
+ },
+ "secret_ref": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "env:NAME, a reference; never the secret."
+ },
+ "default_model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "type": "array",
+ "items": {
+ "enum": [
+ "chat",
+ "generate",
+ "summarize",
+ "classify",
+ "extract",
+ "enrich",
+ "embed",
+ "vision",
+ "json",
+ "transcribe"
+ ]
+ }
+ },
+ "timeout_ms": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "priority": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "metadata": {
+ "type": "object"
+ },
+ "origin": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "credentials": {
+ "enum": [
+ "not_required",
+ "configured",
+ "missing"
+ ]
+ },
+ "health": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "provider_key": {
+ "type": "string"
+ },
+ "state": {
+ "enum": [
+ "closed",
+ "open",
+ "half_open"
+ ]
+ },
+ "consecutive_failures": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,223 @@
@@ -0,0 +1,223 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/usage-result.v1.json",
+ "title": "AiUsageResult",
+ "description": "ai.usage-result@1: answers of ai.usage.read on OpenVibe.AI (all GETs, parameters in the query). GET /api/v1/status → { providers, runs, today, workflows, templates, routes, inflight, quotas }; GET /api/v1/usage?from=&to=&requester= → { usage } (daily rows); GET /api/v1/quotas → { quotas, current }; GET /api/v1/requests?provider=&status=&run=&fallback=&limit= → { requests } (request-log metadata: hashes, tokens, cost, latency; raw debug fields only for provider managers who ask); GET /api/v1/audit → { audit }; GET /api/v1/cache → { cache }; GET /api/v1/providers → { providers }; GET /api/v1/models?provider= → { models }. Never raw prompts, never secrets.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "providers",
+ "runs",
+ "today"
+ ],
+ "properties": {
+ "providers": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "credentials": {
+ "type": "string"
+ },
+ "health": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "runs": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer"
+ },
+ "description": "Run counts by status."
+ },
+ "today": {
+ "type": "object",
+ "required": [
+ "day"
+ ],
+ "properties": {
+ "day": {
+ "type": "string"
+ },
+ "requests": {
+ "type": "number"
+ },
+ "cost_usd": {
+ "type": "number"
+ }
+ }
+ },
+ "workflows": {
+ "type": "integer"
+ },
+ "templates": {
+ "type": "integer"
+ },
+ "routes": {
+ "type": "integer"
+ },
+ "inflight": {
+ "type": "integer"
+ },
+ "quotas": {
+ "type": "array"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "providers"
+ ],
+ "properties": {
+ "providers": {
+ "type": "array",
+ "items": {
+ "$ref": "provider.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "models"
+ ],
+ "properties": {
+ "models": {
+ "type": "array",
+ "items": {
+ "$ref": "model.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "quotas",
+ "current"
+ ],
+ "properties": {
+ "quotas": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "current": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "quota": {
+ "type": "object"
+ },
+ "scope_id": {
+ "type": "string"
+ },
+ "window_start": {
+ "type": "string"
+ },
+ "resets_in_seconds": {
+ "type": "number"
+ },
+ "used": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "usage"
+ ],
+ "properties": {
+ "usage": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "requests"
+ ],
+ "properties": {
+ "requests": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "audit"
+ ],
+ "properties": {
+ "audit": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "cache"
+ ],
+ "properties": {
+ "cache": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "workflow_key": {
+ "type": "string"
+ },
+ "privacy": {
+ "type": "string"
+ },
+ "entries": {
+ "type": "integer"
+ },
+ "hits": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+}
@@ -270,6 +270,78 @@
@@ -270,6 +270,78 @@
"status": "active",
"status": "active",
"adr": "ADR-026"
"adr": "ADR-026"
},
},
+ {
+ "id": "events.delivery-admin-request",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/delivery-admin-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "events.delivery-admin-result",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/delivery-admin-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "events.publish-result",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/publish-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "events.read-request",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/read-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "events.read-result",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "events.subscription-request",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/subscription-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "events.subscription-result",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/subscription-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "events.subscription",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/subscription.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "chat.message.deleted",
"id": "chat.message.deleted",
"version": "1.0.0",
"version": "1.0.0",
@@ -627,6 +699,69 @@
@@ -627,6 +699,69 @@
"status": "planned",
"status": "planned",
"adr": "ADR-015"
"adr": "ADR-015"
},
},
+ {
+ "id": "ai.definition-version-request",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/definition-version-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.definition-version-result",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/definition-version-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.model",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/model.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.provider-manage-request",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/provider-manage-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.provider-manage-result",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/provider-manage-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.provider",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/provider.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.usage-result",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/usage-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "tips.interaction.ready",
"id": "tips.interaction.ready",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/delivery-admin-request.v1.json",
+ "title": "EventsDeliveryAdminRequest",
+ "description": "events.delivery-admin-request@1: the body of POST /api/v1/deliveries/replay on OpenVibe.Events (events.delivery.admin, operators): { subscription_id } and exactly one of from_seq (requeue everything the subscription matches from that seq) or event_ids (at most 1000). GET /api/v1/deliveries?status=&subscription_id=&after_seq=&limit= takes no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "subscription_id"
+ ],
+ "oneOf": [
+ {
+ "required": [
+ "from_seq"
+ ]
+ },
+ {
+ "required": [
+ "event_ids"
+ ]
+ }
+ ],
+ "properties": {
+ "subscription_id": {
+ "type": "string"
+ },
+ "from_seq": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "event_ids": {
+ "type": "array",
+ "maxItems": 1000,
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/delivery-admin-result.v1.json",
+ "title": "EventsDeliveryAdminResult",
+ "description": "events.delivery-admin-result@1: answers of events.delivery.admin on OpenVibe.Events. GET /api/v1/deliveries → { deliveries, counts } (per delivery: event, subscription, seq, status pending|delivered|failed|dead, attempt, next_attempt_at, the last HTTP status and error, delivered_at; counts by status); POST /api/v1/deliveries/replay → { subscription_id, queued }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "deliveries",
+ "counts"
+ ],
+ "properties": {
+ "deliveries": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "event_id",
+ "subscription_id",
+ "seq",
+ "status",
+ "attempt"
+ ],
+ "properties": {
+ "event_id": {
+ "type": "string"
+ },
+ "subscription_id": {
+ "type": "string"
+ },
+ "seq": {
+ "type": "integer"
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "delivered",
+ "failed",
+ "dead"
+ ]
+ },
+ "attempt": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "next_attempt_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_status": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "last_error": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "delivered_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "counts": {
+ "type": "object",
+ "required": [
+ "pending",
+ "delivered",
+ "failed",
+ "dead"
+ ],
+ "properties": {
+ "pending": {
+ "type": "integer"
+ },
+ "delivered": {
+ "type": "integer"
+ },
+ "failed": {
+ "type": "integer"
+ },
+ "dead": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "subscription_id",
+ "queued"
+ ],
+ "properties": {
+ "subscription_id": {
+ "type": "string"
+ },
+ "queued": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/publish-result.v1.json",
+ "title": "EventsPublishResult",
+ "description": "events.publish-result@1: the answer of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps). One envelope → { event_id, seq, duplicate } (201 when stored, 200 when every event was a repeat); a batch { events: [...] } (at most 100, atomic) → { results } in the same order. Refusals are problem+json: 422 events.invalid_envelope / events.invalid_redaction, 403 events.source_mismatch / events.type_not_allowed / events.actor_mismatch, 413 events.payload_too_large / events.batch_too_large, 429 events.quota_exceeded (apps).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "event_id",
+ "seq",
+ "duplicate"
+ ],
+ "properties": {
+ "event_id": {
+ "type": "string",
+ "pattern": "^evt_"
+ },
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "duplicate": {
+ "type": "boolean",
+ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "results"
+ ],
+ "properties": {
+ "results": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 100,
+ "items": {
+ "type": "object",
+ "required": [
+ "event_id",
+ "seq",
+ "duplicate"
+ ],
+ "properties": {
+ "event_id": {
+ "type": "string",
+ "pattern": "^evt_"
+ },
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "duplicate": {
+ "type": "boolean",
+ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/read-request.v1.json",
+ "title": "EventsReadRequest",
+ "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "topic",
+ "cursor"
+ ],
+ "properties": {
+ "topic": {
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",
+ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
+ },
+ "cursor": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "A seq (next_after_seq of the last page handled)."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/read-result.v1.json",
+ "title": "EventsReadResult",
+ "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, event }], next_after_seq, latest_seq, gap? } (keep next_after_seq as the cursor: it moves past events that did not match; gap { from_seq, to_seq } says events after after_seq were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). An app sees only its project's events in its token's environment plus public first-party events.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "events",
+ "next_after_seq",
+ "latest_seq"
+ ],
+ "properties": {
+ "events": {
+ "type": "array",
+ "maxItems": 1000,
+ "items": {
+ "type": "object",
+ "required": [
+ "seq",
+ "event"
+ ],
+ "properties": {
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "event": {
+ "$ref": "event-envelope.v1.json"
+ }
+ }
+ }
+ },
+ "next_after_seq": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "latest_seq": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "gap": {
+ "type": "object",
+ "required": [
+ "from_seq",
+ "to_seq"
+ ],
+ "properties": {
+ "from_seq": {
+ "type": "integer"
+ },
+ "to_seq": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "seq",
+ "event"
+ ],
+ "properties": {
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "event": {
+ "$ref": "event-envelope.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "consumer",
+ "topic",
+ "cursor",
+ "updated_at"
+ ],
+ "properties": {
+ "consumer": {
+ "type": "string"
+ },
+ "topic": {
+ "type": "string"
+ },
+ "cursor": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/subscription-request.v1.json",
+ "title": "EventsSubscriptionRequest",
+ "description": "events.subscription-request@1: the body of POST /api/v1/subscriptions on OpenVibe.Events (events.subscription.manage for services, events.app.subscribe for developer apps; the consumer is always the caller). topic_pattern (topic is accepted for it) and endpoint are required: a service's endpoint must be http(s) on the allow-list (127.0.0.1, *.openvibe.*), an app's must be https resolving only to public addresses. secret, when given, is 32 to 256 characters (else one is generated); retry_policy accepts max_attempts (1..20) and backoff_ms (1..20 delays of 0..86400000 ms) and nothing else. The same pattern and endpoint twice is 409 events.subscription_exists. GET /api/v1/subscriptions[/:id] and POST /api/v1/subscriptions/:id/disable|enable take no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "endpoint"
+ ],
+ "anyOf": [
+ {
+ "required": [
+ "topic_pattern"
+ ]
+ },
+ {
+ "required": [
+ "topic"
+ ]
+ }
+ ],
+ "properties": {
+ "topic_pattern": {
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",
+ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
+ },
+ "topic": {
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",
+ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
+ },
+ "endpoint": {
+ "type": "string",
+ "pattern": "^https?://"
+ },
+ "secret": {
+ "type": "string",
+ "minLength": 32,
+ "maxLength": 256
+ },
+ "retry_policy": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "max_attempts": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 20
+ },
+ "backoff_ms": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 20,
+ "items": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 86400000
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/subscription-result.v1.json",
+ "title": "EventsSubscriptionResult",
+ "description": "events.subscription-result@1: answers of the subscription routes on OpenVibe.Events. POST /api/v1/subscriptions → 201 events.subscription@1 with its secret (shown once); GET /api/v1/subscriptions → { subscriptions } (the caller's own, no secrets); GET /api/v1/subscriptions/:id and POST …/disable|enable → events.subscription@1. Another consumer's subscription is 404.",
+ "anyOf": [
+ {
+ "$ref": "subscription.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "subscriptions"
+ ],
+ "properties": {
+ "subscriptions": {
+ "type": "array",
+ "items": {
+ "$ref": "subscription.v1.json"
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/subscription.v1.json",
+ "title": "EventsSubscription",
+ "description": "events.subscription@1: one push subscription as OpenVibe.Events shows it to its consumer (server/store.js subscriptionView). The signing secret appears only in the answer that creates it (and in a rotation's). A developer app's subscription also names its project and environment.",
+ "type": "object",
+ "required": [
+ "id",
+ "consumer",
+ "topic_pattern",
+ "endpoint",
+ "enabled",
+ "retry_policy",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^sub_"
+ },
+ "consumer": {
+ "type": "string",
+ "description": "The calling service, or app:app_<ULID>."
+ },
+ "topic_pattern": {
+ "type": "string"
+ },
+ "endpoint": {
+ "type": "string"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "retry_policy": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "max_attempts": {
+ "type": "integer"
+ },
+ "backoff_ms": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "project_id": {
+ "type": "string"
+ },
+ "env": {
+ "type": "string"
+ },
+ "secret": {
+ "type": "string",
+ "description": "Only when the subscription is created: shown once."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "status": "live" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "primary": { "provider": "anthropic", "model": "claude-sonnet-4-5" }, "fallbacks": [ { "provider": "openai", "model": "gpt-4.1-mini" } ], "response_format": "json" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "name": "Clip title", "system_prompt": "You title stream clips.", "user_prompt": "Transcript: {{transcript}}", "input_schema": { "type": "object", "required": ["transcript"] }, "output_schema": { "type": "object", "required": ["title"] }, "visibility": "first-party", "status": "draft" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "steps": [ { "kind": "llm", "template": "live.clip_title" } ], "cache_mode": "service", "cache_ttl_sec": 86400 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "workflow": { "key": "live.clip_title", "status": "active" } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "route": { "key": "default.chat", "version": 3, "status": "disabled" } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "template": { "key": "live.clip_title", "version": 4, "status": "active", "name": "Clip title" } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "provider_key": "anthropic", "model_key": "x", "type": "image", "status": "active", "cost": {}, "supports": {} }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "provider_key": "anthropic", "model_key": "claude-sonnet-4-5", "display_name": "Claude Sonnet 4.5", "type": "chat", "status": "active", "context_window": 200000, "max_output": 64000, "cost": { "in_per_mtok": 3, "out_per_mtok": 15, "cached_per_mtok": 0.3 }, "supports": { "json": true, "tools": true, "streaming": true, "vision": true }, "metadata": {}, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-20T10:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "openai", "kind": "openai", "api_key": "sk-live-123", "status": "paused" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "openai", "kind": "openai", "auth_mode": "bearer", "secret_ref": "env:OPENAI_API_KEY", "capabilities": ["chat", "embed"], "priority": 20 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "scope_type": "service", "scope_id": "live", "window": "day", "max_cost_usd": 25 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "removed": -1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "removed": 42 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "provider": { "key": "anthropic", "display_name": "Anthropic", "kind": "anthropic", "status": "active", "base_url": null, "auth_mode": "x-api-key", "secret_ref": "env:ANTHROPIC_API_KEY", "default_model": "claude-sonnet-4-5", "capabilities": ["chat", "generate", "summarize", "json"], "timeout_ms": 30000, "priority": 10, "metadata": {}, "origin": "admin", "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "credentials": "configured", "health": { "provider_key": "anthropic", "state": "closed", "consecutive_failures": 0, "opened_at": null, "last_error": null } } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "anthropic", "kind": "anthropic", "status": "active", "auth_mode": "x-api-key", "capabilities": ["chat"], "credentials": "sk-ant-secret" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "anthropic", "display_name": "Anthropic", "kind": "anthropic", "status": "active", "base_url": null, "auth_mode": "x-api-key", "secret_ref": "env:ANTHROPIC_API_KEY", "default_model": "claude-sonnet-4-5", "capabilities": ["chat", "generate", "summarize", "json"], "timeout_ms": 30000, "priority": 10, "metadata": {}, "origin": "admin", "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "credentials": "configured", "health": { "provider_key": "anthropic", "state": "closed", "consecutive_failures": 0, "opened_at": null, "last_error": null } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "runs": { "succeeded": 1 } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "models": [ { "provider_key": "anthropic", "model_key": "claude-sonnet-4-5", "display_name": "Claude Sonnet 4.5", "type": "chat", "status": "active", "context_window": 200000, "max_output": 64000, "cost": { "in_per_mtok": 3, "out_per_mtok": 15, "cached_per_mtok": 0.3 }, "supports": { "json": true, "tools": true, "streaming": true, "vision": true }, "metadata": {}, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-20T10:00:00.000Z" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "providers": [ { "key": "anthropic", "kind": "anthropic", "status": "active", "credentials": "configured", "health": "closed" } ], "runs": { "succeeded": 1203, "failed": 7 }, "today": { "day": "2026-09-25", "requests": 311, "cost_usd": 1.84 }, "workflows": 23, "templates": 19, "routes": 6, "inflight": 2, "quotas": [] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "from_seq": 1, "event_ids": ["evt_01JAB2C3D4E5F6G7H8J9K0MNPQ"] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "from_seq": 88000 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "deliveries": [], "counts": { "pending": 0 } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "deliveries": [ { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 88213, "status": "dead", "attempt": 8, "next_attempt_at": null, "last_status": 502, "last_error": "HTTP 502", "delivered_at": null } ], "counts": { "pending": 3, "delivered": 91822, "failed": 1, "dead": 1 } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subscription_id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "queued": 213 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 0 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "results": [ { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 88213, "duplicate": false }, { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPR", "seq": 70001, "duplicate": true } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "seq": 88213, "duplicate": false }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "topic": "media..vod", "cursor": -1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "topic": "media.vod.*", "cursor": 88213 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "events": [ { "seq": 1, "event": { "event_type": "x" } } ], "next_after_seq": 1, "latest_seq": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "consumer": "live", "topic": "media.vod.*", "cursor": 0, "updated_at": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "events": [ { "seq": 88213, "event": { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPR", "event_type": "app.p01jab2c3d4e5f6g7h8j9k0mnpq.order.created", "version": 1, "source": "app-01jab2c3d4e5f6g7h8j9k0mnps", "actor": { "type": "app", "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPS" }, "timestamp": "2026-09-23T12:00:00Z", "visibility": "internal", "subject": { "type": "order", "id": "42" }, "payload": { "total": 3 }} } ], "next_after_seq": 88240, "latest_seq": 88240 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "topic_pattern": "media.vod.*", "endpoint": "https://hooks.example.com/ov", "retry_policy": { "max_attempts": 8, "jitter": true } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/internal/media-events", "retry_policy": { "max_attempts": 8, "backoff_ms": [1000, 5000, 30000] } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subscriptions": [ { "id": "sub_1" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"sub_01JAB2C3D4E5F6G7H8J9K0MNPQ","consumer":"live","topic_pattern":"media.vod.*","endpoint":"http://127.0.0.1:3000/internal/media-events","enabled":true,"retry_policy":null,"created_at":"2026-09-25T18:00:00.000Z","updated_at":"2026-09-25T18:00:00.000Z","secret":"whsec_4f1c0b8e2a9d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subscriptions": [ { "id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "consumer": "live", "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/internal/media-events", "enabled": true, "retry_policy": null, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "consumer": "live", "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/x", "enabled": "yes", "retry_policy": null, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "sub_01JAB2C3D4E5F6G7H8J9K0MNPQ", "consumer": "live", "topic_pattern": "media.vod.*", "endpoint": "http://127.0.0.1:3000/internal/media-events", "enabled": true, "retry_policy": null, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }
@@ -3347,372 +3347,1703 @@
@@ -3347,372 +3347,1703 @@
}
}
}
}
},
},
+ "events.delivery-admin-request": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/delivery-admin-request.v1.json",
+ "title": "EventsDeliveryAdminRequest",
+ "description": "events.delivery-admin-request@1: the body of POST /api/v1/deliveries/replay on OpenVibe.Events (events.delivery.admin, operators): { subscription_id } and exactly one of from_seq (requeue everything the subscription matches from that seq) or event_ids (at most 1000). GET /api/v1/deliveries?status=&subscription_id=&after_seq=&limit= takes no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "subscription_id"
+ ],
+ "oneOf": [
+ {
+ "required": [
+ "from_seq"
+ ]
+ },
+ {
+ "required": [
+ "event_ids"
+ ]
+ }
+ ],
+ "properties": {
+ "subscription_id": {
+ "type": "string"
+ },
+ "from_seq": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "event_ids": {
+ "type": "array",
+ "maxItems": 1000,
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "events.delivery-admin-result": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/delivery-admin-result.v1.json",
+ "title": "EventsDeliveryAdminResult",
+ "description": "events.delivery-admin-result@1: answers of events.delivery.admin on OpenVibe.Events. GET /api/v1/deliveries → { deliveries, counts } (per delivery: event, subscription, seq, status pending|delivered|failed|dead, attempt, next_attempt_at, the last HTTP status and error, delivered_at; counts by status); POST /api/v1/deliveries/replay → { subscription_id, queued }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "deliveries",
+ "counts"
+ ],
+ "properties": {
+ "deliveries": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "event_id",
+ "subscription_id",
+ "seq",
+ "status",
+ "attempt"
+ ],
+ "properties": {
+ "event_id": {
+ "type": "string"
+ },
+ "subscription_id": {
+ "type": "string"
+ },
+ "seq": {
+ "type": "integer"
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "delivered",
+ "failed",
+ "dead"
+ ]
+ },
+ "attempt": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "next_attempt_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_status": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "last_error": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "delivered_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "counts": {
+ "type": "object",
+ "required": [
+ "pending",
+ "delivered",
+ "failed",
+ "dead"
+ ],
+ "properties": {
+ "pending": {
+ "type": "integer"
+ },
+ "delivered": {
+ "type": "integer"
+ },
+ "failed": {
+ "type": "integer"
+ },
+ "dead": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "subscription_id",
+ "queued"
+ ],
+ "properties": {
+ "subscription_id": {
+ "type": "string"
+ },
+ "queued": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ ]
+ }
+ },
+ "events.publish-result": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/publish-result.v1.json",
+ "title": "EventsPublishResult",
+ "description": "events.publish-result@1: the answer of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps). One envelope → { event_id, seq, duplicate } (201 when stored, 200 when every event was a repeat); a batch { events: [...] } (at most 100, atomic) → { results } in the same order. Refusals are problem+json: 422 events.invalid_envelope / events.invalid_redaction, 403 events.source_mismatch / events.type_not_allowed / events.actor_mismatch, 413 events.payload_too_large / events.batch_too_large, 429 events.quota_exceeded (apps).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "event_id",
+ "seq",
+ "duplicate"
+ ],
+ "properties": {
+ "event_id": {
+ "type": "string",
+ "pattern": "^evt_"
+ },
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "duplicate": {
+ "type": "boolean",
+ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "results"
+ ],
+ "properties": {
+ "results": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 100,
+ "items": {
+ "type": "object",
+ "required": [
+ "event_id",
+ "seq",
+ "duplicate"
+ ],
+ "properties": {
+ "event_id": {
+ "type": "string",
+ "pattern": "^evt_"
+ },
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "duplicate": {
+ "type": "boolean",
+ "description": "true: this event_id was stored before; the stored seq is returned and nothing is stored twice."
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ "events.read-request": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/read-request.v1.json",
+ "title": "EventsReadRequest",
+ "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "topic",
+ "cursor"
+ ],
+ "properties": {
+ "topic": {
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",
+ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
+ },
+ "cursor": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "A seq (next_after_seq of the last page handled)."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "events.read-result": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/read-result.v1.json",
+ "title": "EventsReadResult",
+ "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, event }], next_after_seq, latest_seq, gap? } (keep next_after_seq as the cursor: it moves past events that did not match; gap { from_seq, to_seq } says events after after_seq were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). An app sees only its project's events in its token's environment plus public first-party events.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "events",
+ "next_after_seq",
+ "latest_seq"
+ ],
+ "properties": {
+ "events": {
+ "type": "array",
+ "maxItems": 1000,
+ "items": {
+ "type": "object",
+ "required": [
+ "seq",
+ "event"
+ ],
+ "properties": {
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "event": {
+ "$ref": "event-envelope.v1.json"
+ }
+ }
+ }
+ },
+ "next_after_seq": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "latest_seq": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "gap": {
+ "type": "object",
+ "required": [
+ "from_seq",
+ "to_seq"
+ ],
+ "properties": {
+ "from_seq": {
+ "type": "integer"
+ },
+ "to_seq": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "seq",
+ "event"
+ ],
+ "properties": {
+ "seq": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "event": {
+ "$ref": "event-envelope.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "consumer",
+ "topic",
+ "cursor",
+ "updated_at"
+ ],
+ "properties": {
+ "consumer": {
+ "type": "string"
+ },
+ "topic": {
+ "type": "string"
+ },
+ "cursor": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "events.subscription-request": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/subscription-request.v1.json",
+ "title": "EventsSubscriptionRequest",
+ "description": "events.subscription-request@1: the body of POST /api/v1/subscriptions on OpenVibe.Events (events.subscription.manage for services, events.app.subscribe for developer apps; the consumer is always the caller). topic_pattern (topic is accepted for it) and endpoint are required: a service's endpoint must be http(s) on the allow-list (127.0.0.1, *.openvibe.*), an app's must be https resolving only to public addresses. secret, when given, is 32 to 256 characters (else one is generated); retry_policy accepts max_attempts (1..20) and backoff_ms (1..20 delays of 0..86400000 ms) and nothing else. The same pattern and endpoint twice is 409 events.subscription_exists. GET /api/v1/subscriptions[/:id] and POST /api/v1/subscriptions/:id/disable|enable take no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "endpoint"
+ ],
+ "anyOf": [
+ {
+ "required": [
+ "topic_pattern"
+ ]
+ },
+ {
+ "required": [
+ "topic"
+ ]
+ }
+ ],
+ "properties": {
+ "topic_pattern": {
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",
+ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
+ },
+ "topic": {
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^(\\*|[a-z0-9_]+)(\\.(\\*|[a-z0-9_]+))*$",
+ "description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
+ },
+ "endpoint": {
+ "type": "string",
+ "pattern": "^https?://"
+ },
+ "secret": {
+ "type": "string",
+ "minLength": 32,
+ "maxLength": 256
+ },
+ "retry_policy": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "max_attempts": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 20
+ },
+ "backoff_ms": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 20,
+ "items": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 86400000
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "events.subscription-result": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/subscription-result.v1.json",
+ "title": "EventsSubscriptionResult",
+ "description": "events.subscription-result@1: answers of the subscription routes on OpenVibe.Events. POST /api/v1/subscriptions → 201 events.subscription@1 with its secret (shown once); GET /api/v1/subscriptions → { subscriptions } (the caller's own, no secrets); GET /api/v1/subscriptions/:id and POST …/disable|enable → events.subscription@1. Another consumer's subscription is 404.",
+ "anyOf": [
+ {
+ "$ref": "subscription.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "subscriptions"
+ ],
+ "properties": {
+ "subscriptions": {
+ "type": "array",
+ "items": {
+ "$ref": "subscription.v1.json"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ "events.subscription": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/subscription.v1.json",
+ "title": "EventsSubscription",
+ "description": "events.subscription@1: one push subscription as OpenVibe.Events shows it to its consumer (server/store.js subscriptionView). The signing secret appears only in the answer that creates it (and in a rotation's). A developer app's subscription also names its project and environment.",
+ "type": "object",
+ "required": [
+ "id",
+ "consumer",
+ "topic_pattern",
+ "endpoint",
+ "enabled",
+ "retry_policy",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^sub_"
+ },
+ "consumer": {
+ "type": "string",
+ "description": "The calling service, or app:app_<ULID>."
+ },
+ "topic_pattern": {
+ "type": "string"
+ },
+ "endpoint": {
+ "type": "string"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "retry_policy": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "max_attempts": {
+ "type": "integer"
+ },
+ "backoff_ms": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "project_id": {
+ "type": "string"
+ },
+ "env": {
+ "type": "string"
+ },
+ "secret": {
+ "type": "string",
+ "description": "Only when the subscription is created: shown once."
+ }
+ }
+ }
+ },
"chat.message.deleted": {
"chat.message.deleted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "chat",
+ "owner": "chat",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/chat.message.deleted.v1.json",
+ "title": "ChatMessageDeletedPayload",
+ "description": "chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.",
+ "type": "object",
+ "required": [
+ "message_ids",
+ "redacts"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "message_ids": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 500,
+ "uniqueItems": true,
+ "items": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "description": "Chat message ids (the id in chat.message.created)."
+ },
+ "redacts": {
+ "$ref": "../redaction-directive.v1.json",
+ "description": "{ subject_type: chat_message, subject_ids: message_ids as strings }."
+ }
+ }
+ }
+ },
+ "sources.item.created": {
+ "version": "1.0.0",
+ "owner": "sources",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/sources.item.created.v1.json",
+ "title": "SourcesItemCreatedPayload",
+ "description": "sources.item.created v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch, or a manual entry, produced an item Sources had not seen (revision 1). A summary only: consumers fetch the full sources.item@1 with sources.item.read. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",
+ "type": "object",
+ "required": [
+ "item_id",
+ "source_key",
+ "category",
+ "kind",
+ "canonical_url",
+ "title",
+ "revision",
+ "content_hash",
+ "retrieved_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "item_id": {
+ "type": "string",
+ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "source_key": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
+ },
+ "category": {
+ "enum": [
+ "news",
+ "blog",
+ "reviews",
+ "deals",
+ "coupons",
+ "trade"
+ ]
+ },
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,39}$",
+ "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "content_hash": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "retrieved_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Last successful fetch (or manual entry) that contained the item."
+ }
+ }
+ }
+ },
+ "sources.item.updated": {
+ "version": "1.0.0",
+ "owner": "sources",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/sources.item.updated.v1.json",
+ "title": "SourcesItemUpdatedPayload",
+ "description": "sources.item.updated v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch or manual entry changed an existing item's content hash, which made a new revision. A removed item is never updated (removal is sticky). Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",
+ "type": "object",
+ "required": [
+ "item_id",
+ "source_key",
+ "category",
+ "kind",
+ "canonical_url",
+ "title",
+ "revision",
+ "content_hash",
+ "retrieved_at",
+ "previous_content_hash"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "item_id": {
+ "type": "string",
+ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "source_key": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
+ },
+ "category": {
+ "enum": [
+ "news",
+ "blog",
+ "reviews",
+ "deals",
+ "coupons",
+ "trade"
+ ]
+ },
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,39}$",
+ "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "content_hash": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "retrieved_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Last successful fetch (or manual entry) that contained the item."
+ },
+ "previous_content_hash": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$"
+ }
+ }
+ }
+ },
+ "sources.item.removed": {
+ "version": "1.0.0",
+ "owner": "sources",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/sources.item.removed.v1.json",
+ "title": "SourcesItemRemovedPayload",
+ "description": "sources.item.removed v1 (OpenVibe.Sources server/items.js remove → itemEvent, from DELETE /api/v1/items/:id). Staff removed an item (takedown, licence, error). Sticky: a later fetch that still lists it does not bring it back. The removal bumps the revision; content_hash is the last content's. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",
+ "type": "object",
+ "required": [
+ "item_id",
+ "source_key",
+ "category",
+ "kind",
+ "canonical_url",
+ "title",
+ "revision",
+ "content_hash",
+ "retrieved_at",
+ "reason"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "item_id": {
+ "type": "string",
+ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "source_key": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
+ },
+ "category": {
+ "enum": [
+ "news",
+ "blog",
+ "reviews",
+ "deals",
+ "coupons",
+ "trade"
+ ]
+ },
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,39}$",
+ "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "content_hash": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "retrieved_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Last successful fetch (or manual entry) that contained the item."
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 300
+ }
+ }
+ }
+ },
+ "sources.fetch.failed": {
+ "version": "1.0.0",
+ "owner": "sources",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/sources.fetch.failed.v1.json",
+ "title": "SourcesFetchFailedPayload",
+ "description": "sources.fetch.failed v1 (OpenVibe.Sources server/ingest.js failedEvent). One endpoint fetch of a source failed and counts as a failure: robots denied or unreachable, an HTTP error, the site's 429, a parse error or an internal error. Not sent for 304, a disabled source, a missing credential or the local rate limit. No item is created or changed. Envelope: subject { type: source, id: <source_key> }, visibility internal, priority important, actor service:sources.",
+ "type": "object",
+ "required": [
+ "source_key",
+ "category",
+ "run_id",
+ "endpoint_url",
+ "state",
+ "error_code",
+ "http_status",
+ "consecutive_failures"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "source_key": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
+ },
+ "category": {
+ "enum": [
+ "news",
+ "blog",
+ "reviews",
+ "deals",
+ "coupons",
+ "trade"
+ ]
+ },
+ "run_id": {
+ "type": "string",
+ "pattern": "^frn_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The recorded fetch run (GET /api/v1/sources/:key/runs)."
+ },
+ "endpoint_url": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 2048
+ },
+ "state": {
+ "enum": [
+ "robots_denied",
+ "http_error",
+ "rate_limited",
+ "parse_error"
+ ]
+ },
+ "error_code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,39}$",
+ "description": "robots | address_refused | port_refused | bad_url | upstream_429 | http_<status> | unreadable | adapter_error | internal."
+ },
+ "http_status": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 100,
+ "maximum": 599
+ },
+ "consecutive_failures": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The source's consecutive failed runs including this one."
+ }
+ }
+ }
+ },
+ "sources.index_document.upserted": {
+ "version": "1.0.0",
+ "owner": "sources",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/chat.message.deleted.v1.json",
- "title": "ChatMessageDeletedPayload",
- "description": "chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.",
+ "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.upserted.v1.json",
+ "title": "SourcesIndexDocumentUpsertedPayload",
+ "description": "sources.index_document.upserted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was created or updated; the payload is its search.index-document@1 (owner sources, type item, visibility members for role:admin and role:global_mod, authorship imported, indexability noindex third_party_content). Consumed by OpenVibe.Search (*.index_document.*). Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.",
+ "$ref": "../../search/index-document.v1.json"
+ }
+ },
+ "sources.index_document.deleted": {
+ "version": "1.0.0",
+ "owner": "sources",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.deleted.v1.json",
+ "title": "SourcesIndexDocumentDeletedPayload",
+ "description": "sources.index_document.deleted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was removed; OpenVibe.Search stores a tombstone at this revision. Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.",
"type": "object",
"type": "object",
"required": [
"required": [
- "message_ids",
- "redacts"
+ "type",
+ "id",
+ "revision"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "message_ids": {
- "type": "array",
- "minItems": 1,
- "maxItems": 500,
- "uniqueItems": true,
- "items": {
- "type": "integer",
- "minimum": 1
- },
- "description": "Chat message ids (the id in chat.message.created)."
+ "type": {
+ "const": "item"
},
},
- "redacts": {
- "$ref": "../redaction-directive.v1.json",
- "description": "{ subject_type: chat_message, subject_ids: message_ids as strings }."
+ "id": {
+ "type": "string",
+ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ }
+ },
+ "search.document.indexed": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/search.document.indexed.v1.json",
+ "title": "SearchDocumentIndexedPayload",
+ "description": "search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "id",
+ "revision",
+ "exposure",
+ "reindexed",
+ "canonical_url"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "owner": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "type": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
+ },
+ "exposure": {
+ "enum": [
+ "restricted",
+ "public_unlisted",
+ "public_listed"
+ ]
+ },
+ "reindexed": {
+ "type": "boolean",
+ "description": "true when Search already held an earlier revision of the document."
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none."
+ }
+ }
+ }
+ },
+ "search.document.removed": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/search.document.removed.v1.json",
+ "title": "SearchDocumentRemovedPayload",
+ "description": "search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "id",
+ "revision",
+ "reason",
+ "previous_exposure",
+ "exposure",
+ "canonical_url"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "owner": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "type": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
+ },
+ "reason": {
+ "enum": [
+ "deleted",
+ "draft",
+ "not_published",
+ "visibility_changed",
+ "noindex"
+ ]
+ },
+ "previous_exposure": {
+ "enum": [
+ "restricted",
+ "public_unlisted",
+ "public_listed"
+ ]
+ },
+ "exposure": {
+ "enum": [
+ "none",
+ "restricted",
+ "public_unlisted"
+ ],
+ "description": "Lower than previous_exposure. none: no longer served to anyone."
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The URL only when the document was public before (so it must be purged); null otherwise."
+ }
+ }
+ }
+ },
+ "wiki.space.updated": {
+ "version": "1.0.0",
+ "owner": "wiki",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.space.updated.v1.json",
+ "title": "WikiSpaceUpdatedPayload",
+ "description": "wiki.space.updated v1 (OpenVibe.Wiki server/wiki/service.js spaceEvent). Emitted when a space is created, renamed, changes visibility or settings, has its roles changed, or is deleted, in the transaction of the change. Envelope: subject { type: space, id: spc_… }, visibility public for a public space that is not deleted, otherwise internal, priority important, actor the person (user) or service.",
+ "type": "object",
+ "required": [
+ "slug",
+ "name",
+ "kind",
+ "visibility",
+ "deleted",
+ "change",
+ "url"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "slug": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "kind": {
+ "enum": [
+ "official",
+ "user"
+ ]
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "private"
+ ]
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "change": {
+ "enum": [
+ "created",
+ "renamed",
+ "visibility",
+ "settings",
+ "roles",
+ "deleted"
+ ],
+ "description": "What changed. renamed = a new slug."
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://",
+ "maxLength": 2048,
+ "description": "Canonical URL of the space."
}
}
}
}
}
}
},
},
- "sources.item.created": {
+ "wiki.revision.created": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "sources",
+ "owner": "wiki",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/sources.item.created.v1.json",
- "title": "SourcesItemCreatedPayload",
- "description": "sources.item.created v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch, or a manual entry, produced an item Sources had not seen (revision 1). A summary only: consumers fetch the full sources.item@1 with sources.item.read. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.revision.created.v1.json",
+ "title": "WikiRevisionCreatedPayload",
+ "description": "wiki.revision.created v1 (OpenVibe.Wiki server/wiki/service.js revisionEvent). Emitted when a page gets a new immutable revision: an edit, a revert or an AI proposal. A new revision is a draft until it is published (then wiki.page.published|updated follows). Envelope: subject { type: page, id: pg_…, revision: <number> }, visibility internal, priority important, actor the person or service.",
"type": "object",
"type": "object",
"required": [
"required": [
- "item_id",
- "source_key",
- "category",
+ "space_id",
+ "space",
+ "slug",
+ "number",
"kind",
"kind",
- "canonical_url",
- "title",
- "revision",
- "content_hash",
- "retrieved_at"
+ "parent_number",
+ "reverted_to",
+ "authorship",
+ "author",
+ "message"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "item_id": {
+ "space_id": {
"type": "string",
"type": "string",
- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^spc_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
- "source_key": {
+ "space": {
"type": "string",
"type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
+ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
+ "description": "Space slug."
},
},
- "category": {
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80,
+ "description": "Page slug."
+ },
+ "number": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The new revision number."
+ },
+ "kind": {
"enum": [
"enum": [
- "news",
- "blog",
- "reviews",
- "deals",
- "coupons",
- "trade"
+ "edit",
+ "revert",
+ "import"
]
]
},
},
- "kind": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,39}$",
- "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."
+ "parent_number": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1
},
},
- "canonical_url": {
+ "reverted_to": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision a revert restored; null otherwise."
+ },
+ "authorship": {
+ "enum": [
+ "human",
+ "ai_assisted",
+ "ai_generated",
+ "imported",
+ null
+ ],
+ "description": "Search's authorship vocabulary; null when the revision has no authorship record."
+ },
+ "author": {
"type": [
"type": [
"string",
"string",
"null"
"null"
],
],
- "format": "uri"
+ "maxLength": 200,
+ "description": "Who wrote it: a subject id (usr_…) or a service (svc:<id>)."
},
},
- "title": {
+ "message": {
"type": [
"type": [
"string",
"string",
"null"
"null"
- ]
+ ],
+ "maxLength": 2000
+ },
+ "proposal_id": {
+ "type": "string",
+ "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Present when the revision is an AI proposal awaiting a person's approval."
+ }
+ }
+ }
+ },
+ "wiki.watch.triggered": {
+ "version": "1.0.0",
+ "owner": "wiki",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.watch.triggered.v1.json",
+ "title": "WikiWatchTriggeredPayload",
+ "description": "wiki.watch.triggered v1 (OpenVibe.Wiki server/wiki/service.js notifyWatchers). One event per change to a watched page, listing the watchers who may read the page (never the actor); not emitted when nobody is left. Envelope: subject { type: page, id: pg_…, revision: <revision or 0> }, visibility internal, priority important, actor the person or service.",
+ "type": "object",
+ "required": [
+ "action",
+ "space",
+ "slug",
+ "title",
+ "revision",
+ "url",
+ "recipients"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "action": {
+ "enum": [
+ "published",
+ "updated",
+ "unpublished",
+ "deleted",
+ "revised",
+ "proposed"
+ ],
+ "description": "revised = a new draft revision; proposed = an AI proposal awaits approval; the rest follow wiki.page.*."
+ },
+ "space": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
+ "description": "Space slug."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80,
+ "description": "Page slug."
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 200
},
},
"revision": {
"revision": {
- "type": "integer",
+ "type": [
+ "integer",
+ "null"
+ ],
"minimum": 1
"minimum": 1
},
},
- "content_hash": {
+ "url": {
"type": "string",
"type": "string",
- "pattern": "^[0-9a-f]{64}$"
+ "format": "uri",
+ "pattern": "^https?://",
+ "maxLength": 2048
},
},
- "retrieved_at": {
+ "recipients": {
+ "type": "array",
+ "minItems": 1,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "description": "Watchers to notify."
+ }
+ }
+ }
+ },
+ "wiki.page.published": {
+ "version": "1.0.0",
+ "owner": "wiki",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.published.v1.json",
+ "title": "WikiPagePublishedPayload",
+ "description": "wiki.page.published v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page became published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).",
+ "type": "object",
+ "required": [
+ "canonical_url",
+ "publication_state",
+ "indexability",
+ "space",
+ "slug",
+ "visibility"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "canonical_url": {
"type": "string",
"type": "string",
- "format": "date-time",
- "description": "Last successful fetch (or manual entry) that contained the item."
+ "format": "uri",
+ "pattern": "^https?://",
+ "maxLength": 2048
+ },
+ "publication_state": {
+ "const": "published"
+ },
+ "indexability": {
+ "type": "object",
+ "required": [
+ "decision",
+ "reasons"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "decision": {
+ "enum": [
+ "index",
+ "noindex"
+ ]
+ },
+ "reasons": {
+ "type": "array",
+ "maxItems": 20,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,63}$"
+ }
+ }
+ }
+ },
+ "space": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
+ "description": "Space slug."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80,
+ "description": "Page slug inside the space."
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "private"
+ ],
+ "description": "Effective visibility (the page's, narrowed by its space's)."
}
}
}
}
}
}
},
},
- "sources.item.updated": {
+ "wiki.page.updated": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "sources",
+ "owner": "wiki",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/sources.item.updated.v1.json",
- "title": "SourcesItemUpdatedPayload",
- "description": "sources.item.updated v1 (OpenVibe.Sources server/items.js ingest → itemEvent). A successful fetch or manual entry changed an existing item's content hash, which made a new revision. A removed item is never updated (removal is sticky). Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.updated.v1.json",
+ "title": "WikiPageUpdatedPayload",
+ "description": "wiki.page.updated v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page got a new published revision or visibility, or a review changed what Search holds, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).",
"type": "object",
"type": "object",
"required": [
"required": [
- "item_id",
- "source_key",
- "category",
- "kind",
"canonical_url",
"canonical_url",
- "title",
- "revision",
- "content_hash",
- "retrieved_at",
- "previous_content_hash"
+ "publication_state",
+ "indexability",
+ "space",
+ "slug",
+ "visibility"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "item_id": {
- "type": "string",
- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "source_key": {
+ "canonical_url": {
"type": "string",
"type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
- },
- "category": {
- "enum": [
- "news",
- "blog",
- "reviews",
- "deals",
- "coupons",
- "trade"
- ]
+ "format": "uri",
+ "pattern": "^https?://",
+ "maxLength": 2048
},
},
- "kind": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,39}$",
- "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."
+ "publication_state": {
+ "const": "published"
},
},
- "canonical_url": {
- "type": [
- "string",
- "null"
+ "indexability": {
+ "type": "object",
+ "required": [
+ "decision",
+ "reasons"
],
],
- "format": "uri"
- },
- "title": {
- "type": [
- "string",
- "null"
- ]
- },
- "revision": {
- "type": "integer",
- "minimum": 1
+ "additionalProperties": false,
+ "properties": {
+ "decision": {
+ "enum": [
+ "index",
+ "noindex"
+ ]
+ },
+ "reasons": {
+ "type": "array",
+ "maxItems": 20,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,63}$"
+ }
+ }
+ }
},
},
- "content_hash": {
+ "space": {
"type": "string",
"type": "string",
- "pattern": "^[0-9a-f]{64}$"
+ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
+ "description": "Space slug."
},
},
- "retrieved_at": {
+ "slug": {
"type": "string",
"type": "string",
- "format": "date-time",
- "description": "Last successful fetch (or manual entry) that contained the item."
+ "minLength": 1,
+ "maxLength": 80,
+ "description": "Page slug inside the space."
},
},
- "previous_content_hash": {
- "type": "string",
- "pattern": "^[0-9a-f]{64}$"
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "private"
+ ],
+ "description": "Effective visibility (the page's, narrowed by its space's)."
}
}
}
}
}
}
},
},
- "sources.item.removed": {
+ "wiki.page.unpublished": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "sources",
+ "owner": "wiki",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/sources.item.removed.v1.json",
- "title": "SourcesItemRemovedPayload",
- "description": "sources.item.removed v1 (OpenVibe.Sources server/items.js remove → itemEvent, from DELETE /api/v1/items/:id). Staff removed an item (takedown, licence, error). Sticky: a later fetch that still lists it does not bring it back. The removal bumps the revision; content_hash is the last content's. Envelope: subject { type: item, id: <item_id>, revision }, visibility internal, priority important, actor service:sources.",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.unpublished.v1.json",
+ "title": "WikiPageUnpublishedPayload",
+ "description": "wiki.page.unpublished v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).",
"type": "object",
"type": "object",
"required": [
"required": [
- "item_id",
- "source_key",
- "category",
- "kind",
"canonical_url",
"canonical_url",
- "title",
- "revision",
- "content_hash",
- "retrieved_at",
- "reason"
+ "publication_state",
+ "indexability",
+ "space",
+ "slug",
+ "visibility"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "item_id": {
- "type": "string",
- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "source_key": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
- },
- "category": {
- "enum": [
- "news",
- "blog",
- "reviews",
- "deals",
- "coupons",
- "trade"
- ]
- },
- "kind": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,39}$",
- "description": "article | url | sitemap | product | offer | review | record … (sources.item@1 kind)."
- },
"canonical_url": {
"canonical_url": {
"type": [
"type": [
"string",
"string",
"null"
"null"
],
],
- "format": "uri"
+ "description": "null: the event carries a tombstone, which has no URL."
},
},
- "title": {
+ "publication_state": {
+ "const": "unpublished"
+ },
+ "indexability": {
"type": [
"type": [
- "string",
+ "object",
"null"
"null"
- ]
- },
- "revision": {
- "type": "integer",
- "minimum": 1
- },
- "content_hash": {
- "type": "string",
- "pattern": "^[0-9a-f]{64}$"
+ ],
+ "required": [
+ "decision",
+ "reasons"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "decision": {
+ "enum": [
+ "index",
+ "noindex"
+ ]
+ },
+ "reasons": {
+ "type": "array",
+ "maxItems": 20,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,63}$"
+ }
+ }
+ },
+ "description": "The last gate decision, or null when there is none."
},
},
- "retrieved_at": {
+ "space": {
"type": "string",
"type": "string",
- "format": "date-time",
- "description": "Last successful fetch (or manual entry) that contained the item."
+ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
+ "description": "Space slug."
},
},
- "reason": {
+ "slug": {
"type": "string",
"type": "string",
"minLength": 1,
"minLength": 1,
- "maxLength": 300
+ "maxLength": 80,
+ "description": "Page slug inside the space."
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "private"
+ ],
+ "description": "Effective visibility (the page's, narrowed by its space's)."
}
}
}
}
}
}
},
},
- "sources.fetch.failed": {
+ "wiki.page.deleted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "sources",
+ "owner": "wiki",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/sources.fetch.failed.v1.json",
- "title": "SourcesFetchFailedPayload",
- "description": "sources.fetch.failed v1 (OpenVibe.Sources server/ingest.js failedEvent). One endpoint fetch of a source failed and counts as a failure: robots denied or unreachable, an HTTP error, the site's 429, a parse error or an internal error. Not sent for 304, a disabled source, a missing credential or the local rate limit. No item is created or changed. Envelope: subject { type: source, id: <source_key> }, visibility internal, priority important, actor service:sources.",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.deleted.v1.json",
+ "title": "WikiPageDeletedPayload",
+ "description": "wiki.page.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page (or its whole space) was deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).",
"type": "object",
"type": "object",
"required": [
"required": [
- "source_key",
- "category",
- "run_id",
- "endpoint_url",
- "state",
- "error_code",
- "http_status",
- "consecutive_failures"
+ "canonical_url",
+ "publication_state",
+ "indexability",
+ "space",
+ "slug",
+ "visibility"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "source_key": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
- },
- "category": {
- "enum": [
- "news",
- "blog",
- "reviews",
- "deals",
- "coupons",
- "trade"
- ]
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "null: the event carries a tombstone, which has no URL."
},
},
- "run_id": {
+ "publication_state": {
+ "const": "deleted"
+ },
+ "indexability": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "decision",
+ "reasons"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "decision": {
+ "enum": [
+ "index",
+ "noindex"
+ ]
+ },
+ "reasons": {
+ "type": "array",
+ "maxItems": 20,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,63}$"
+ }
+ }
+ },
+ "description": "The last gate decision, or null when there is none."
+ },
+ "space": {
"type": "string",
"type": "string",
- "pattern": "^frn_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The recorded fetch run (GET /api/v1/sources/:key/runs)."
+ "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
+ "description": "Space slug."
},
},
- "endpoint_url": {
+ "slug": {
"type": "string",
"type": "string",
"minLength": 1,
"minLength": 1,
- "maxLength": 2048
+ "maxLength": 80,
+ "description": "Page slug inside the space."
},
},
- "state": {
+ "visibility": {
"enum": [
"enum": [
- "robots_denied",
- "http_error",
- "rate_limited",
- "parse_error"
- ]
- },
- "error_code": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,39}$",
- "description": "robots | address_refused | port_refused | bad_url | upstream_429 | http_<status> | unreadable | adapter_error | internal."
- },
- "http_status": {
- "type": [
- "integer",
- "null"
+ "public",
+ "members",
+ "private"
],
],
- "minimum": 100,
- "maximum": 599
- },
- "consecutive_failures": {
- "type": "integer",
- "minimum": 1,
- "description": "The source's consecutive failed runs including this one."
+ "description": "Effective visibility (the page's, narrowed by its space's)."
}
}
}
}
}
}
},
},
- "sources.index_document.upserted": {
+ "wiki.index_document.upserted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "sources",
+ "owner": "wiki",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.upserted.v1.json",
- "title": "SourcesIndexDocumentUpsertedPayload",
- "description": "sources.index_document.upserted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was created or updated; the payload is its search.index-document@1 (owner sources, type item, visibility members for role:admin and role:global_mod, authorship imported, indexability noindex third_party_content). Consumed by OpenVibe.Search (*.index_document.*). Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.",
- "$ref": "../../search/index-document.v1.json"
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.upserted.v1.json",
+ "title": "WikiIndexDocumentUpsertedPayload",
+ "description": "wiki.index_document.upserted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable page, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision: <document revision> }, visibility internal, priority important, actor service:wiki.",
+ "allOf": [
+ {
+ "$ref": "../../search/index-document.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "deleted"
+ ],
+ "properties": {
+ "owner": {
+ "const": "wiki"
+ },
+ "type": {
+ "const": "page"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "deleted": {
+ "const": false
+ }
+ }
+ }
+ ]
}
}
},
},
- "sources.index_document.deleted": {
+ "wiki.index_document.deleted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "sources",
+ "owner": "wiki",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/sources.index_document.deleted.v1.json",
- "title": "SourcesIndexDocumentDeletedPayload",
- "description": "sources.index_document.deleted v1 (OpenVibe.Sources server/items.js indexEvent). An item of a source with search_visibility was removed; OpenVibe.Search stores a tombstone at this revision. Envelope: subject { type: item, id: <item id>, revision }, visibility internal, priority important, actor service:sources.",
+ "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.deleted.v1.json",
+ "title": "WikiIndexDocumentDeletedPayload",
+ "description": "wiki.index_document.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the page is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a page Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision }, visibility internal, priority important, actor service:wiki.",
"type": "object",
"type": "object",
"required": [
"required": [
"type",
"type",
@@ -3722,406 +5053,359 @@
@@ -3722,406 +5053,359 @@
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
"type": {
"type": {
- "const": "item"
+ "const": "page"
},
},
"id": {
"id": {
"type": "string",
"type": "string",
- "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
"revision": {
"revision": {
"type": "integer",
"type": "integer",
- "minimum": 1
+ "minimum": 1,
+ "maximum": 9007199254740991,
+ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
}
}
}
}
}
}
},
},
- "search.document.indexed": {
+ "live.index_document.upserted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "search",
+ "owner": "live",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/search.document.indexed.v1.json",
- "title": "SearchDocumentIndexedPayload",
- "description": "search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",
+ "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.upserted.v1.json",
+ "title": "LiveIndexDocumentUpsertedPayload",
+ "description": "live.index_document.upserted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). The OpenVibe.Search document of one of Live's canonical pages: a streamer's channel (type channel, openvibe.live/@username: its name, bio, what it streams and whether it is live), or a public VOD or clip of a Live stream (types vod and clip, openvibe.live/vod/<id> and /clip/<id>: title, channel, AI overview and transcript; the id is the Media id; AI clips are noindex). Sent whenever what Search should hold changed; a banned channel, or a VOD or clip that is deleted, made private or failed, gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:live.",
+ "allOf": [
+ {
+ "$ref": "../../search/index-document.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "deleted"
+ ],
+ "properties": {
+ "owner": {
+ "const": "live"
+ },
+ "type": {
+ "enum": [
+ "channel",
+ "vod",
+ "clip"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[0-9]{1,15}$"
+ },
+ "deleted": {
+ "const": false
+ }
+ }
+ }
+ ]
+ }
+ },
+ "live.index_document.deleted": {
+ "version": "1.0.0",
+ "owner": "live",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.deleted.v1.json",
+ "title": "LiveIndexDocumentDeletedPayload",
+ "description": "live.index_document.deleted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:live.",
"type": "object",
"type": "object",
"required": [
"required": [
- "owner",
"type",
"type",
"id",
"id",
- "revision",
- "exposure",
- "reindexed",
- "canonical_url"
+ "revision"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "owner": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9-]{1,39}$"
- },
"type": {
"type": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,39}$"
+ "enum": [
+ "channel",
+ "vod",
+ "clip"
+ ]
},
},
"id": {
"id": {
"type": "string",
"type": "string",
- "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"
+ "pattern": "^[0-9]{1,15}$"
},
},
"revision": {
"revision": {
"type": "integer",
"type": "integer",
- "minimum": 0,
- "maximum": 9007199254740991
- },
- "exposure": {
- "enum": [
- "restricted",
- "public_unlisted",
- "public_listed"
- ]
- },
- "reindexed": {
- "type": "boolean",
- "description": "true when Search already held an earlier revision of the document."
+ "minimum": 1,
+ "maximum": 9007199254740991,
+ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
+ }
+ }
+ }
+ },
+ "media.index_document.upserted": {
+ "version": "1.0.0",
+ "owner": "media",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.upserted.v1.json",
+ "title": "MediaIndexDocumentUpsertedPayload",
+ "description": "media.index_document.upserted v1 (OpenVibe.Media; roadmap WS-O task 10). The OpenVibe.Search document of a public, playable VOD or clip whose canonical page is on openvibe.media (openvibe.media/v/<id>, /c/<id>): readiness playable, visibility public, made by a person, and owned by an app other than Live (Live's own VODs and clips are live.index_document, type vod or clip, on Live's pages). Sent whenever what Search should hold changed; a VOD or clip that is deleted, made private or stops being playable gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:media.",
+ "allOf": [
+ {
+ "$ref": "../../search/index-document.v1.json"
},
},
- "canonical_url": {
- "type": [
- "string",
- "null"
+ {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "deleted"
],
],
- "description": "Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none."
+ "properties": {
+ "owner": {
+ "const": "media"
+ },
+ "type": {
+ "enum": [
+ "vod",
+ "clip"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[0-9]{1,15}$"
+ },
+ "deleted": {
+ "const": false
+ }
+ }
}
}
- }
+ ]
}
}
},
},
- "search.document.removed": {
+ "media.index_document.deleted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "search",
+ "owner": "media",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/search.document.removed.v1.json",
- "title": "SearchDocumentRemovedPayload",
- "description": "search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.",
+ "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.deleted.v1.json",
+ "title": "MediaIndexDocumentDeletedPayload",
+ "description": "media.index_document.deleted v1 (OpenVibe.Media; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:media.",
"type": "object",
"type": "object",
"required": [
"required": [
- "owner",
"type",
"type",
"id",
"id",
- "revision",
- "reason",
- "previous_exposure",
- "exposure",
- "canonical_url"
+ "revision"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "owner": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9-]{1,39}$"
- },
"type": {
"type": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,39}$"
+ "enum": [
+ "vod",
+ "clip"
+ ]
},
},
"id": {
"id": {
"type": "string",
"type": "string",
- "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$"
+ "pattern": "^[0-9]{1,15}$"
},
},
"revision": {
"revision": {
"type": "integer",
"type": "integer",
- "minimum": 0,
- "maximum": 9007199254740991
- },
- "reason": {
- "enum": [
- "deleted",
- "draft",
- "not_published",
- "visibility_changed",
- "noindex"
- ]
- },
- "previous_exposure": {
- "enum": [
- "restricted",
- "public_unlisted",
- "public_listed"
- ]
- },
- "exposure": {
- "enum": [
- "none",
- "restricted",
- "public_unlisted"
- ],
- "description": "Lower than previous_exposure. none: no longer served to anyone."
- },
- "canonical_url": {
- "type": [
- "string",
- "null"
- ],
- "description": "The URL only when the document was public before (so it must be purged); null otherwise."
+ "minimum": 1,
+ "maximum": 9007199254740991,
+ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
}
}
}
}
}
}
},
},
- "wiki.space.updated": {
+ "community.index_document.upserted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "wiki",
+ "owner": "community",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.space.updated.v1.json",
- "title": "WikiSpaceUpdatedPayload",
- "description": "wiki.space.updated v1 (OpenVibe.Wiki server/wiki/service.js spaceEvent). Emitted when a space is created, renamed, changes visibility or settings, has its roles changed, or is deleted, in the transaction of the change. Envelope: subject { type: space, id: spc_… }, visibility public for a public space that is not deleted, otherwise internal, priority important, actor the person (user) or service.",
- "type": "object",
- "required": [
- "slug",
- "name",
- "kind",
- "visibility",
- "deleted",
- "change",
- "url"
- ],
- "additionalProperties": false,
- "properties": {
- "slug": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$"
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "maxLength": 200
- },
- "kind": {
- "enum": [
- "official",
- "user"
- ]
- },
- "visibility": {
- "enum": [
- "public",
- "members",
- "private"
- ]
- },
- "deleted": {
- "type": "boolean"
+ "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.upserted.v1.json",
+ "title": "CommunityIndexDocumentUpsertedPayload",
+ "description": "community.index_document.upserted v1 (OpenVibe.Community; roadmap WS-O task 10). The OpenVibe.Search document of A public forum thread (in a public, open space) or a public paste on openvibe.community. Sent whenever what Search should hold changed; a thread or paste that is deleted, made private or members-only, or burns after reading gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:community.",
+ "allOf": [
+ {
+ "$ref": "../../search/index-document.v1.json"
},
},
- "change": {
- "enum": [
- "created",
- "renamed",
- "visibility",
- "settings",
- "roles",
+ {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
"deleted"
"deleted"
],
],
- "description": "What changed. renamed = a new slug."
- },
- "url": {
- "type": "string",
- "format": "uri",
- "pattern": "^https?://",
- "maxLength": 2048,
- "description": "Canonical URL of the space."
+ "properties": {
+ "owner": {
+ "const": "community"
+ },
+ "type": {
+ "enum": [
+ "thread",
+ "paste"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_-]{1,80}$"
+ },
+ "deleted": {
+ "const": false
+ }
+ }
}
}
- }
+ ]
}
}
},
},
- "wiki.revision.created": {
+ "community.index_document.deleted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "wiki",
+ "owner": "community",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.revision.created.v1.json",
- "title": "WikiRevisionCreatedPayload",
- "description": "wiki.revision.created v1 (OpenVibe.Wiki server/wiki/service.js revisionEvent). Emitted when a page gets a new immutable revision: an edit, a revert or an AI proposal. A new revision is a draft until it is published (then wiki.page.published|updated follows). Envelope: subject { type: page, id: pg_…, revision: <number> }, visibility internal, priority important, actor the person or service.",
+ "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.deleted.v1.json",
+ "title": "CommunityIndexDocumentDeletedPayload",
+ "description": "community.index_document.deleted v1 (OpenVibe.Community; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:community.",
"type": "object",
"type": "object",
"required": [
"required": [
- "space_id",
- "space",
- "slug",
- "number",
- "kind",
- "parent_number",
- "reverted_to",
- "authorship",
- "author",
- "message"
+ "type",
+ "id",
+ "revision"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "space_id": {
- "type": "string",
- "pattern": "^spc_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "space": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
- "description": "Space slug."
- },
- "slug": {
- "type": "string",
- "minLength": 1,
- "maxLength": 80,
- "description": "Page slug."
- },
- "number": {
- "type": "integer",
- "minimum": 1,
- "description": "The new revision number."
- },
- "kind": {
- "enum": [
- "edit",
- "revert",
- "import"
- ]
- },
- "parent_number": {
- "type": [
- "integer",
- "null"
- ],
- "minimum": 1
- },
- "reverted_to": {
- "type": [
- "integer",
- "null"
- ],
- "minimum": 1,
- "description": "The revision a revert restored; null otherwise."
- },
- "authorship": {
+ "type": {
"enum": [
"enum": [
- "human",
- "ai_assisted",
- "ai_generated",
- "imported",
- null
- ],
- "description": "Search's authorship vocabulary; null when the revision has no authorship record."
- },
- "author": {
- "type": [
- "string",
- "null"
- ],
- "maxLength": 200,
- "description": "Who wrote it: a subject id (usr_…) or a service (svc:<id>)."
- },
- "message": {
- "type": [
- "string",
- "null"
- ],
- "maxLength": 2000
+ "thread",
+ "paste"
+ ]
},
},
- "proposal_id": {
+ "id": {
"type": "string",
"type": "string",
- "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "Present when the revision is an AI proposal awaiting a person's approval."
+ "pattern": "^[A-Za-z0-9_-]{1,80}$"
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 9007199254740991,
+ "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
}
}
}
}
}
}
},
},
- "wiki.watch.triggered": {
+ "blog.post.created": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "wiki",
+ "owner": "blog",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.watch.triggered.v1.json",
- "title": "WikiWatchTriggeredPayload",
- "description": "wiki.watch.triggered v1 (OpenVibe.Wiki server/wiki/service.js notifyWatchers). One event per change to a watched page, listing the watchers who may read the page (never the actor); not emitted when nobody is left. Envelope: subject { type: page, id: pg_…, revision: <revision or 0> }, visibility internal, priority important, actor the person or service.",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.created.v1.json",
+ "title": "BlogPostCreatedPayload",
+ "description": "blog.post.created v1 (OpenVibe.Blog server/domain/posts.js create). A post exists as a draft (its first revision). Envelope: subject { type: post, id: pst_…, revision: 1 }, visibility internal, priority low, actor the person or the calling service.",
"type": "object",
"type": "object",
"required": [
"required": [
- "action",
- "space",
- "slug",
- "title",
- "revision",
- "url",
- "recipients"
+ "blog",
+ "state",
+ "visibility",
+ "authorship"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "action": {
- "enum": [
- "published",
- "updated",
- "unpublished",
- "deleted",
- "revised",
- "proposed"
+ "blog": {
+ "type": "object",
+ "required": [
+ "id",
+ "handle"
],
],
- "description": "revised = a new draft revision; proposed = an AI proposal awaits approval; the rest follow wiki.page.*."
- },
- "space": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
- "description": "Space slug."
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "handle": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
+ }
+ }
},
},
- "slug": {
- "type": "string",
- "minLength": 1,
- "maxLength": 80,
- "description": "Page slug."
+ "state": {
+ "const": "draft"
},
},
- "title": {
- "type": "string",
- "maxLength": 200
+ "visibility": {
+ "enum": [
+ "public",
+ "unlisted",
+ "members",
+ "private"
+ ]
},
},
- "revision": {
- "type": [
- "integer",
- "null"
+ "authorship": {
+ "enum": [
+ "human",
+ "ai",
+ "hybrid",
+ "imported"
],
],
- "minimum": 1
- },
- "url": {
- "type": "string",
- "format": "uri",
- "pattern": "^https?://",
- "maxLength": 2048
+ "description": "openvibe-publishing authorship mode (not Search's vocabulary)."
},
},
- "recipients": {
- "type": "array",
- "minItems": 1,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "description": "Watchers to notify."
+ "workflow": {
+ "type": "object",
+ "required": [
+ "id",
+ "runId"
+ ],
+ "additionalProperties": false,
+ "description": "The OpenVibe.AI workflow and run behind ai and hybrid posts; absent for human posts.",
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
+ },
+ "runId": {
+ "type": "string",
+ "minLength": 1
+ },
+ "version": {
+ "type": [
+ "integer",
+ "string"
+ ]
+ },
+ "model": {
+ "type": "string"
+ }
+ }
}
}
}
}
}
}
},
},
- "wiki.page.published": {
+ "blog.post.published": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "wiki",
+ "owner": "blog",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.published.v1.json",
- "title": "WikiPagePublishedPayload",
- "description": "wiki.page.published v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page became published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.published.v1.json",
+ "title": "BlogPostPublishedPayload",
+ "description": "blog.post.published v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post became published (now or by its schedule), in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).",
"type": "object",
"type": "object",
"required": [
"required": [
"canonical_url",
"canonical_url",
"publication_state",
"publication_state",
"indexability",
"indexability",
- "space",
- "slug",
+ "blog",
"visibility"
"visibility"
],
],
"additionalProperties": false,
"additionalProperties": false,
@@ -4160,44 +5444,51 @@
@@ -4160,44 +5444,51 @@
}
}
}
}
},
},
- "space": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
- "description": "Space slug."
- },
- "slug": {
- "type": "string",
- "minLength": 1,
- "maxLength": 80,
- "description": "Page slug inside the space."
+ "blog": {
+ "type": "object",
+ "required": [
+ "id",
+ "handle"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "handle": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
+ }
+ }
},
},
"visibility": {
"visibility": {
"enum": [
"enum": [
"public",
"public",
+ "unlisted",
"members",
"members",
"private"
"private"
],
],
- "description": "Effective visibility (the page's, narrowed by its space's)."
+ "description": "The post's own visibility (members = entitlement-gated)."
}
}
}
}
}
}
},
},
- "wiki.page.updated": {
+ "blog.post.updated": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "wiki",
+ "owner": "blog",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.updated.v1.json",
- "title": "WikiPageUpdatedPayload",
- "description": "wiki.page.updated v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page got a new published revision or visibility, or a review changed what Search holds, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:wiki (scheduled and system changes).",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.updated.v1.json",
+ "title": "BlogPostUpdatedPayload",
+ "description": "blog.post.updated v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post got a new published revision, visibility or URL, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).",
"type": "object",
"type": "object",
"required": [
"required": [
"canonical_url",
"canonical_url",
"publication_state",
"publication_state",
"indexability",
"indexability",
- "space",
- "slug",
+ "blog",
"visibility"
"visibility"
],
],
"additionalProperties": false,
"additionalProperties": false,
@@ -4236,44 +5527,51 @@
@@ -4236,44 +5527,51 @@
}
}
}
}
},
},
- "space": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
- "description": "Space slug."
- },
- "slug": {
- "type": "string",
- "minLength": 1,
- "maxLength": 80,
- "description": "Page slug inside the space."
+ "blog": {
+ "type": "object",
+ "required": [
+ "id",
+ "handle"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "handle": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
+ }
+ }
},
},
"visibility": {
"visibility": {
"enum": [
"enum": [
"public",
"public",
+ "unlisted",
"members",
"members",
"private"
"private"
],
],
- "description": "Effective visibility (the page's, narrowed by its space's)."
+ "description": "The post's own visibility (members = entitlement-gated)."
}
}
}
}
}
}
},
},
- "wiki.page.unpublished": {
+ "blog.post.unpublished": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "wiki",
+ "owner": "blog",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.unpublished.v1.json",
- "title": "WikiPageUnpublishedPayload",
- "description": "wiki.page.unpublished v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published page stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.unpublished.v1.json",
+ "title": "BlogPostUnpublishedPayload",
+ "description": "blog.post.unpublished v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).",
"type": "object",
"type": "object",
"required": [
"required": [
"canonical_url",
"canonical_url",
"publication_state",
"publication_state",
"indexability",
"indexability",
- "space",
- "slug",
+ "blog",
"visibility"
"visibility"
],
],
"additionalProperties": false,
"additionalProperties": false,
@@ -4305,56 +5603,63 @@
@@ -4305,56 +5603,63 @@
"noindex"
"noindex"
]
]
},
},
- "reasons": {
- "type": "array",
- "maxItems": 20,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,63}$"
- }
+ "reasons": {
+ "type": "array",
+ "maxItems": 20,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,63}$"
+ }
+ }
+ },
+ "description": "The last gate decision, or null when there is none."
+ },
+ "blog": {
+ "type": "object",
+ "required": [
+ "id",
+ "handle"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "handle": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
}
}
- },
- "description": "The last gate decision, or null when there is none."
- },
- "space": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
- "description": "Space slug."
- },
- "slug": {
- "type": "string",
- "minLength": 1,
- "maxLength": 80,
- "description": "Page slug inside the space."
+ }
},
},
"visibility": {
"visibility": {
"enum": [
"enum": [
"public",
"public",
+ "unlisted",
"members",
"members",
"private"
"private"
],
],
- "description": "Effective visibility (the page's, narrowed by its space's)."
+ "description": "The post's own visibility (members = entitlement-gated)."
}
}
}
}
}
}
},
},
- "wiki.page.deleted": {
+ "blog.post.deleted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "wiki",
+ "owner": "blog",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.page.deleted.v1.json",
- "title": "WikiPageDeletedPayload",
- "description": "wiki.page.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks publicationEvent). Emitted when a page (or its whole space) was deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from wiki.index_document.*). Envelope: subject { type: page, id: pg_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:wiki (scheduled and system changes).",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.post.deleted.v1.json",
+ "title": "BlogPostDeletedPayload",
+ "description": "blog.post.deleted v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post was soft-deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).",
"type": "object",
"type": "object",
"required": [
"required": [
"canonical_url",
"canonical_url",
"publication_state",
"publication_state",
"indexability",
"indexability",
- "space",
- "slug",
+ "blog",
"visibility"
"visibility"
],
],
"additionalProperties": false,
"additionalProperties": false,
@@ -4398,189 +5703,112 @@
@@ -4398,189 +5703,112 @@
},
},
"description": "The last gate decision, or null when there is none."
"description": "The last gate decision, or null when there is none."
},
},
- "space": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
- "description": "Space slug."
- },
- "slug": {
- "type": "string",
- "minLength": 1,
- "maxLength": 80,
- "description": "Page slug inside the space."
- },
- "visibility": {
- "enum": [
- "public",
- "members",
- "private"
- ],
- "description": "Effective visibility (the page's, narrowed by its space's)."
- }
- }
- }
- },
- "wiki.index_document.upserted": {
- "version": "1.0.0",
- "owner": "wiki",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.upserted.v1.json",
- "title": "WikiIndexDocumentUpsertedPayload",
- "description": "wiki.index_document.upserted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable page, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision: <document revision> }, visibility internal, priority important, actor service:wiki.",
- "allOf": [
- {
- "$ref": "../../search/index-document.v1.json"
- },
- {
+ "blog": {
"type": "object",
"type": "object",
"required": [
"required": [
- "owner",
- "type",
- "deleted"
+ "id",
+ "handle"
],
],
+ "additionalProperties": false,
"properties": {
"properties": {
- "owner": {
- "const": "wiki"
- },
- "type": {
- "const": "page"
- },
"id": {
"id": {
"type": "string",
"type": "string",
- "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
- "deleted": {
- "const": false
+ "handle": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
}
}
}
}
- }
- ]
- }
- },
- "wiki.index_document.deleted": {
- "version": "1.0.0",
- "owner": "wiki",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/wiki.index_document.deleted.v1.json",
- "title": "WikiIndexDocumentDeletedPayload",
- "description": "wiki.index_document.deleted v1 (OpenVibe.Wiki server/wiki/service.js sync, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the page is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a page Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: page, id, revision }, visibility internal, priority important, actor service:wiki.",
- "type": "object",
- "required": [
- "type",
- "id",
- "revision"
- ],
- "additionalProperties": false,
- "properties": {
- "type": {
- "const": "page"
- },
- "id": {
- "type": "string",
- "pattern": "^pg_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "revision": {
- "type": "integer",
- "minimum": 1,
- "maximum": 9007199254740991,
- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
- }
- }
- }
- },
- "live.index_document.upserted": {
- "version": "1.0.0",
- "owner": "live",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.upserted.v1.json",
- "title": "LiveIndexDocumentUpsertedPayload",
- "description": "live.index_document.upserted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). The OpenVibe.Search document of one of Live's canonical pages: a streamer's channel (type channel, openvibe.live/@username: its name, bio, what it streams and whether it is live), or a public VOD or clip of a Live stream (types vod and clip, openvibe.live/vod/<id> and /clip/<id>: title, channel, AI overview and transcript; the id is the Media id; AI clips are noindex). Sent whenever what Search should hold changed; a banned channel, or a VOD or clip that is deleted, made private or failed, gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:live.",
- "allOf": [
- {
- "$ref": "../../search/index-document.v1.json"
},
},
- {
- "type": "object",
- "required": [
- "owner",
- "type",
- "deleted"
+ "visibility": {
+ "enum": [
+ "public",
+ "unlisted",
+ "members",
+ "private"
],
],
- "properties": {
- "owner": {
- "const": "live"
- },
- "type": {
- "enum": [
- "channel",
- "vod",
- "clip"
- ]
- },
- "id": {
- "type": "string",
- "pattern": "^[0-9]{1,15}$"
- },
- "deleted": {
- "const": false
- }
- }
+ "description": "The post's own visibility (members = entitlement-gated)."
}
}
- ]
+ }
}
}
},
},
- "live.index_document.deleted": {
+ "blog.schedule.failed": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "live",
+ "owner": "blog",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/live.index_document.deleted.v1.json",
- "title": "LiveIndexDocumentDeletedPayload",
- "description": "live.index_document.deleted v1 (OpenVibe.Live server/events/search-documents.js; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:live.",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.schedule.failed.v1.json",
+ "title": "BlogScheduleFailedPayload",
+ "description": "blog.schedule.failed v1 (OpenVibe.Blog server/domain/posts.js runScheduled). A scheduled publish or unpublish gave up after its retries; a scheduled post goes back to draft. Envelope: subject { type: post, id: pst_…, revision?: <scheduled revision> }, visibility internal, priority important, actor service:blog.",
"type": "object",
"type": "object",
"required": [
"required": [
- "type",
- "id",
- "revision"
+ "job_id",
+ "action",
+ "revision",
+ "run_at",
+ "attempts",
+ "error",
+ "blog_id"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "type": {
+ "job_id": {
+ "type": "string",
+ "pattern": "^job_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "action": {
"enum": [
"enum": [
- "channel",
- "vod",
- "clip"
+ "publish",
+ "unpublish"
]
]
},
},
- "id": {
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision a publish was to make live; null for an unpublish."
+ },
+ "run_at": {
"type": "string",
"type": "string",
- "pattern": "^[0-9]{1,15}$"
+ "format": "date-time",
+ "description": "When the job was last due."
},
},
- "revision": {
+ "attempts": {
"type": "integer",
"type": "integer",
- "minimum": 1,
- "maximum": 9007199254740991,
- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
+ "minimum": 0
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 1000,
+ "description": "The last error."
+ },
+ "blog_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "null when the post no longer exists."
}
}
}
}
}
}
},
},
- "media.index_document.upserted": {
+ "blog.index_document.upserted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "media",
+ "owner": "blog",
"status": "active",
"status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.upserted.v1.json",
- "title": "MediaIndexDocumentUpsertedPayload",
- "description": "media.index_document.upserted v1 (OpenVibe.Media; roadmap WS-O task 10). The OpenVibe.Search document of a public, playable VOD or clip whose canonical page is on openvibe.media (openvibe.media/v/<id>, /c/<id>): readiness playable, visibility public, made by a person, and owned by an app other than Live (Live's own VODs and clips are live.index_document, type vod or clip, on Live's pages). Sent whenever what Search should hold changed; a VOD or clip that is deleted, made private or stops being playable gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:media.",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.upserted.v1.json",
+ "title": "BlogIndexDocumentUpsertedPayload",
+ "description": "blog.index_document.upserted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable post, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision: <document revision> }, visibility internal, priority important, actor service:blog.",
"allOf": [
"allOf": [
{
{
"$ref": "../../search/index-document.v1.json"
"$ref": "../../search/index-document.v1.json"
@@ -4594,17 +5822,14 @@
@@ -4594,17 +5822,14 @@
],
],
"properties": {
"properties": {
"owner": {
"owner": {
- "const": "media"
+ "const": "blog"
},
},
"type": {
"type": {
- "enum": [
- "vod",
- "clip"
- ]
+ "const": "post"
},
},
"id": {
"id": {
"type": "string",
"type": "string",
- "pattern": "^[0-9]{1,15}$"
+ "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
"deleted": {
"deleted": {
"const": false
"const": false
@@ -4614,15 +5839,15 @@
@@ -4614,15 +5839,15 @@
]
]
}
}
},
},
- "media.index_document.deleted": {
+ "blog.index_document.deleted": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "media",
+ "owner": "blog",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/media.index_document.deleted.v1.json",
- "title": "MediaIndexDocumentDeletedPayload",
- "description": "media.index_document.deleted v1 (OpenVibe.Media; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:media.",
+ "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.deleted.v1.json",
+ "title": "BlogIndexDocumentDeletedPayload",
+ "description": "blog.index_document.deleted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the post is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a post Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision }, visibility internal, priority important, actor service:blog.",
"type": "object",
"type": "object",
"required": [
"required": [
"type",
"type",
@@ -4632,14 +5857,11 @@
@@ -4632,14 +5857,11 @@
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
"type": {
"type": {
- "enum": [
- "vod",
- "clip"
- ]
+ "const": "post"
},
},
"id": {
"id": {
"type": "string",
"type": "string",
- "pattern": "^[0-9]{1,15}$"
+ "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
"revision": {
"revision": {
"type": "integer",
"type": "integer",
@@ -4650,766 +5872,964 @@
@@ -4650,766 +5872,964 @@
}
}
}
}
},
},
- "community.index_document.upserted": {
- "version": "1.0.0",
- "owner": "community",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.upserted.v1.json",
- "title": "CommunityIndexDocumentUpsertedPayload",
- "description": "community.index_document.upserted v1 (OpenVibe.Community; roadmap WS-O task 10). The OpenVibe.Search document of A public forum thread (in a public, open space) or a public paste on openvibe.community. Sent whenever what Search should hold changed; a thread or paste that is deleted, made private or members-only, or burns after reading gets a tombstone. The revision grows with every change. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type, id, revision: <document revision> }, visibility internal, actor service:community.",
- "allOf": [
- {
- "$ref": "../../search/index-document.v1.json"
- },
- {
- "type": "object",
- "required": [
- "owner",
- "type",
- "deleted"
- ],
- "properties": {
- "owner": {
- "const": "community"
- },
- "type": {
- "enum": [
- "thread",
- "paste"
- ]
- },
- "id": {
- "type": "string",
- "pattern": "^[A-Za-z0-9_-]{1,80}$"
- },
- "deleted": {
- "const": false
- }
- }
- }
- ]
- }
- },
- "community.index_document.deleted": {
+ "ai.run-request": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "community",
+ "owner": "ai",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/community.index_document.deleted.v1.json",
- "title": "CommunityIndexDocumentDeletedPayload",
- "description": "community.index_document.deleted v1 (OpenVibe.Community; roadmap WS-O task 10). A Search tombstone: Search drops the document at this revision or older. Only sent for a document Search was sent before. Envelope: subject { type, id, revision }, visibility internal, actor service:community.",
+ "$id": "https://openvibe.network/contracts/ai/run-request.v1.json",
+ "title": "AiRunRequest",
+ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",
"type": "object",
"type": "object",
"required": [
"required": [
- "type",
- "id",
- "revision"
+ "workflow"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "type": {
- "enum": [
- "thread",
- "paste"
- ]
- },
- "id": {
+ "workflow": {
"type": "string",
"type": "string",
- "pattern": "^[A-Za-z0-9_-]{1,80}$"
+ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)+$",
+ "maxLength": 200,
+ "description": "Workflow key, <namespace>.<name> (live.chat_reply, news.summarize_story, network.site_copy…)."
},
},
- "revision": {
+ "version": {
"type": "integer",
"type": "integer",
"minimum": 1,
"minimum": 1,
- "maximum": 9007199254740991,
- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
+ "description": "Pin a workflow version (active or deprecated). Omitted: the newest active version."
+ },
+ "input": {
+ "type": "object",
+ "description": "Workflow input; validated against the workflow version's input_schema. Default {}."
+ },
+ "target": {
+ "$ref": "../common/entity-ref.v1.json"
+ },
+ "attribution": {
+ "$ref": "../common/entity-ref.v1.json"
+ },
+ "on_behalf_of": {
+ "$ref": "../identity/subject-ref.v1.json"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9._:-]{8,128}$",
+ "description": "Same (requester, key) returns the existing run; a different workflow or input under it is 409 idempotency.conflict. The Idempotency-Key header is used when this is absent."
+ },
+ "options": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cache": {
+ "type": "boolean",
+ "default": true,
+ "description": "false skips the scoped cache lookup and store."
+ },
+ "debug": {
+ "type": "boolean",
+ "default": false,
+ "description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
+ }
+ }
}
}
}
}
}
}
},
},
- "blog.post.created": {
+ "ai.run": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "blog",
+ "owner": "ai",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.created.v1.json",
- "title": "BlogPostCreatedPayload",
- "description": "blog.post.created v1 (OpenVibe.Blog server/domain/posts.js create). A post exists as a draft (its first revision). Envelope: subject { type: post, id: pst_…, revision: 1 }, visibility internal, priority low, actor the person or the calling service.",
+ "$id": "https://openvibe.network/contracts/ai/run.v1.json",
+ "title": "AiRun",
+ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted.",
"type": "object",
"type": "object",
"required": [
"required": [
- "blog",
- "state",
- "visibility",
- "authorship"
+ "id",
+ "status",
+ "workflow",
+ "template",
+ "route",
+ "requester",
+ "on_behalf_of",
+ "attribution",
+ "source_service",
+ "target",
+ "input",
+ "output",
+ "error",
+ "synthetic",
+ "provenance",
+ "usage",
+ "citations_count",
+ "retry_of",
+ "idempotency_key",
+ "trace_id",
+ "created_at",
+ "started_at",
+ "finished_at"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "blog": {
+ "id": {
+ "type": "string",
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "status": {
+ "enum": [
+ "queued",
+ "running",
+ "succeeded",
+ "failed",
+ "cancelled",
+ "cached"
+ ],
+ "description": "Terminal: succeeded, failed, cancelled, cached (a scoped cache hit, no provider call; provenance.cached_from names the run it reused)."
+ },
+ "workflow": {
"type": "object",
"type": "object",
"required": [
"required": [
- "id",
- "handle"
+ "key",
+ "version"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "id": {
- "type": "string",
- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ "key": {
+ "type": "string"
},
},
- "handle": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
+ "version": {
+ "type": "integer",
+ "minimum": 1
}
}
}
}
},
},
- "state": {
- "const": "draft"
- },
- "visibility": {
- "enum": [
- "public",
- "unlisted",
- "members",
- "private"
- ]
- },
- "authorship": {
- "enum": [
- "human",
- "ai",
- "hybrid",
- "imported"
+ "template": {
+ "type": [
+ "object",
+ "null"
],
],
- "description": "openvibe-publishing authorship mode (not Search's vocabulary)."
+ "required": [
+ "key",
+ "version"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "description": "The prompt template of the workflow's llm step; null for workflows without one."
},
},
- "workflow": {
- "type": "object",
+ "route": {
+ "type": [
+ "object",
+ "null"
+ ],
"required": [
"required": [
- "id",
- "runId"
+ "key",
+ "version"
],
],
"additionalProperties": false,
"additionalProperties": false,
- "description": "The OpenVibe.AI workflow and run behind ai and hybrid posts; absent for human posts.",
"properties": {
"properties": {
- "id": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
- },
- "runId": {
- "type": "string",
- "minLength": 1
+ "key": {
+ "type": "string"
},
},
"version": {
"version": {
"type": [
"type": [
"integer",
"integer",
- "string"
+ "null"
]
]
+ }
+ },
+ "description": "Provider route; version null while the route is unresolved or disabled."
+ },
+ "requester": {
+ "$ref": "../identity/subject-ref.v1.json"
+ },
+ "on_behalf_of": {
+ "anyOf": [
+ {
+ "$ref": "../identity/subject-ref.v1.json"
},
},
- "model": {
- "type": "string"
+ {
+ "type": "null"
}
}
- }
- }
- }
- }
- },
- "blog.post.published": {
- "version": "1.0.0",
- "owner": "blog",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.published.v1.json",
- "title": "BlogPostPublishedPayload",
- "description": "blog.post.published v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post became published (now or by its schedule), in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).",
- "type": "object",
- "required": [
- "canonical_url",
- "publication_state",
- "indexability",
- "blog",
- "visibility"
- ],
- "additionalProperties": false,
- "properties": {
- "canonical_url": {
- "type": "string",
- "format": "uri",
- "pattern": "^https?://",
- "maxLength": 2048
+ ]
},
},
- "publication_state": {
- "const": "published"
+ "attribution": {
+ "anyOf": [
+ {
+ "$ref": "../common/entity-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
},
},
- "indexability": {
- "type": "object",
- "required": [
- "decision",
- "reasons"
+ "source_service": {
+ "type": [
+ "string",
+ "null"
],
],
- "additionalProperties": false,
- "properties": {
- "decision": {
- "enum": [
- "index",
- "noindex"
- ]
+ "description": "The requester's service id when the requester is a service."
+ },
+ "target": {
+ "anyOf": [
+ {
+ "$ref": "../common/entity-ref.v1.json"
},
},
- "reasons": {
- "type": "array",
- "maxItems": 20,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,63}$"
- }
+ {
+ "type": "null"
}
}
- }
+ ]
},
},
- "blog": {
+ "input": {
"type": "object",
"type": "object",
+ "description": "The retained input (see the description)."
+ },
+ "output": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "Workflow output, or null."
+ },
+ "error": {
+ "type": [
+ "object",
+ "null"
+ ],
"required": [
"required": [
- "id",
- "handle"
+ "code",
+ "detail"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "id": {
+ "code": {
"type": "string",
"type": "string",
- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",
+ "description": "run.cancelled, run.interrupted, run.internal, output.invalid, output.empty, provider.unavailable, route.unavailable, source.unavailable, media.unresolvable…"
},
},
- "handle": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
+ "detail": {
+ "type": [
+ "string",
+ "null"
+ ]
}
}
}
}
},
},
- "visibility": {
- "enum": [
- "public",
- "unlisted",
- "members",
- "private"
- ],
- "description": "The post's own visibility (members = entitlement-gated)."
- }
- }
- }
- },
- "blog.post.updated": {
- "version": "1.0.0",
- "owner": "blog",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.updated.v1.json",
- "title": "BlogPostUpdatedPayload",
- "description": "blog.post.updated v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post got a new published revision, visibility or URL, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility public when the document is public and the gate lets it be listed, otherwise internal, priority important, actor the person or service:blog (scheduled and system changes).",
- "type": "object",
- "required": [
- "canonical_url",
- "publication_state",
- "indexability",
- "blog",
- "visibility"
- ],
- "additionalProperties": false,
- "properties": {
- "canonical_url": {
- "type": "string",
- "format": "uri",
- "pattern": "^https?://",
- "maxLength": 2048
- },
- "publication_state": {
- "const": "published"
+ "synthetic": {
+ "type": "boolean",
+ "description": "Produced by the stub provider; never served to production callers and never cached."
},
},
- "indexability": {
+ "provenance": {
"type": "object",
"type": "object",
+ "additionalProperties": false,
"required": [
"required": [
- "decision",
- "reasons"
+ "origin",
+ "workflow",
+ "workflow_version",
+ "template_version",
+ "route",
+ "route_version",
+ "provider",
+ "model",
+ "fallback_used",
+ "run_id",
+ "cached_from",
+ "synthetic"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "decision": {
- "enum": [
- "index",
- "noindex"
+ "origin": {
+ "const": "ai"
+ },
+ "workflow": {
+ "type": "string"
+ },
+ "workflow_version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "template_version": {
+ "type": [
+ "integer",
+ "null"
]
]
},
},
- "reasons": {
- "type": "array",
- "maxItems": 20,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,63}$"
- }
+ "route": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "route_version": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "provider": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fallback_used": {
+ "type": "boolean",
+ "description": "The primary provider was degraded and the run fell back (ADR-015: fallbacks are recorded)."
+ },
+ "run_id": {
+ "type": "string",
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "cached_from": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "synthetic": {
+ "type": "boolean"
}
}
}
}
},
},
- "blog": {
+ "usage": {
"type": "object",
"type": "object",
"required": [
"required": [
- "id",
- "handle"
+ "tokens_in",
+ "tokens_out",
+ "cost_usd",
+ "attempts"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "id": {
- "type": "string",
- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ "tokens_in": {
+ "type": "integer",
+ "minimum": 0
},
},
- "handle": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
+ "tokens_out": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "cost_usd": {
+ "type": "number",
+ "minimum": 0
+ },
+ "attempts": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Provider calls made (skipped routes not counted)."
}
}
}
}
},
},
- "visibility": {
- "enum": [
- "public",
- "unlisted",
- "members",
- "private"
- ],
- "description": "The post's own visibility (members = entitlement-gated)."
- }
- }
- }
- },
- "blog.post.unpublished": {
- "version": "1.0.0",
- "owner": "blog",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.unpublished.v1.json",
- "title": "BlogPostUnpublishedPayload",
- "description": "blog.post.unpublished v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a published post stopped being published, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).",
- "type": "object",
- "required": [
- "canonical_url",
- "publication_state",
- "indexability",
- "blog",
- "visibility"
- ],
- "additionalProperties": false,
- "properties": {
- "canonical_url": {
+ "citations_count": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."
+ },
+ "retry_of": {
"type": [
"type": [
"string",
"string",
"null"
"null"
],
],
- "description": "null: the event carries a tombstone, which has no URL."
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
- "publication_state": {
- "const": "unpublished"
+ "idempotency_key": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- "indexability": {
+ "trace_id": {
"type": [
"type": [
- "object",
+ "string",
"null"
"null"
- ],
- "required": [
- "decision",
- "reasons"
- ],
- "additionalProperties": false,
- "properties": {
- "decision": {
- "enum": [
- "index",
- "noindex"
- ]
- },
- "reasons": {
- "type": "array",
- "maxItems": 20,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,63}$"
- }
- }
- },
- "description": "The last gate decision, or null when there is none."
+ ]
},
},
- "blog": {
- "type": "object",
- "required": [
- "id",
- "handle"
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "started_at": {
+ "type": [
+ "string",
+ "null"
],
],
- "additionalProperties": false,
- "properties": {
- "id": {
- "type": "string",
- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "handle": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
- }
- }
+ "format": "date-time"
},
},
- "visibility": {
- "enum": [
- "public",
- "unlisted",
- "members",
- "private"
+ "finished_at": {
+ "type": [
+ "string",
+ "null"
],
],
- "description": "The post's own visibility (members = entitlement-gated)."
+ "format": "date-time"
}
}
}
}
}
}
},
},
- "blog.post.deleted": {
+ "ai.run.queued": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "blog",
- "status": "active",
+ "owner": "ai",
+ "status": "planned",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.post.deleted.v1.json",
- "title": "BlogPostDeletedPayload",
- "description": "blog.post.deleted v1 (OpenVibe.Blog server/domain/publication.js afterChange, via openvibe-publishing/index-hooks publicationEvent). Emitted when a post was soft-deleted, in the transaction of the change. For subscribers other than Search: canonical URL, publication state and Search-shaped indexability, never the body (Search gets that from blog.index_document.*). Envelope: subject { type: post, id: pst_…, revision: <published revision, 0 if none> }, visibility internal, priority important, actor the person or service:blog (scheduled and system changes).",
+ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.queued.v1.json",
+ "title": "AiRunQueuedPayload",
+ "description": "ai.run.queued v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run was stored queued (it then runs inline within ?wait or on the worker). Not published for a cache hit (ai.run.cached) or a create refused by quota or a full queue. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.",
"type": "object",
"type": "object",
"required": [
"required": [
- "canonical_url",
- "publication_state",
- "indexability",
- "blog",
- "visibility"
+ "run_id",
+ "status",
+ "workflow",
+ "requester",
+ "on_behalf_of",
+ "target",
+ "attribution",
+ "provider",
+ "model",
+ "fallback_used",
+ "synthetic",
+ "cached_from",
+ "usage",
+ "error",
+ "citations_count",
+ "retry_of",
+ "created_at",
+ "finished_at"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "canonical_url": {
- "type": [
- "string",
- "null"
- ],
- "description": "null: the event carries a tombstone, which has no URL."
+ "run_id": {
+ "type": "string",
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
- "publication_state": {
- "const": "deleted"
+ "status": {
+ "const": "queued"
},
},
- "indexability": {
- "type": [
- "object",
- "null"
- ],
+ "workflow": {
+ "type": "object",
"required": [
"required": [
- "decision",
- "reasons"
+ "key",
+ "version"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "decision": {
- "enum": [
- "index",
- "noindex"
- ]
+ "key": {
+ "type": "string"
},
},
- "reasons": {
- "type": "array",
- "maxItems": 20,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,63}$"
- }
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ },
+ "requester": {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ "on_behalf_of": {
+ "anyOf": [
+ {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "target": {
+ "anyOf": [
+ {
+ "$ref": "../../common/entity-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "attribution": {
+ "anyOf": [
+ {
+ "$ref": "../../common/entity-ref.v1.json"
+ },
+ {
+ "type": "null"
}
}
- },
- "description": "The last gate decision, or null when there is none."
+ ]
},
},
- "blog": {
+ "provider": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Provider that produced the output; null before a provider call and for cached runs."
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fallback_used": {
+ "type": "boolean"
+ },
+ "synthetic": {
+ "type": "boolean",
+ "description": "Stub provider output."
+ },
+ "cached_from": {
+ "type": "null"
+ },
+ "usage": {
"type": "object",
"type": "object",
"required": [
"required": [
- "id",
- "handle"
+ "tokens_in",
+ "tokens_out",
+ "cost_usd",
+ "attempts"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "id": {
- "type": "string",
- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$"
+ "tokens_in": {
+ "type": "integer",
+ "minimum": 0
},
},
- "handle": {
- "type": "string",
- "pattern": "^[a-z0-9][a-z0-9_-]{1,39}$"
+ "tokens_out": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "cost_usd": {
+ "type": "number",
+ "minimum": 0
+ },
+ "attempts": {
+ "type": "integer",
+ "minimum": 0
}
}
}
}
},
},
- "visibility": {
- "enum": [
- "public",
- "unlisted",
- "members",
- "private"
+ "error": {
+ "type": "null"
+ },
+ "citations_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "retry_of": {
+ "type": [
+ "string",
+ "null"
],
],
- "description": "The post's own visibility (members = entitlement-gated)."
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": "null"
}
}
}
}
}
}
},
},
- "blog.schedule.failed": {
+ "ai.run.succeeded": {
"version": "1.0.0",
"version": "1.0.0",
- "owner": "blog",
- "status": "active",
+ "owner": "ai",
+ "status": "planned",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.schedule.failed.v1.json",
- "title": "BlogScheduleFailedPayload",
- "description": "blog.schedule.failed v1 (OpenVibe.Blog server/domain/posts.js runScheduled). A scheduled publish or unpublish gave up after its retries; a scheduled post goes back to draft. Envelope: subject { type: post, id: pst_…, revision?: <scheduled revision> }, visibility internal, priority important, actor service:blog.",
+ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.succeeded.v1.json",
+ "title": "AiRunSucceededPayload",
+ "description": "ai.run.succeeded v1 (OpenVibe.AI server/runs.js execute). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run finished with output that matched the workflow output schema. The output itself is not in the event: a consumer that needs it reads GET /api/v1/runs/:id with ai.run.read (own runs only). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.",
"type": "object",
"type": "object",
"required": [
"required": [
- "job_id",
- "action",
- "revision",
- "run_at",
- "attempts",
+ "run_id",
+ "status",
+ "workflow",
+ "requester",
+ "on_behalf_of",
+ "target",
+ "attribution",
+ "provider",
+ "model",
+ "fallback_used",
+ "synthetic",
+ "cached_from",
+ "usage",
"error",
"error",
- "blog_id"
+ "citations_count",
+ "retry_of",
+ "created_at",
+ "finished_at"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "job_id": {
+ "run_id": {
"type": "string",
"type": "string",
- "pattern": "^job_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
- "action": {
- "enum": [
- "publish",
- "unpublish"
- ]
+ "status": {
+ "const": "succeeded"
},
},
- "revision": {
- "type": [
- "integer",
- "null"
+ "workflow": {
+ "type": "object",
+ "required": [
+ "key",
+ "version"
],
],
- "minimum": 1,
- "description": "The revision a publish was to make live; null for an unpublish."
+ "additionalProperties": false,
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
},
},
- "run_at": {
- "type": "string",
- "format": "date-time",
- "description": "When the job was last due."
+ "requester": {
+ "$ref": "../../identity/subject-ref.v1.json"
},
},
- "attempts": {
- "type": "integer",
- "minimum": 0
+ "on_behalf_of": {
+ "anyOf": [
+ {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
},
},
- "error": {
+ "target": {
+ "anyOf": [
+ {
+ "$ref": "../../common/entity-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "attribution": {
+ "anyOf": [
+ {
+ "$ref": "../../common/entity-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "provider": {
"type": [
"type": [
"string",
"string",
"null"
"null"
],
],
- "maxLength": 1000,
- "description": "The last error."
+ "description": "Provider that produced the output; null before a provider call and for cached runs."
},
},
- "blog_id": {
+ "model": {
"type": [
"type": [
"string",
"string",
"null"
"null"
- ],
- "pattern": "^blg_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "null when the post no longer exists."
- }
- }
- }
- },
- "blog.index_document.upserted": {
- "version": "1.0.0",
- "owner": "blog",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.upserted.v1.json",
- "title": "BlogIndexDocumentUpsertedPayload",
- "description": "blog.index_document.upserted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). The OpenVibe.Search document of a published, public, listable post, sent whenever what Search should hold changed (the revision grows with every change). Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision: <document revision> }, visibility internal, priority important, actor service:blog.",
- "allOf": [
- {
- "$ref": "../../search/index-document.v1.json"
+ ]
},
},
- {
+ "fallback_used": {
+ "type": "boolean"
+ },
+ "synthetic": {
+ "type": "boolean",
+ "description": "Stub provider output."
+ },
+ "cached_from": {
+ "type": "null"
+ },
+ "usage": {
"type": "object",
"type": "object",
"required": [
"required": [
- "owner",
- "type",
- "deleted"
+ "tokens_in",
+ "tokens_out",
+ "cost_usd",
+ "attempts"
],
],
+ "additionalProperties": false,
"properties": {
"properties": {
- "owner": {
- "const": "blog"
+ "tokens_in": {
+ "type": "integer",
+ "minimum": 0
},
},
- "type": {
- "const": "post"
+ "tokens_out": {
+ "type": "integer",
+ "minimum": 0
},
},
- "id": {
- "type": "string",
- "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"
+ "cost_usd": {
+ "type": "number",
+ "minimum": 0
},
},
- "deleted": {
- "const": false
+ "attempts": {
+ "type": "integer",
+ "minimum": 0
}
}
}
}
- }
- ]
- }
- },
- "blog.index_document.deleted": {
- "version": "1.0.0",
- "owner": "blog",
- "status": "active",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/blog.index_document.deleted.v1.json",
- "title": "BlogIndexDocumentDeletedPayload",
- "description": "blog.index_document.deleted v1 (OpenVibe.Blog server/domain/publication.js syncIndex, via openvibe-publishing/index-hooks indexEvent). A Search tombstone: the post is no longer published, public and listable, so Search drops it at this revision or older. Only sent for a post Search was sent before. Consumed by OpenVibe.Search ('*.index_document.*'). Envelope: subject { type: post, id, revision }, visibility internal, priority important, actor service:blog.",
- "type": "object",
- "required": [
- "type",
- "id",
- "revision"
- ],
- "additionalProperties": false,
- "properties": {
- "type": {
- "const": "post"
},
},
- "id": {
- "type": "string",
- "pattern": "^pst_[0-9A-HJKMNP-TV-Z]{26}$"
+ "error": {
+ "type": "null"
},
},
- "revision": {
+ "citations_count": {
"type": "integer",
"type": "integer",
- "minimum": 1,
- "maximum": 9007199254740991,
- "description": "Index revision of the tombstone; wins over any document at the same or an older revision."
+ "minimum": 0
+ },
+ "retry_of": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": "string",
+ "format": "date-time"
}
}
}
}
}
}
},
},
- "ai.run-request": {
+ "ai.run.failed": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "ai",
"owner": "ai",
- "status": "active",
+ "status": "planned",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/ai/run-request.v1.json",
- "title": "AiRunRequest",
- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",
+ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.failed.v1.json",
+ "title": "AiRunFailedPayload",
+ "description": "ai.run.failed v1 (OpenVibe.AI server/runs.js execute / recoverInterrupted). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run ended failed: a provider, route, source or output error, run.internal, or run.interrupted after a restart or shutdown (retryable with POST /api/v1/runs/:id/retry unless its input was not retained). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.",
"type": "object",
"type": "object",
"required": [
"required": [
- "workflow"
+ "run_id",
+ "status",
+ "workflow",
+ "requester",
+ "on_behalf_of",
+ "target",
+ "attribution",
+ "provider",
+ "model",
+ "fallback_used",
+ "synthetic",
+ "cached_from",
+ "usage",
+ "error",
+ "citations_count",
+ "retry_of",
+ "created_at",
+ "finished_at"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "workflow": {
+ "run_id": {
"type": "string",
"type": "string",
- "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)+$",
- "maxLength": 200,
- "description": "Workflow key, <namespace>.<name> (live.chat_reply, news.summarize_story, network.site_copy…)."
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
- "version": {
- "type": "integer",
- "minimum": 1,
- "description": "Pin a workflow version (active or deprecated). Omitted: the newest active version."
+ "status": {
+ "const": "failed"
},
},
- "input": {
+ "workflow": {
"type": "object",
"type": "object",
- "description": "Workflow input; validated against the workflow version's input_schema. Default {}."
+ "required": [
+ "key",
+ "version"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ },
+ "requester": {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ "on_behalf_of": {
+ "anyOf": [
+ {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
},
},
"target": {
"target": {
- "$ref": "../common/entity-ref.v1.json"
+ "anyOf": [
+ {
+ "$ref": "../../common/entity-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
},
},
"attribution": {
"attribution": {
- "$ref": "../common/entity-ref.v1.json"
+ "anyOf": [
+ {
+ "$ref": "../../common/entity-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
},
},
- "on_behalf_of": {
- "$ref": "../identity/subject-ref.v1.json"
+ "provider": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Provider that produced the output; null before a provider call and for cached runs."
},
},
- "idempotency_key": {
- "type": "string",
- "pattern": "^[A-Za-z0-9._:-]{8,128}$",
- "description": "Same (requester, key) returns the existing run; a different workflow or input under it is 409 idempotency.conflict. The Idempotency-Key header is used when this is absent."
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- "options": {
+ "fallback_used": {
+ "type": "boolean"
+ },
+ "synthetic": {
+ "type": "boolean",
+ "description": "Stub provider output."
+ },
+ "cached_from": {
+ "type": "null"
+ },
+ "usage": {
"type": "object",
"type": "object",
+ "required": [
+ "tokens_in",
+ "tokens_out",
+ "cost_usd",
+ "attempts"
+ ],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "cache": {
- "type": "boolean",
- "default": true,
- "description": "false skips the scoped cache lookup and store."
+ "tokens_in": {
+ "type": "integer",
+ "minimum": 0
},
},
- "debug": {
- "type": "boolean",
- "default": false,
- "description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
+ "tokens_out": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "cost_usd": {
+ "type": "number",
+ "minimum": 0
+ },
+ "attempts": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "error": {
+ "type": "object",
+ "required": [
+ "code",
+ "detail"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
+ },
+ "detail": {
+ "type": [
+ "string",
+ "null"
+ ]
}
}
}
}
+ },
+ "citations_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "retry_of": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": "string",
+ "format": "date-time"
}
}
}
}
}
}
},
},
- "ai.run": {
+ "ai.run.cached": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "ai",
"owner": "ai",
- "status": "active",
+ "status": "planned",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/ai/run.v1.json",
- "title": "AiRun",
- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted.",
+ "$id": "https://openvibe.network/contracts/events/payloads/ai.run.cached.v1.json",
+ "title": "AiRunCachedPayload",
+ "description": "ai.run.cached v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A create was served from the scoped cache: no provider call, cached_from names the run whose output was reused. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.",
"type": "object",
"type": "object",
"required": [
"required": [
- "id",
+ "run_id",
"status",
"status",
"workflow",
"workflow",
- "template",
- "route",
"requester",
"requester",
"on_behalf_of",
"on_behalf_of",
- "attribution",
- "source_service",
"target",
"target",
- "input",
- "output",
- "error",
+ "attribution",
+ "provider",
+ "model",
+ "fallback_used",
"synthetic",
"synthetic",
- "provenance",
+ "cached_from",
"usage",
"usage",
+ "error",
"citations_count",
"citations_count",
"retry_of",
"retry_of",
- "idempotency_key",
- "trace_id",
"created_at",
"created_at",
- "started_at",
"finished_at"
"finished_at"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "id": {
+ "run_id": {
"type": "string",
"type": "string",
"pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
"pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
},
},
"status": {
"status": {
- "enum": [
- "queued",
- "running",
- "succeeded",
- "failed",
- "cancelled",
- "cached"
- ],
- "description": "Terminal: succeeded, failed, cancelled, cached (a scoped cache hit, no provider call; provenance.cached_from names the run it reused)."
+ "const": "cached"
},
},
"workflow": {
"workflow": {
"type": "object",
"type": "object",
@@ -5428,958 +6848,1329 @@
@@ -5428,958 +6848,1329 @@
}
}
}
}
},
},
- "template": {
- "type": [
- "object",
- "null"
- ],
- "required": [
- "key",
- "version"
- ],
- "additionalProperties": false,
- "properties": {
- "key": {
- "type": "string"
- },
- "version": {
- "type": [
- "integer",
- "null"
- ]
- }
- },
- "description": "The prompt template of the workflow's llm step; null for workflows without one."
- },
- "route": {
- "type": [
- "object",
- "null"
- ],
- "required": [
- "key",
- "version"
- ],
- "additionalProperties": false,
- "properties": {
- "key": {
- "type": "string"
- },
- "version": {
- "type": [
- "integer",
- "null"
- ]
- }
- },
- "description": "Provider route; version null while the route is unresolved or disabled."
- },
"requester": {
"requester": {
- "$ref": "../identity/subject-ref.v1.json"
+ "$ref": "../../identity/subject-ref.v1.json"
},
},
"on_behalf_of": {
"on_behalf_of": {
"anyOf": [
"anyOf": [
{
{
- "$ref": "../identity/subject-ref.v1.json"
+ "$ref": "../../identity/subject-ref.v1.json"
},
},
{
{
"type": "null"
"type": "null"
}
}
]
]
},
},
- "attribution": {
+ "target": {
"anyOf": [
"anyOf": [
{
{
- "$ref": "../common/entity-ref.v1.json"
+ "$ref": "../../common/entity-ref.v1.json"
},
},
{
{
"type": "null"
"type": "null"
}
}
]
]
},
},
- "source_service": {
- "type": [
- "string",
- "null"
- ],
- "description": "The requester's service id when the requester is a service."
- },
- "target": {
+ "attribution": {
"anyOf": [
"anyOf": [
{
{
- "$ref": "../common/entity-ref.v1.json"
+ "$ref": "../../common/entity-ref.v1.json"
},
},
{
{
"type": "null"
"type": "null"
}
}
]
]
},
},
- "input": {
- "type": "object",
- "description": "The retained input (see the description)."
- },
- "output": {
+ "provider": {
"type": [
"type": [
- "object",
+ "string",
"null"
"null"
],
],
- "description": "Workflow output, or null."
+ "description": "Provider that produced the output; null before a provider call and for cached runs."
},
},
- "error": {
+ "model": {
"type": [
"type": [
- "object",
+ "string",
"null"
"null"
- ],
+ ]
+ },
+ "fallback_used": {
+ "type": "boolean"
+ },
+ "synthetic": {
+ "type": "boolean",
+ "description": "Stub provider output."
+ },
+ "cached_from": {
+ "type": "string",
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "usage": {
+ "type": "object",
"required": [
"required": [
- "code",
- "detail"
+ "tokens_in",
+ "tokens_out",
+ "cost_usd",
+ "attempts"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
- "code": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",
- "description": "run.cancelled, run.interrupted, run.internal, output.invalid, output.empty, provider.unavailable, route.unavailable, source.unavailable, media.unresolvable…"
+ "tokens_in": {
+ "type": "integer",
+ "minimum": 0
},
},
- "detail": {
+ "tokens_out": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "cost_usd": {
+ "type": "number",
+ "minimum": 0
+ },
+ "attempts": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "error": {
+ "type": "null"
+ },
+ "citations_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "retry_of": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "ai.definition-version-request": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/definition-version-request.v1.json",
+ "title": "AiDefinitionVersionRequest",
+ "description": "ai.definition-version-request@1: bodies of ai.workflow.manage on OpenVibe.AI. An edit is always a new version (the previous one stays readable); fields left out are taken from the newest existing version. POST /api/v1/templates/:key/versions: a prompt template (input_schema and output_schema must compile as JSON Schema; visibility public, first-party or internal). POST /api/v1/workflows/:key/versions (key <namespace>.<name>): steps (at least one; kind llm (naming an existing template), passthrough, transcribe or embed), schemas, default route, cache mode. POST /api/v1/routes/:key/versions: primary { provider, model } and fallbacks (every provider must exist), or alias_of another route. POST …/:key/versions/:version/status: { status } (templates and workflows draft|active|deprecated|archived; routes active|disabled). Refusals are 422 ai.invalid.",
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "input_schema": {
+ "type": "object",
+ "description": "A JSON Schema the run input must match."
+ },
+ "output_schema": {
+ "type": "object",
+ "description": "A JSON Schema the output must match."
+ },
+ "system_prompt": {
+ "type": "string"
+ },
+ "user_prompt": {
+ "type": "string"
+ },
+ "default_route": {
"type": [
"type": [
"string",
"string",
"null"
"null"
]
]
+ },
+ "owner": {
+ "type": "string"
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "first-party",
+ "internal"
+ ]
+ },
+ "status": {
+ "enum": [
+ "draft",
+ "active",
+ "deprecated",
+ "archived"
+ ]
+ },
+ "metadata": {
+ "type": "object"
}
}
}
}
},
},
- "synthetic": {
- "type": "boolean",
- "description": "Produced by the stub provider; never served to production callers and never cached."
- },
- "provenance": {
+ {
"type": "object",
"type": "object",
- "additionalProperties": false,
- "required": [
- "origin",
- "workflow",
- "workflow_version",
- "template_version",
- "route",
- "route_version",
- "provider",
- "model",
- "fallback_used",
- "run_id",
- "cached_from",
- "synthetic"
- ],
"properties": {
"properties": {
- "origin": {
- "const": "ai"
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "type": "string"
+ },
+ "input_schema": {
+ "type": "object",
+ "description": "A JSON Schema."
+ },
+ "output_schema": {
+ "type": "object",
+ "description": "A JSON Schema."
+ },
+ "steps": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "required": [
+ "kind"
+ ],
+ "properties": {
+ "kind": {
+ "enum": [
+ "llm",
+ "passthrough",
+ "transcribe",
+ "embed"
+ ]
+ },
+ "template": {
+ "type": "string"
+ }
+ }
+ }
},
},
- "workflow": {
- "type": "string"
+ "default_route": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- "workflow_version": {
- "type": "integer",
- "minimum": 1
+ "cache_mode": {
+ "enum": [
+ "none",
+ "private",
+ "service"
+ ]
},
},
- "template_version": {
+ "cache_ttl_sec": {
"type": [
"type": [
"integer",
"integer",
"null"
"null"
]
]
},
},
- "route": {
- "type": [
- "string",
- "null"
+ "status": {
+ "enum": [
+ "draft",
+ "active",
+ "deprecated",
+ "archived"
]
]
},
},
- "route_version": {
+ "metadata": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "primary": {
+ "type": "object",
+ "required": [
+ "provider"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "fallbacks": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "provider"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ }
+ },
+ "options": {
+ "type": "object"
+ },
+ "max_output_tokens": {
"type": [
"type": [
"integer",
"integer",
"null"
"null"
]
]
},
},
- "provider": {
- "type": [
- "string",
- "null"
+ "response_format": {
+ "enum": [
+ "text",
+ "json"
]
]
},
},
- "model": {
+ "timeout_ms": {
"type": [
"type": [
- "string",
+ "integer",
"null"
"null"
]
]
},
},
- "fallback_used": {
- "type": "boolean",
- "description": "The primary provider was degraded and the run fell back (ADR-015: fallbacks are recorded)."
- },
- "run_id": {
- "type": "string",
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "cached_from": {
+ "alias_of": {
"type": [
"type": [
"string",
"string",
"null"
"null"
- ],
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "synthetic": {
- "type": "boolean"
- }
- }
- },
- "usage": {
- "type": "object",
- "required": [
- "tokens_in",
- "tokens_out",
- "cost_usd",
- "attempts"
- ],
- "additionalProperties": false,
- "properties": {
- "tokens_in": {
- "type": "integer",
- "minimum": 0
- },
- "tokens_out": {
- "type": "integer",
- "minimum": 0
- },
- "cost_usd": {
- "type": "number",
- "minimum": 0
+ ]
},
},
- "attempts": {
- "type": "integer",
- "minimum": 0,
- "description": "Provider calls made (skipped routes not counted)."
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
}
}
}
}
- },
- "citations_count": {
- "type": "integer",
- "minimum": 0,
- "description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."
- },
- "retry_of": {
- "type": [
- "string",
- "null"
- ],
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "idempotency_key": {
- "type": [
- "string",
- "null"
- ]
- },
- "trace_id": {
- "type": [
- "string",
- "null"
- ]
- },
- "created_at": {
- "type": "string",
- "format": "date-time"
- },
- "started_at": {
- "type": [
- "string",
- "null"
- ],
- "format": "date-time"
- },
- "finished_at": {
- "type": [
- "string",
- "null"
- ],
- "format": "date-time"
}
}
- }
+ ]
}
}
},
},
- "ai.run.queued": {
+ "ai.definition-version-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "ai",
"owner": "ai",
- "status": "planned",
+ "status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.queued.v1.json",
- "title": "AiRunQueuedPayload",
- "description": "ai.run.queued v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run was stored queued (it then runs inline within ?wait or on the worker). Not published for a cache hit (ai.run.cached) or a create refused by quota or a full queue. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.",
- "type": "object",
- "required": [
- "run_id",
- "status",
- "workflow",
- "requester",
- "on_behalf_of",
- "target",
- "attribution",
- "provider",
- "model",
- "fallback_used",
- "synthetic",
- "cached_from",
- "usage",
- "error",
- "citations_count",
- "retry_of",
- "created_at",
- "finished_at"
- ],
- "additionalProperties": false,
- "properties": {
- "run_id": {
- "type": "string",
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "status": {
- "const": "queued"
- },
- "workflow": {
+ "$id": "https://openvibe.network/contracts/ai/definition-version-result.v1.json",
+ "title": "AiDefinitionVersionResult",
+ "description": "ai.definition-version-result@1: answers of ai.workflow.manage on OpenVibe.AI. POST …/:key/versions → 201 { template | workflow | route } (the new version, whole: prompts and schemas for a template, steps for a workflow, primary/fallbacks for a route); POST …/versions/:version/status → { template | workflow | route: { key, version, status } }.",
+ "anyOf": [
+ {
"type": "object",
"type": "object",
"required": [
"required": [
- "key",
- "version"
+ "template"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "key": {
- "type": "string"
- },
- "version": {
- "type": "integer",
- "minimum": 1
- }
- }
- },
- "requester": {
- "$ref": "../../identity/subject-ref.v1.json"
- },
- "on_behalf_of": {
- "anyOf": [
- {
- "$ref": "../../identity/subject-ref.v1.json"
- },
- {
- "type": "null"
- }
- ]
- },
- "target": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
- },
- {
- "type": "null"
- }
- ]
- },
- "attribution": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
- },
- {
- "type": "null"
+ "template": {
+ "type": "object",
+ "required": [
+ "key",
+ "version",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "status": {
+ "type": "string"
+ }
+ }
}
}
- ]
- },
- "provider": {
- "type": [
- "string",
- "null"
- ],
- "description": "Provider that produced the output; null before a provider call and for cached runs."
- },
- "model": {
- "type": [
- "string",
- "null"
- ]
- },
- "fallback_used": {
- "type": "boolean"
- },
- "synthetic": {
- "type": "boolean",
- "description": "Stub provider output."
- },
- "cached_from": {
- "type": "null"
+ }
},
},
- "usage": {
+ {
"type": "object",
"type": "object",
"required": [
"required": [
- "tokens_in",
- "tokens_out",
- "cost_usd",
- "attempts"
+ "workflow"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "tokens_in": {
- "type": "integer",
- "minimum": 0
- },
- "tokens_out": {
- "type": "integer",
- "minimum": 0
- },
- "cost_usd": {
- "type": "number",
- "minimum": 0
- },
- "attempts": {
- "type": "integer",
- "minimum": 0
+ "workflow": {
+ "type": "object",
+ "required": [
+ "key",
+ "version",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "status": {
+ "type": "string"
+ }
+ }
}
}
}
}
},
},
- "error": {
- "type": "null"
- },
- "citations_count": {
- "type": "integer",
- "minimum": 0
- },
- "retry_of": {
- "type": [
- "string",
- "null"
+ {
+ "type": "object",
+ "required": [
+ "route"
],
],
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "created_at": {
- "type": "string",
- "format": "date-time"
- },
- "finished_at": {
- "type": "null"
+ "properties": {
+ "route": {
+ "type": "object",
+ "required": [
+ "key",
+ "version",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
}
}
- }
+ ]
}
}
},
},
- "ai.run.succeeded": {
+ "ai.model": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "ai",
"owner": "ai",
- "status": "planned",
+ "status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.succeeded.v1.json",
- "title": "AiRunSucceededPayload",
- "description": "ai.run.succeeded v1 (OpenVibe.AI server/runs.js execute). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run finished with output that matched the workflow output schema. The output itself is not in the event: a consumer that needs it reads GET /api/v1/runs/:id with ai.run.read (own runs only). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.",
+ "$id": "https://openvibe.network/contracts/ai/model.v1.json",
+ "title": "AiModel",
+ "description": "ai.model@1: one model of a provider in OpenVibe.AI's registry (server/registry.js decodeModel): type, status, limits, per-million-token costs and what it supports.",
"type": "object",
"type": "object",
"required": [
"required": [
- "run_id",
+ "provider_key",
+ "model_key",
+ "type",
"status",
"status",
- "workflow",
- "requester",
- "on_behalf_of",
- "target",
- "attribution",
- "provider",
- "model",
- "fallback_used",
- "synthetic",
- "cached_from",
- "usage",
- "error",
- "citations_count",
- "retry_of",
- "created_at",
- "finished_at"
+ "cost",
+ "supports"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "run_id": {
- "type": "string",
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "status": {
- "const": "succeeded"
- },
- "workflow": {
- "type": "object",
- "required": [
- "key",
- "version"
- ],
- "additionalProperties": false,
- "properties": {
- "key": {
- "type": "string"
- },
- "version": {
- "type": "integer",
- "minimum": 1
- }
- }
+ "provider_key": {
+ "type": "string"
},
},
- "requester": {
- "$ref": "../../identity/subject-ref.v1.json"
+ "model_key": {
+ "type": "string",
+ "maxLength": 200
},
},
- "on_behalf_of": {
- "anyOf": [
- {
- "$ref": "../../identity/subject-ref.v1.json"
- },
- {
- "type": "null"
- }
+ "display_name": {
+ "type": [
+ "string",
+ "null"
]
]
},
},
- "target": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
- },
- {
- "type": "null"
- }
+ "type": {
+ "enum": [
+ "chat",
+ "vision",
+ "embedding",
+ "stt"
]
]
},
},
- "attribution": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
- },
- {
- "type": "null"
- }
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
]
]
},
},
- "provider": {
+ "context_window": {
"type": [
"type": [
- "string",
+ "integer",
"null"
"null"
- ],
- "description": "Provider that produced the output; null before a provider call and for cached runs."
+ ]
},
},
- "model": {
+ "max_output": {
"type": [
"type": [
- "string",
+ "integer",
"null"
"null"
]
]
},
},
- "fallback_used": {
- "type": "boolean"
- },
- "synthetic": {
- "type": "boolean",
- "description": "Stub provider output."
- },
- "cached_from": {
- "type": "null"
+ "cost": {
+ "type": "object",
+ "properties": {
+ "in_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "out_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "cached_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ }
},
},
- "usage": {
+ "supports": {
"type": "object",
"type": "object",
- "required": [
- "tokens_in",
- "tokens_out",
- "cost_usd",
- "attempts"
- ],
- "additionalProperties": false,
"properties": {
"properties": {
- "tokens_in": {
- "type": "integer",
- "minimum": 0
+ "json": {
+ "type": "boolean"
},
},
- "tokens_out": {
- "type": "integer",
- "minimum": 0
+ "tools": {
+ "type": "boolean"
},
},
- "cost_usd": {
- "type": "number",
- "minimum": 0
+ "streaming": {
+ "type": "boolean"
},
},
- "attempts": {
- "type": "integer",
- "minimum": 0
+ "vision": {
+ "type": "boolean"
}
}
}
}
},
},
- "error": {
- "type": "null"
- },
- "citations_count": {
- "type": "integer",
- "minimum": 0
+ "metadata": {
+ "type": "object"
},
},
- "retry_of": {
+ "created_at": {
"type": [
"type": [
"string",
"string",
"null"
"null"
- ],
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "created_at": {
- "type": "string",
- "format": "date-time"
+ ]
},
},
- "finished_at": {
- "type": "string",
- "format": "date-time"
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ]
}
}
}
}
}
}
},
},
- "ai.run.failed": {
+ "ai.provider-manage-request": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "ai",
"owner": "ai",
- "status": "planned",
+ "status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.failed.v1.json",
- "title": "AiRunFailedPayload",
- "description": "ai.run.failed v1 (OpenVibe.AI server/runs.js execute / recoverInterrupted). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A run ended failed: a provider, route, source or output error, run.internal, or run.interrupted after a restart or shutdown (retryable with POST /api/v1/runs/:id/retry unless its input was not retained). A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority important, actor service:ai.",
- "type": "object",
- "required": [
- "run_id",
- "status",
- "workflow",
- "requester",
- "on_behalf_of",
- "target",
- "attribution",
- "provider",
- "model",
- "fallback_used",
- "synthetic",
- "cached_from",
- "usage",
- "error",
- "citations_count",
- "retry_of",
- "created_at",
- "finished_at"
- ],
- "additionalProperties": false,
- "properties": {
- "run_id": {
- "type": "string",
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "status": {
- "const": "failed"
- },
- "workflow": {
+ "$id": "https://openvibe.network/contracts/ai/provider-manage-request.v1.json",
+ "title": "AiProviderManageRequest",
+ "description": "ai.provider-manage-request@1: bodies of ai.provider.manage on OpenVibe.AI. POST /api/v1/providers creates or replaces a provider (key required) and PATCH /api/v1/providers/:key changes one (fields left out keep their value); a body carrying api_key or secret is refused (422: send secret_ref env:NAME, never a secret value). POST /api/v1/models (provider_key and model_key required) and PATCH /api/v1/models/:provider/:model upsert a model. POST /api/v1/quotas upserts a quota (scope_type and window required; scope_id too unless global; the same scope, window and workflow_prefix is one quota). POST /api/v1/providers/:key/disable|enable|reset and DELETE /api/v1/cache[?workflow=] take no body. Unknown fields are ignored.",
+ "anyOf": [
+ {
"type": "object",
"type": "object",
- "required": [
- "key",
- "version"
- ],
- "additionalProperties": false,
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "api_key"
+ ]
+ },
+ {
+ "required": [
+ "secret"
+ ]
+ }
+ ]
+ },
"properties": {
"properties": {
"key": {
"key": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"
+ },
+ "display_name": {
"type": "string"
"type": "string"
},
},
- "version": {
- "type": "integer",
- "minimum": 1
- }
- }
- },
- "requester": {
- "$ref": "../../identity/subject-ref.v1.json"
- },
- "on_behalf_of": {
- "anyOf": [
- {
- "$ref": "../../identity/subject-ref.v1.json"
+ "kind": {
+ "enum": [
+ "stub",
+ "openai",
+ "anthropic",
+ "http",
+ "whisper"
+ ]
},
},
- {
- "type": "null"
- }
- ]
- },
- "target": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
},
},
- {
- "type": "null"
- }
- ]
- },
- "attribution": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
+ "base_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^https?://\\S+$"
},
},
- {
- "type": "null"
+ "auth_mode": {
+ "enum": [
+ "none",
+ "bearer",
+ "x-api-key"
+ ]
+ },
+ "secret_ref": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^(env:[A-Z][A-Z0-9_]{0,63})?$"
+ },
+ "default_model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "type": "array",
+ "items": {
+ "enum": [
+ "chat",
+ "generate",
+ "summarize",
+ "classify",
+ "extract",
+ "enrich",
+ "embed",
+ "vision",
+ "json",
+ "transcribe"
+ ]
+ }
+ },
+ "timeout_ms": {
+ "type": [
+ "integer",
+ "string"
+ ],
+ "description": "Clamped to 1000..600000."
+ },
+ "priority": {
+ "type": [
+ "number",
+ "string"
+ ]
+ },
+ "metadata": {
+ "type": "object"
}
}
- ]
- },
- "provider": {
- "type": [
- "string",
- "null"
- ],
- "description": "Provider that produced the output; null before a provider call and for cached runs."
- },
- "model": {
- "type": [
- "string",
- "null"
- ]
- },
- "fallback_used": {
- "type": "boolean"
- },
- "synthetic": {
- "type": "boolean",
- "description": "Stub provider output."
- },
- "cached_from": {
- "type": "null"
+ }
},
},
- "usage": {
+ {
"type": "object",
"type": "object",
- "required": [
- "tokens_in",
- "tokens_out",
- "cost_usd",
- "attempts"
- ],
- "additionalProperties": false,
"properties": {
"properties": {
- "tokens_in": {
- "type": "integer",
- "minimum": 0
+ "provider_key": {
+ "type": "string"
},
},
- "tokens_out": {
- "type": "integer",
- "minimum": 0
+ "model_key": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
},
},
- "cost_usd": {
- "type": "number",
- "minimum": 0
+ "display_name": {
+ "type": "string"
},
},
- "attempts": {
- "type": "integer",
- "minimum": 0
+ "type": {
+ "enum": [
+ "chat",
+ "vision",
+ "embedding",
+ "stt"
+ ]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
+ },
+ "context_window": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "max_output": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "cost": {
+ "type": "object",
+ "properties": {
+ "in_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "out_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "cached_per_mtok": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ }
+ },
+ "supports": {
+ "type": "object",
+ "properties": {
+ "json": {
+ "type": "boolean"
+ },
+ "tools": {
+ "type": "boolean"
+ },
+ "streaming": {
+ "type": "boolean"
+ },
+ "vision": {
+ "type": "boolean"
+ }
+ }
+ },
+ "metadata": {
+ "type": "object"
}
}
}
}
},
},
- "error": {
+ {
"type": "object",
"type": "object",
"required": [
"required": [
- "code",
- "detail"
+ "scope_type",
+ "window"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "code": {
+ "scope_type": {
+ "enum": [
+ "global",
+ "service",
+ "actor",
+ "attribution"
+ ]
+ },
+ "scope_id": {
"type": "string",
"type": "string",
- "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
+ "maxLength": 200,
+ "description": "* on a non-global scope = each one separately."
},
},
- "detail": {
+ "window": {
+ "enum": [
+ "minute",
+ "hour",
+ "day"
+ ]
+ },
+ "max_requests": {
+ "type": [
+ "number",
+ "string",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "max_tokens": {
+ "type": [
+ "number",
+ "string",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "max_cost_usd": {
+ "type": [
+ "number",
+ "string",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "workflow_prefix": {
"type": [
"type": [
"string",
"string",
"null"
"null"
]
]
+ },
+ "status": {
+ "enum": [
+ "active",
+ "disabled"
+ ]
}
}
}
}
},
},
- "citations_count": {
- "type": "integer",
- "minimum": 0
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "ai.provider-manage-result": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/provider-manage-result.v1.json",
+ "title": "AiProviderManageResult",
+ "description": "ai.provider-manage-result@1: answers of ai.provider.manage on OpenVibe.AI. Provider create/update/disable/enable → { provider } (201 on POST); POST …/reset → { health } (the circuit breaker, closed again); model upserts → { model }; POST /api/v1/quotas → 201 { quota } (the stored row); DELETE /api/v1/cache → { removed } (entries purged). Every change is audited.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "provider"
+ ],
+ "properties": {
+ "provider": {
+ "$ref": "provider.v1.json"
+ }
+ }
},
},
- "retry_of": {
- "type": [
- "string",
- "null"
+ {
+ "type": "object",
+ "required": [
+ "health"
],
],
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ "properties": {
+ "health": {
+ "type": "object",
+ "required": [
+ "state"
+ ],
+ "properties": {
+ "provider_key": {
+ "type": "string"
+ },
+ "state": {
+ "enum": [
+ "closed",
+ "open",
+ "half_open"
+ ]
+ },
+ "consecutive_failures": {
+ "type": "integer"
+ }
+ }
+ }
+ }
},
},
- "created_at": {
- "type": "string",
- "format": "date-time"
+ {
+ "type": "object",
+ "required": [
+ "model"
+ ],
+ "properties": {
+ "model": {
+ "$ref": "model.v1.json"
+ }
+ }
},
},
- "finished_at": {
- "type": "string",
- "format": "date-time"
+ {
+ "type": "object",
+ "required": [
+ "quota"
+ ],
+ "properties": {
+ "quota": {
+ "type": "object",
+ "required": [
+ "id",
+ "scope_type",
+ "scope_id",
+ "window",
+ "status"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "scope_type": {
+ "type": "string"
+ },
+ "scope_id": {
+ "type": "string"
+ },
+ "window": {
+ "type": "string"
+ },
+ "max_requests": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "max_tokens": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "max_cost_usd": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "workflow_prefix": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "removed"
+ ],
+ "properties": {
+ "removed": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
}
}
- }
+ ]
}
}
},
},
- "ai.run.cached": {
+ "ai.provider": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "ai",
"owner": "ai",
- "status": "planned",
+ "status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/ai.run.cached.v1.json",
- "title": "AiRunCachedPayload",
- "description": "ai.run.cached v1 (OpenVibe.AI server/runs.js create). PLANNED: AI does not publish run events yet (its manifest notes list ai.run.queued|succeeded|failed|cached once the Events client is wired in); this is the shape it is to send. A create was served from the scoped cache: no provider call, cached_from names the run whose output was reused. A projection of ai.run@1 without input or output (ADR-015: request metadata, never raw prompts). requester is the token principal that created the run (service, app or mod). Envelope: subject { type: run, id: <run_id> }, visibility internal, priority low, actor service:ai.",
+ "$id": "https://openvibe.network/contracts/ai/provider.v1.json",
+ "title": "AiProvider",
+ "description": "ai.provider@1: one model provider as OpenVibe.AI's registry API shows it (server/registry.js publicProvider): never a secret value, only the secret reference name (env:NAME) and whether it resolves (credentials), plus the circuit-breaker health.",
"type": "object",
"type": "object",
"required": [
"required": [
- "run_id",
+ "key",
+ "kind",
"status",
"status",
- "workflow",
- "requester",
- "on_behalf_of",
- "target",
- "attribution",
- "provider",
- "model",
- "fallback_used",
- "synthetic",
- "cached_from",
- "usage",
- "error",
- "citations_count",
- "retry_of",
- "created_at",
- "finished_at"
+ "auth_mode",
+ "capabilities",
+ "credentials"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "run_id": {
+ "key": {
"type": "string",
"type": "string",
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "enum": [
+ "stub",
+ "openai",
+ "anthropic",
+ "http",
+ "whisper"
+ ]
},
},
"status": {
"status": {
- "const": "cached"
+ "enum": [
+ "active",
+ "disabled"
+ ]
},
},
- "workflow": {
- "type": "object",
- "required": [
- "key",
- "version"
- ],
- "additionalProperties": false,
- "properties": {
- "key": {
- "type": "string"
- },
- "version": {
- "type": "integer",
- "minimum": 1
- }
- }
+ "base_url": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- "requester": {
- "$ref": "../../identity/subject-ref.v1.json"
+ "auth_mode": {
+ "enum": [
+ "none",
+ "bearer",
+ "x-api-key"
+ ]
},
},
- "on_behalf_of": {
- "anyOf": [
- {
- "$ref": "../../identity/subject-ref.v1.json"
- },
- {
- "type": "null"
- }
+ "secret_ref": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "env:NAME, a reference; never the secret."
+ },
+ "default_model": {
+ "type": [
+ "string",
+ "null"
]
]
},
},
- "target": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
- },
- {
- "type": "null"
- }
+ "capabilities": {
+ "type": "array",
+ "items": {
+ "enum": [
+ "chat",
+ "generate",
+ "summarize",
+ "classify",
+ "extract",
+ "enrich",
+ "embed",
+ "vision",
+ "json",
+ "transcribe"
+ ]
+ }
+ },
+ "timeout_ms": {
+ "type": [
+ "integer",
+ "null"
]
]
},
},
- "attribution": {
- "anyOf": [
- {
- "$ref": "../../common/entity-ref.v1.json"
- },
- {
- "type": "null"
- }
+ "priority": {
+ "type": [
+ "number",
+ "null"
]
]
},
},
- "provider": {
+ "metadata": {
+ "type": "object"
+ },
+ "origin": {
"type": [
"type": [
"string",
"string",
"null"
"null"
- ],
- "description": "Provider that produced the output; null before a provider call and for cached runs."
+ ]
},
},
- "model": {
+ "created_at": {
"type": [
"type": [
"string",
"string",
"null"
"null"
]
]
},
},
- "fallback_used": {
- "type": "boolean"
- },
- "synthetic": {
- "type": "boolean",
- "description": "Stub provider output."
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- "cached_from": {
- "type": "string",
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ "credentials": {
+ "enum": [
+ "not_required",
+ "configured",
+ "missing"
+ ]
},
},
- "usage": {
+ "health": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "provider_key": {
+ "type": "string"
+ },
+ "state": {
+ "enum": [
+ "closed",
+ "open",
+ "half_open"
+ ]
+ },
+ "consecutive_failures": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ }
+ },
+ "ai.usage-result": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/usage-result.v1.json",
+ "title": "AiUsageResult",
+ "description": "ai.usage-result@1: answers of ai.usage.read on OpenVibe.AI (all GETs, parameters in the query). GET /api/v1/status → { providers, runs, today, workflows, templates, routes, inflight, quotas }; GET /api/v1/usage?from=&to=&requester= → { usage } (daily rows); GET /api/v1/quotas → { quotas, current }; GET /api/v1/requests?provider=&status=&run=&fallback=&limit= → { requests } (request-log metadata: hashes, tokens, cost, latency; raw debug fields only for provider managers who ask); GET /api/v1/audit → { audit }; GET /api/v1/cache → { cache }; GET /api/v1/providers → { providers }; GET /api/v1/models?provider= → { models }. Never raw prompts, never secrets.",
+ "anyOf": [
+ {
"type": "object",
"type": "object",
"required": [
"required": [
- "tokens_in",
- "tokens_out",
- "cost_usd",
- "attempts"
+ "providers",
+ "runs",
+ "today"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "tokens_in": {
- "type": "integer",
- "minimum": 0
+ "providers": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "credentials": {
+ "type": "string"
+ },
+ "health": {
+ "type": "string"
+ }
+ }
+ }
},
},
- "tokens_out": {
- "type": "integer",
- "minimum": 0
+ "runs": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer"
+ },
+ "description": "Run counts by status."
},
},
- "cost_usd": {
- "type": "number",
- "minimum": 0
+ "today": {
+ "type": "object",
+ "required": [
+ "day"
+ ],
+ "properties": {
+ "day": {
+ "type": "string"
+ },
+ "requests": {
+ "type": "number"
+ },
+ "cost_usd": {
+ "type": "number"
+ }
+ }
},
},
- "attempts": {
- "type": "integer",
- "minimum": 0
+ "workflows": {
+ "type": "integer"
+ },
+ "templates": {
+ "type": "integer"
+ },
+ "routes": {
+ "type": "integer"
+ },
+ "inflight": {
+ "type": "integer"
+ },
+ "quotas": {
+ "type": "array"
}
}
}
}
},
},
- "error": {
- "type": "null"
+ {
+ "type": "object",
+ "required": [
+ "providers"
+ ],
+ "properties": {
+ "providers": {
+ "type": "array",
+ "items": {
+ "$ref": "provider.v1.json"
+ }
+ }
+ }
},
},
- "citations_count": {
- "type": "integer",
- "minimum": 0
+ {
+ "type": "object",
+ "required": [
+ "models"
+ ],
+ "properties": {
+ "models": {
+ "type": "array",
+ "items": {
+ "$ref": "model.v1.json"
+ }
+ }
+ }
},
},
- "retry_of": {
- "type": [
- "string",
- "null"
+ {
+ "type": "object",
+ "required": [
+ "quotas",
+ "current"
],
],
- "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$"
+ "properties": {
+ "quotas": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "current": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "quota": {
+ "type": "object"
+ },
+ "scope_id": {
+ "type": "string"
+ },
+ "window_start": {
+ "type": "string"
+ },
+ "resets_in_seconds": {
+ "type": "number"
+ },
+ "used": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
},
},
- "created_at": {
- "type": "string",
- "format": "date-time"
+ {
+ "type": "object",
+ "required": [
+ "usage"
+ ],
+ "properties": {
+ "usage": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
},
},
- "finished_at": {
- "type": "string",
- "format": "date-time"
+ {
+ "type": "object",
+ "required": [
+ "requests"
+ ],
+ "properties": {
+ "requests": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "audit"
+ ],
+ "properties": {
+ "audit": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "cache"
+ ],
+ "properties": {
+ "cache": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "workflow_key": {
+ "type": "string"
+ },
+ "privacy": {
+ "type": "string"
+ },
+ "entries": {
+ "type": "integer"
+ },
+ "hits": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
}
}
- }
+ ]
}
}
},
},
"tips.interaction.ready": {
"tips.interaction.ready": {
@@ -5793,6 +5793,178 @@ export interface TombstonePayload {
@@ -5793,6 +5793,178 @@ export interface TombstonePayload {
redacted_by: string;
redacted_by: string;
}
}
+/** [email protected] (owner: events) */
+/**
+ * events.delivery-admin-request@1: the body of POST /api/v1/deliveries/replay on OpenVibe.Events (events.delivery.admin, operators): { subscription_id } and exactly one of from_seq (requeue everything the subscription matches from that seq) or event_ids (at most 1000). GET /api/v1/deliveries?status=&subscription_id=&after_seq=&limit= takes no body.
+ */
+export type EventsDeliveryAdminRequest =
+ | {
+ [k: string]: unknown | undefined;
+ }
+ | NoBody;
+
+/** [email protected] (owner: events) */
+/**
+ * events.delivery-admin-result@1: answers of events.delivery.admin on OpenVibe.Events. GET /api/v1/deliveries → { deliveries, counts } (per delivery: event, subscription, seq, status pending|delivered|failed|dead, attempt, next_attempt_at, the last HTTP status and error, delivered_at; counts by status); POST /api/v1/deliveries/replay → { subscription_id, queued }.
+ */
+export type EventsDeliveryAdminResult =
+ | {
+ deliveries: {
+ event_id: string;
+ subscription_id: string;
+ seq: number;
+ status: "pending" | "delivered" | "failed" | "dead";
+ attempt: number;
+ next_attempt_at?: string | null;
+ last_status?: number | null;
+ last_error?: string | null;
+ delivered_at?: string | null;
+ }[];
+ counts: {
+ pending: number;
+ delivered: number;
+ failed: number;
+ dead: number;
+ };
+ }
+ | {
+ subscription_id: string;
+ queued: number;
+ };
+
+/** [email protected] (owner: events) */
+/**
+ * events.publish-result@1: the answer of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps). One envelope → { event_id, seq, duplicate } (201 when stored, 200 when every event was a repeat); a batch { events: [...] } (at most 100, atomic) → { results } in the same order. Refusals are problem+json: 422 events.invalid_envelope / events.invalid_redaction, 403 events.source_mismatch / events.type_not_allowed / events.actor_mismatch, 413 events.payload_too_large / events.batch_too_large, 429 events.quota_exceeded (apps).
+ */
+export type EventsPublishResult =
+ | {
+ event_id: string;
+ seq: number;
+ /**
+ * true: this event_id was stored before; the stored seq is returned and nothing is stored twice.
+ */
+ duplicate: boolean;
+ }
+ | {
+ /**
+ * @minItems 1
+ * @maxItems 100
+ */
+ results: [
+ {
+ event_id: string;
+ seq: number;
+ /**
+ * true: this event_id was stored before; the stored seq is returned and nothing is stored twice.
+ */
+ duplicate: boolean;
+ },
+ ...{
+ event_id: string;
+ seq: number;
+ /**
+ * true: this event_id was stored before; the stored seq is returned and nothing is stored twice.
+ */
+ duplicate: boolean;
+ }[]
+ ];
+ };
+
+/** [email protected] (owner: events) */
+/**
+ * events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.
+ */
+export type EventsReadRequest =
+ | {
+ /**
+ * Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused).
+ */
+ topic: string;
+ /**
+ * A seq (next_after_seq of the last page handled).
+ */
+ cursor: number;
+ }
+ | NoBody;
+
+/** [email protected] (owner: events) */
+/**
+ * events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, event }], next_after_seq, latest_seq, gap? } (keep next_after_seq as the cursor: it moves past events that did not match; gap { from_seq, to_seq } says events after after_seq were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). An app sees only its project's events in its token's environment plus public first-party events.
+ */
+export type EventsReadResult =
+ | {
+ /**
+ * @maxItems 1000
+ */
+ events: {
+ seq: number;
+ event: EventEnvelope;
+ }[];
+ next_after_seq: number;
+ latest_seq: number;
+ gap?: {
+ from_seq: number;
+ to_seq: number;
+ };
+ }
+ | {
+ seq: number;
+ event: EventEnvelope;
+ }
+ | {
+ consumer: string;
+ topic: string;
+ cursor: number;
+ updated_at: string | null;
+ };
+
+/** [email protected] (owner: events) */
+/**
+ * events.subscription-request@1: the body of POST /api/v1/subscriptions on OpenVibe.Events (events.subscription.manage for services, events.app.subscribe for developer apps; the consumer is always the caller). topic_pattern (topic is accepted for it) and endpoint are required: a service's endpoint must be http(s) on the allow-list (127.0.0.1, *.openvibe.*), an app's must be https resolving only to public addresses. secret, when given, is 32 to 256 characters (else one is generated); retry_policy accepts max_attempts (1..20) and backoff_ms (1..20 delays of 0..86400000 ms) and nothing else. The same pattern and endpoint twice is 409 events.subscription_exists. GET /api/v1/subscriptions[/:id] and POST /api/v1/subscriptions/:id/disable|enable take no body.
+ */
+export type EventsSubscriptionRequest =
+ | {
+ [k: string]: unknown | undefined;
+ }
+ | NoBody;
+
+/** [email protected] (owner: events) */
+/**
+ * events.subscription-result@1: answers of the subscription routes on OpenVibe.Events. POST /api/v1/subscriptions → 201 events.subscription@1 with its secret (shown once); GET /api/v1/subscriptions → { subscriptions } (the caller's own, no secrets); GET /api/v1/subscriptions/:id and POST …/disable|enable → events.subscription@1. Another consumer's subscription is 404.
+ */
+export type EventsSubscriptionResult =
+ | EventsSubscription
+ | {
+ subscriptions: EventsSubscription[];
+ };
+
+/** [email protected] (owner: events) */
+/**
+ * events.subscription@1: one push subscription as OpenVibe.Events shows it to its consumer (server/store.js subscriptionView). The signing secret appears only in the answer that creates it (and in a rotation's). A developer app's subscription also names its project and environment.
+ */
+export interface EventsSubscription {
+ id: string;
+ /**
+ * The calling service, or app:app_<ULID>.
+ */
+ consumer: string;
+ topic_pattern: string;
+ endpoint: string;
+ enabled: boolean;
+ retry_policy: {
+ max_attempts?: number;
+ backoff_ms?: number[];
+ } | null;
+ created_at: string;
+ updated_at: string;
+ project_id?: string;
+ env?: string;
+ /**
+ * Only when the subscription is created: shown once.
+ */
+ secret?: string;
+}
+
/** [email protected] (owner: chat) */
/** [email protected] (owner: chat) */
/**
/**
* chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.
* chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.
@@ -8024,6 +8196,337 @@ export interface AiRunCachedPayload {
@@ -8024,6 +8196,337 @@ export interface AiRunCachedPayload {
finished_at: string;
finished_at: string;
}
}
+/** [email protected] (owner: ai) */
+/**
+ * ai.definition-version-request@1: bodies of ai.workflow.manage on OpenVibe.AI. An edit is always a new version (the previous one stays readable); fields left out are taken from the newest existing version. POST /api/v1/templates/:key/versions: a prompt template (input_schema and output_schema must compile as JSON Schema; visibility public, first-party or internal). POST /api/v1/workflows/:key/versions (key <namespace>.<name>): steps (at least one; kind llm (naming an existing template), passthrough, transcribe or embed), schemas, default route, cache mode. POST /api/v1/routes/:key/versions: primary { provider, model } and fallbacks (every provider must exist), or alias_of another route. POST …/:key/versions/:version/status: { status } (templates and workflows draft|active|deprecated|archived; routes active|disabled). Refusals are 422 ai.invalid.
+ */
+export type AiDefinitionVersionRequest =
+ | {
+ name?: string;
+ description?: string | null;
+ /**
+ * A JSON Schema the run input must match.
+ */
+ input_schema?: {};
+ /**
+ * A JSON Schema the output must match.
+ */
+ output_schema?: {};
+ system_prompt?: string;
+ user_prompt?: string;
+ default_route?: string | null;
+ owner?: string;
+ visibility?: "public" | "first-party" | "internal";
+ status?: "draft" | "active" | "deprecated" | "archived";
+ metadata?: {};
+ }
+ | {
+ name?: string;
+ description?: string | null;
+ namespace?: string;
+ /**
+ * A JSON Schema.
+ */
+ input_schema?: {};
+ /**
+ * A JSON Schema.
+ */
+ output_schema?: {};
+ /**
+ * @minItems 1
+ */
+ steps?: [
+ {
+ kind: "llm" | "passthrough" | "transcribe" | "embed";
+ template?: string;
+ },
+ ...{
+ kind: "llm" | "passthrough" | "transcribe" | "embed";
+ template?: string;
+ }[]
+ ];
+ default_route?: string | null;
+ cache_mode?: "none" | "private" | "service";
+ cache_ttl_sec?: number | null;
+ status?: "draft" | "active" | "deprecated" | "archived";
+ metadata?: {};
+ }
+ | {
+ primary?: {
+ provider: string;
+ model?: string | null;
+ };
+ fallbacks?: {
+ provider: string;
+ model?: string | null;
+ }[];
+ options?: {};
+ max_output_tokens?: number | null;
+ response_format?: "text" | "json";
+ timeout_ms?: number | null;
+ alias_of?: string | null;
+ status?: "active" | "disabled";
+ };
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.definition-version-result@1: answers of ai.workflow.manage on OpenVibe.AI. POST …/:key/versions → 201 { template | workflow | route } (the new version, whole: prompts and schemas for a template, steps for a workflow, primary/fallbacks for a route); POST …/versions/:version/status → { template | workflow | route: { key, version, status } }.
+ */
+export type AiDefinitionVersionResult =
+ | {
+ template: {
+ key: string;
+ version: number;
+ status: string;
+ };
+ }
+ | {
+ workflow: {
+ key: string;
+ version: number;
+ status: string;
+ };
+ }
+ | {
+ route: {
+ key: string;
+ version: number;
+ status: string;
+ };
+ };
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.model@1: one model of a provider in OpenVibe.AI's registry (server/registry.js decodeModel): type, status, limits, per-million-token costs and what it supports.
+ */
+export interface AiModel {
+ provider_key: string;
+ model_key: string;
+ display_name?: string | null;
+ type: "chat" | "vision" | "embedding" | "stt";
+ status: "active" | "disabled";
+ context_window?: number | null;
+ max_output?: number | null;
+ cost: {
+ in_per_mtok?: number | null;
+ out_per_mtok?: number | null;
+ cached_per_mtok?: number | null;
+ };
+ supports: {
+ json?: boolean;
+ tools?: boolean;
+ streaming?: boolean;
+ vision?: boolean;
+ };
+ metadata?: {};
+ created_at?: string | null;
+ updated_at?: string | null;
+}
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.provider-manage-request@1: bodies of ai.provider.manage on OpenVibe.AI. POST /api/v1/providers creates or replaces a provider (key required) and PATCH /api/v1/providers/:key changes one (fields left out keep their value); a body carrying api_key or secret is refused (422: send secret_ref env:NAME, never a secret value). POST /api/v1/models (provider_key and model_key required) and PATCH /api/v1/models/:provider/:model upsert a model. POST /api/v1/quotas upserts a quota (scope_type and window required; scope_id too unless global; the same scope, window and workflow_prefix is one quota). POST /api/v1/providers/:key/disable|enable|reset and DELETE /api/v1/cache[?workflow=] take no body. Unknown fields are ignored.
+ */
+export type AiProviderManageRequest =
+ | {
+ key?: string;
+ display_name?: string;
+ kind?: "stub" | "openai" | "anthropic" | "http" | "whisper";
+ status?: "active" | "disabled";
+ base_url?: string | null;
+ auth_mode?: "none" | "bearer" | "x-api-key";
+ secret_ref?: string | null;
+ default_model?: string | null;
+ capabilities?: (
+ | "chat"
+ | "generate"
+ | "summarize"
+ | "classify"
+ | "extract"
+ | "enrich"
+ | "embed"
+ | "vision"
+ | "json"
+ | "transcribe"
+ )[];
+ /**
+ * Clamped to 1000..600000.
+ */
+ timeout_ms?: number | string;
+ priority?: number | string;
+ metadata?: {};
+ }
+ | {
+ provider_key?: string;
+ model_key?: string;
+ display_name?: string;
+ type?: "chat" | "vision" | "embedding" | "stt";
+ status?: "active" | "disabled";
+ context_window?: number | null;
+ max_output?: number | null;
+ cost?: {
+ in_per_mtok?: number | null;
+ out_per_mtok?: number | null;
+ cached_per_mtok?: number | null;
+ };
+ supports?: {
+ json?: boolean;
+ tools?: boolean;
+ streaming?: boolean;
+ vision?: boolean;
+ };
+ metadata?: {};
+ }
+ | {
+ scope_type: "global" | "service" | "actor" | "attribution";
+ /**
+ * * on a non-global scope = each one separately.
+ */
+ scope_id?: string;
+ window: "minute" | "hour" | "day";
+ max_requests?: number | string | null;
+ max_tokens?: number | string | null;
+ max_cost_usd?: number | string | null;
+ workflow_prefix?: string | null;
+ status?: "active" | "disabled";
+ }
+ | NoBody;
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.provider-manage-result@1: answers of ai.provider.manage on OpenVibe.AI. Provider create/update/disable/enable → { provider } (201 on POST); POST …/reset → { health } (the circuit breaker, closed again); model upserts → { model }; POST /api/v1/quotas → 201 { quota } (the stored row); DELETE /api/v1/cache → { removed } (entries purged). Every change is audited.
+ */
+export type AiProviderManageResult =
+ | {
+ provider: AiProvider;
+ }
+ | {
+ health: {
+ provider_key?: string;
+ state: "closed" | "open" | "half_open";
+ consecutive_failures?: number;
+ };
+ }
+ | {
+ model: AiModel;
+ }
+ | {
+ quota: {
+ id: number;
+ scope_type: string;
+ scope_id: string;
+ window: string;
+ max_requests?: number | null;
+ max_tokens?: number | null;
+ max_cost_usd?: number | null;
+ workflow_prefix?: string | null;
+ status: string;
+ };
+ }
+ | {
+ removed: number;
+ };
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.provider@1: one model provider as OpenVibe.AI's registry API shows it (server/registry.js publicProvider): never a secret value, only the secret reference name (env:NAME) and whether it resolves (credentials), plus the circuit-breaker health.
+ */
+export interface AiProvider {
+ key: string;
+ display_name?: string | null;
+ kind: "stub" | "openai" | "anthropic" | "http" | "whisper";
+ status: "active" | "disabled";
+ base_url?: string | null;
+ auth_mode: "none" | "bearer" | "x-api-key";
+ /**
+ * env:NAME, a reference; never the secret.
+ */
+ secret_ref?: string | null;
+ default_model?: string | null;
+ capabilities: (
+ "chat" | "generate" | "summarize" | "classify" | "extract" | "enrich" | "embed" | "vision" | "json" | "transcribe"
+ )[];
+ timeout_ms?: number | null;
+ priority?: number | null;
+ metadata?: {};
+ origin?: string | null;
+ created_at?: string | null;
+ updated_at?: string | null;
+ credentials: "not_required" | "configured" | "missing";
+ health?: {
+ provider_key?: string;
+ state?: "closed" | "open" | "half_open";
+ consecutive_failures?: number;
+ } | null;
+}
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.usage-result@1: answers of ai.usage.read on OpenVibe.AI (all GETs, parameters in the query). GET /api/v1/status → { providers, runs, today, workflows, templates, routes, inflight, quotas }; GET /api/v1/usage?from=&to=&requester= → { usage } (daily rows); GET /api/v1/quotas → { quotas, current }; GET /api/v1/requests?provider=&status=&run=&fallback=&limit= → { requests } (request-log metadata: hashes, tokens, cost, latency; raw debug fields only for provider managers who ask); GET /api/v1/audit → { audit }; GET /api/v1/cache → { cache }; GET /api/v1/providers → { providers }; GET /api/v1/models?provider= → { models }. Never raw prompts, never secrets.
+ */
+export type AiUsageResult =
+ | {
+ providers: {
+ key: string;
+ kind?: string;
+ status?: string;
+ credentials?: string;
+ health?: string;
+ }[];
+ /**
+ * Run counts by status.
+ */
+ runs: {
+ [k: string]: number | undefined;
+ };
+ today: {
+ day: string;
+ requests?: number;
+ cost_usd?: number;
+ };
+ workflows?: number;
+ templates?: number;
+ routes?: number;
+ inflight?: number;
+ quotas?: unknown[];
+ }
+ | {
+ providers: AiProvider[];
+ }
+ | {
+ models: AiModel[];
+ }
+ | {
+ quotas: {}[];
+ current: {
+ quota?: {};
+ scope_id?: string;
+ window_start?: string;
+ resets_in_seconds?: number;
+ used?: {};
+ }[];
+ }
+ | {
+ usage: {}[];
+ }
+ | {
+ /**
+ * @maxItems 500
+ */
+ requests: {}[];
+ }
+ | {
+ audit: {}[];
+ }
+ | {
+ cache: {
+ workflow_key?: string;
+ privacy?: string;
+ entries?: number;
+ hits?: number | null;
+ }[];
+ };
+
/** [email protected] (owner: tips) */
/** [email protected] (owner: tips) */
/**
/**
* tips.interaction.ready v1 (OpenVibe.Tips server/domain/interactions.js settle, summary()). The interaction settled: it is active and its effects (overlay alert, chat line, paid message, TTS, media request) are queued. Envelope: subject { type: interaction, id: tint_… }, visibility internal, priority important, actor service:tips. Never emitted for tests or imports.
* tips.interaction.ready v1 (OpenVibe.Tips server/domain/interactions.js settle, summary()). The interaction settled: it is active and its effects (overlay alert, chat line, paid message, TTS, media request) are queued. Envelope: subject { type: interaction, id: tint_… }, visibility internal, priority important, actor service:tips. Never emitted for tests or imports.
@@ -23,5 +23,7 @@
@@ -23,5 +23,7 @@
"PATCH /api/v1/models/:provider/:model",
"PATCH /api/v1/models/:provider/:model",
"POST /api/v1/quotas",
"POST /api/v1/quotas",
"DELETE /api/v1/cache"
"DELETE /api/v1/cache"
- ]
+ ],
+ "inputSchema": "ai.provider-manage-request@1",
+ "outputSchema": "ai.provider-manage-result@1"
}
}
@@ -18,5 +18,6 @@
@@ -18,5 +18,6 @@
"GET /api/v1/runs",
"GET /api/v1/runs",
"GET /api/v1/runs/:id",
"GET /api/v1/runs/:id",
"GET /api/v1/runs/:id/citations"
"GET /api/v1/runs/:id/citations"
- ]
+ ],
+ "inputSchema": "common.no-body@1"
}
}
@@ -22,5 +22,7 @@
@@ -22,5 +22,7 @@
"GET /api/v1/cache",
"GET /api/v1/cache",
"GET /api/v1/providers",
"GET /api/v1/providers",
"GET /api/v1/models"
"GET /api/v1/models"
- ]
+ ],
+ "inputSchema": "common.no-body@1",
+ "outputSchema": "ai.usage-result@1"
}
}
@@ -20,5 +20,7 @@
@@ -20,5 +20,7 @@
"POST /api/v1/workflows/:key/versions/:version/status",
"POST /api/v1/workflows/:key/versions/:version/status",
"POST /api/v1/routes/:key/versions",
"POST /api/v1/routes/:key/versions",
"POST /api/v1/routes/:key/versions/:version/status"
"POST /api/v1/routes/:key/versions/:version/status"
- ]
+ ],
+ "inputSchema": "ai.definition-version-request@1",
+ "outputSchema": "ai.definition-version-result@1"
}
}
@@ -16,5 +16,6 @@
@@ -16,5 +16,6 @@
"events": [],
"events": [],
"implementedBy": [
"implementedBy": [
"POST /api/v1/events"
"POST /api/v1/events"
- ]
+ ],
+ "outputSchema": "events.publish-result@1"
}
}
@@ -18,5 +18,7 @@
@@ -18,5 +18,7 @@
"GET /api/v1/events/:event_id",
"GET /api/v1/events/:event_id",
"GET /api/v1/checkpoints",
"GET /api/v1/checkpoints",
"PUT /api/v1/checkpoints"
"PUT /api/v1/checkpoints"
- ]
+ ],
+ "inputSchema": "events.read-request@1",
+ "outputSchema": "events.read-result@1"
}
}
@@ -19,5 +19,7 @@
@@ -19,5 +19,7 @@
"GET /api/v1/subscriptions/:id",
"GET /api/v1/subscriptions/:id",
"POST /api/v1/subscriptions/:id/disable",
"POST /api/v1/subscriptions/:id/disable",
"POST /api/v1/subscriptions/:id/enable"
"POST /api/v1/subscriptions/:id/enable"
- ]
+ ],
+ "inputSchema": "events.subscription-request@1",
+ "outputSchema": "events.subscription-result@1"
}
}
@@ -16,5 +16,7 @@
@@ -16,5 +16,7 @@
"implementedBy": [
"implementedBy": [
"GET /api/v1/deliveries",
"GET /api/v1/deliveries",
"POST /api/v1/deliveries/replay"
"POST /api/v1/deliveries/replay"
- ]
+ ],
+ "inputSchema": "events.delivery-admin-request@1",
+ "outputSchema": "events.delivery-admin-result@1"
}
}
@@ -16,5 +16,6 @@
@@ -16,5 +16,6 @@
"events": [],
"events": [],
"implementedBy": [
"implementedBy": [
"POST /api/v1/events"
"POST /api/v1/events"
- ]
+ ],
+ "outputSchema": "events.publish-result@1"
}
}
@@ -20,5 +20,6 @@
@@ -20,5 +20,6 @@
"GET /api/v1/checkpoints",
"GET /api/v1/checkpoints",
"PUT /api/v1/checkpoints",
"PUT /api/v1/checkpoints",
"GET /realtime/stream"
"GET /realtime/stream"
- ]
+ ],
+ "inputSchema": "events.read-request@1"
}
}
@@ -19,5 +19,7 @@
@@ -19,5 +19,7 @@
"GET /api/v1/subscriptions/:id",
"GET /api/v1/subscriptions/:id",
"POST /api/v1/subscriptions/:id/disable",
"POST /api/v1/subscriptions/:id/disable",
"POST /api/v1/subscriptions/:id/enable"
"POST /api/v1/subscriptions/:id/enable"
- ]
+ ],
+ "inputSchema": "events.subscription-request@1",
+ "outputSchema": "events.subscription-result@1"
}
}