AgentsRun agents.run@1

Generated at from openvibe-contracts v0.419.0 and openvibe-sdk v0.161.0.

Version
1.3.0
Owner
actor
Visibility
public
Status
active
Compatibility
backward
Decision
ADR-044
Schema
https://openvibe.network/contracts/agents/run.v1.json

agents.run@1 (one agent system, 2026-11-12): one run of an agent, the same shape on every product (Actor, Codes, Run and anything else on openvibe-sdk/agents). The owner is the person or agent the work belongs to; AGENT names the agent or harness that runs it and the product it comes from; PARENT_RUN makes sub-agent runs a tree; BUDGET is the hard limit and COST what the run has spent; STATUS is where the run is now. The run is stored by its product, not here: this contract is the wire shape. GET /api/v1/runs, /api/v1/runs/{id} and /api/v1/runs/{id}/steps serve it (OpenVibe.Actor) and the SDK's runs client reads it. The product's own id is kept, never reused. A run that ends keeps its end status. Since 1.1.0 (agent-engine.md) a run also says what a person calls it (TITLE), what sort it is (KIND), where it lives (REF on the Frame, URL on its product), which model runs it and where, what it is doing now (DOING), what it waits for (WAITING), its last line (PREVIEW), where it was asked (CONTEXT) and what it made (OUTPUTS); a chat with Vibe is a run of KIND chat that is waiting for a message. Every added field is optional. This v1 contract may grow optional fields; a step is agents.step@1 and an approval is agents.approval@1. Since 1.2.0 (agent-engine.md §§3-5, from the code study) a run may also say why it exists (MANDATE: an ask taken in from the feed, a room, an issue), what it continues (LINEAGE: a fork, a hand-off, a regenerated turn), the play its tree follows and a child's ROLE in it, what its agent's harness can do (AGENT.CAPABILITIES), and what it spent on each rung of the ladder (COST.BY). Every added field is optional.

Fields

FieldTypeRequiredDescriptionConstraints
idstringyesThe product's own run id (Actor's tsk_…, Codes' tsk_…, a Run job's job_…). Unique within its product and never reused.
  • minLength 1
  • maxLength 160
owneranyyesThe person or agent the run belongs to: the subject the work is done for.
agentobjectyesThe agent or harness that runs it. ID is the product's own agent id or slug; NAME is what a person reads; PRODUCT is the product it comes from (actor, codes, run, chat, …). GLYPH, HUE and AVATAR_URL are its face; HARNESS is the loop that runs it. Since 1.2.0, CAPABILITIES says what its harness can do. Since 1.3.0, BLUEPRINT is the blueprint version it ran.
  • no other fields
agent.idstringyes
  • minLength 1
  • maxLength 160
agent.namestring
  • minLength 1
  • maxLength 160
agent.productstring
  • minLength 1
  • maxLength 40
agent.glyphstringThe agent's face when it has no picture: a glyph from the registry. A chat's persona sets it per run.
  • pattern ^[a-z][a-z0-9-]{0,40}$
agent.hueintegerThe hue its face is tinted with.
  • minimum 0
  • maximum 359
agent.avatar_urlstringThe agent's picture, when it has one.
  • format uri
  • maxLength 2000
