Skip to content
-
Capability pools placed by the SDK planner, with explain on every run (plan T6 phase 1; openvibe-sdk v0.25.2): a route may name a capability instead of a fixed primary and fallbacks, and the router asks openvibe-sdk/placement for the order over every provider that can serve it (price from the model rate cards, latency and health from provider_stats_daily and the breaker, hysteresis through placement_state), then runs its unchanged attempt/retry/breaker/log loop over that order. The seeded text routes (Live's roles, default.chat, default.json) are pools whose authority is the shared provider (objective correctness): it serves while healthy and the local model takes over only when it is not, so a small local model never wins Live's text work on price. media.local/media.paid stay pinned; speech and embeddings answer an explicit 503 provider.unavailable until a second provider exists (never synthetic output). Every run response and GET /api/v1/runs/:id carry explain {objective, reasons, selected, candidates}. Migration 0003: routes.capability/constraints/pinned, runs.explain. AI_FALLBACK_* and AI_MODEL_<ROLE> are no longer read. npm test and test:pg 27/27.OpenVibers · 29 Sept 2026
5a418a5
-
chat.global and chat.profile workflows under Chat's own namespace (plan T3): OpenVibe.Chat runs the chat summaries now, with its own token (ai.run.* on chat.*), so it never needs Live's namespace. Same templates, schemas and post-processing as live.chat.global/live.chat.profile.OpenVibers · 29 Sept 2026
ad7be2a
-
AI phase 0 of the provider router (plan T6): every finished attempt rolls up into provider_stats_daily (requests, ok, errors, tokens, cost, a fixed-bucket latency histogram with p50/p95) and moves the route's placement_state EWMA (latency and error rate, alpha 0.2), in the same transaction as usage_daily, so a rollup failure rolls the accounting back; a failed run now records the provider error even when nothing was spent. statsFor() reads the rollup only (the price/latency page's source). Migration 0002 (expand). test/router-stats.test.js; both suites green (verified).OpenVibers · 29 Sept 2026
2ac514b
-
Network tokens verify with keys from openvibe-sdk/auth's JWKS client (v0.23.1): fresh keys, the last good ones through a Network outage, backoff, and a rotation honoured on an unknown kid, in place of the hand-written key store. Every claim rule is still openvibe-contracts' verifyServiceToken (a sandbox token and a sub that is not a principal stay 401); the console's sign-in verifies with the SDK's verifyUserToken. Nothing public names the internal JWKS URL or its fetch error (503 detail, /api/ready), an unexpected guard error answers a plain 500, and /api/ready stays 503 until the key loads. Tests: a stub JWKS, an outage with cached keys, an unreachable JWKS, and an expired sign-in token (the SDK takes the clock as a number; a function made every expiry pass).OpenVibers · 29 Sept 2026
dde5e8f
-
A run and its ai.run.* event commit together again: events.runChanged enqueued into the PostgreSQL outbox without awaiting it (the async enqueue arrived with the PostgreSQL move) and all five callers in runs.js dropped the promise, so a failed event write was an unhandled rejection while the run change committed. runChanged is async and awaited inside each run transaction; a failed write rolls the change back. The outbox prune timer catches its async rejection (its try/catch could never see it). test/events.test.js: a rejecting enqueue fails recoverInterrupted and the run stays 'running'; a rejecting prune is not unhandled. Console handlers were checked and are covered by openvibe-shared's routeAsyncErrors. Found by the estate-wide floating-promise sweep; reviewed independently (VERDICT: SHIP). npm test 23/23 test files passed.OpenVibers · 29 Sept 2026
0c98c51
-
The SQLite era is gone (on PostgreSQL since 2026-09-28; no compatibility kept): the one-time migrate-to-postgres script, *_DB_PATH settings (config, .env.example, systemd unit, test helpers), the unused better-sqlite3 dependency and its lockfile entries, and docs, docstrings and STATUS lines about SQLite or its rollback window. Reviewed independently; its blockers (an edited applied migration, the lockfile) are resolved here, Host's inventory follow-up is separate. npm test 23/23 test files passed.OpenVibers · 29 Sept 2026
fa943d1
-
Readiness names the store: the db check makes a real round trip (db.ready) and reports postgresql or pglite with the pool, as the other services on PostgreSQL do. npm test 23/23.OpenVibers · 28 Sept 2026
9f24032
-
Sums of bigint columns (tokens, requests, cache hits) are cast to bigint: on PostgreSQL they are numeric, which openvibe-sdk/db returns as exact text, and the usage summary, status and run totals would have carried strings. cost_usd is double precision and stays a number. npm test 23/23; npm run test:pg 23/23.OpenVibers · 28 Sept 2026
af9166a
-
AI moves to PostgreSQL (ADR-035, roadmap WS-X2, repository-pass group G2), async from end to end.OpenVibers · 28 Sept 2026
1a52447
-
npm test no longer counts a skipped test as passed (roadmap WS-Q task 7; openvibe-shared/test-runner, Shared v1.25.0). test/run.js counted every file that exited 0 as passed, so media-analysis without ffmpeg, which runs only its parser part, read as green. It is now the shared runner (same files, one at a time as before, 60 s timeout per file): a test prints "<label>: skipped (<why>)" for what it could not run, and that file is listed with ○ and its reasons and not counted as passed; --strict makes a skip fail the run. media-analysis's four ffmpeg parts printed "(skipped: no ffmpeg/ffprobe)" and now print "media-analysis whisper language / real run / local model down / free disk check: skipped (no ffmpeg/ffprobe)"; workflows.test.js left media.analyze out of its end-to-end run without a word when ffprobe is missing and now prints "workflow media.analyze end to end: skipped (no ffprobe)". Without ffmpeg the run reads 22/24 test files passed, 2 skipped. openvibe-shared is pinned at v1.25.0 (from v1.22.0; 1.23 and 1.24 add the web runtime, release-watch fixes and the readiness skip, and /api/ready answers as before because its db check really runs; the README follows). npm test (Node 22, ffmpeg installed) 24/24.OpenVibers · 28 Sept 2026
f6fdbc9
-
README and STATUS.json are current (roadmap WS-U task 3): the README gains Purpose (the opening paragraph), Owns, Does not own, Depends on, Capabilities (all seven ai.* capabilities, including ai.credential.manage and ai.quota.attribution.manage, and the grants AI uses at Events and Network) and Security sections; the guarantees table is the Acceptance section; Deploy opens with sudo ovhost deploy ai (unit, port, env file, the local model unit, rollback). Not done yet no longer lists ai.run.* events or people's own provider keys (both built) and dates the citation and passthrough notes. STATUS.json names openvibe-contracts v0.75.0, the seven capabilities and the local model, gains features, dates the Live patch note and is updated 2026-09-28.OpenVibers · 28 Sept 2026
6c8b833
-
Offloaded recordings are readable: a redirect from Media to its object storage is followed (roadmap WS-O task 2). Most VODs live in B2 (676) or R2 (19), and Media answers /v/:id with a 302 to a presigned URL there. The fetcher refused those hosts, so a transcript of an offloaded VOD failed at the first window, found by the production check on private VOD 3780. The storage hosts (AI_MEDIA_STORAGE_HOSTS, default *.backblazeb2.com and *.r2.cloudflarestorage.com) are followed only as a redirect from Media itself (its public host or the internal origin), over https, with no credentials in the URL and no IP literal, with connect-time address checks as always. They are never a URL a caller may name. test/ssrf.test.js covers Media to B2 and to R2, and refuses another host sending there, naming storage directly, http, another host and credentials. npm test passes.OpenVibers · 28 Sept 2026
549bed0
-
Recordings are loudness-normalised before speech-to-text, as Live's VOD transcripts were (loudnorm I=-16 before whisper), so a quiet streamer is still heard. It applies to every batch window (transcripts and media.analyze), not live segments. npm test passes.OpenVibers · 28 Sept 2026
be66fa4
-
live.media.transcribe runs one window of a recording where it lies (roadmap WS-O task 2, so Live's VOD and clip transcripts can move here). The transcribe step streams through the loopback reader, now shared with media.analyze (one per engine), instead of downloading; AI_MEDIA_STREAM=0 still downloads. start_sec seeks and offsets the timestamps, unless offset_sec says otherwise; before this, offset_sec shifted timestamps without seeking. low_power caps whisper at two threads, as Live does while streams are live. test/transcribe-window.test.js: a window's segments sit at start_sec, offset_sec overrides it, nothing is written to disk, a URL outside the rules is refused. npm test passes.OpenVibers · 28 Sept 2026
d46c217
-
Namespaces are the token's ns claim, nothing else: the fallback for service tokens without ns (AI_NS_FALLBACK) and the open-rule lever (AI_NS_REQUIRED=false) are retired (shims C-22 and C-23, roadmap WS-O task 2). Every caller's token names its namespaces: Live's grant lists live.*, network.site_copy and media.analyze; news and blog list their own; Network mints network.*. Checked in production on 2026-09-28, where this week's runs came only from those services and none of the settings was set. A token without ns now runs nothing. test/auth.test.js: ns-less and empty-ns service tokens are refused even in their own namespace, Live's named namespaces decide, and the retired settings have no effect. README, .env.example and docs/migration.md follow. npm test passes.OpenVibers · 28 Sept 2026
5b73bb9
-
The AI viewers are versioned templates (roadmap WS-O task 2). live.viewers.plan, live.viewers.reply and live.viewers.fold move from passthrough to structured workflows (version 2), and live.viewers.clone is new. The director's rules, its plan schema, the fast-reply ask, the fold instructions and the clone brief come from Live's viewers/director.js, fold.js and roster.js. Live sends the stream's stable context and volatile tail it builds from the roster, settings, transcript and chat, the personas' lines and memories, or what is known about a chatter to clone. Token budgets follow the size as before (70 per line + 120, 160 per persona + 160), and the plan is normalised (targets, delays, threads). A run on a streamer's own key now works for templated workflows: every text operation is a chat completion on their provider (it refused generate), and their model is the one for the role the workflow's route names (live.director uses their director model). Tests: rendering in live-templates, end to end in workflows, and the director model on a person's key in credentials. npm test passes.OpenVibers · 28 Sept 2026
65b6a80
-
Arena personas, quotes and headlines and the auto-clip check are versioned templates (roadmap WS-O task 2). live.arena.persona, live.arena.quotes, live.arena.headline and live.clips.confirm move from passthrough to structured workflows (version 2 of each key), carrying Live's system prompts and JSON schemas verbatim. Live now sends data: the fighter's facts, the candidate lines, the beef event with its fighters, and the scene notes, timed speech, sounds and chat around a spike. The prepare hooks render them as Live did; the quotes schema only allows indexes of the lines given; answers without a fighter name, a valid pick or a headline are no answer, and clip is true only when the model said true. Parameters as before (persona 1200 tokens at 0.95, quotes 500 at 0.7, headline 80 at 1.0, clip check 220). test/live-templates.test.js checks the rendering; workflows.test runs all four end to end. npm test passes.OpenVibers · 28 Sept 2026
d271436
-
Chat analysis, person overviews, session titles and VOD ranking are versioned templates (roadmap WS-O task 2). Live's prompts from chat-ai.js, chat-ai-routes.js and ai-moments-job.js become five structured workflows. live.chat.global takes the window, prior notes, moments already recorded and the recent messages. live.chat.profile takes a signed-in, bridged or anonymous chatter, one template with the subject's wording chosen by subject_kind. live.person.overview fuses a streamer and a chatter summary. live.stream.titles gives a title per session summary. live.moments.rank (version 2) takes VODs with overviews and stats. Messages arrive as data (minutes ago, where, author, kind, text) and are formatted as Live formatted them, keeping the freshest 9000 characters. Every answer is JSON with every key its schema asks for; titles and ranks keep only indexes that exist. live.chat.insight stays a passthrough until Live's previous release is gone. test/live-templates.test.js checks the rendering; workflows.test runs all five end to end. npm test passes.OpenVibers · 28 Sept 2026
6b14064
-
The home slogans, the daily secret and the Star of OpenVibe are versioned templates (roadmap WS-O task 2). live.hero.slogans, live.easter_egg and live.home.star move from passthrough to structured workflows (version 2 of each key), with Live's prompts verbatim (slogan-job.js, easter-egg-job.js, home/star-job.js). Live now sends data: the chat vibe, per-user notes, streamers, VODs and usernames; today's vibe; recent stars and candidates with their numbers. The prepare hooks format it as Live did ('(quiet)', '(none yet)', the optional vibe line, the same JSON object). All three answer in JSON mode; the postprocess keeps string lists, maps an unknown effect to confetti and refuses a star without a username. test/live-templates.test.js checks the rendering; workflows.test runs them end to end. npm test passes.OpenVibers · 28 Sept 2026
7c01c65
-
The Arena judges are versioned templates, not prompts Live renders (roadmap WS-O task 2). live.arena.judge_beef and live.arena.judge_mic carry Live's system prompts and JSON schemas (arena/listener.js) and take what Live heard: the target's names and how the name was matched, what was already said about them and the newest speech; or 60-90 seconds of mic speech. The prepare hooks send the same JSON object Live sent, keys in order; an answer without a numeric quality is no answer, and quality is clamped to 0-10. Both run on live.chat at temperature 0.4, 240 tokens, uncached. live.arena.judge stays a passthrough until Live's release before this one is gone. test/moments-template.test.js becomes test/live-templates.test.js with the Arena checks; workflows.test runs both end to end. npm test passes.OpenVibers · 28 Sept 2026
f1564ac
-
live.moments.pick is a versioned template, not a prompt Live renders (roadmap WS-O task 2). The prompt from Live's ai-moments-job.js is the template live.moments.pick, with JSON output t, title and desc. The prepare hook formats the timeline, transcript and sounds as [m:ss] lines, names the clip and chat signals, adds the paste or clip hint, and writes the already-used line only when there is something to avoid; nothing to work from is no run. The postprocess reads the model's JSON as Live did. The workflow takes structured input (title, flavor, timeline, transcript, sounds, clipped, spikes, avoid) and leaves the passthrough list. test/moments-template.test.js checks the rendering against Live's and the parsing; workflows.test runs it end to end on the stub. npm test passes.OpenVibers · 28 Sept 2026
2574aad
-
.env.example documents AI_MEDIA_STREAM (media.analyze streams through the loopback reader by default; 0 downloads first).OpenVibers · 28 Sept 2026
60eca1e
-
media.analyze reads a recording where it lies instead of downloading it (roadmap WS-O tasks 2 and 5: the ranged fetch Live's adoption waited for).OpenVibers · 28 Sept 2026
a5512c3
-
Per-streamer AI budgets are AI quotas (roadmap WS-O task 2; openvibe-contracts 0.74.0 -> 0.75.0 ai.quota.attribution.manage). server/api/attribution-quotas.js: a service sets (PUT /api/v1/attribution-quotas/:attribution, hour or day, cost and/or requests, optional workflow prefix), reads with the window's use, or removes caps on its own attributions only (Live: live:user:<id>, prefix live.viewers.). The rows are quotas with scope attribution and origin service:<svc>, so reserve() refuses a run over the cap with 429 quota.exceeded before any provider call, the operator console lists them, and upsert audits every change. test/attribution-quotas.test.js: capability and ownership, validation, a run over the cap refused while other workflows and streamers run, removal. npm test passes.OpenVibers · 28 Sept 2026
960f14a
-
A credential's models or budget change without the key; a new provider or base URL needs it again (openvibe-contracts 0.73.0 -> 0.74.0, ai.credential-put@1 api_key optional after the first put). A key never follows a new endpoint on its own: 400 credential.key_required. test/credentials.test.js covers both. npm test passes.OpenVibers · 28 Sept 2026
0b9f7aa
-
A person's own provider key lives in OpenVibe.AI (roadmap WS-O task 2; openvibe-contracts 0.49.0 -> 0.73.0).OpenVibers · 28 Sept 2026
fca04c7
-
media.analyze transcribes long recordings in windows (WS-O task 5). The whisper provider caps a call at its own 10-minute timeout, so a whole hour-long VOD would have come back without a transcript. Speech-to-text now runs in windows (AI_MEDIA_STT_WINDOW_SEC, 10 minutes, about 2 minutes of work each on this host); the whisper provider decodes from startSec (-ss) and offsets the timestamps. A failed window is named in gaps and the others still count. The transcript's language is the one most windows detected. The stub places its synthetic segments at the window's offset. Tests: 10 s windows on the 28 s fixture give segments from 0, 10 and 20 s; a whisper window from 20 s keeps the recording's times.OpenVibers · 27 Sept 2026
6f61c11
-
A local model for media.analyze (WS-O task 5): deploy/systemd/openvibe-llm.service runs llama.cpp's llama-server on 127.0.0.1:8090 (OpenAI API, no key), held back for live streams (2 threads, CPUQuota 150%, nice 19, MemoryMax 3G). Installed on production 2026-09-27: llama.cpp b11222 CPU build and Qwen2.5-1.5B-Instruct Q4_K_M (Apache-2.0), both checked against their published sha256; ai.env gains AI_LOCAL_LLM_URL/AI_LOCAL_LLM_MODEL, so the provider local and route media.local exist. First production run: the overview came from the local model (65 s for 3 min of a Japanese VOD, about 20 s of it the model), but the 1.5B model retold the transcript past the limit. The template now asks for English, at most 500 characters and one short quote; answers over 600 characters are cut after the last whole sentence (sentences()); max_tokens is 250. README: Local model.OpenVibers · 27 Sept 2026
566822e
-
media.analyze: a highlight's speech is measured in time (at least half the window inside transcript segments), not in space-separated words, so Japanese speech counts (production: JapaneseOldGuy's VOD had loud and scene-change highlights but never speech). Test: a Japanese segment gives a speech highlight.OpenVibers · 27 Sept 2026
e587ce1
-
media.analyze on real streams (WS-O task 5, after the first production runs). Three fixes, from a 3-minute stretch of a JapaneseOldGuy VOD with speech 94% of the time but no highlights. Silence is -50 dB, real silence: a quiet stream talks around -40 LUFS, which -35 dB called silent (43%). Only black picture and silence make a window dead air, not a still picture (a talking head, a menu), and a window with speech is never dead. whisper.cpp's detected language (result.language with -l auto) is reported instead of 'auto'. Tests: a quiet talking stream over a still picture gives speech highlights and none in its silent black stretch; a fake whisper-cli reports ja for auto and keeps en.OpenVibers · 27 Sept 2026
2d9a16f
More commits