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

FieldTypeRequiredDescriptionConstraints
idstringyesMinted 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.
  • pattern ^tsk_[0-9A-HJKMNP-TV-Z]{26}$
project_idstringyesThe project the caller's token names (ADR-034 §5): budgets, usage and the bill are the project's.
  • pattern ^prj_[0-9A-HJKMNP-TV-Z]{26}$
requesteranyyesThe 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).
taskstringyesThe 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.
  • minLength 1
  • maxLength 100000
modeenumyesThe 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.
  • one of "cheapest", "balanced", "best", "fastest", "private"
  • default "balanced"
budgetobjectyesHard 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.
  • no other fields
budget.per_task_usdnumberyes
  • minimum 0
budget.per_day_usdnumberyes
  • minimum 0
stateenumyesqueued: 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.
  • one of "queued", "running", "verifying", "succeeded", "failed", "cancelled"
created_atstringyes
  • format date-time
finished_atstring | nullWhen the task reached its end state; null before.
  • format date-time
resultanyThe 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.
costone ofPresent 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).
explanationone ofOne 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.
progressone ofStreaming progress (plan T17). Null before the router opens a stream and after the end.
cancelone ofCancellation (plan T17): null until a cancel is requested, then it records who asked and when; the task's outcome is `state`.
webhooksarray of objectWebhooks (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.
  • maxItems 8
  • items: no other fields
webhooks[].urlstringyesThe HTTPS endpoint the router POSTs a state change to. A plain-HTTP URL is refused.
  • pattern ^https://
  • format uri
webhooks[].eventsarray of enumyesThe state changes delivered; the same names as `state`.
  • minItems 1
  • items: one of "queued", "running", "verifying", "succeeded", "failed", "cancelled"
webhooks[].secret_refstringThe 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.
  • pattern ^[A-Z0-9_]+$

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 }] }