openvibers/openvibe.sdk CHANGELOG.md
CHANGELOG.md
Changelog
0.166.0 — 2026-11-13
scripts/test-services.sh without Docker: the same services from local binaries (CI checks on shared nodes). A
check job on a shared node runs a repository's GitHub Actions job with act in host mode, with no Docker daemon and no
root, so eval "$(node_modules/openvibe-sdk/scripts/test-services.sh up)" && npm test && npm run test:pg, the test line
of nearly every OpenVibers repository, could not start its containers there.
- With no working Docker (
docker infodoes not answer),upstarts PostgreSQL 18 (initdb, SCRAM, fsync off),
PgBouncer (transaction mode; each test role is looked up withauth_queryand SCRAM is passed through) and Valkey from
local binaries, as the current user, under one throwaway directory (OV_TEST_SERVICES_DIR, default
$TMPDIR/ovsdk-services-<uid>), on the same ports with the same users, and prints the same three variables. A second
upreuses what runs;downstops them and removes the directory (and the containers, when Docker is there). OV_TEST_SERVICES=docker|localchooses;OV_TEST_SERVICES_PATHlimits where the binaries are looked for. Ubuntu:
apt install postgresql-18 pgbouncer valkey-server(their own services can stay off).- Verified on ov-node-01 as an unprivileged user with no Docker:
testing-db,db,pg-outbox,db-ambient,
valkeyandgovernall ran against the local services (no case skipped) anddownleft nothing behind. Tests:
test-services(no Docker and no binaries says what is missing and starts nothing;OV_TEST_SERVICESoverrides).
0.165.0 — 2026-11-13
openvibe-sdk/agents/soul: who an agent is (agents.soul@1, Contracts 0.424.0; Design rules 175
actor-is-the-work, 176 souls-grow-by-yes). The owner, 11-13: "you should have different bots … they each have a
soul … your agents are created based on your memories and personality". Pure functions:
validate(soul): the shape and the rules a schema cannot say (no secret in a soul; no permission in a soul, which
is the blueprint's hands and rules).toPrompt(soul, { name }): one stable block of plain words (the dials in words, habits, beliefs, pushback, never,
notes), placed before memory so a provider's cached prefix never changes inside a conversation; it says the soul
never widens what the agent may do.toSoulMd,toIdentityMd,fromSoulMd: the SOUL.md and IDENTITY.md layout OpenClaw and Hermes read, out and back;
a SOUL.md from elsewhere has its sections mapped (Core Truths, Boundaries, Vibe…) and the rest kept as notes.change(soul, patch): an agent's suggestion for its own soul, as a proposed soul and the change in words; nothing
is applied (its person says yes).- Tests:
agents-soul.
0.164.0 — 2026-11-12
- New
openvibe-sdk/ai-meter: every product meters its AI calls the same way (ai-metrics.md; Contracts 0.421.0
ai.call.recorded@11.1.0 andai.call-report@1). startCall()measures each call on its own clock: TTFT and latency less any queue, tokens per second after the
first token (or Ollama's own decode time), tool calls, fallbacks, the error class, and the charged price when the
provider gives one.normalize()reads the usage of OpenAI chat completions and Responses, Anthropic Messages, Gemini and Ollama.
The input it reports is the uncached input, with cache reads and writes and reasoning tokens apart.createMeterClient()batches the records to AI'sPOST /api/v1/usage/callswith the service token.
- It retries a network error, a 408, 425, 429 or 5xx with backoff, and asks for a new token after a 401.
- A batch refused for any other reason is dropped and counted, never sent again.
- AI prices any call that brings no price and publishes it.
0.163.0 — 2026-11-12
From reading 24 open agent projects (one-system-v2.md). Three new modules, all pure functions:
openvibe-sdk/agents/rules: permissions as data, from Gemini CLI and OpenCode (agents.permission-rule@1).decide()lets the last matching rule win; with no rule, the run's level decides.- Unattended work never waits on an ask. It settles to allow at the levels approve-for-me and full, and to deny below
them. - The floor always asks; with nobody there, it is denied.
fromBlueprint()compiles a blueprint to rules: only the tools it names, never.git/hooksor its own config,
and its sandbox and network.openvibe-sdk/agents/router: pays for the strong model only when it is likely to matter, in RouteLLM's shape
over OpenVibe Decisions.- It asks for a calibrated probability that the small model is not enough, and routes against a threshold
calibrated to a budget (the share of steps that should go strong). - It draws the quality-cost points the scoreboards show.
- The strategy chain is the person's pick, then the blueprint's pin, then the router. When Decisions is down, the
cheaper model runs. openvibe-sdk/agents/metrics: what a run measured (agents.run@1 1.4.0).- The charged cost comes first; an estimate counts only where nothing was charged, and unknown is null, never 0.
- It records tokens with cache reads and writes, steps, latency, a named exit and evidence.
- Also
passAtK(unbiased),passHatKandflakeRate.
0.162.0 — 2026-11-12
- New
openvibe-sdk/agents/blueprint: make your own agent (agent-blueprints.md; Contracts 0.419.0's
agents.blueprint@1andagents.install@1). These are pure functions, so a browser, Link, a CLI and every service
check and run a blueprint the same way: validate: the shape a person edits, plus the rules a schema cannot say. It carries no secret (anything that
looks like a key or a token is refused where it is). Every{{input}}is declared. Levelfullhas a budget.
Machine and robot tools have somewhere to run. A shared place says what it needs, and never names a machine. A
tool served over MCP has its shapes. A team never contains itself.withDefaults: it talks only, asks first, keeps nothing, answers its owner, and spends 25¢ a run and $2 a day.asks: the install screen in plain words, with the keys an install records as agreed.unagreedlists what an
install has not agreed to yet.wider: what a new version asks for that the old one did not. That includes a tool going from ask to allow, a
higher level, a bigger budget, more people who may ask, more memory, a new place, trigger or team member. An
empty list means an install set to "same-or-less" takes the version without asking.fill: inputs, typed and checked.compile: one run. It returnsagents.run-start@1, the loop's system prompt (with the floor said in words) and
tools, the Dispatcher's need, pin, rungs and price cap, the places and the install's machines, memory, and the
team. Try it, or a run without an install, asks before every tool and spends at most 5¢.estimate: the price of a run on the cheapest choice it may use, throughagents/dispatch, and the rungs on
which it is free.
0.161.0 — 2026-11-12
openvibe-sdk/cam:capture(options)opens a camera through the site's own shared capture core (Shared's
ov-capture.js, OVCapture).- It loads the core once from the calling site's own
/shared/(never another origin) and resolves to a
MediaStream. A page that already has OVCapture uses it. - The options are OVCapture's own: mode, facing, lens, mic, resolution, audio and video.
- Cam's in-page kit (
CAM_KIT_SRC/CAM_KIT_SRI) is re-pinned to the kit Cam serves today, checked against
the live file at its versioned address.
0.160.0 — 2026-11-12
The agent engine's loop, Dispatcher and Run API kit: openvibe-sdk/agents/loop, /dispatch and /api (agent-engine.md §§3, 9). A model, tools and the person's words become
a run's thread. It runs chat and ask turns on a server with no workspace (Vibe answering, an agent using OpenVibe's own
products as tools).
runLoop(options)resolves{ messages, stop, error, usage, cost, turns, toolCalls }. Stop isdone,
terminated,budget,turns,abortedorerror.- The model adapter:
complete({ messages, tools, system, signal, budgetLeft, onDelta })returns the next turn:
text, thinking, tool calls, why it stopped, usage and cost.onDeltastreams the turn as it is written. - Tools run in parallel, or in sequence when the loop or a tool asks. Parallel runs are prepared in order, run
together, and their results kept in the model's order. beforeToolCallis the approval gate (block, with a reason, or end the run);afterToolCallmay rewrite a
result.steering(): words the person sent while it works, taken before the next turn.followUp(): words after
it would stop.finishTurn()may end or continue the loop.transformContext()compacts or filters the
thread before each turn, and entries markedui_onlynever reach the model.- The budget (usd, tokens, turns, steps) stops it between turns, never inside one; the model is told what is left.
- What it keeps, whatever the hooks say:
- A call cut off by the output limit never runs; the model is asked to send it again.
- An unknown tool, input its schema refuses, a throw or a timeout comes back to the model as an error, never as a
crash. - The same call repeated past
repeatLimitgets a reminder instead of a run, and an idempotent tool answers the same
call from the run's memo. - A long output is clipped before the model sees it; the whole result stays in the thread.
createStepSink({ append, update, delta, startSeq, startTurn, runId })turns the loop's events into
agents.step@1steps. In order: the person's words, the thinking (folded), one tool step updated when it ends, then
the agent's words streamed into their step. A person's message opens the nextturn(Contracts 0.417.0).checkInput(schema, value): the JSON Schema subset tools use.- New
openvibe-sdk/agents/dispatch: the Dispatcher (agent-engine.md §3), as pure functions over a person's
choices: ladder(choices, { need, pin, avoid, budgetLeft })orders them: their own machine, a signed-in plan, their own
key, the free allowance, then credits, cheapest first inside a rung, and a smaller model before a stronger one
unless the step needs strength. What cannot do the step (a capability, the context, the speed, the budget) is
listed with why. A person's pick wins when it can, and is otherwise passed over with the reason.escalate(current, evidence, choices)moves to a stronger model only on checkable evidence: a failed test,
schema, tool or grader, or the model's own "not sure". A grader's failure also changes family.harnessFor(task, harnesses, { plans })chooses by capability: the loop for turns with tools, the person's
signed-in plan, else dsh.placeFor(run, { machines, cloud }): the person's computer first (free), a Codes runner for a repository
elsewhere, an OpenVibe computer when allowed, Bot for a robot.estimate(choice, tokens)andslice(parentBudget, share).- New
openvibe-sdk/agents/api: the Run API from a product's own store (server). createRunApi({ product, subject, store, prefix })returnshandle(req, res, next)for Express or any Node http
server: the list, start, get, steps, events, messages, stop and approvals routes of Contracts'docs/agents-runs.md.- The store has
list, get, steps, start, say, stop, answerand, optionally,followto push its own events
(otherwise the stream reads new steps). A method a store lacks answers 409. - No subject is 401, another person's run 404, and a start without words or with an unknown kind 422.
- Every run carries its ref. A store's own
{ status, code }errors go out as they are; anything else is a 500 that
says nothing of the store. A mandate from a caller is dropped (intake's hook sets it). - Read back with the SDK's own client in its test.
- Ported from pi's agent loop (earendil-works/pi,
packages/agent/src/agent-loop.ts, MIT, Copyright (c) 2025 Mario
Zechner): the inner loop over tools and steering, the outer loop over follow-ups, the turn hook, parallel and
sequential tools, and the two tool hooks. The model API, the message shape, the budget, the memo, the repeat guard,
the timeouts and the step sink are OpenVibe's.
0.159.0 — 2026-11-12
The agent engine's client (Contracts 0.414.0: everything a person asks an AI to do is a run with a thread of steps).
openvibe-sdk/agentsruns:runs.list({ kind, status, q, limit, cursor, before, agent, parentRun }),
runs.get(run)(null on 404),runs.steps(run, { after, limit }),runs.start(text | agents.run-start@1, { idempotencyKey }),runs.say(run, text | { text, attachments })(continue a conversation or steer work),
runs.stop(run, { reason }),runs.events(run, { after })andruns.follow(run, { onRun, onStep, onDelta, onApproval, onEvent }), which resolves with the end status.approvals.answer(run, approval, answer)by the approval's id, its object, or the step seq that asked.createFrameAgentsClient(client): every product's runs through the Frame (/api/frame/runs…), where a run is
named by its ref (<product>:<id>);createAgentsClient(client, { prefix })for any product's Run API.- Helpers:
refOf,parseRef,isEnd,needsPerson(a run waiting for a message is a conversation between
turns, not something that needs its person),KINDS,STATUSES,END_STATUSES. - The stream: the engine's events (
run,step,delta,approval,end) are read beside AG-UI's; only a
step advances the resume cursor, so a snapshot or more text that shares a step's id is never skipped. - The plugin interfaces (model adapter, tool, sandbox, memory, planner, reviewer) stay as JSDoc types; the loop
that calls them is the next release. run,post,events,desks,takeOver,handBackandreplayare unchanged.
0.158.0 — 2026-11-12
openvibe-sdk/views: one view API for every content kind (Contractsmedia.view-request@1, themedia.viewed
andmedia.engagedevents).createViewsClient(client).record(event | [events])posts a preview, watch or live heartbeat for
ref: { service, kind, id }to Media'sPOST /api/v1/views, up to 20 at once.secondsis cumulative for the
session and item, so a retry does not count twice; a signed-out viewer keeps an opaque session id..counts(ref)reads one item's{ views, engaged, previews, preview_share, peak_concurrent }from
GET /api/v1/views/counts. Media never stores an IP address or a user agent with a view, and a preview counts
from 1 second.
0.157.0 — 2026-11-12
See it work: a run's desks, control and replay
- Add to
openvibe-sdk/agents:desks(run)→ the run's desks with their view and still,takeOver(run, desk),
handBack(run, desk, note)andreplay(run)→ the steps with the still each saw. They call the same-named routes
under the client's service (/api/v1/runs/:id/desks/...), so a page and a script make the same calls. No tag: the
main session releases this after the sites' stage lands.
0.156.0 — 2026-11-12
openvibe-sdk/exchange: reviewed compute prices, keyless.createExchangeClient(…).computePrices({ kind })and the standalonecreateComputePriceClient()read
GET https://openvibe.exchange/api/compute/prices: each GPU or computer's hourly and per-second price, the
provider's own price page, the date it was checked and the date it must be reviewed again, filtered to
kind: 'cpu' | 'gpu'when asked.- The public endpoint needs no key, sends
Access-Control-Allow-Origin: *and is cached for 60 seconds. The
keyed client still speaks the API host (unchanged); its price read goes tositeUrl(default
https://openvibe.exchange) without its developer key.
0.155.0 — 2026-11-12
- Add
design.icons.suggest(label, { context, limit, rank })toopenvibe-sdk/design: the best glyphs for a button's
words by meaning (the glyph's name, its META keywords and its other-set names), withrank: trueasking a small model
to order the shortlist when the words are new. The answer is the same shape asGET /api/v1/glyphs/suggest, including
new,ranked,modeland, when known,draft_price. design.icons.get()and.search()are public now, like the Design API they call: no access token..draft()still
needs one, and the client still sends a token when it has one.
0.154.0 — 2026-11-12
- Add the Frame's data client to
openvibe-sdk/frame(andsdk.framein the root, browser and ESM entries):
createFrameClient(client)giveschoices(task),query(q, { scopes }),providers(),ask(body)and
events(run), the same calls every site's composer makes through OpenVibe.Network.choicesanswers the Model sheet
(OpenVibe's models enriched from Exchange, the person's keys, computers and plans);askstarts a question and
eventsreusesagents.eventsfor its AG-UI stream, whoseANSWERevent carries the numbered citations. Types are
intypes/frame.d.ts. The shared navbar, footer andonline()are unchanged.
0.153.0 — 2026-11-11
- Add
objects.rotate(id, { degrees })toopenvibe-sdk/media(the v2 object client): turn a stored JPEG, PNG or WebP
picture clockwise by 90, 180 or 270 degrees. Media keeps the uploader's original beside the turned copy, every
generated size follows, and only the picture's owner may turn it (subject/actingUserIdon a service client).
0.152.0 — 2026-11-11
A train of two pull requests: openvibe-sdk/elements (#227) and openvibe-sdk/ui-layout (#226).
- Reserved for
openvibe-sdk/ui-layoutin OpenVibers/OpenVibe.SDK#226. That module is not released by this entry; it
keeps the changelog's minor sequence while that pull request is open, and its own entry replaces this one at merge. (#227) - Add
openvibe-sdk/ui-layout(alsosdk.uiLayoutin both entries):validate(catalog, layout)is the one
check the server, the editor and the AI answer use, returning{ ok, reasons[] }in plain words. It refuses a
missing, hidden or undersized required part, anything pressed under 44 px (and any part under the catalog's own
pixel minimum, e.g. Stop at 64), a part covering a required part or breaking anever_coversrule, a tint that
is not a catalog theme token or a style/binding the part does not allow, a part the catalog does not have, and
any URL, markup or code.applyDefaults(catalog, device?)builds the valid default arrangement (required parts
first, every part visible, 44 px met) andmerge(layout, patch)applies a patch by part id without mutating
the layout. Browser-safe, no dependencies; exhaustive tests. (#226)
0.151.0 — 2026-11-11
- Add related-origin passkey sign-in to
openvibe-sdk/sso(opt-inrelatedPasskeys: true):POST /auth/passkey/options
andPOST /auth/passkey/verifybroker a WebAuthn ceremony for this site's exact origin through OpenVibe.Network as
its OAuth client. The random browser binding stays in the site's own__Host-cookie; Network verifies the exact
origin and RP ID and returns a single-use result that becomes the site's ordinary session. Browsers without
PublicKeyCredential.getClientCapabilities().relatedOriginskeep the existing OAuth/passkey path.
0.150.0 — 2026-11-11
- Add
ai.turn(input, options)for file-capableai.completeruns. Its typedoutput_fileoption stores a text, Markdown, CSV or JSON answer in Media and returns privateai.fileartifact references; existingai.files.read()reads a returned object. - Type the image task's owner daily cap and guest find switch in
AiTaskDefaults.
0.149.0 — 2026-11-11
- Extend the existing
codesclient withmodelPlans.get/put/presets()andtasks.create({ plan | preset }), typed forcodes.model-plan@1on Contracts 0.406.0.
0.148.0 — 2026-11-11
- #208: add
openvibe-sdk/composer, the shared composer's client:attachments.upload(file, { onProgress, signal })(multipart throughXMLHttpRequest, real browser progress),attachments.remove(id),composer.prefs.get()/put(prefs, rev)andmentions.search(q, kinds), typed bycomposer.attachment@1,composer.prefs@1andcomposer.mention@1. - #210: add
feed.list({ kind, cursor })for the cross-site Community Feed, keep the existing Community reader callable, and type preview video media. - #211: add the
openvibe-sdk/ssoone-time cross-site sign-in handoff:POST /auth/handoffmints a 60 s ticket andsso.handoff()arrival middleware redeems it before the page renders. - #212: add
agentsSDK methods to fetch run snapshots, follow resumable AG-UI event streams and post approvals. - #213: add
wallet.changes()for the owner-scoped Vibes, OpenCoins and channel-points gain and loss window, and
wallet.markRead()for each currency's own read cursor. - #214: add browser-safe
player.current(),player.handoff(url)andplayer.on(listener)for a live stream carried
between OpenVibe sites by Shared's floating player. - #215: add typed Chat readers for a person's compacted day, week and month memory, plus personal erase and opt-out.
- #216: add
exchange.createCatalogSearchClient().search(q, filters)for public model and provider offers (prices,
measured speed and Bench quality when available), and type the image search filters and source metadata. - #217: add the owner-only agent sheet reader and Actor pause, resume and memory controls through Network's relay.
- #219: the app sign-in test checks a token's shape, not the letters "eyJ" (a macOS flake).
- #220:
homeFeed.network.popular({ limit })(the signed-out front door's five things people do now) and
homeFeed.network.tryNext({ limit })(the signed-in person's own suggestions). - #222: type the exact reply target and its display name on a Community comment; a thread's first page includes two
replies and the rest use the parent cursor. - #207: add
codes.tasksfor the Dock's Code tab:list({ limit }),create({ text, mode, repo?, attachments? })and
stop(id)(codes.task-summary@1). - openvibe-contracts pinned at v0.406.0.
0.147.0 — 2026-11-10
- #181: add
chat.activity(the person's feed: mentions, replies, reactions, server invites, accepted friend requests and roles),rooms.react()andreply_to_idon room and DM sends. - #182: add the
systemandcan_runmachine metadata to the machine reader type. - #201: add
quest.cardData({ token, timeZone })for the shared quest card: today's quest, the public catalog kept ten minutes and the person's badges and quest log. - #202: add
chat.embeds(message)andreadEmbeds()for the typedchat.embed@1address previews on a chat message. - #204: add
tools.run(tool, input, { prefer })for worker placement (auto,home,host) over JSON and multipart requests, with execution receipt types andtools.jobs.get(id)examples. - #205: add the browser-safe, typed
userCard.createUserCardClient(core).get(username)public Network aggregate; hidden or unavailable fields stay optional and the read sends no credentials.
0.146.0 — 2026-11-10
- #183: add the private history reader for cursor reads, search, summaries, agent recent reads, settings and owner-scoped deletes.
- #184: add
frame.online()with member presence, anonymous guest counts and live channel/site items. - #186: add
quest.today()for the authenticated daily Quest response. - #195: add
bot.embedUrl(robotId, mode)for panel, overlay and compact embeds. - #196: add merged code search, cited deep answers and cursor iteration to Search.
- #197: add filtered, paged Inventory holdings reads and counts, plus async holding pages.
0.145.0 — 2026-11-10
bench.ratings.get: shared fitted ratings and confidence intervals with a twenty-game floor. Browser-safe, anonymous and typed; pairs withai.metricsacross sites.
0.144.0 — 2026-11-10
ai.metrics: public daily model series, up-to-six-model comparisons, task leaderboards, listed price history and hourly provider health. Typed sample counts and nullable measurements; available in browser and server clients without credentials.
0.143.0 (2026-11-10)
sso: a plain sign-in is quiet first.GET /auth/loginwith nosilent,promptorlogin_hintnow asks the Network withprompt=nonebefore it shows a page: a browser signed in at openvibe.network comes back signed in, with no chooser. Before, a page that needs a person (and every link to one from another site, such as "Open" in the Dock) landed on the chooser although the Network knew the browser. When the Network knows nobody (login_required), or always asks for this app (interaction_required), the callback goes on to the chooser by itself (/auth/login?ask=1, the new way to ask for the chooser outright). A person who signed out on this site (ov_sso_hint=guest) gets the chooser at once, as do "switch" and "add an account" (prompt,login_hint). The navbar's own silent sign-in (?silent=1) is unchanged: with nobody at the Network it returns to the page with?sso=none.
0.142.0 (2026-11-10)
chat.rooms.memory(id)and the moderator/person writes: what a room remembers between summaries (GET /api/v1/rooms/:id/memory).memory()answers{ room, entries, kinds, can_edit, me }, each entry with itskind,text,confidence, when it was learned and itssources(the message it came from).addMemory(id, { text, kind?, confidence?, source_message_ids? })andcorrectMemory(id, entryId, patch)are a moderator's;removeMemory(id, entryId)removes any for a moderator and one learned from the caller's own messages for anyone;forgetMyMemory(id)forgets everything learned from the caller's messages in that room. Typed asRoomMemory,MemoryEntryandMemoryInputintypes/chat.d.ts.
0.141.0 — 2026-11-10
community.comments.upload(ref, file)uploads a Community paste comment attachment through Community;comments.create()accepts typed attachment references.
0.140.0 — 2026-11-10
chat.history.pages()asynchronously iterates newest-first cursor pages;chat.history.attach()loads older pages on scroll while preserving the visible message anchor. The same helper is available to Live and Dock.
0.139.0 (2026-11-10)
community.comments: shared comment thread resolve/read/write/edit/delete and visibility methods, including ordered pages and paged replies.
0.138.0 (2026-11-10)
chat.rooms.insight(id): a room's public AI insight (chat.room-insight-result@1), asGET /api/v1/rooms/:id/insightanswers it:{ overview: { text, topics, people, messages, from, to, updated_at }, timeline: [{ at, kind, title, text, message_id }], state: 'ready' | 'quiet' | 'off' }.idis a room slug orglobal; a room the rollout does not cover answersnull(404 likerooms.get). No account is needed, so an anonymous client reads it too. Typed asRoomInsightintypes/chat.d.ts.
0.137.0 (2026-11-10)
- Add
moneyReasons(billing.money-reason@1): the plain words for a movement of OpenCoins, Vibes or Balance, for a service that holds the rawreasonrow. It re-exportsopenvibe-contracts' list when the installed package has it, otherwise the same list copied at Contracts 0.392.0:describe(key, { site, resolve, subject })answers{ key, text, subject: { name, url } | null, site, known }, anddescribeRow(row)reads{ reason, app_id }. An undeclared key reads "for something on <site>" withknown: false.
0.136.0 — 2026-11-09
- Network levels client: public person, batch and board reads, and service awards.
0.135.0 (2026-11-10)
ov.ai.files: attach files through Media, list/read/remove them, and expose typedai.file@1and conversational turn shapes.
0.134.0 (2026-11-09)
ov.ai.creations(): the public catalogue of everything AI can make on OpenVibe (GET /api/v1/creations,ai.creation-list@1) — id, product, a plain name, what you give it and what comes back, the workflow or registered task behind it, who may use it (guest, signed-in, 18+ floor), whether it can cost coins, and where to start. Cached five minutes and CORS open; no token is sent. Types:AiCreation,AiCreationList.
0.133.0 (2026-11-09)
- ov.blog.changelog({ service, kind, area, q, limit, before }): the network changelog's blog.changelog-entry@2 rows, newest first
0.132.0 (2026-11-09)
openvibe-sdk/wiki: articles on blocks (articles.create/update({ blocks }),pages.blocks), picture suggestions from thewiki.article.imagestask (images.suggest), and thecontent.wikiwriter (articles.draft), whose revision is a pending proposal a person reviews.
0.131.0 (2026-11-09)
- openvibe-sdk/community: the Feed's types complete, with pagination guidance
0.130.0 (2026-11-09)
- openvibe-sdk/meme: ov.meme.templates(), .render() (PNG, GIF or MP4) and .get()
0.129.0 — 2026-11-09
openvibe-sdk/git(G2): changes and review —changes.list/get/create/comment/approve/merge/close/summary,
issues.list/get/create/labels/close,compare(),blame(),search()andreviewModel(). Review requests
carry the expected head SHA, merge methods are checked, and mutations are POSTs with retries disabled.
0.128.0 (2026-11-09)
- ov.ai: tasks(), choices.get/set, run(task, input, {choice}) and admin.defaults(); runs.create accepts
taskandchoice
0.127.0 — 2026-11-09
openvibe-sdk/git(ov.git): repositories, trees, blobs, commits, branches, tags, releases, contributors,
insights and stars;commits.create()writes a branch and files without a clone;tokens.create()makes a
short-lived clone token;clone(repo, { to: 'link' })hands a clone to Link.
0.126.0 (2026-11-09)
- Add
openvibe-sdk/pics: the public gallery and picture reads, private uploads with resumable chunks, publication, likes and albums on OpenVibe.Pics.
0.125.0 (2026-11-09)
- openvibe-sdk/blog: posts are written and read as Markdown or as blocks (publishing.block@1), ?format=blocks
0.124.0 (2026-11-09)
- OpenVibe Link:
openvibe-sdk/nodeisopenvibe-sdk/link(ov.nodeisov.link), building
platform.link-capabilities@1and mapping a Link to itsplatform.resource-offer@1and
platform.runtime-offer@1. A link principal token is never a user token. The Contracts pin moves to
v0.372.0.
0.123.0 (2026-11-09)
- ov.exchange: keys (list, create, revoke), private usage, deployments, and web, image and icon search with fetch (the Exchange API platform)
0.122.0 (2026-11-09)
- dms.viewPreference() and setViewPreference(): a conversation's saved view, bubbles or room
0.121.0 (2026-11-09)
- ov.cam.settings.get/put: a person's saved camera choices (Cam #116)
0.120.0 (2026-11-09)
- createUploadClient(): resumable Media uploads in 8 MiB parts, three at a time, with a session reference to resume; ov.ui.progress(el)
0.119.0 (2026-11-09)
- openvibe-sdk/wallet: the wallet, Vibes, the store and items, through Billing and Inventory with the person's bearer and idempotency keys
0.118.0 (2026-11-09)
- openvibe-sdk/design:
components()andcomponent(name)read OpenVibe.Design's public design.component@1 registry
(a missing name is null, a bad one is refused before a request). They are on the samecreateDesignClientas the
icons;accessTokenis now optional and only the icon API needs it.
0.117.0 (2026-11-09)
- ov.cam loads Cam's kit at its versioned URL and its new hash (Cam #116 changed the kit)
0.116.0 (2026-11-09)
- npm metadata (keywords, homepage, funding), the README's first screen and CITATION.cff
0.115.0 (2026-11-09)
- Tools discovery: ov.tools.popular() and ov.tools.trending()
0.114.0 (2026-11-09)
- Dock v2: Vibe conversations (ov.chat.vibe.conversations) and Actor skill runs (ov.actor.skills.run)
0.113.0 — 2026-11-09
- Add the Feed and status posts to
openvibe-sdk/community:feed({ kind, site, q, following, cursor, limit })reads
Community'sGET /api/v1/feed(one stream over Pulse from every OpenVibe site) andposts.create({ text, pictures })
posts up to four Media pictures with required alt text, in the Feed at once.
0.112.0 — 2026-11-09
- Add the person's stats to
openvibe-sdk/report:report.me({ days })(GET/api/v1/me/stats, the signed-in person's own numbers),report.person(username)(GET/api/v1/u/:name/stats, only the sections they published) andreport.publish({ sites, activity, made, ai, money }, { days })(PUT/api/v1/me/stats/published, all five switches named). The answers arereport.me-stats@1; a source that does not answer leaves its section out, never a zero.meandpublishneed the person's own session token;personis public and is a 404 when nothing is published.
0.111.0 — 2026-11-09
openvibe-sdk/shoes: the Open.Shoes catalogue and sizing tools.createShoesClient(client)(server-side;./shoesis browser-blocked) exposes public reads only:
products.list({ kind, q, size }),products.get(id),products.offers(id),tools.laceLength({...})
andsizes.convert({ system, size, brand }).
0.110.0 — 2026-11-09
openvibe-sdk/design: a typed client for the Design glyph API.createDesignClient({ accessToken, baseUrl })returnsicons.search(q),icons.draft({ name, sentence, styles, sourceSvg })
andicons.get(name, { style })for the authenticated glyph draft, get and search calls. It is server-side only
(the./designsubpath is browser-blocked) and takes a signed-in access token rather than a service token.- Styles are checked against the set the API accepts (
thin,light,regular,bold,solid,duotone).
0.109.0 — 2026-11-09
- Add Vibe compare methods to
openvibe-sdk/chatfor submitting answers and recording a user's preferred model.
0.108.0 — 2026-11-09
- Add daily quest reads, progress, step type listings and reviewable step contributions to
openvibe-sdk/quest.
0.107.0 — 2026-11-09
- Add
ov.cam.open()for the Cam recorder, with typed modes, options and file results across Node and browser exports.
0.106.0 (2026-11-09)
- Add
openvibe-sdk/home-feedandhomeFeedto the top-level and browser exports, with home layout, Today frame, and personal feed methods.
0.105.0 (2026-11-09)
- Add Report AI leaderboard, model, provider, comparison and picks reads, plus Exchange Arena start and vote methods.
0.104.0 (2026-11-08)
- Add
createAccountEmailsClienttoopenvibe-sdk/identityfor managing the signed-in person's verified Network email addresses.
0.103.0 (2026-11-08)
- Add
openvibe-sdk/credentials:createCredentialsClientfor a person's own provider credentials on OpenVibe.AI:
list()(metadata only: kind, provider, label, created, last used, expired),add()for an API key or a browser
session cookie,remove(), andbalance()(that credential's fixed read-only balance readers: Anthropic Console
prepaid credits and current spend, OpenAI credit grants). A secret never comes back from any call.
0.102.0 (2026-11-08)
- Add
openvibe-sdk/trust:createTrustClient({ networkUrl, getToken })with.me()(the caller's own signals, a
person's or a guest's token) and.of(subject)(a service token withidentity.trust.read), the typed
identity.trust@1result for accounts and guests, and pure helpers (isNew,atLeast, the age-band order) for Node
and the browser bundle. Signals are age bands and aggregate counts only: never an IP, email or last-seen time.
0.101.0 (2026-11-08)
- Add bounded
locker/server.inspectEnvelopePrefixvalidation against an actual stored ciphertext byte count. - Allow service-bound staged envelope receipts in share updates, with the existing recipient, nonce and atomic reseal checks.
- Allow Locker public/private key export after
importPersonKeys, so a recovered locker can upload and share using the same WebCrypto operations.
0.100.0 (2026-11-08)
- Add
openvibe-sdk/compute: shared compute strategies, hard placement constraints, RAM-aware pool scaling, exact billing arithmetic and provider quote/lifecycle adapters. Seedocs/compute.mdfor gates and unsupported purchase paths.
0.99.0 (2026-11-08)
openvibe-sdk/events:outboxSchema()indexes the rejected rowsrejected()counts. Every outbox made with the
helper now also gets<table>_rejectedon(rejected_at) WHERE rejected_at IS NOT NULL. Before it, the count read
the whole outbox every time it was not cached (523 ms and 112 M buffers a day on Deals, 2026-11-08), because the
product had no index onrejected_at. A service that already has its outbox table must add the same statement in an
expand migration: the DDL is a migration helper, never run at boot, soCREATE INDEX IF NOT EXISTSin this helper
only reaches tables created after it.
0.98.1 (2026-11-07)
ai.batches.cancelsays what cancelling does: only the prompts that never had a run are cancelled, and
nothing is charged for them. A prompt that already has a run (the provider is working on it) goes on and
settles on its own path: it keeps its status, the answer says so, and nobody is told it was not charged.
The batch itself is cancelled only when nothing is left in flight. The same note is onAiBatchItem.reason.
No runtime change.
0.98.0 (2026-11-07)
ai.runs.create(and the direct operations) may ask for the class the caller may wait in:
{ latency: 'interactive' | 'background' | 'batch' }, and for a batch run{ deadlineHours: 1..72 }
(how long the answer may take). The request carriesoptions.latencyandoptions.deadline_hours.
The class belongs to the workflow: a caller may ask for a slower class and never for a faster one, and a
faster ask is clamped by the service, not refused — the run answers with the class it really ran in
(run.latency_class), the tier it was priced on (run.service_tier) and, when it was clamped,
run.latency_clamped.ai.runs.waitForlearns that a batch run may take hours: when the wait is over and the run is a batch
run, the pending run is returned (its status says where it stands) instead of an error. The answer comes
later, with the run's completion event (ai.run.succeeded) or the webhook the run was made with.- Add
ai.batches:create(items, { model, deadlineHours, idempotencyKey }),get(id),list()and
cancel(id)for OpenVibe.AI's/api/v1/batches— many prompts answered later, at half the retail
price. A cancelled batch cancels the items that had not run; nothing is charged for them. - Add the types
LatencyClass,ServiceTier,AiBatch,AiBatchItemandAiBatches, and
CLASSES(the three classes) next toTERMINAL.
0.97.1 (2026-11-07)
types/menu.d.ts:MenuRequestgivesurlandmethodthe| undefinedthatnode:http's
IncomingMessagehas, andMenuResponseno longer adds an index signature, so a plain
IncomingMessage/ServerResponsehandler type-checks underexactOptionalPropertyTypes
(Games'createMenuCardHandler). No runtime change.
0.97.0 (2026-11-07)
- Add
openvibe-sdk/menu:menuCard({ id, site, title, kind, items, more, origin, ttlSeconds, updatedAt })
builds one validatedframe.menu-card@1with the house rules applied — plain text (markup and control
characters stripped) cut to the contract's lengths, at most six items (the contract's cap), https links on
the site's own origin or anopenvibe.*host, and an optional item image ({ src, w, h, alt }) with the same
rule. A bad field throws aTypeErrornaming it; an emptyitemsarray is valid and Network skips the card. - Add
menuRoute(getCards, { maxAgeSeconds = 300, origin, log }): the Express/node:http handler for
GET /.well-known/openvibe-menu.json. It callsgetCards(), validates and caps the file at three cards, and
answersCache-Control: public, max-age=…with CORS*; a build failure or an invalid card answers[]
(never a 500) and logs once.MENU_PATHis the route. - TypeScript types, README ("Put your content in the OpenVibe menu"), the API surface and the pack test follow;
the devDependency moves to openvibe-contracts v0.324.0, whoseframe.menu-card@1tests validate every card
this module builds.
0.96.0 (2026-11-07)
openvibe-sdk/actorgainscontacts:list(),presets(),get(),create()(optionally from a preset),
update(),remove()andsetLevel(), for a person's agents as contacts and their approval levels (plan §12
A-1).contacts.list()isGET /api/v1/agentswith the person's own token, which now answers their contacts
(Vibe first), soagents.list()and the newagents.catalog()call the explicit public catalog
GET /api/v1/agent-systemsand keep their old meaning.tasks.createtypesagent_idandlevel.
0.95.0 (2026-11-07)
- Add
openvibe-sdk/cam:defineOvCamera({ src })injects OpenVibe.Cam's framework-free<ov-camera>element
once (defaulthttps://openvibe.cam/embed/ov-camera.js) and resolves to the class. It is browser-only by nature:
calling it in Node rejects instead of touchingdocument.OV_CAMERA_SRCis the default URL. - Type the element:
OvCameraElement(start(),stop(),capture(),flip()),OvCameraAttributes(facing,
filters, timer, mode, upload),OvCameraCaptureDetail/OvCameraErrorDetail, the two event types and the
HTMLElementTagNameMapentry.upload="off"is still the default: a capture is aBlobin the page until the
site sends it somewhere.
0.94.0 (2026-11-07)
openvibe-sdk/streamsgains S7:schedule(),listSchedules(),cancelSchedule()andremindSchedule()for
scheduled public agent streams;steps.post()andpostStep()for any harness;digest.read()andpoll.open()/
poll.result()for a Co-pilot's service token; andonControl()to honour pause, curtain, mute and end by polling
the stream's control log. The guards run before anything leaves the process (anscd_…id, a step's title and
status, two to four poll options, a 10–300 second poll).- Add
openvibe-sdk/reports:write()posts a session report with the person's own token (private by default,
idempotent per source and task ref) andread()gets one the caller may see. The Report service redacts every text
field; the client never sends a raw transcript. - Add
examples/cli-agent-stream.js: about 40 lines that start a stream, spawn any CLI command, post its steps and end
the stream with the command's result, using the same ticket path a Link desk uses.
0.93.0 (2026-11-06)
openvibe-sdk/exchange: the seller side gains the endpoint form, so any OpenAI-compatible server can sell, not
only an OpenVibe Link machine.seller.offer({ endpoint, models, price, hours, concurrency, audience, name })— with
nomachineId— registers the server on OpenVibe.Exchange's public seller API
(POST /api/v1/exchange/seller/offers) with the same developer key the buyer side uses. The owner is the person the
key acts for (a developer app's project owner), never a body field, and the SDK refuses an endpoint that is not https
before anything is sent.- The platform reaches that address only through its egress guard (https, a public DNS answer checked inside the
connection, never an IP literal, redirects judged again, a timeout and a response byte cap, no buyer identity in a
header) and verifies it exactly as a machine running Link (canaries, the model fingerprint, about 1% spot re-runs, the free
tier until verified, self-dealing earns 0). Its answers are labelledcommunity-endpoint:<id>on the receipt. seller.endpointOffers()lists the caller's endpoint offers with their verification;
seller.removeEndpointOffer(id)removes one.machineIdstill selects the Network registry form.seller.probe()is unchanged, andseller.offerstill probes the server's OpenAI model list first whenendpoint
is given (probe: falseturns that off).
0.92.0 (2026-11-06)
- Add
openvibe-sdk/chat-apps: the three chat apps (Telegram, Discord, Slack) in one place, so a service that answers
a chat app reads them the same way (the plan of 2026-10-30, stage A4: "your agent in Telegram, Discord and Slack"). - The checks:
verifyTelegram()(theX-Telegram-Bot-Api-Secret-Tokenheader, compared in constant time),
verifySlack()(v0 over "v0:<timestamp>:<body>", the timestamp within five minutes),verifyDiscord()(Ed25519
over "<timestamp><body>" with the application's public key) andverifyDelivery()(the provider names its own
scheme; a provider this does not know is never verified). The code came from OpenVibe.Watch, which has checked
these deliveries since 2026-11-03 (Watch #194 and #195). - The buttons:
signCallback()andverifyCallback()carry a whole signed payload with its expiry;
signButton()andverifyButton()carry a 16-character row id in 44 bytes, inside Telegram's 64-byte
callback_data, and the caller checks who may press it, what it does, that it is used once and that it has
not expired against its own row. A token that was changed, replayed or is too old is refused. - The messages:
parseTelegramUpdate(),parseDiscordInteraction(),parseDiscordMessage(),parseSlackEvent()
andparseSlackInteraction()give each app's payload the same shape (who said it, where, what they said,
whether it was said to the bot, and the button that was pressed), never the app's own JSON and never a
message the bot itself sent.splitText()splits one answer the way the app accepts it, andcommandOf()
reads a command and its arguments.
0.91.0 (2026-11-06)
openvibe-sdk/streams: the Agent Streams plan, stage 1 — an agent's own stream on OpenVibe.Live (a stream of kindagent, idags_…, no stream key and no slot, published with a ticket instead of a key).startAgentStream({ title, task, visibility, allowSites })answers{ stream, publish }: the stream (live.agent-stream@1) and the publish part (live.agent-stream-publish@1) with the WHIP url, the publish ticket (shown once; Live keeps only its hash) and the moment it dies.renewTicket(id)andrevokeTicket(id): the ticket lives four hours (the desk renews it at three) and revoking it is the kill switch — Live ends the WHIP session at once.state(id, { ticket })is what the desk polls every ten seconds:{ viewers, encode, curtain, ends_at }. Whileencodeis false the desk stops encoding and sends a snapshot every five seconds withpostFrame(id, bytes, { ticket, contentType })(JPEG or WebP, at most 200 KB).setCurtain(id, up, reason)raises the desk's privacy screen (while it is up Live pauses every viewer's consumer, so a viewer sees "Private screen" and never why),setVisibility(id, v)switches who may watch,end(id)ends the stream (the card stays, with the last frame and the VOD) andviewTicket(id)mints the player's view ticket (ten minutes).- The publish ticket is a bearer of its own: on the desk's routes (
state,postFrame) the desk sends the ticket, never the person's token. Every body is checked in the module before it leaves, and a bad id, visibility, curtain reason or an oversized frame is refused with a TypeError. - The capability behind it is
live.agent_stream.publishfor the audienceopenvibe.live(Contracts 0.298.0): a Link principal acting for its owner, Codes or Actor. Never a developer-app token, never another person's stream.
0.90.0 (2026-11-06)
- Subject assertions (ADR-003, the 2026-11-06 amendment; plan H1). A money call carries a Network-signed
proof that the person allowed the calling service to act for them, beside the name of the person. requestSubjectAssertion({ userToken, audience, token | tokenClient, network | internalUrl })asks Network to
sign the person's own access token into a 60-second assertion for one audience (openvibe.billing). The
person's token is the only thing that can produce one: without it Network signs nothing.verifySubjectAssertion(assertion, { jwks, audience, act, seen })checks the ES256 signature against
Network's JWKS (the EC keys, cached like the RS256 ones), the audience, the expiry and the jti; a forged,
expired, wrong-audience or wrong-service assertion is refused with a stable code
(assertion.bad_signature,assertion.expired,assertion.wrong_audience,assertion.actor_mismatch, …).createSubjectAssertionVerifier({ jwks, audience })remembers the jtis it has seen, so a replayed assertion is
refused inside the replay window.subjectAssertionHeader(assertion)gives thex-ov-subject-assertionheader for the call.
0.89.0 (2026-11-06)
- Add
openvibe-sdk/exchange: OpenVibe.Exchange, the buyer and the seller side of the model exchange (the plan of 2026-11-06, "For builders", item 7). - The buyer side speaks the Exchange’s v1 API with a Network developer key:
chat()(streaming and not, the OpenAI shape),models(),offers(model),catalog(),presets()(list, create, update, delete),usage()andreceipt(id). Every call takes anAbortSignal, and a 429 or a 5xx is retried with a jittered backoff: a safe method always, a write only when it is idempotent, so a completion is never run twice. - The seller side registers an offer in the Network’s user-owned AI offers — the same registry OpenVibe Link’s "Offer to the network" writes to (
POST /internal/ai/offers/report, the capabilitynetwork.resource.report), so any server that speaks the OpenAI API can be a provider and not only a Node. The SDK sends the document that registry validates, field for field, and refuses an offer the registry would refuse before anything is sent;earnings()andverification()read the seller dashboard (the tokens served, the credits earned, the canaries, the spot re-runs, the reputation, the uptime). - The webhooks (usage, earnings, an offer going down) carry
X-OpenVibe-Signature-V2;webhooks.verifychecks the raw body in constant time and refuses a timestamp more than 300 s either way.
0.88.0 (2026-11-06)
openvibe-sdk/sharelearns locked items (Locker). A share records how its item is sealed —lock: none | people | password | link, set byregister(ref, { lock })and answered by GET — anddescribe()cannot override it. A locked item is never public (share.lock_public), and the lock cannot change after registration (share.lock_fixed). Expand-only migration:shares.lock,share_members.key_slot,share_transfers.key_slot.- A people-locked item carries each member's key slot (
locker.people-slot@1).addMembers/updaterefuse a member without a slot (share.slot_required), refuse a slot for another subject or key, and answershare.key_changed(409) when the slot'srecipient_keyis not the RFC 7638 thumbprint of that person's key in Network's key directory now (GET /api/v1/people/:subject/keys, cached a minute). A person with no locker yet isshare.no_key. - Removing somebody from a people-locked item is a re-seal: the save carries
envelope(base64 of a people-lockedlocker.envelope@1), orremoveMember(..., { envelope }). The envelope must not still hold the leaving member's slot (share.reseal_invalid), and it is stored through the newonReseal(ref, envelopeBytes, tx)hook inside the transaction, before the member row goes; a hook error rolls everything back. Without an envelope:share.reseal_required; without the hook:share.reseal_unavailable. A save that carries an envelope may be up to 8 MiB; every other body stays at 64 KiB. - A re-seal must be fresh (
share.reseal_not_fresh): its nonce base may not repeat the one the module stored last, and a staying member's slot may not be byte for byte the stored one. Either means the envelope already stored was sent again, so the person leaving could still open what is served next. The last nonce base is kept inshares.reseal_nonce_base(expand-only). - A transfer of a people-locked item carries the recipient's
key_slotwith the offer (required, checked like a member slot); accepting drops the former owner's membership unlesskeep_access. - GET for a people-locked item answers each member's
key_slot, and for the owner each member'skey_fingerprintandfingerprint_changed, so the sheet can warn and re-wrap. - The agent client gains
addLocked('@name', { contentKey })(looks up the person's key, wraps, saves the member and the slot) andremoveLocked('@name', { contentKey | envelope })(re-seals for the owner and the members who stay, then removes);share({ base, token, network })names the key directory.transfer(to, { key_slot })passes the recipient's slot. openvibe-sdk/locker:open/rewrapaccept{ contentKey }, so an agent that sealed an item can open or re-seal it without a private key.openvibe-sdk/locker/serverexportspeopleSlotOk.- Pins openvibe-contracts v0.296.0 (was v0.292.0):
[email protected],[email protected]_slotand[email protected]_fingerprintare the shapes the locked sharing uses.
0.87.0 (2026-11-06)
- The 1.0 core surface is frozen now.
core,auth,events,dbandmediaare stable before 1.0:
removing or renaming one of their exports needs a major release.docs/one-point-oh.jsonkeeps each name's tier,
docs/api-surface.jsonrecords every export, andtest/surface.test.jsnow pins the exact export list of the five
entries, so a break there is a deliberate line in a diff rather than a surprise on a pin round. - Removed the two
authleaving names, planned for 1.0 indocs/one-point-oh.md:createCodeVerifierand
pkceChallenge. They were the two halves ofcreatePkcePair, which stays frozen; no OpenVibers repository'smain
imported them (checked 2026-11-06). UsecreatePkcePair()for both halves. signDeliveryandverifyDelivery(the v1 signature) stay for now. They are still the last leaving names, but
signDeliveryis imported by the test helpers of OpenVibe.Deals, Examples, Reviews, Tips, Trade and VIP onmain, so
they go once a pin round moves those ontosignDeliveryHeaders/signDeliveryV2.parseDeliverystill requires
v2 by default.
0.86.0 (2026-11-06)
- Add
openvibe-sdk/locker: the Locker envelope (locker.envelope@1), end-to-end encryption for anything on OpenVibe, in Node and in the browser. WebCrypto only (globalThis.crypto.subtle), no other crypto library and nothing hand-rolled. sealandopenfor text and bytes, andsealStreamandopenStreamfor files: a stream holds one 1 MiB chunk at a time, so a 64 MiB file costs a few MiB of memory. A stream that ends early throws at the end: discard what it yielded.- The size is padded with Padmé (PoPETs 2019): at most about twelve per cent over the real size, never below 256 bytes, and O(log log n) bits of leakage. Every chunk but the last is a whole chunk; the last holds the rest.
- A people slot names the recipient's key by its RFC 7638 thumbprint, and the reader checks it before it derives anything.
- The PBKDF2 iteration count is bounded on both sides: 600000 to 10000000, so a hostile header cannot make a reader work for hours.
- The three locks:
people(the content key is wrapped for each recipient with ECDH P-256 and HKDF-SHA-256),password(PBKDF2-SHA-256, 600000 iterations, the parameters read from the header) andlink(the key travels in the part of the link after the#). - Person key pairs (ECDH P-256 for content keys, ECDSA P-256 for the fingerprint), the eight-word fingerprint (sixty-four bits, in two groups of four) and the signature that proves a person owns their signing key.
wrapForandrewrap: adding a member to a people-locked item re-wraps the content key and leaves the content alone.- Add
openvibe-sdk/locker/server: what a service needs to know about an envelope without opening it, the header's shape and the envelope's size, with no key and no decryption. - Add
openvibe-sdk/locker/vectors: the shared test vectors, so the browser module (openvibe-shared/ov-locker.js) runs the same cases and gets the same bytes. - Add
recoveryWords(n): a phrase of words from the same 256-word list, one byte per word from the platform's CSPRNG, so 24 words carry 192 bits.isRecoveryPhrasesays whether every word of a typed phrase is one of the 256, andWORDSis the list itself. - A people lock can now be opened by key:
open(bytes, { person })finds the slot that names the reader's key, without a subject. An envelope with no slot for that key fails with the new codelocker.no_slot, which says the key is not on the list (locker.wrong_keyis for a key that is on it). - Add
inspect(bytes): what an envelope says about itself — the lock, the version, the padded size, the recipients' key thumbprints and the password's iteration count — without a key and without decrypting anything. - The fingerprint's two groups of four are joined by a middle dot, not a hyphen: a hyphen reads as one compound word.
- Pins openvibe-contracts v0.292.0 (locker.envelope.v1 and the key directory API).
0.85.0 (2026-11-05)
- Add server-only
openvibe-sdk/share: PostgreSQL/PGlite store and expand-only migration, audience/role checks, exact name resolution with persistent per-caller limits, expiring membership, access requests, and recipient-bound seven-day ownership transfers. Each mutation and its append-only audit and delivery records commit atomically. Transactional owner/audience hooks update local resources. - Add uniform HTTP router, OpenAPI 3.1 fragment, CJS/ESM/TypeScript exports and
sdk.share({ base, token }).for(kind, id)agent client. Conditional member updates detect concurrent ACL changes. Failures return generic 404 without resource content; staff overrides are audited. - Pins openvibe-contracts v0.291.0 (share.v1). No service integrations or media migration are included in this core package.
0.84.0 (2026-11-04)
openvibe-sdk/vibes: Vibes Pay for developer apps (MONEY.md, D12). A server creates a charge with its app
token (billing.vibes.charge), the person pays on OpenVibe's pay sheet in whole Vibes, and Billing moves them
once to the project's Vibes wallet.createVibesClient({appToken | tokenProvider, billingUrl})withcharges.create({amount, description, reference, return_url}),charges.get(id)andcharges.refund(id, {reason?}).amountis a positive whole number of Vibes;
a float, zero or negative amount is aTypeErrorbefore any request. The app'sreferenceis the
Idempotency-Key, so a retried create is the same charge. A token provider may be a function, an object with
getToken()/invalidate(), or one withauthHeaders(); a 401 invalidates it and retries once with the same key.
problem+json becomesVibesErrorwithstatus,code,bodyandretryable.webhooks.verify(rawBody, signatureHeader, secret)checks the raw body in constant time against
X-OpenVibe-Signature-V2: t=<unix seconds>,v2=<hex HMAC-SHA256 of "<t>.<raw body>">, and rejects a timestamp
more than 300 s from the server's clock either way. The legacysha256=<hex>(or bare hex) body-only form is
accepted. Bad input isfalse, never an exception. The same function is exported asverifyWebhook.
0.83.0 (2026-11-03)
openvibe-sdk/placement:planAi(), the AI Exchange face of the ONE planner (plan-state 2026-10-22 §E,
G-P1/P7; decisions D1 and D5). Not a second router:plan()still answersplatform.placement-result@1and is
untouched. Off by default — withoutenabled: truethe planner answers nothing and no behaviour changes.- The hard gates, before any scoring. Each one is a reason, never a weight: the exact artifact (an exact id
is never substituted), the commercial right (a paid managed route needsOWN_DEPLOYMENTor
AUTHORIZED_WHOLESALE), the privacy class and the trust tier, the capabilities (tools,json,vision,
stream), the context bound, the health, and the cost ceiling. A candidate with no rate is never assumed
free: it is rejected withno rate: the cost is unknown. - The objective is the cost of one success.
cost_per_success = expected_cost / max(p_success, floor), so a
candidate that is cheaper per token but fails more often loses to one that is dearer per token and succeeds
more.p_successcomes from optional quality evidence with a confidence: sparse evidence is marked, not
trusted (no confidence, or fewer than 20 samples →sparse_evidence: true, no confidence, and the probability
is pulled back to the prior), so a thin measurement can never dominate the ranking. - Money is integer nano-USD (decision D1), never a float of dollars:
ceil(tokens * rate_nano_per_m / 1e6)
charged once per token class (input, cached read, cache write, output) plus the per-request fee, and the cost
per success rounded up to a whole nano-USD.openvibe-sdk/costgainsnanoUsd,chargeTokens,
expectedCostNano,costPerSuccessNanoandformatNanoUsd; the rates travel in the flatai.model@1
retail_rateshape or the nested settlement shape. - The answer explains itself.
explain.rejectedis[{ id, reason }](asai.route-explain@1names it) and
explain.objectivesays what was optimised for. Every reason is stripped of keys, subject ids, hosts, IPs,
URLs and paths: no secret and no internal host ever reaches the answer. - Hysteresis and affinity stay the ones
plan()has:currentis kept unless another candidate is clearly
better (minGain), andaffinitykeeps a workload on the deployment it is warm on.
0.82.0 (2026-11-02)
openvibe-sdk/eventsoutbox:pending()andrejected()keep their count (countsCacheMs, 30 s when
NODE_ENVis production, off elsewhere), and callers at the same moment share one count. The metric gauges and
readiness ask on every scrape and probe, andrejected()has no index, so a large outbox was read whole each time:
115 ms on Deals, 56 ms on Trade, 49 ms on Sources, 45 ms on Search and 24 ms on Reviews (pg_stat_statements,
2026-11-02). PasscountsCacheMs: 0to count on every call.
0.81.0 (2026-11-02)
openvibe-sdk/vision: familiar faces (face.familiar), F1 of the L24 plan. The camera owner's own Vision
library, reached with the owner's token:list,create({ camera_ids, region, threshold }),get,updateandremovethe library. The region picks
strict mode (Illinois, Texas, Washington, the EU, the UK; an unknown region too) or open mode.enroll(libraryId, image, { name, subject })names the crop the camera saw; in strict mode it answers a
pending_consentperson with no template and a privateconsent_request.urluntil the person accepts.match(libraryId, image)matches only that library: a top match over its threshold, orunknown person.people,patchPerson(rename or mute),removePerson,requestConsent,checkConsent,acceptConsentand
denyConsent.- The types in
types/vision.d.tssay what the JSON says; no template, crop or other library is ever in an answer. openvibe-sdk/meme: the client for OpenVibe.Meme's encyclopedia, trends, analyser, deep dives and generator.createMemeClient(client, { baseUrl? })(both browser and server; audienceopenvibe.meme, service idmeme).search({ scope, q, status, label, kind, sort, window, channel, limit, cursor }),entry(slug),revisions(slug),
trends({ window: 'day'|'week', limit }),analyze({ url | media_id }),research(topic)(202, poll
researchGet(id)),saveResearch(id)andgenerate({ template | template_slug | topic, lines? }).analyzerefuses locally unless exactly one ofurl/media_idis given, andgeneraterefuses with neither a
template nor a topic; no request is sent for either. Types are intypes/meme.d.ts; the browser bundle includes
the client. Thememenamespace is onopenvibe-sdkand in the browser bundle, andopenvibe-sdk/meme
(createMemeClient) is in docs/api-surface.json.openvibe-sdk/app-signin: "Sign in with OpenVibe" for developer apps, in one call.createAppSignIn({ clientId, clientSecret, redirectUri, networkUrl, audience, scope, session })gives a router
with/login,/callback,/logout,/meand/health, built on the SDK's own auth helpers
(authorization code + PKCE, the state check): nothing here re-implements OAuth.- The same recovery as
openvibe-sdk/sso0.73.0: a lost or expired flow and a code the Network no longer accepts
start the sign-in once more by themselves; the second failure shows the same plain page (failurePage, now
exported byopenvibe-sdk/sso), with "Try again" and the way back. Neither ever sets a session. - The same client check:
GET /healthandcheckClient()answer whether the Network accepts the app's
credentials (a code that is never issued; cached 10 minutes; 503 only when they are refused). - The app token is verified offline against the Network's JWKS and only trusted when it was minted for THIS app
(sub = app:<clientId>), beforeon_behalf_ofnames the person. - The session: your own store (
session: { get, set, clear }) or, by default, an in-memory store with an opaque
httpOnly cookie. The secret, the PKCE verifier and the token never reach the browser and never appear in a log. openvibe-sdk/live: the rest of the Go live screen.live.slots.history(id)(the slot's past streams),
live.slots.quality(id, { resolution, fps, bitrate })andlive.slots.titleIdeas(id, { weekday, hour, title })(the
card's ✨ Suggest: three titles for the next stream). With these, an app does everything Live's Go live page does
(https://openvibe.live/docs/streams-api).
0.80.0 (2026-11-01)
openvibe-sdk/explicit: nudity is art. The owner, 2026-10-31: "naked and nude is fine… stop being against art on
my platform."isExplicit()now gates what OpenVibe.Live's own rule gates (Live#469), word for word.- Still gated: sex acts and pornography (sexual acts or activity, having sex, porn, masturbation, intercourse,
oral sex, penetration, pornographic or sexually explicit content), and any text that puts a child and nudity
together. - No longer gated: nudity and body words on their own. A nude figure-drawing class publishes; "a child is shown
naked" never does.
0.79.0 (2026-10-31)
openvibe-sdk/machines: the network's machines hub (plan L34, step M2; Contracts 0.258.0). A person's machines
are the network's: Bot stays the registry underneath, and OpenVibe.Network fronts it on openvibe.link/machines for
the person and for the apps the person consented to.createMachines({ networkUrl, token, fetch, timeoutMs }):tokenreturns the caller's own bearer token — the
person's session token, or an app's token holdingnetwork.machines.readornetwork.machines.managewith the
person's consent.list()→{ machines },get(id)→{ machine },catalogue()→{ roles }(what a machine may do for the
network),pair()→{ pairing }(a one-time code and the command to run),rename(id, name)→{ machine },
roles(id, roles)→{ machine }(the whole set, not a change),revoke(id)→{ machine }.- A refusal throws a
MachinesErrorwith the hub's ownstatusandcode(capability.denied,
capability.owner_denied,registry.unknown_role, a 404 for a machine that is not the caller's). A hub that does
not answer is amachines.timeout, never a hang. - The machines' own capabilities stay where they are:
openvibe-sdk/bot'smachines.desk()andmachines.chat()
talk to Bot itself and are unchanged.
0.78.0 (2026-10-31)
openvibe-sdk/consent: the client for the L24 consent registry. Nothing a person makes is off-limits to AI or
data use by design; each use is off until that person turns it on in OpenVibe.Network. A service asks this check
before it uses a person's content or likeness.createConsent({ networkUrl, token, ttlMs = 60_000, fetch, log }):tokenreturns a service token holding
network.consent.read.allowed(subject, purpose, { kind, client, feature })→ a boolean;
filter(subjects, purpose, opts)→ the allowed subset, in input order, in batches of 500;
onChange(event)drops a subject's answers for a purpose onnetwork.consent.changed(wire the payload or the
envelope);mock(map)answers from{ subject: ['purpose'] }without the network.- Fails closed: a network error, the 2 s timeout, a non-200, a malformed answer or a token that cannot be
minted is false for everyone asked. A failure is never cached as a yes; it logs at most once a minute. - An answer is cached for
ttlMs(60 s), per subject, purpose, kind, client and feature; an answer in flight when
a change arrives is not stored, so a revocation cannot be overwritten. - Server-side (
browser: null): the token is a service token. Types are intypes/consent.d.ts, the dataset-build
example indocs/consent.md.
0.77.0 (2026-10-31)
- AI text may describe people (the owner, 2026-10-31: "there should be no reason it can't describe people … remove
all that crap").openvibe-sdk/no-looksis deprecated.noLooks,noLooksTagsandhasLooksreturn what they are
given, andRULES_VERSIONis'off', so a service that still calls them keeps working until its next release
removes the calls. openvibe-sdk/explicit:isExplicit(...texts), the rule that stays. AI text that says a scene shows sexual
content is not published.no-looksre-exports it.
0.76.0 (2026-10-30)
openvibe-sdk/placement: placement v1 — quality, the person's rules, learning and a reason (agent plan A1).place(requirements, offers, ctx)isplan()with the plan's road: the person's rules and ownership, a quality
floor (default 0.5) with a fallback, the success estimate, cost per success, escalation and one plain sentence
saying why. It returns{ result, line, ranked, rejected, fallback };resultis exactly whatplan()returns
for the offers left in, validated as today.learn(outcomes, {person, key, kind, ok, at})andsuccessOf(outcomes, {person, key, kind, prior, now})keep
and read plain counts with a 14-day half-life, a Beta mean, Bench priors, the global weightmin(1, 20/nG)over
everyone else's counts (the person's rows are not counted twice) and the dotted-kind fallback.
escalate(previous, failure, requirements, offers, ctx)handles quality, busy, offline, refused, budget and
timeout, and asks the person after two escalations. It learns the failed try exactly once and returns the new
counts asoutcomes; the caller persists that set and must not record the same failure again.
optionKey(offer)andlineFor(facts)are exported too.- On the person's own free machine the line claims only what the step asked for:
harness:tools(the harness's
own tools, including the signed-in CLI) says "your sign-in", andharness:host-access(the person's machine and
its files) says "your files". With neither it says "your own machine". plan()takes an optionalsuccess: { [offer_id]: p }: when present the cost part of the score is
estimated_cost_usd / p, whileestimated_cost_usdin the result stays the raw cost. When absent, scoring is
unchanged.- Behaviour fix:
user-ownedis never a default. Whenrequirements.trustis absent,plan()now uses
DEFAULT_TRUST(first-party,partner,community,external). The contract
(platform.workload-requirements@1) says exactly that: "when absent, first-party, partner, community and
external. user-owned is never a default: a user-owned offer is eligible only when this list names it". A
stranger's machine must never be a default.TRUST_ORDERkeeps all five classes as the preference order;
DEFAULT_TRUSTis the four-class default.place()also requires a user-owned offer'sownerto be the person
of the step. - Streaming machines. A step that asks for a GPU or 2 or more cores excludes an offer whose
health.streamingistrue, with the reason "it is streaming". The field is not in
platform.resource-offer@1yet (Link reportsscreen.statein its own status, Bot's offer does not carry it),
so this release fixes the name Link can start sending; a top-leveloffer.streamingis tolerated too. - README and [docs/placement.md](docs/placement.md): the API, the rules, the estimate, escalation and the line
templates.
All notable changes to openvibe-sdk. The package follows semver; while it is 0.x, a minor
release may change an API and says so here.
0.75.0 (2026-10-30)
openvibe-sdk/redact: one secret scrubber for Link and AI (Memory M2).redact(text)→{ text, count }ports OpenVibe Link'sinternal/memory/redact.go(Node #87): the same shapes,
the whole-text pass with a sentinel before the per-item pass, the unterminated private key block, Luhn card
numbers, and the deliberately wide environment-assignment rule.onlySecret(text)says an item was nothing but a
secret, so AI refuses it.test/redact-corpus.jsonholds every case of Node'sTestRedactEveryShape, the split and unterminated key
blocks, the.envblock and the harmless texts that must not change. Node copies the file and runs the same
cases against its Go scrubber, so the two ends share one list and one corpus.- Pure functions, no dependencies: exported for Node and the browser, with ESM and TypeScript entries.
All notable changes to openvibe-sdk. The package follows semver; while it is 0.x, a minor
release may change an API and says so here.
0.74.0 (2026-10-30)
openvibe-sdk/account-data: telemetry leaves with the person, and a guard finds a table nobody covers (ADR-033).createAccountDatahandlestelemetry_samplesby itself when the table exists: the person's samples are exported
astelemetry.json(at,service,operation,resource,status,latency_ms), and a deletion anonymizes
them (subjectset to NULL, the sample kept) instead of removing them. A service that lists the table itself
keeps its own entry.uncovered(db, {tables, taskKeyed, exempt})lists every table in the current schema with a person-shaped column
(subject,owner,created_by,user_id,person) that the table map, the telemetry handling, a task-keyed
list or a reasoned exemption does not cover. A service's test asserts the list is empty, so a new table fails CI
until it is covered or exempted with a reason.
0.73.0 (2026-10-30)
openvibe-sdk/sso: a sign-in that recovers, explains itself and can be checked.- A lost or expired flow (no or another state cookie) and a code the Network no longer accepts (
invalid_grant) start
the sign-in once more by themselves. A two-minuteov_oauth_retrycookie keeps it from looping, and neither ever
sets a session. - A second failure, a client the Network refuses (
invalid_client) and a Network that does not answer show a small,
self-contained page in plain words, with "Try again" and "Back to <site>", in place of a bare line of text. - A refused client is logged as a configuration error that names
OV_OAUTH_CLIENT_ID/OV_OAUTH_CLIENT_SECRET. GET /auth/healthandcheckClient(): a token request with a code that is never issued.invalid_grantmeans the
client is fine;invalid_clientthat its id or secret is wrong (503). The answer is cached 10 minutes.- Why: OpenVibe.Cam ran for a day on the placeholder secret from its
.env.example. Every sign-in ended on "The
Network refused the sign-in code", and nothing checked. siteNameoption for the pages.legacyCookies: { access, refresh }moves a site off its own sign-in code without signing anyone out. Its old
cookies are read as the session until the next sign-in or refresh writesov_token/ov_refreshand clears them;
logout clears both.httpOnlyAccess: truekeeps the access token httpOnly, as those sites did (their navbar asks
/auth/me).
0.72.0 (2026-10-29)
openvibe-sdk/vision(VIS-5, plan §5.8b): typed browser and server calls for Vision's image tools, private search, face privacy and consent APIs, and MCP discovery. The service enforces token grants and consent; the client preserves problem errors and binary blur results.
0.71.0 (2026-10-28)
openvibe-sdk/bench(BENCH-4, plan §4.12): browser-safe Bench client for kinds, tasks, pinned rounds, outside entries, judgements, boards, model summaries, streamed open JSONL datasets and private evaluations.mcpTools()lists the live Bench MCP tools throughPOST /mcp. Calls use thebenchservice origin andopenvibe.benchaudience; optionalactingSubjectsendsX-OV-Subject. HTTP errors remainOpenVibeError.
0.70.0 (2026-10-28)
openvibe-sdk/search: the Search Fabric (Contracts 0.225.0search.fabric.query; plan 2026-10-28 §4.5). One
query over several scopes, routed the cheapest way that answers.search.fabric(text, { scopes, freshness, depth, answer, limit, cursor, owner, type, lang, site })returns
search.fabric-result@1:
-results: each has a scope and a kind (a document in the index, or a web page with its provider, URL and
dates);
-groups,citations, androute.explain(what each step did and what it cost).- Scopes:
-networkandmine(the person's own; a service passesactingSubject);
-web: OpenVibe's open index first, then DeepSeek for a signed-in caller or a service, then Wikipedia,
cached;
-news,images,video,products,people,placesandcode. search.query(text, { scopes })is the same call; withoutscopes,query()is the index search as before.search.providers()lists the web ladder'ssearch.provider-offer@1items.- Reads use GET, so the client may retry them.
- Types:
FabricOptions,FabricAnswer,FabricResult,FabricExplainStep,ProviderOffer;fabricOf()builds
the query string.
0.69.0 (2026-10-28)
openvibe-sdk/tools: domain history (OpenVibe.Tools'/api/v1/domains, 2026-10-28). What the WHOIS and RDAP
tools saw of a domain over time, public registration facts only (registrar, dates, status, name servers, DNSSEC;
never contact details).tools.domains.history('example.com')gives its snapshots and what changed when; it is null when nothing was kept.tools.domains.lists({ limit })gives the most looked up domains and those of them expiring soon (each looked up
five times or more).- Both read without a token; a malformed name is refused in the client. Types:
DomainHistory,DomainLists,
DomainFacts.
0.68.0 (2026-10-28)
openvibe-sdk/food: food boxes (OpenVibe.Food's/boxes, 2026-10-28). Every meal kit, ready meal and food box
side by side:food.boxes.list({ kind, diet, q }),.get(slug)(with the box's codes on OpenVibe.Coupons and its
score on OpenVibe.Reviews, null when there are none) and.cost(slug, { people, meals, weeks })(what its stated price
comes to).- Each box's facts were read on the service's own pages, with the page and the day (
sources,checked). - Types:
FoodBox,FoodBoxCost,BoxKind.
0.67.0 (2026-10-27)
openvibe-sdk/bot: a machine's own models (AI on your own machines; OpenVibe Link v0.27.0's ai plugin).bot.machines.chat(id, { model, messages, max_tokens?, temperature? })asks one of them for one reply (bot.command@1
kindai) and answers{ model, message, usage?, ms? }. A machine that does not answer throws anOpenVibeError
with codebot.ai_nack.bot.machines.aiModels(id, on)is the owner's "Let my AI use this computer's models".- Types:
Machineformachines.list(), withcomputer.ai(the models and whether a server answers) and
computer.ai_speed(how fast the last model answered there, Link v0.29.0);MachineChatInputandMachineChatResult;
MachineCommandInput.kindtakesai.
0.66.0 (2026-10-27)
openvibe-sdk/bot: machines and desks (the Desks plan; the roadmap's SDK section).bot.machines.list(),
.computer(id),.command(id, { kind, value })(bot.command@1's computer kinds) and.deskTurn(id, desk).bot.machines.desk(id, { name?, kind?, profile? })is a desk of your own on a machine: a browser alone or a whole
screen, with its own files and sign-ins (OpenVibe Link v0.19.0 to v0.26.0).start,stopandstatus.browser,screen,inputandexecon that desk only, never on the person's own screen.exectakes
detachfor an app that keeps running until the desk stops.turn()says whether a person is using the desk now.- Each method answers the machine's
data. A command the machine does not do throws anOpenVibeErrorwith code
bot.desk_nack.
0.65.0 (2026-10-26)
openvibe-sdk/sso: switch account and use another account (the owner, 2026-10-26: sign-in "like the Google
sign-in does … open to SDK and API").GET /auth/login?prompt=select_accountshows OpenVibe.Network's account
chooser,?prompt=loginits sign-in form, and?login_hint=<username>names the account the chooser continues as when
the browser has it (OpenID Connect; Network's docs/sign-in.md).?silent=1staysprompt=none; anything else is
dropped.openvibe-sdk/chat: Vibe, support inboxes and feeds (#78; the owner, 2026-10-24: OpenVibe Chat "fully modular
and SDK and API").createChatClientgainsvibe.*(a person's chats with Vibe),inboxes.*(the team's side of a
support inbox: conversations, replies, members, verified visitors, saved replies, actions, assign, translate) and
feeds.*(the owner's side of a feed;viewandonLivesend no token).createInboxVisitor(client, { inbox, token, user })is a visitor's side with no account and no cookies (visit, send, messages, handoff, rate, sendPicture), and
createFeedPublisher(client, { feed, key })publishes vibe coding events with a feed's key (claudeCode(hookJson)).openvibe-sdk/themes(#80, #81; the owner, 2026-10-25: the theme system in the SDK and API).createThemesClient
reads and sets the account's theme on Network (catalog with seasons, active, set, setDisplay, seasons, submissions,
import), andfollowTheme(fn)keeps a page, canvas or game in step with the theme someone picked on any OpenVibe site
(readTheme(): id, mode, tokens, display, season).DISPLAY_OPTIONS.schemeismanual | auto(Shared 4.12's Light /
Dark / Auto), and each season says its light half.openvibe-sdk/play:titles.remove(#82) deletes a sandbox title you manage (OpenVibe.Play#42; a developer's
sandbox app registers up to 3sandbox-titles since Play#41). A production title is kept (409play.title_kept).openvibe-contractsv0.213.0 (#79).
All additions: nothing exported before changes.
0.64.0 (2026-10-25)
parseDeliveryrequires a v2 signature by default. A delivery withoutX-OpenVibe-Signature-V2is refused
unless the caller passes{ requireV2: false }, which is deprecated. A v1 signature (X-OpenVibe-Signature) signs no
time, so a captured delivery verified forever. OpenVibe.Events sends only v2, and every production receiver
already passedrequireV2: true, so nothing that runs changes. A test that signs withsignDeliveryand parses
without options now getsnull: sign withsignDeliveryHeadersinstead.signDeliveryandverifyDeliveryare deprecated and leave in 1.0, withcreateCodeVerifierand
pkceChallenge(the halves ofcreatePkcePair, unused anywhere).- What 1.0 freezes, name by name. [docs/one-point-oh.md](docs/one-point-oh.md) puts each name of the five core
entries in a tier (frozen, platform or leaving), with the reasons and how many repositories use it.
docs/one-point-oh.jsonholds the lists.test/surface.test.jsfails when a name there has no tier, and
scripts/surface.js --checkmarks a breaking change with its tier. types/events.d.ts:signDeliveryHeadersreturns the timestamp and the v2 header. The type had also listed a v1
header that the function never set.- no-looks knows nudity words (
naked,half-naked,nude,undressedand their variants), andhasLooksskips
character names (Iron Man, Pac-Man, Spider-Man), asnoLooksalready did. Live's AI moments
job titled a clip "A Naked Man Lying On Their Back With Legs" on Live's home (2026-10-24), and a Community paste
read "Noteworthy Half-Naked Moment".hasLooksnow rejects both, andnoLooksrepairs sentences as it does for
"shirtless".RULES_VERSIONchanges, so services that repair stored rows run their repair again. - no-looks knows clothes below the waist after "in" (
shorts,jeans,pants,trousers,leggings,skirts,
sweatpants,swimwearand the like): "The streamer in shorts kneels on grass…" had survived every one of
Community's repairs since April 2026. A shop word after it ("in the pants department") or a pocket keeps the phrase.
Team kits and uniforms are not touched. openvibe-sdk/front-page(new):frontPageOk(text)says whether a title or line may lead a front page (no
built-in slur category, no strong profanity in five languages, no hate topic), andcontainsCoreSlur(text)is the
slur check alone. The categories are OpenVibe.Chat's built-in ones, ported unchanged. An anonymous screenshot titled
with a slur sat in OpenVibe.Community's "Most viewed" (2026-10-24), and Live had its own copy of the rule for its
quote strip; front pages now share one.no-looksgainsisExplicit(...texts): true when AI text says a scene shows sexual content (body parts, sex
acts, pornography, nudity). The looks rules only remove an adjective ("a man … reaching toward their genitals"), so a
service asks before it publishes AI text at all. Live's AI auto-clip 483 went public on openvibe.media with such a
description on 2026-10-23. Live#344 keeps its own copy of the pattern until it pins this release.RULES_VERSION
includes it.- A JWKS client with no key retries by itself on its backoff (1 s up to 5 min) until a fetch succeeds, instead of
only when a token next needs checking. On 2026-10-23, Trade and the Tools gateway started before Network after the
reboot and reported "no key" on/statusfor six hours, until a restart; their first sign-in would have waited for
the fetch.
0.63.0 (2026-10-24)
- The public surface is pinned.
docs/api-surface.jsonlists every name each of the 66 entries exports and what it
is;test/surface.test.jsfails when they disagree, andnode scripts/surface.js --checknames each change as
breaking (a name gone, a kind changed, a constant's key gone) or added. The first step toward 1.0's freeze of
core,auth,events,dbandmedia. Nothing in the package's behaviour changes. - Outbox relays pause on a 429.
createPgOutbox(and socreateServiceOutboxand the usage relay) used to hit
Events' per-actor limit again on every tick after a 429, because the rows it claims next are under the same limit.
Search's re-index bursts were refused 935 times on 2026-10-23, and every event was delivered only on a retry. - A 429 now pauses the whole relay until the limit's window reopens: its
retry_after_seconds, else a minute, at
most an hour. - Passes before then send nothing. The refused rows back off as before, and a 429 never rejects a row.
- Nothing changes for a service that stays under its limit.
createServiceOutboxprunes sent rows. Rows sent more thanpruneAfterMsago (default 7 days;falsekeeps
them) are deleted every 6 hours while the relay runs, and once a minute afterstart();out.prune()runs it now.- Services that never called
outbox.prune()kept every event they ever sent: on 2026-10-23, Work had 52,130 rows
(119 MB) since 10-10, and Trade, Deals and Help since September or early October. - A service that already prunes itself is unaffected (a second prune finds nothing).
0.62.0 (2026-10-23)
openvibe-sdk/no-looks: pronouns. A pronoun is a guessed gender too, so the 27 Community summaries repaired on
10-22 that still said "he looks at the camera" or "on his device" are covered. Outside quotes:- he and she become "the person" (the verb stays as written: "he looks" → "the person looks"; he'll → the person
will, he'd → the person would); - his and her before a word become "their"; him, and her after a verb, a preposition or at the end, become "them";
- himself and herself become "themself", hers and a final his "theirs".
Quoted speech stays word for word.RULES_VERSIONchanges, so services that repair stored rows run their repair
once more. openvibe-sdk/live: where a slot publishes.live.slots.profile(slotId)reads a slot's settings and ingest
(GET /api/streams/managed/:id/profile, owner): its stream key, Live's WHIP origin and RTMP URL, or, for a slot
OpenRestream ingests, OpenRestream's own WHIP and RTMP URLs and no key.live.whip.forSlot(slotId)turns that into
{ url, key, authority }to pass tolive.whip.publish(), so going live from code no longer builds the WHIP URL by
hand (OpenVibe.Examples' go-live example, plan G2).
0.61.0 (2026-10-22)
openvibe-sdk/no-looks: the owner's rule that public AI text never describes a person, as one shared filter.
noLooks(text),noLooksTags(tags),hasLooks(title)andRULES_VERSION, moved from OpenVibe.Live
(Live#298–#302) with its test corpus, so Community, Search, MediaHub and every service that stores public AI text
run the same rules. Pure and dependency-free (Node and browsers).
0.60.0 (2026-10-20)
openvibe-sdk/hostandopenvibe-sdk/news: the public reads of the last two products without a module.
Host: the hosting catalog (providers, plans, promos, community offers, domain endings with registrar prices, a
name's availability, YABS benchmarks), with Host's own filters (any other key is refused). News: topics and the
published and retracted stories with their sources, timeline and perspectives. Both are sent without a token;
editing stays with each site's editors.
0.59.0 (2026-10-20)
openvibe-sdk/space(createSpaceClient): OpenVibe.Space for apps and CI, public in openvibe-contracts 0.209.0.
Deploy a static site as a tar.gz archive or files with their paths (activate at once, or keep a preview), activate,
roll back, read a deploy's log; manage projects (members as deployer, maintainer or owner), sites, their serving
settings and Git source, and custom domains. An app is a principal of its own in Space and reaches only the
projects a person added it to. Uploads and creations are never retried on their own.
0.58.0 (2026-10-20)
openvibe-sdk/coupons,openvibe-sdk/dealsandopenvibe-sdk/trade: what a person does for themselves
on those sites, public in openvibe-contracts 0.208.0. Coupons: which store a site is, its codes, submitting a code and
reporting whether one worked (what a browser extension needs). Deals: hot and new offers, posting, price
observations, votes, flags and watches. Trade: instruments with their sourced observations and filings, and the
person's watchlists and alert rules. Moderation stays with each site. Writes that make a new row each time are
never retried on their own.
0.57.0 (2026-10-20)
openvibe-sdk/reviews(createReviewsClient): OpenVibe.Reviews for apps, whose reads became public in
openvibe-contracts 0.207.0: resolve a business, product or place from its identifiers (a match or candidates), list
and read entities with their aggregate, signals, summary and history, and send a correction to the editors for the
person (never retried on its own).openvibe-sdk/wiki(createWikiClient): OpenVibe.Wiki's reads: search, spaces, and pages with their revisions,
diffs and citations, as the person may read them (without one, public pages only).
0.56.0 (2026-10-20)
openvibe-sdk/blog(createBlogClient): OpenVibe.Blog, the first product whose API only its own site could use
(its capabilities became public in openvibe-contracts 0.206.0). Read a blog's published posts and feed; for the
person who authorized the app, create drafts, edit (a new revision,expected_revisionguards a race), publish,
unpublish, schedule, revert and delete posts. Blog acts for an app only as itson_behalf_ofperson. Creating a
post or reverting makes a new one each time and is never retried on its own.
0.55.0 (2026-10-20)
- Six more products through the SDK, each a server module over the routes its capabilities name in Contracts:
openvibe-sdk/food(createFoodClient): places to get food, the food list with prices, meal plans, a
person's pantry with its "what can I make" matches, and AI recipe ideas.openvibe-sdk/actor(createActorClient): tasks for AI agent systems (create, list, get, cancel, and
tasks.events, the SSE stream until the task ends), the public agent list and dry-run routing, and a person's
own agents with their runs and memory.openvibe-sdk/run(createRunClient): jobs on worker nodes (submit with an Idempotency-Key, list, get,
cancel,jobs.eventsuntilend, a browser stream ticket),forProjectfor X-OV-Project.openvibe-sdk/video(createVideoClient): OpenVibe.Video's playlists of recordings and clips, and a
person's public playlists.openvibe-sdk/work(createWorkClient): the job board's search and sources, saved searches, tracked
applications and the agent's help with one.openvibe-sdk/zone(createZoneClient): a project's object zones and their usage.- A write that makes something new each time (a plan, recipe ideas, an agent, a run, a memory note, a playlist or an
item in it, a saved search, an application without a listing) is never retried on its own. Keyed writes (Run's
submit, Zone's create, an Actor task with an idempotency_key, an application for a listing) and changes are.
0.54.0 (2026-10-20)
openvibe-sdk/tips: OpenVibe.Tips through the SDK (it had 19 capabilities and no module).createTipsClient(client)
covers its/api/v1: a creator's tip page (profiles), tipping (tip→{ interaction, checkout_url, checkout_ref }) and paid messages, TTS, media and sound requests (send.*), receipts and external tips
(interactions),goals,moderation(queue, log, moderators),overlays(tokens, configs, a test alert) and the
supporter's ownme.export/erase. Tips replays an Idempotency-Key once, so every write carries one and a
retried tip is safe.
0.53.0 (2026-10-20)
openvibe-sdk/inventory: OpenVibe.Inventory through the SDK, as the owner asked of it ("cross site network wide
shared multi purpose modular system … through sdk / api").createInventoryClient(client)covers its/api/v1: the
public reads (kinds,definitions, a person'sitemsandequipped,equipped([subjects])for up to 100 at once),
the person's own (me.items,me.equip,instance_id: nullclears a slot), and an issuer's (definitions.create/
update,grants.create→{ instance, created },instances.consume/revoke). A grant is retried safely: its
body'sidempotency_keyis what Inventory dedupes on.forPerson(usr_…)is the same client sendingX-OV-Subject
for a service acting for a person. Ids are checked against Inventory's own rules before any call.
0.52.0 (2026-10-20)
openvibe-sdk/live: OpenVibe.Live in the SDK, its flagship product had no module.createLiveClient(client)
covers the routes documented at openvibe.live/documentation: who is live (streams.list,recentlyOnline), a
channel (channels.get,url,toggleFollow), slots (slots.create…regenerateKey), going live
(goLive(slotId, opts)→{ stream, endpoint }), the session (update,heartbeat,endpoint,end), the content
feeds (content.feed,content.moments), and WHIP publishing (whip.url,whip.publish→{ answer, location },
whip.end). Signed calls carry the person's Network token;/whipgets only the stream key, never the client's
token. Writes carry no idempotency key and are never retried (a second go-live is a second session).
0.51.1 (2026-10-17)
openvibe-sdk/sso:GET /auth/mefor a signed-out visitor (or an expired, foreign or non-session token) answers
200 { user: null }instead of 401, as Blog, Community and Wiki do. The shared navbar treats both the same; the 401
showed as a failed request in every signed-out visitor's console.
0.51.0 (2026-10-17)
openvibe-sdk/commerce: subscription tiers priced by OpenVibe.Billing (openvibe-contracts 0.201.0).createIntent
(kind subscription) andsubscribeWithCredittake an optionaltier;listTiers(creator)reads a creator's tier
prices andsetTier({ creator, tier, priceCents, status?, key })sets one (caps.tier=billing.tier.manage;
listTiersreads with the intent capability whencaps.tieris not given). Without a tier nothing changes.
0.50.1 (2026-10-17)
openvibe-sdk/db: a nesteddb.tx(a savepoint) marks its scope done when it ends, like the outermost one. Async
work started inside a savepoint and still running afterwards (a timer, a fire-and-forget call) no longer keeps
using the transaction's connection after it goes back to the pool.- Outbox: the relay's timers run in the async context the outbox was created in. A
kick()from inside a
transaction that stays open (an inbox handler that goes on fanning out) used to make the relay's ticks run inside
that transaction, on its connection. OpenVibe.Network logged "Client was closed and is not queryable" for about a
minute after each go-live notice.
0.50.0 (2026-10-16)
openvibe-sdk/play: cloud saves —play.saves.put(title, player, slot, { data, version }),get(null when the
slot is empty),listandremove, for OpenVibe.Play's new save slots (8 per player per title, 64 KB of JSON each,
versioned: a staleversionis409 play.save_conflict). Same token and capabilities as scores and achievements.
0.49.1 (2026-10-16)
- Outbox: Events'
409 events.loop_detectedis a permanent refusal: the row is marked rejected instead of being
retried with backoff forever (Search retried 18 such events up to 35 times each after a Reviews loop was fixed).
Every other 409 is still retried.
0.49.0 (2026-10-15)
openvibe-sdk/download:createDownloadClient({ key })for OpenVibe.Download (the owner's "file storage and backup"):
files.list/all/get/remove,folders.list/create/ensurePath,upload({ path | data, name, folderId })in the
drive's parts,usage(), and `sy