AgentsStep agents.step@1

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

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

agents.step@1 (one agent system, 2026-11-12): one entry of a run's append-only step log, as GET /api/v1/runs/{id}/steps returns it and as agents.step.appended@1 publishes it. SEQ is the run's own order (1, 2, …), never renumbered; a reader pages with ?after=SEQ. KIND says what the entry is: think (a plan, a check, a note to self), tool (a tool call or its result), message (words for the person), approval (the run asked), child (a sub-agent or a hand-off) or file (a file the run made). TEXT is what a person reads; TOOL_CALL is the tool's name and input on a tool entry; RESULT is what came back. The product's own event may split a call and its result into two steps: both carry KIND tool and the call's seq is the earlier one. A step carries no credential, no key and no token; a secret a tool saw is redacted before it is stored. Since 1.1.0 (agent-engine.md) a step also says who spoke (FROM: the person's own words are steps too), whether it is still under way (STATUS), a short TITLE and GLYPH for a tool or a thought, the files that go with it (ATTACHMENTS), numbered SOURCES, a rich card (UI) and, where a computer was used, the DESK picture and action for replay. Every added field is optional. Since 1.2.0 a step says the TURN it belongs to.

Fields

FieldTypeRequiredDescriptionConstraints
run_idstringyesThe run this step belongs to (agents.run@1's id).
  • minLength 1
  • maxLength 160
seqintegeryesThe run's own order for this entry, starting at 1 and never renumbered. ?after=SEQ reads the entries after it.
  • minimum 1
kindenumyesthink: a plan, a check or reasoning. tool: a tool call or its result. message: words for the person. approval: the run asked its person. child: a sub-agent run or a hand-off. file: a file the run made.
  • one of "think", "tool", "message", "approval", "child", "file"
textstring | nullWhat a person reads for this step: the plan line, the message, the short tool answer or the file's name. Never a prompt or a secret.
  • maxLength 65536
tool_callobject | nullThe tool this entry called. On a product that logs a call and its result as two steps, the call names the tool and the result entry has no TOOL_CALL.
resultstring | object | array | number | boolean | nullWhat the step answered: a tool's output, a check's verdict, a file's reference. Any JSON; never a credential.
costobject | nullWhat this step spent, when the product meters per step. Null when it meters per run only.
started_atstringyes
  • format date-time
ended_atstring | nullWhen the step ended, when the product records an end. A product that logs a point event sets it to the event's time; null when the step is still open.
  • format date-time
child_runstring | nullThe child run this step started, on KIND child. Null elsewhere.
  • maxLength 160
fromenumWho this entry is from. person: the run's person (their message). agent: the agent. system: the product (a notice, a limit reached). tool: a tool's own words. Absent means agent.
  • one of "person", "agent", "system", "tool"
authorobjectWho spoke, when a thread has several people or agents: a name and a picture. Absent for the run's own person and agent.
  • no other fields
author.idstring
  • maxLength 160
author.namestringyes
  • minLength 1
  • maxLength 160
author.avatar_urlstringTheir picture.
  • format uri
  • maxLength 2000
statusenumWhether the entry is still under way (a tool that runs, a message being written). Absent means done.
  • one of "running", "done", "failed"
titlestringThe entry in a few words, for a tool or a thought: "Searching the web", "Reading 3 files".
  • minLength 1
  • maxLength 200
glyphstringA glyph's name in the openvibe-shared glyph registry (openvibe.design/icons). A reader that does not know it draws its default.
  • pattern ^[a-z][a-z0-9-]{0,40}$
attachmentsarray of object
  • maxItems 20
  • items: no other fields
attachments[].kindenumyes
  • one of "image", "file", "audio", "video"
attachments[].media_idstring
  • minLength 1
  • maxLength 60
attachments[].urlstringAn https address the reader may fetch.
  • format uri
  • maxLength 2000
attachments[].namestring
  • maxLength 300
attachments[].mimestring
  • maxLength 120
attachments[].bytesinteger
  • minimum 0
attachments[].widthinteger
  • minimum 1
attachments[].heightinteger
  • minimum 1
attachments[].altstringWhat a picture shows, in words.
  • maxLength 500
sourcesarray of objectThe sources a message cites, numbered as its text cites them ([1], [2]).
  • maxItems 40
  • items: no other fields
sources[].nintegeryes
  • minimum 1
sources[].titlestring
  • maxLength 300
sources[].urlstringyesThe source's address.
  • format uri
  • maxLength 2000
sources[].sitestring
  • maxLength 120
uiobjectA rich card for this entry. TYPE names it (link, table, code, diff, image, choice, timer, repo, desk, run, or a product's own, prefixed with its product: live-clip); DATA is that card's own content. A reader that does not know the type shows TEXT instead, so TEXT always says the same thing in words.
  • no other fields
ui.typestringyes
  • pattern ^[a-z][a-z0-9-]{0,40}$
ui.dataobject
deskobjectThe computer this step used, for the screen tile and the replay: the desk, the picture it saw and what it did. A secret field is blanked before a still is stored.
  • no other fields
desk.idstringyes
  • minLength 1
  • maxLength 160
desk.still_urlstringThe picture the step saw.
  • format uri
  • maxLength 2000
desk.actionobject
  • no other fields
desk.action.typestringyes
  • pattern ^[a-z][a-z0-9-]{0,40}$
desk.action.xnumber
desk.action.ynumber
desk.action.textstring
  • maxLength 500
turnintegerThe turn the step belongs to. A person's message opens a turn (turn 0 is what the run started with); every step until their next message is in it. A fork, a hand-off or a regenerated answer starts at a turn boundary (agents.run@1's LINEAGE.AT_TURN).
  • minimum 0

Examples

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

Valid: a-persons-message-with-a-picture
{
  "run_id": "c_8f3k2m",
  "seq": 1,
  "kind": "message",
  "from": "person",
  "text": "Find me a good deal on a mechanical keyboard like this one.",
  "attachments": [
    {
      "kind": "image",
      "media_id": "med_01K9R2V6M8Q4T0B7C3D5E9F2GH",
      "name": "keyboard.jpg",
      "mime": "image/jpeg",
      "bytes": 182044,
      "width": 1200,
      "height": 800,
      "alt": "A grey 75% mechanical keyboard"
    }
  ],
  "started_at": "2026-11-12T10:00:00Z",
  "ended_at": "2026-11-12T10:00:00Z"
}
Valid: a-step-in-turn-two
{
  "run_id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "seq": 7,
  "kind": "tool",
  "from": "tool",
  "status": "done",
  "title": "Typing in the search box",
  "glyph": "search",
  "text": "Typed \"mechanical keyboard 75%\" in the search box.",
  "tool_call": {
    "name": "computer.type",
    "input": {
      "text": "mechanical keyboard 75%"
    }
  },
  "desk": {
    "id": "desk_private_1",
    "still_url": "https://openvibe.media/s/still_01K9R2V6M8Q4T0B7C3D5E9F2GH.jpg",
    "action": {
      "type": "type",
      "x": 512,
      "y": 96,
      "text": "mechanical keyboard 75%"
    }
  },
  "started_at": "2026-11-12T10:03:00Z",
  "ended_at": "2026-11-12T10:03:01Z",
  "turn": 2
}
Valid: a-tool-on-a-desk
{
  "run_id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "seq": 7,
  "kind": "tool",
  "from": "tool",
  "status": "done",
  "title": "Typing in the search box",
  "glyph": "search",
  "text": "Typed \"mechanical keyboard 75%\" in the search box.",
  "tool_call": {
    "name": "computer.type",
    "input": {
      "text": "mechanical keyboard 75%"
    }
  },
  "desk": {
    "id": "desk_private_1",
    "still_url": "https://openvibe.media/s/still_01K9R2V6M8Q4T0B7C3D5E9F2GH.jpg",
    "action": {
      "type": "type",
      "x": 512,
      "y": 96,
      "text": "mechanical keyboard 75%"
    }
  },
  "started_at": "2026-11-12T10:03:00Z",
  "ended_at": "2026-11-12T10:03:01Z"
}
Valid: an-answer-being-written-with-sources-and-a-card
{
  "run_id": "c_8f3k2m",
  "seq": 4,
  "kind": "message",
  "from": "agent",
  "status": "running",
  "text": "Three picks under $90 [1][2]:",
  "sources": [
    {
      "n": 1,
      "title": "Keychron K2 at $74",
      "url": "https://openvibe.deals/d/keychron-k2",
      "site": "openvibe.deals"
    },
    {
      "n": 2,
      "title": "r/MechanicalKeyboards buying guide",
      "url": "https://www.reddit.com/r/MechanicalKeyboards/wiki/buying_guide",
      "site": "reddit.com"
    }
  ],
  "ui": {
    "type": "table",
    "data": {
      "columns": [
        "Keyboard",
        "Price",
        "Switches"
      ],
      "rows": [
        [
          "Keychron K2",
          "$74",
          "Gateron Brown"
        ],
        [
          "Royal Kludge RK84",
          "$59",
          "Red"
        ],
        [
          "Epomaker TH80",
          "$89",
          "Budgerigar"
        ]
      ]
    }
  },
  "started_at": "2026-11-12T10:01:40Z",
  "ended_at": null
}
Valid: tool
{
  "run_id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "seq": 4,
  "kind": "tool",
  "text": "Ran the test suite",
  "tool_call": {
    "name": "terminal",
    "input": {
      "command": "npm test",
      "cwd": "/home/person/app"
    }
  },
  "result": "124 checks passed",
  "cost": {
    "usd": 0.002,
    "tokens": 310,
    "seconds": 18
  },
  "started_at": "2026-11-12T10:00:10Z",
  "ended_at": "2026-11-12T10:00:28Z",
  "child_run": null
}
Rejected: a-card-without-a-type
{
  "run_id": "c_8f3k2m",
  "seq": 2,
  "kind": "message",
  "text": "hi",
  "ui": {
    "data": {}
  },
  "started_at": "2026-11-12T10:00:00Z"
}
Rejected: a-turn-before-the-first
{
  "run_id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "seq": 7,
  "kind": "tool",
  "from": "tool",
  "status": "done",
  "title": "Typing in the search box",
  "glyph": "search",
  "text": "Typed \"mechanical keyboard 75%\" in the search box.",
  "tool_call": {
    "name": "computer.type",
    "input": {
      "text": "mechanical keyboard 75%"
    }
  },
  "desk": {
    "id": "desk_private_1",
    "still_url": "https://openvibe.media/s/still_01K9R2V6M8Q4T0B7C3D5E9F2GH.jpg",
    "action": {
      "type": "type",
      "x": 512,
      "y": 96,
      "text": "mechanical keyboard 75%"
    }
  },
  "started_at": "2026-11-12T10:03:00Z",
  "ended_at": "2026-11-12T10:03:01Z",
  "turn": -1
}
Rejected: from-nobody-known
{
  "run_id": "c_8f3k2m",
  "seq": 2,
  "kind": "message",
  "from": "narrator",
  "text": "hi",
  "started_at": "2026-11-12T10:00:00Z"
}
Rejected: unknown-kind
{
  "run_id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "seq": 5,
  "kind": "thought",
  "text": "The test suite is the next thing to run.",
  "started_at": "2026-11-12T10:00:09Z"
}

Validate

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