# Changelog ## Unreleased — agents have souls - **A soul** (`agents.soul@1`, Contracts 0.424.0; `openvibe-sdk/agents/soul` 0.165.0; migration 0072): who a personal agent is, kept apart from its instructions and its memory. `actor_agents.soul` is jsonb (null: no soul yet); a create and a patch take a `soul` that the SDK validates, and a catalogue answer carries it back with its `soul_md`. - **The prompt:** a soul is the first block of a personal agent's prompt (`toPrompt(soul, { name })`) and the first prompt layer, before the person's instructions, so the cached prefix stays stable. It says who the agent is, never what it may do. - **Import:** `POST /api/v1/agents/:id/soul/import` takes a `SOUL.md` of at most 20 KB and stores what `fromSoulMd` reads; an invalid result (a secret, a bad part) is 422. - **Suggestions (Design rule 176):** `POST /api/v1/agents/:id/soul/suggest` waits in `actor_agent_proposals` as kind `soul` with `change()`'s words; the person accepts or dismisses it on the existing suggestions route. An agent never writes its own soul. - **Your agents in the menu:** `GET /api/v1/me/agents/faces` answers each face's name, avatar, hue, glyph, state (working, asks, paused, resting), current step and number of asks, newest activity first and at most twelve; the same first-party CORS as the Actor strip, cached privately for 15 seconds. - Pins: openvibe-contracts v0.424.0, openvibe-sdk v0.165.0. ## Unreleased — every AI call measured - **AI meter** (`openvibe-sdk/ai-meter`, SDK 0.164.0): every model call Actor makes straight to a provider is recorded and sent to OpenVibe.AI's `POST /api/v1/usage/calls` (a call through OpenVibe.AI, with a person's own key or the router, is recorded by AI itself, so it is never counted twice): provider, model, task and call id, TTFT, tokens per second, latency, tokens, tool calls, the outcome, a failure's class, and the task's wall time on its last step. Actor's service token is narrowed to `ai.usage.report`. The meter is off in a drill and in tests, and it never fails or blocks a model call. - Pins: openvibe-contracts v0.421.0, openvibe-sdk v0.164.0. ## Unreleased — make your own agent (blueprints) - **Blueprints** (`agents.blueprint@1`, Contracts 0.419.0): a person's agent as one document of ten parts. - `POST /api/v1/blueprints` makes a draft, checked against the schema and `openvibe-sdk/agents/blueprint`'s rules; a blueprint carries no secret. - `PUT …/:id/draft` edits it, and `POST …/:id/publish` freezes it as version N. A published version never changes. - `GET /api/v1/blueprints` lists mine (`?scope=public` lists the gallery), and `GET …/:id?version=N` reads one version. - `POST …/:id/remix` makes a private copy that credits its source, only when the author named a licence. - **Installs** (`agents.install@1`): - `GET …/:id/asks` is the install screen in words. - `POST …/:id/install` makes the person's own agent once every ask is agreed and the inputs are filled. Its instructions carry the inputs and the floor in words. - When a version is published, an install set to `same-or-less` takes it if it asks for no more. Any other install keeps it `pending`. - `POST /api/v1/agents/:id/install/update` takes the new version once its new asks are agreed. - Migration 0071 adds the blueprint, version and install tables and lets an agent's instructions run to 20,000 characters. - Pins: openvibe-contracts v0.419.1, openvibe-sdk v0.162.0. ## Unreleased — Stop, not E-STOP - The robot tools and page copy say Stop; `list_my_robots` no longer reads a latch field. What changed in OpenVibe.Actor, newest first. Each site also publishes its patch notes at /updates. ## Unreleased — the same run shape as Codes and Run (`agents.run@1`, `agents.step@1`) - **`GET /api/v1/runs`** lists the person's own runs newest first (`limit`, `before`, `status`, `agent`) in the one agent system's `agents.run@1` shape: owner, agent and product, parent run, budget, status, timestamps, result summary and cost. A task is the run; nothing new is stored. - **`GET /api/v1/runs/:id/steps?after=`** reads the task's own event log as `agents.step@1`, one entry per output event, with the task event's seq as the cursor. `GET /api/v1/runs/:id` answers the same run shape when the reader asks for it with `Accept: application/vnd.openvibe.agents.run+json` (what `openvibe-sdk/agents` sends) or `?shape=agents.run@1`; the existing `agent.run@1` snapshot stays the default for its callers. - Read-only and behind the existing `actor.task.read`/`actor.task.list`: only the person's own Network token or session, and another person's run is the same 404 as one that does not exist. Actor's tables are unchanged. - The field map and what does not fit (per-step cost, Actor's `needs_you`, a tool call and its result as two log entries) are in OpenVibe.Contracts `docs/agents-runs.md`; the routes are in [docs/agent-runs.md](docs/agent-runs.md). Tests: `test/runs-api.test.js`. ## Unreleased — See it work (W1): the desk, the viewer and the replay - **The desk tile** in a running task's card: the desk's private Live stream (started with the task's desk through Link's `desk.stream` and stopped with the task), with the still at 1/s when no stream is up. Under it the latest step in plain words, a ring where it last clicked, the browser page's address and where it is ("Your PC · its own desk", "OpenVibe computer"). The tile pauses its stream while off screen. - **The viewer** (a tap on the tile): the desk fills a phone and sits beside the steps from 900 px, with Take over, Pause, Stop and Hand back. Take over takes a Run desk's live lease or makes Bot take the person's own input on a Link desk (their token, mapped to desk coordinates); the agent's input is refused while they drive. Hand back takes an optional note that reaches the run as the person's message. A `hand_to_person` step is an Ask that opens the viewer in control. - **Replay**: every computer step keeps its still and its action. A finished task's card has a timeline to scrub, and each step opens the picture it saw. Typed text is redacted before it reaches the task's events, so neither the replay nor the task log carries it; a click keeps only its x,y. - Migration 0070 (task stills); `GET /api/v1/tasks/:id/desks`, `/desk-still`, `/replay`, the desk take-over/hand-back and the person's input. Tests: `test/see-it-work.test.js`; screenshots: `test/see-it-work-shots.js`. ## Unreleased — the chat is the one place you use Actor (one agent system, stage 1) - **Composer v2** (openvibe-shared 4.215.0, the `agent` preset): the chat mounts `OpenVibeComposer.mount` with Attach, Speak, the mode chip (Fast, Balanced, Best, Cheapest, Private) and the round send; files go through Actor's existing path (`POST /chat` with the file as data) and their chips show the read's progress. The dashed "Attach files" box and the separate model/mode controls are gone; the model lives at `/chat/model`. - **Settings** (`/chat/settings`): the run defaults ("New runs: Only me … Apply to all my existing runs") moved off the chat. The route (`POST /chat/defaults`) and its behaviour are unchanged. - **Four suggestion chips** on an empty chat, one line each, instead of the wall of starter cards; a chat with messages shows none. - **The Console is folded in**: `/console` answers 301 to `/chat` (carrying `?ask=`/`?from=`), the bottom tab bar is Home, Chat, Agents, and the running tasks the Console listed are the chat's "Now" strip — one line each, Stop on each, hidden when nothing runs. `/me/spend` and the approvals, agents, computers and robots APIs and pages stay. - **A running task is a card in the conversation** (title, state, the last steps, Stop), refreshed in place from `GET /chat/turn/:id`; stopping goes through `POST /chat/:id/cancel` and returns to the chat. - Migration-free. Tests: `test/one-page.test.js`, plus the updated chat, console, model, sharing, allowance and self-host suites; screenshots at 390 and 1280: `test/one-page-shots.js`. ## Unreleased — skills that run code, sandboxed (HO8 step 3) - **`actor.skill@2`: code in a sandbox** (plan HO8, ADR-060; contracts 0.343.0): a sandbox skill carries one to five tools and its code as ordinary `.js` text resources. The registry reads the code, never runs it, and fails a tool that reads `process.env`, starts a process, worker or `vm`, writes a file, builds code at run time, requires anything but Link's safe modules, carries an encoded blob or minified code, or reaches the network outside the `net` its tool declares; it flags any network use, timers over 5 s and loops over input. Every finding names its tool. The cross-field rules (a tool's entry is a resource, every code resource is a tool's entry, tool names are unique) are checked before anything is listed. - **The bytes that run are the bytes that were scanned**: publishing a sandbox skill stores each code resource as a private OpenVibe.Media object under Actor's tenant and pins `{ name, media_id, sha256 }` on the `actor.skill-listing@2`; `server/skills/publish` is the registry's path (a prompt skill stays `actor.skill@1`). - **`skill.run`**: a new HO1 kind ("running a skill's code in a sandbox"), allowable, with the exact target `@ ` (plus ` · internet` when `net` is `public`), so "always allow" covers one version's tool. - **The tool the agent calls**: each installed sandbox tool becomes a runtime function whose description is the tool's words plus its network in plain language and whose input is the tool's `args` schema; a bad input never becomes a job. A run is one OpenVibe.Run function job as the person (`X-OV-Subject` + `X-OV-Project`), artifact `openvibe-skill-runner@1.0.0`, the pinned inputs, the declared `net` and limits (never above the caps), `ttl_ms` 120000, and the person's own machines before OpenVibe's pool. No node: a plain message and Link's "Run it on my machine" link, and nothing runs anywhere else. The tool's JSON comes back as untrusted data, cut to 64 KiB, with the job id, wall time and exit; Run's per-second metering is the cost. - **The card and install page** say "Runs code in a sandbox on your machine or OpenVibe's", list each tool with its network in plain words ("no internet", "OpenVibe sites only", "the internet") and its limits, and show the scan's findings per tool. A flagged code skill installs only after the person reads them; a failed one never. - Migration 0047 (`actor_skill_listings.code`, the document's own text, the `skill.run` kind). Tests: `test/skill-sandbox.test.js`. ## Unreleased — an agent's live canvas (HO9) - **An agent's live panel beside the conversation** (plan track HO, wave 2; ADR-059, `actor.canvas@1`): the agent calls `canvas_set({ title, blocks })` and the panel is drawn beside its thread — a trip board, a price table, a checklist, a small chart — from data only (no HTML, no style, no script). The whole document is replaced and its version grows; it is validated against the released contract, and a picture outside openvibe.media, a markup block or another block the contract does not know is refused with the schema's plain reason and nothing is written. One run may call it at most 20 times. The panel lives in `actor_canvases`: one row per conversation (the thread's first turn), with the document, its version and the ticks waiting for the next turn (migration 0045). - **The person's moves, never the canvas's**: a button's label is posted by the API as the person's own message — a normal turn from the newest turn of the conversation, at the agent's level and through its approvals, exactly as typing it would (nothing else happens). A checklist tick flips that item in the document (version + 1) and starts no turn; the next turn is told which item the person ticked, once, as its conversation context. Both go through the same-origin API (`GET /api/v1/me/canvases?thread=`, `GET /api/v1/me/canvases/:id`, `POST /api/v1/me/canvases/:id/events`) and another person's canvas is the same 404 as one that does not exist. - **Where it shows**: the thread page draws it in the third column on a wide screen and as one closed "Canvas · N items" bar above the composer on a phone (open on tap; the open body scrolls inside at 45vh), with openvibe-shared v4.145.0's ov-canvas.js. The page redraws it from the thread's live events when `canvas_set` runs; a closed bar still updates its count and says "updated" for a few seconds. The document is in the account export and goes with the account. Test: `test/canvas.test.js`; openvibe-contracts v0.341.0 names the routes. - **Chat can draw it beside a Vibe chat** (HO9 step 2): the two reads answer a cross-origin request from `https://openvibe.chat` only, with the person's own Network Bearer and no credentials mode (a cookie from another site is refused), and carry `Vary: Origin`; every canvas write stays same-origin. Test: `test/canvas.test.js`. ## Unreleased — the skills registry (plan HO2) - **A skill is a document, and the registry vouches for it** (plan HO2, ADR-058; contracts 0.340.0): `actor.skill@1` is a slug, a name, a one-line description, a version, Markdown instructions, up to ten text resources and what it may ask an agent to do. It runs as a prompt and nothing else — no code, no binary, no install recipe — and it grants nothing: the agent's level and its approvals still decide every step. The document's integrity hash is not a signature (as for an agent's voice): the author in a document is a claim, and the listing is the only place a publisher is vouched for. - **The scan comes first** (`server/skills/scan.js`): deterministic, no model, five rules over the words of the document — an act of a kind the skill did not declare, a request for a password, a key, a token or a seed phrase, files or notes sent to an address or a URL, a resource that reads as a script, a URL outside the hosts it declared. A block fails the skill (it never installs), a warning flags it (the person reads the findings and confirms), no finding passes it (it installs in one tap). - **OpenVibe's own skills first** (`skills/`): four real documents — a stream recording's summary with its moments, the last seven days as a changelog draft, a price watch as a goal, a research brief with its sources — published as listings of publisher kind `openvibe`. Nobody may upload a skill in this wave. - **Progressive disclosure**: an agent's prompt carries each installed skill's name and description only; the tool `load_skill(slug)` brings the instructions and the resources, framed as guidance from that skill. A skill changes no level and no approval. - **The pages**: `/skills` (the catalogue, the scan's state in words) and `/skills/` (the instructions, what it may ask for in plain words, the findings, and the install). The agent's page lists its own skills, with Remove. `GET /api/v1/skills` and `GET /api/v1/skills/:id` answer the listing; `POST/DELETE /api/v1/me/agents/:id/skills` install and remove. - **The curator ships with the catalogue**: a listing nobody has loaded for 30 days goes stale, for 90 days it is archived, and a stale or archived listing is not offered. The job asks at night what is due; it is off in a drill. - Migration 0043 (`actor_skill_listings`, `actor_skill_installs`). Both tables go with the person: an account export carries the installs, and an account deletion erases them. Test: `test/skills.test.js`. ## Unreleased — memory that tidies itself (HO5) - **An agent's memory that consolidates itself** (plan track HO, wave 2): once a person turns on "Tidy my agents' memory each night" on /memory, one due-based job runs each due agent's own notes through ONE model call a night, in the cheapest class that can wait (OpenVibe.AI's `batch`, or `background` when the client cannot ask for batch), and the answer is DATA: a list of merges, retires and promotions and a short diary, checked against the agent's own notes before anything happens. The sources of a merge are kept and marked `merged_into`, a stale note is marked `retired_at` and never deleted, a note that keeps coming up becomes a pinned note and the agent reads pinned notes first, and the diary is two to five plain sentences in the agent's voice (migration 0041). - **The rules the model cannot break**: only the agent's own notes are touched — a note the person wrote, or pinned by hand, is left alone; every id in the answer must be one of the notes that were sent, so an id from another agent or another person is ignored; a merge longer than its sources together is refused; at most 20 changes a night, per person; and the person's own words still make the only active memory. - **The person sees it and corrects it**: /memory and the agent's page show the latest diary entry and every change, each with Undo for thirty days (an undo brings the sources back and drops the merged note). A diary entry can be edited or deleted, and a note can be pinned by hand. - **Consent and the job**: the setting is off by default and lives in a row per person with a `next_due_at` (never a weekly interval in the code); nothing runs for a person who has not turned it on, and the job is off in a drill and in a test. The diary and the history of the nights are in the account export and go with the account (ADR-033). Test: `test/memory-tidy.test.js`. ## Unreleased — triggers in conversation - **The routine's card in the chat** (Opus): the turn that made a trigger shows it — when, in words (with its zone), what runs, the next run in the reader's own time, how the last run went (a link to it), which agent runs it, and On, Paused or Deleted. Its moves are plain forms that work with no script: Pause or Resume, Run now, and Undo for ten minutes, then Delete with a confirmation; a routine paused after failed runs says so and leads with Resume. Only the turn's requester, same-origin, and only a trigger that turn made (`POST /chat/:id/trigger/:tid`); Watch is called with the person's session. Schedules read the way a person says them: "Every hour at :30" (was "Every 30 minutes"), "Every 15 minutes", "Every 6 hours", "Every day at 08:00 and 20:00", "Every month on the 1st at 09:00", "Every year on 25 December at 09:00"; a rule with no plain phrase is "On a custom schedule, ". Test: `test/trigger-card.test.js`. - **Triggers made in the conversation** (plan §12 A-4): `schedule_trigger` turns "every weekday at 9, summarise my inbox" or "when a new issue lands in my repo, triage it" into a Watch subscription the person owns, created with their own token — never Actor's service token acting for them. The tool parses the sentence, and when it cannot (no time, no timezone, two times, no event it can name) it makes nothing and asks the one question instead. The card is a `trigger` step on the turn: when in words and cron, what it runs, the next run, the last result (ok, failed, needs you) and active/paused (migration 0037). - **The person's moves** are Actor routes: `GET/POST /api/v1/triggers`, `PATCH /api/v1/triggers/:id`, `…/pause`, `…/resume`, `…/run` and `DELETE` (undo for ten minutes, then Delete with a confirmation). Another person gets 404. At most 20 active triggers, the smallest interval is 15 minutes, and five failed runs in a row pause the trigger and say so on the card. - **Watch runs come back as assignment roots** with origin `routine` and the trigger's own level, capped there: a routine cannot borrow a higher standing level from its agent. Money, passwords, deletions and new recipients still always ask inside the run. - Watch gets the routine's cron (`POST /api/v1/watches/:id/schedule`), a scheduled check that queues the action without fetching, and its own five-failed-check pause; an actor-agent delivery now names the watch and the Actor trigger. - Contracts 0.330.0 adds `actor.trigger@1` and the `trigger` step on `actor.task-event@2`; Actor still pins 0.328.0 until it is released, so the routes are guarded by `actor.task.read`/`actor.task.create` in the meantime. ## Unreleased — one Computer - **One Computer** (plan 2026-11-07: "Surf, Computer and Actor are one experience"): when a task opens a desk, its own step carries `computer` (`session_id` — OpenVibe.Run's `ses_…`, or null on the person's own machine through Bot — `machine`, `where` and `cost_hint`) from the tool's answer, never the model's words. `GET /api/v1/tasks/:id` reads the same object off the task's own steps, with `driving` while the person holds the desk, and `agent.timeline-entry@1` stores it (migration 0035). - **Take over / Give it back** are one API on the task: `POST /api/v1/tasks/:id/takeover` and `…/giveback`, built on the `hand_to_person` hand-off. While the person drives, `computer_input` and the acting `computer_browser` actions are refused with a clear reason; the task's timeline gets one `handoff` step and the Dock's timeline one step with the same Computer. Only the task's own person. - **The still**: `GET /api/v1/tasks/:id/still` asks the session's own service (Run) for the newest frame as Actor's service token acting for the person, and answers the bytes with `Cache-Control: private, no-store`. A person's task only; another person, a signed-out visitor and an app token all get the same 404 as a missing task. ## 0.3.0 — 2026-11-07 - **Space's builder as a task of the person's one agent** (plan 2026-10-22 §Z, stage Z1): six new tools — `space_create`, `space_files_list`, `space_files_read`, `space_files_write`, `space_preview` and `space_publish`. The agent works on the person's own spaces through OpenVibe.Space's builder API (`/api/v1/builder/spaces`), as the person: another person's space is the same 404 as one that does not exist, and every write goes through the same path, limits and audit as Space's own editor. - **Publishing waits for the person:** `space_publish` asks, and the answer lives in the Dock ("Needs you"); the tool says the space is not published yet until the person says yes. ## 0.2.0 — 2026-11-07 - **The one timeline behind the Dock** (design 2026-11-07): every mode of the agent (Chat, Codes, Run, Watch, Live, Surf) publishes the step it took as agent.step.started|updated|ended, and Actor keeps the one timeline: GET /api/v1/agent/timeline (cursor, mode and needs-you filters, the per-mode counts) and the live stream GET /api/v1/agent/timeline/stream. An entry is a title, an outcome and a link: the table has no column for a prompt or a diff, so none can be stored. - **Needs you and the kill switch:** POST /api/v1/agent/approvals/:product/:id/decide answers an approval in the mode that owns it (the person's own token goes with it), and POST /api/v1/agent/kill-switch stops what is open and pauses the agent until the person resumes it. - **Only me by default:** an entry is private until the person shares it through the one Share sheet; another person's entry is the same 404 as one that does not exist. The timeline is in the account export and goes with the person on deletion. ## 0.1.0 — 2026-10-08 - **First release:** the task router (plan T17, ADR-044). A task is classified, routed by `openvibe-sdk/placement`, run, checked by a second model family, and delivered as `platform.task@1` with its cost and explanation. - **Agents:** the OpenVibe runtime (DeepSeek acting through OpenVibe.Search and OpenVibe.Tools), OpenAI's web agent, and an open model on OpenVibe's server for private mode. OpenVibe.Codes is listed for coding. - **API:** tasks, a live stream, cancel, the agent catalog and a dry-run router. Capabilities: `actor.task.create`, `actor.task.read`, `actor.task.list`, `actor.agent.read` (openvibe-contracts 0.115.0). - **Money:** a free tier per person and an operator daily ceiling. Budgets are hard.