Task platform.task@1
Generated at from
openvibe-contracts v0.107.0 and
openvibe-sdk v0.26.0.
- Version
- 1.0.0
- Owner
- network
- Visibility
- public
- Status
- planned
- Compatibility
- backward
- Decision
- ADR-044
- Schema
https://openvibe.network/contracts/platform/task.v1.json
platform.task@1 (PLANNED; plan T17, ADR-044): one task at OpenVibe.Actor's task router. POST /v1/tasks creates it from a plain-language `task` plus a `mode` and a `budget`; the router mints `id`, sets `state` and `created_at`, picks a backend from the registered adapters, runs the task, verifies the result, escalates or fails over, and answers with `result`, `cost` and `explanation`. GET /v1/tasks/{id} reads the same resource. Progress streams as server-sent events from `progress.stream_url` (a task's own counter in `progress.last_seq`, as platform.job-frame@1 counts on a link); `cancel` records that a cancel was requested (POST /v1/tasks/{id}/cancel), and `webhooks` are the registered endpoints the router delivers each state change to. No Actor service exists yet: OpenVibe.Actor is a product page (manifests/products/openvibe.actor.json, noRepo) with no service manifest, so the contract is owned by `network`, like every other platform.* contract, and stays PLANNED until T17 ships. This v1 contract may grow optional fields.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
id | string | yes | Minted by the router (tsk_<ULID>): the idempotency key of the task and the stem of its usage readings, as platform.job@1's id is for a job. Never reused. |
|
project_id | string | yes | The project the caller's token names (ADR-034 §5): budgets, usage and the bill are the project's. |
|
requester | any | yes | The person or agent the task belongs to: a user subject for a person, an agent subject for an agent acting under the grants its owner delegated (roadmap WS-Z2). | |
task | string | yes | The instruction in plain words, exactly as the caller gave it. Not a backend command and not a model prompt: the planner turns it into one. |
|
mode | enum | yes | The plan's five modes, in contract spelling: Cheapest, Balanced (the default), Best, Fastest and Private are `cheapest`, `balanced`, `best`, `fastest` and `private`. A mode is the planner objective, never a fixed backend list (plan T17). `cheapest`: the lowest expected cost that still meets the task class's quality bar. `balanced` (the default): cost, quality and speed weighed together. `best`: the highest chance of a right answer first time. `fastest`: the shortest time to a checked result. `private`: only backends that keep the data on OpenVibe (first-party, and a user-owned node only when the task names it), per ADR-046. |
|
budget | object | yes | Hard limits the router never exceeds (plan T17: 'a hard limit per task and per day'). A task whose cheapest eligible backend would take it over `per_task_usd` ends failed (actor.budget.exceeded) rather than running; `per_day_usd` caps the project's tasks in one UTC day and a new task waits or fails, it never overruns. |
|
budget.per_task_usd | number | yes |
| |
budget.per_day_usd | number | yes |
| |
state | enum | yes | queued: stored, waiting for the planner. running: a backend is working, or a cascade is between attempts. verifying: a result exists and a check (the task's own tests, a citation check, a postcondition) runs before it is delivered. succeeded, failed and cancelled are the end states; an end state never changes. |
|
created_at | string | yes |
| |
finished_at | string | null | When the task reached its end state; null before. |
| |
result | any | The checked deliverable (any JSON, as a platform.job@1 `result` is): for a code task a diff or branch reference, for research a brief with citations, for an action a postcondition report. Never a credential and never raw sandbox output. Null (or absent) in every state but succeeded, which the allOf below enforces. | ||
cost | one of | Present once the task has run an attempt (also when it ends failed or cancelled); null while queued. One key, one bill (plan T17), through Billing (ADR-012). | ||
explanation | one of | One platform.placement-result@1 per backend the planner tried, oldest first, an escalation appending a new one: which backend won, why and what else was considered, from openvibe-sdk/placement (ADR-046 §1). Null while queued. | ||
progress | one of | Streaming progress (plan T17). Null before the router opens a stream and after the end. | ||
cancel | one of | Cancellation (plan T17): null until a cancel is requested, then it records who asked and when; the task's outcome is `state`. | ||
webhooks | array of object | Webhooks (plan T17), registered at creation. A delivery that fails is retried with backoff and never delays the task; the task's own record is the source of truth. |
| |
webhooks[].url | string | yes | The HTTPS endpoint the router POSTs a state change to. A plain-HTTP URL is refused. |
|
webhooks[].events | array of enum | yes | The state changes delivered; the same names as `state`. |
|
webhooks[].secret_ref | string | The NAME of the project secret used to sign the delivery (an HMAC header), never the secret itself, as community.moderation-request@1 names a webhook_url_ref and ADR-043 keeps device credentials by reference. |
|
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: cancelled
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPT",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Draft a launch announcement for the new pricing page.",
"mode": "balanced",
"budget": {
"per_task_usd": 2,
"per_day_usd": 20
},
"state": "cancelled",
"created_at": "2026-10-05T09:00:00.000Z",
"finished_at": "2026-10-05T09:02:30.000Z",
"result": null,
"cost": {
"usd": 0.01
},
"explanation": [
{
"selected": "hosted-agent",
"objective": "balanced",
"reasons": [
"writing task; first attempt still running when the caller cancelled"
],
"candidates": [
{
"id": "hosted-agent",
"eligible": true,
"estimated_cost_usd": 0.31
}
],
"decided_at": "2026-10-05T09:00:01.000Z"
}
],
"cancel": {
"requested_at": "2026-10-05T09:02:29.000Z",
"requested_by": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
}
},
"progress": null
}Valid: queued
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Research five competitors of my shop and write a one-page brief with sources.",
"mode": "balanced",
"budget": {
"per_task_usd": 5,
"per_day_usd": 50
},
"state": "queued",
"created_at": "2026-10-05T09:00:00.000Z",
"webhooks": [
{
"url": "https://example.com/hooks/actor",
"events": [
"succeeded",
"failed"
],
"secret_ref": "ACTOR_WEBHOOK_SECRET"
}
]
}Valid: running
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPR",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Fix the failing thumbnail test in OpenVibe.Media and open a PR.",
"mode": "best",
"budget": {
"per_task_usd": 5,
"per_day_usd": 50
},
"state": "running",
"created_at": "2026-10-05T09:00:00.000Z",
"progress": {
"stream_url": "https://openvibe.actor/v1/tasks/tsk_01JAB2C3D4E5F6G7H8J9K0MNPR/events",
"last_seq": 12,
"events": [
"output",
"state",
"end"
]
},
"explanation": [
{
"selected": "openvibe-runtime",
"objective": "correctness",
"reasons": [
"the task is code; the Codes adapter exposes a code harness"
],
"candidates": [
{
"id": "openvibe-runtime",
"eligible": true,
"estimated_cost_usd": 0.06
}
],
"decided_at": "2026-10-05T09:00:01.000Z"
}
]
}Valid: succeeded
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "agent",
"id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPT"
},
"task": "Check this week's outage report and summarize the causes.",
"mode": "cheapest",
"budget": {
"per_task_usd": 1,
"per_day_usd": 10
},
"state": "succeeded",
"created_at": "2026-10-05T09:00:00.000Z",
"finished_at": "2026-10-05T09:06:00.000Z",
"result": {
"brief": "Two outages; both were a saturated connection pool.",
"citations": [
"https://openvibe.status/incidents/41"
]
},
"cost": {
"usd": 0.06,
"free_allowance_used": 0.06
},
"explanation": [
{
"selected": "open-model",
"objective": "cheapest",
"reasons": [
"research task; the checks are citations, met by the cheapest candidate"
],
"candidates": [
{
"id": "open-model",
"eligible": true,
"estimated_cost_usd": 0.06
}
],
"decided_at": "2026-10-05T09:00:01.000Z"
}
],
"cancel": null,
"progress": null
}Rejected: budget-missing-per-day
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Summarize the release notes.",
"mode": "balanced",
"budget": {
"per_task_usd": 5
},
"state": "queued",
"created_at": "2026-10-05T09:00:00.000Z"
}Rejected: cancelled-without-cancel
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPT",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Draft a launch announcement for the new pricing page.",
"mode": "balanced",
"budget": {
"per_task_usd": 2,
"per_day_usd": 20
},
"state": "cancelled",
"created_at": "2026-10-05T09:00:00.000Z",
"finished_at": "2026-10-05T09:02:30.000Z",
"result": null,
"cost": {
"usd": 0.01
}
}Rejected: failed-with-result
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Summarize the release notes.",
"mode": "balanced",
"budget": {
"per_task_usd": 5,
"per_day_usd": 50
},
"state": "failed",
"created_at": "2026-10-05T09:00:00.000Z",
"finished_at": "2026-10-05T09:01:00.000Z",
"result": {
"partial": "a half-written brief"
},
"cost": {
"usd": 0.02
}
}Rejected: missing-task
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"mode": "balanced",
"budget": {
"per_task_usd": 5,
"per_day_usd": 50
},
"state": "queued",
"created_at": "2026-10-05T09:00:00.000Z"
}Rejected: negative-budget
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Summarize the release notes.",
"mode": "balanced",
"budget": {
"per_task_usd": -1,
"per_day_usd": 50
},
"state": "queued",
"created_at": "2026-10-05T09:00:00.000Z"
}Rejected: plain-http-webhook
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Summarize the release notes.",
"mode": "balanced",
"budget": {
"per_task_usd": 5,
"per_day_usd": 50
},
"state": "queued",
"created_at": "2026-10-05T09:00:00.000Z",
"webhooks": [
{
"url": "http://example.com/hooks/actor",
"events": [
"succeeded"
]
}
]
}Rejected: succeeded-without-result
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "agent",
"id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPT"
},
"task": "Check this week's outage report and summarize the causes.",
"mode": "cheapest",
"budget": {
"per_task_usd": 1,
"per_day_usd": 10
},
"state": "succeeded",
"created_at": "2026-10-05T09:00:00.000Z",
"finished_at": "2026-10-05T09:06:00.000Z",
"cost": {
"usd": 0.06
},
"explanation": [
{
"selected": "open-model",
"objective": "cheapest",
"candidates": [
{
"id": "open-model",
"eligible": true
}
],
"decided_at": "2026-10-05T09:00:01.000Z"
}
]
}Rejected: unknown-mode
{
"id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPR",
"requester": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
},
"task": "Summarize the release notes.",
"mode": "smart",
"budget": {
"per_task_usd": 5,
"per_day_usd": 50
},
"state": "queued",
"created_at": "2026-10-05T09:00:00.000Z"
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('platform.task@1', value); // { valid, errors: [{ path, message }] }