agent.harnessstringThe loop that runs it: openvibe (the SDK's own), or another harness run through an adapter (codex, claude-code, opencode, deepseek, …).
  • pattern ^[a-z][a-z0-9-]{0,59}$
agent.capabilitiesarray of enumWhat the agent's harness can do, so the Dispatcher chooses by capability and never by name: resume and fork a thread, ask approvals, read images, start sub-agents, use desks, browse, search the web, use MCP tools, plan before acting, read and write files, run a shell, speak and listen.
  • maxItems 20
  • unique items
  • items: one of "resume", "fork", "approvals", "images", "subagents", "desks", "browser", "web-search", "mcp", "plan-mode", "files", "shell", "voice"
agent.blueprintstringSince 1.3.0: the blueprint version this agent ran (agents.blueprint@1), when it was made from one.
  • pattern ^bp_[A-Za-z0-9_-]{3,40}@[1-9][0-9]{0,5}$
parent_runstring | nullThe run that started this one (a sub-agent), or null. The parent's run card nests children; the child's budget is a slice of the parent's.
  • maxLength 160
budgetobjectThe hard limit the run may not exceed. A field that is absent is not limited. USD is money; TOKENS counts model tokens in and out; SECONDS is wall time; STEPS counts the appended steps.
  • no other fields
budget.usdnumber
  • minimum 0
budget.tokensinteger
  • minimum 0
budget.secondsnumber
  • minimum 0
budget.stepsinteger
  • minimum 1
statusenumyesplanning: accepted, no step yet. running: a step is under way. waiting: the run waits for its person (an approval or a takeover) and costs nothing. done, failed and stopped are the end statuses; an end status never changes.
  • one of "planning", "running", "waiting", "done", "failed", "stopped"
created_atstringyes
  • format date-time
updated_atstringyesWhen the run last changed: its newest step, its end, or its creation when nothing has happened yet.
  • format date-time
result_summarystring | nullWhat the run answered or what is left, in plain words, for a run card. The full result stays the product's (Actor's result, Codes' result.summary). Null until there is one.
  • maxLength 8000
costobject | nullWhat the run has spent so far, to the product's own precision. Null before anything is known.
titlestringWhat a person calls this run: a chat's name, a task's goal in a few words.
  • minLength 1
  • maxLength 200
kindstringWhat sort of run it is. chat: a conversation that stays open. ask: one question and its answer. task: work an agent does for its person. code: a coding task. machine: a job on a computer or a robot. goal: a standing goal that runs on its own schedule. A product may add its own; a reader treats an unknown kind as task.
  • pattern ^[a-z][a-z0-9-]{0,30}$
refstringThe run's address on the Frame: its product, a colon, the product's own id (chat:…, codes:tsk_…, actor:tsk_…). /api/frame/runs/{ref} reads it wherever it lives.
  • pattern ^[a-z][a-z0-9-]{0,30}:[A-Za-z0-9._~-]{1,160}$
  • maxLength 200
urlstringThe run's own page on its product.
  • format uri
  • maxLength 2000
modelobjectThe model that runs it, as the picker chose it (frame.choices@1's choice, with the name a person reads). EFFORT is the reasoning effort where the model has one.
  • no other fields
model.idstringyes
  • pattern ^[A-Za-z0-9._:/@+| -]+$
  • minLength 1
  • maxLength 200
model.sourceenumopenvibe: Exchange's directory, paid in credits. keys: the person's own key. machines: a model on the person's own computer (Link). plans: a signed-in plan through a harness.
  • one of "openvibe", "keys", "machines", "plans"
model.machinestring
  • maxLength 40
model.namestring
  • minLength 1
  • maxLength 200
model.effortstring
  • minLength 1
  • maxLength 40
whereobjectWhere the run's work happens. openvibe: OpenVibe's own computers. machine: the person's own computer or robot through Link (ID is the device). runner: a Codes runner. this-computer: the machine that serves the run itself (Link's agent serve).
  • no other fields
where.kindenumyes
  • one of "openvibe", "machine", "runner", "this-computer"
where.idstring
  • minLength 1
  • maxLength 160
where.namestring
  • minLength 1
  • maxLength 200
targetone ofWhat a code run works on. new: a new project in the person's own space. openvibe: OpenVibe itself; the orchestrator finds the repositories and opens pull requests, never merges. An object names one repository.
doingobject | nullWhat the run is doing now, for its agent's face: a few words and a glyph. Null when it is not working.
waitingobject | nullWhy a waiting run waits. message: it is the person's turn to speak (a conversation between turns). approval: it asked before an act (APPROVAL is the agents.approval@1 id). takeover: it met something only its person can do (a sign-in, a code). payment: it needs funds. WHAT says it in words. Null unless STATUS is waiting.
previewobject | nullThe last thing said in the run, for a list: the words, who said them and when.
contextobjectWhere the run was asked: the site, the page and what the page is about. A run may use it; it never grants access.
  • no other fields
context.sitestring
  • minLength 1
  • maxLength 60
context.urlstringThe page's address.
  • format uri
  • maxLength 2000
context.titlestring
  • maxLength 300
context.kindstring
  • maxLength 60
context.idstring
  • maxLength 160
outputsarray of objectWhat the run made, newest last: for its card, and for the feed when it is shared.
  • maxItems 50
  • items: no other fields
outputs[].kindenumyes
  • one of "link", "file", "image", "video", "audio", "pr", "repo", "page", "post", "run"
outputs[].titlestringyes
  • minLength 1
  • maxLength 300
outputs[].urlstringWhere it is.
  • format uri
  • maxLength 2000
outputs[].thumbstringA small picture of it.
  • format uri
  • maxLength 2000
outputs[].media_idstring
  • maxLength 60
outputs[].statestringA pull request's or a check's state in a word (open, green, red, merged).
  • maxLength 40
countsobjectCounts a list shows without reading the steps.
  • no other fields
counts.stepsinteger
  • minimum 0
counts.childreninteger
  • minimum 0
counts.filesinteger
  • minimum 0
counts.approvalsinteger
  • minimum 0
counts.unreadinteger
  • minimum 0
visibilityenumWho may read the run: its owner only, people it was shared with, or anyone with its address.
  • one of "private", "shared", "public"
ended_atstring | nullWhen the run reached an end status. Null while it has not.
  • format date-time
mandateobject | nullWhy the run exists when it came through intake (agent-engine.md §5): SOURCE is where the ask was made; THREAD is the conversation it was made in (a post, a room, an issue: its service, kind and id, and its address), where the run answers and from which later messages reach it; ASKED_BY is who asked; STRENGTH says whether they asked outright (explicit) or the Source's rule read an ask in what they wrote (inferred: such a run confirms before it acts); SCOPE says in words what the run may do for them. Null (or absent) for a run a person started themselves.
lineageobject | nullWhere the run came from when it continues another (agent-engine.md, from the code study): FROM is that run's ref; AT_TURN is the turn it was taken from (agents.step@1's TURN; the steps before it are the new run's history); HOW is fork (a person branched the conversation), handoff (another harness or model took it on, with a written summary as its first step) or regenerate (the same turn answered again). Null for a run that starts fresh.
playstringThe play (agents.play@1's ID) this run's tree follows: solo, advisor, plan-execute, fan-out, team, grader-loop, research-build, issue-to-pr, watch, or a person's own. On a child run it is its parent's.
  • pattern ^[a-z][a-z0-9-]{0,60}$
rolestringA child run's role in its parent's play (one of the play's role ids: planner, executor, advisor, grader, reviewer, searcher, worker…).
  • pattern ^[a-z][a-z0-9-]{0,60}$

Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

Valid: a-fork-at-turn-three
{
  "id": "c_8f3k2mx",
  "ref": "chat:c_8f3k2mx",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z",
  "lineage": {
    "from": "chat:c_8f3k2m",
    "at_turn": 3,
    "how": "fork"
  }
}
Valid: a-reviewer-in-a-play
{
  "id": "tsk_01K9R4C5D6E7F8G9H0J1K2M3NP",
  "ref": "codes:tsk_01K9R4C5D6E7F8G9H0J1K2M3NP",
  "kind": "code",
  "title": "Review: a dark mode toggle",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex"
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [],
  "counts": {
    "steps": 6
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "parent_run": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "play": "issue-to-pr",
  "role": "reviewer"
}
Valid: an-agent-from-a-blueprint
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "agt_01K9R2V6M8Q4T0B7C3D5E9F2GH",
    "name": "Vibe",
    "product": "actor",
    "blueprint": "bp_01JD8C4QJ7Z3K9W2X5V6N1M0PA@3"
  },
  "parent_run": null,
  "budget": {
    "usd": 2.5,
    "tokens": 200000,
    "seconds": 600,
    "steps": 40
  },
  "status": "running",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:00:12Z",
  "result_summary": null,
  "cost": {
    "usd": 0.031,
    "tokens": 4210,
    "seconds": 12,
    "steps": 3
  }
}
Valid: an-ask-from-the-feed
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "resume",
      "approvals",
      "subagents",
      "files",
      "shell",
      "mcp"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "asked_by": {
      "type": "user",
      "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
Valid: chat-waiting-for-a-message
{
  "id": "c_8f3k2m",
  "ref": "chat:c_8f3k2m",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z"
}
Valid: code-on-openvibe
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A goal bar for my sub goal",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex"
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null
}
Valid: done
{
  "id": "job_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "codes",
    "name": "Claude Code",
    "product": "codes"
  },
  "parent_run": null,
  "status": "done",
  "created_at": "2026-11-12T09:00:00Z",
  "updated_at": "2026-11-12T09:04:30Z",
  "result_summary": "Opened a pull request with the two fixes.",
  "cost": {
    "usd": 0.42,
    "tokens": 88000,
    "seconds": 270,
    "steps": 18
  }
}
Valid: running
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "agt_01K9R2V6M8Q4T0B7C3D5E9F2GH",
    "name": "Vibe",
    "product": "actor"
  },
  "parent_run": null,
  "budget": {
    "usd": 2.5,
    "tokens": 200000,
    "seconds": 600,
    "steps": 40
  },
  "status": "running",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:00:12Z",
  "result_summary": null,
  "cost": {
    "usd": 0.031,
    "tokens": 4210,
    "seconds": 12,
    "steps": 3
  }
}
Rejected: a-capability-nobody-has
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "files",
      "telepathy"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "asked_by": {
      "type": "user",
      "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
Rejected: a-lineage-from-no-run
{
  "id": "c_8f3k2mx",
  "ref": "chat:c_8f3k2mx",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z",
  "lineage": {
    "from": "not a run",
    "at_turn": 3,
    "how": "fork"
  }
}
Rejected: a-mandate-without-who-asked
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "resume",
      "approvals",
      "subagents",
      "files",
      "shell",
      "mcp"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
Rejected: a-rung-the-ladder-has-not
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "resume",
      "approvals",
      "subagents",
      "files",
      "shell",
      "mcp"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      },
      "gift": {
        "usd": 1
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "asked_by": {
      "type": "user",
      "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
Rejected: ref-without-a-product
{
  "id": "c_8f3k2m",
  "ref": "c_8f3k2m",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z"
}
Rejected: unknown-status
{
  "id": "run_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "actor"
  },
  "status": "queued",
  "created_at": "2026-11-12T09:00:00Z",
  "updated_at": "2026-11-12T09:00:00Z"
}
Rejected: waiting-for-nothing-known
{
  "id": "c_8f3k2m",
  "ref": "chat:c_8f3k2m",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "luck"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z"
}

Validate

const contracts = require('openvibe-contracts');
contracts.validate('agents.run@1', value);   // { valid, errors: [{ path, message }] }