Skip to content

Releases

v0.12.28 — complete Zoom transcripts; Lite and Compose on versitygw

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 09 Oct 13:40
f1a62fb

v0.12.28

Zoom

  • Complete transcripts on busy calls (#1781). Every Zoom turn is captured in full, even when
    the bot's page is under heavy load, and each turn is finalized from all of its audio.
  • Precise timing (#1781, #1778). Segments are timed from the captured audio on one shared
    clock. Short pauses no longer split a turn, and long turns break at a natural pause.
  • A steadier live view (#1778). The live tail always shows the latest draft.

Install

  • Recordings in versitygw (#1760). Lite and Compose now store recordings in versitygw:
    Apache-2.0, pinned by digest, plain files in a local volume. Thanks to @infocillasas (#1671).
  • Your own S3 on Helm (#1760). Set storage.s3.endpoint, storage.s3.bucket and
    storage.s3.existingSecret.
  • Zero-edit setup (#1760). make all generates its own internal keys; any value you set is kept.

Upgrading

  1. Lite and Compose: run make migrate-storage to copy existing recordings; it verifies every
    object and keeps the old volume. On Compose, set MINIO_ENDPOINT=storage:9000 in .env (and
    IMAGE_TAG=v0.12.28 if you pin it). Guide
  2. Helm: copy your objects to your bucket and set storage.s3 before upgrading.
    Guide

Images (eleven). Seven are the v0.12.27 bytes, unchanged: vexaai/v012-admin-api@sha256:4c702354384eafe3a933cd537a106b7c15067cfd368a5f6da1a6e675e7e03e04, vexaai/v012-runtime@sha256:a1f6448fbb380b9433364e8b572ec25a12aa4f274bf403f1d83a89cbf5812f2d, vexaai/v012-gateway@sha256:2e76319812f28adbb6bc328ac0a7484d3826924a9d38a9dd9599dd190fbb0f19, vexaai/v012-mcp@sha256:4b2dbc08023ff424f40453d61a8f4d38a72de7eba1e5aaad1e428c309f63d338, vexaai/v012-agent-api@sha256:6eb37574b33aab233aabbe5907e06e106bae44a403e5df781a436da8201a928d, vexaai/v012-agent-worker@sha256:a1120b24765ff6b1c86c5f9ddee35b5e71fdbb9543a23b918b278373ad705022, vexaai/v012-flows@sha256:e040c0de48924e78440e9cc4e71ed986471f101f99fbe5a5bd091af7e59996fa. Rebuilt: vexaai/v012-meeting-api@sha256:cca46266feedcff29c3478472fd40133ba51f3c49a489a290040a56f1f0f3dca, vexaai/v012-terminal@sha256:d65f81c284553e98e3ee2db4475ee6deea8691f0bd95ccf064e4a99c564095d3, vexaai/vexa-lite@sha256:9eebb534fdab4079baf11af678968ac8d9679e20ce48db206bcf5a62b85eb538. Bot: vexaai/vexa-bot@sha256:e2398bfac7fa8671c209e996bc6451598b58438c851956ec050a0300f5a5354b. Published from the reviewed candidate packet releases/v0.12.28/candidate-images.json and validated by run 37914860589.

In production: vexa.ai has run this bot since 8 October, and its admin-api, gateway, runtime and
MCP are the same bytes as this release.

v0.13.1 — the minutes line (alpha)

Pre-release

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 07 Sep 10:37

v0.13.1 — the minutes line (alpha)

The chat is the interface to your meetings. Ask a question and the agent answers from your own
transcripts, documents and calendar. One emailed link signs you in and opens the question already
composed. Flows react to meeting events — a prepare message before, minutes after — and you compose a
flow from named steps without a rebuild. Agents reach your meetings over authenticated MCP with
scoped, delegated tokens. Workspaces hold the documents the agent reads and writes.

New tiers in the Helm chart: agent-api, terminal, flows, and a workspaces volume.

Since v0.13.0: a bot requested inside a workspace is the workspace's meeting — live for every
member, not only the person who asked it in (#1648, delivered by #1650). vexaai/v012-terminal now
ships the minutes variant, the same bundle the pilot was shown; both identity legs assert
ai.vexa.terminal.mode=minutes on the published image, on amd64 and arm64. Base-image and chart
pulls in the release workflows no longer read Docker Hub (#715); every identity and alias read still
does, because those verify our own published bytes.

Alpha. This build has not been through a witness pass and is published as a candidate for
evaluation, not for production.

Not in this build: deletion and retention controls; runtime-editable agent prompts; the shipped
MCP does not yet verify delegation tokens.

Images

Published from candidate v0.13.1-alpha.2 (build run 34096619850 at 036911efa) and aliased to
v0.13.1 without a rebuild: eleven images, 21 platform identities — the bot is amd64-only.
The packet is releases/v0.13.1/candidate-images.json, validated against the published bytes by
run 34104704881 with every leg green.

v0.12.27 — the full MCP, flows, and searchable meetings

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 07 Sep 12:50
f64a765

v0.12.27

MCP — an agent can do everything Vexa does, and it knows how

The tools describe themselves: what to call, in what order, and what to say when something is
refused. whats_waiting gives an agent its own queue on connect, scoped to its caller, each item
carrying the sentence to say and the typed reason behind it — a person with no meeting yet gets a
first step instead of an empty answer. A refusal comes back as reason / message / action_url
and the deciding service's own words, not prose to scrape. A word the edge did not expect is passed
to the owning route rather than rejected at the door.

  • Standing notices ride along with unrelated work (#1546, #1551, @DmitriyG228). An item whose
    copy declares itself a standing notice travels on the meeting tools' own results — and on their
    refusals — so an agent hears it without going looking. GET /queue/notices asks for just those
    sentences and nothing else.
  • Friction reporting (#1532, @DmitriyG228). report_friction files what did not work; no field
    is required and no value a caller can send is refused — an unknown word, an over-long value or
    a missing session are all filed rather than rejected, because a report we cannot tie back to a
    conversation is worth strictly more than no report. friction_so_far reads your own back.
  • The queue's words come from files an admin edits, with no deploy on either side of the edit
    (#1532, #1545, @DmitriyG228).

Flows — a tiny n8n you can vibecode in Python

Event queuing, triggering and scheduling at the top-level API, so you can chain bots, agents,
calendar events — anything. POST /flows takes a name, a trigger and an ordered list of step names,
validated against the deployed vocabulary at submission and live in the worker in about ten seconds;
the API never accepts code, and steps are reviewed Python in the image. Ten Postgres tables in the
stack's own database — no message broker, no scheduler, no second state store. Dedup is a UNIQUE
constraint, "every effect exactly once" is a receipt, and every wait is a timestamp column.

Flows are global, not per-user: this is what you set up for a company when self-hosting, to chain
post-meeting summaries and the like.

  • flows-api, flows-worker and the optional flows-mailbox (#1456, @DmitriyG228) — one image,
    three commands. Documented at Workflows.
  • The mailbox decides who it will act for before it admits anything (#1456, @DmitriyG228). An
    empty allow-list means the mailbox's own domain, not everyone; anybody else gets no account, no
    agent turn and no model call — one mail_quarantine row, readable with one SELECT.
  • Tests, fixtures and the flows documentation (#1497, @DmitriyG228).

Meetings are searchable, and your agents can own them

Full-text search over your own transcripts (GET /transcripts/search; the index builds itself out of
band on first boot, without locking the table). Annotation from the API and from MCP: POST /meetings/{id}/annotate attaches a title and arbitrary metadata during or after a meeting, and
GET /meetings?metadata= filters on it in the database (16 KB and 64 keys per meeting).

Once an agent annotates a meeting, it owns it — it tags each call with the deal it belongs to
({"opportunity": "acme-renewal", "stage": "technical-eval"}) and later pulls every meeting on that
deal with one query, instead of guessing from titles and dates.

  • Address a meeting by its row id (#1547, @DmitriyG228). /meetings/{id}/… and
    POST /meetings/{id}/share beside the existing platform/native pair.
  • GET /recordings is a page, newest first (#1547, @DmitriyG228). limit and offset on a list
    shape.
  • Zoom served under a hosted or vanity hostname is recognised (#1547, @DmitriyG228).

Security

  • The gateway strips authority headers by family, not by name (#1456, @DmitriyG228). At 0.12.26
    the strip was an eight-name list of x-user-* spellings, so a public client could send
    x-internal-secret — the value published in docker-compose.yml — and be believed by the internal
    tier. Any header beginning x-user-, x-internal- or x-vexa-internal-, plus x-admin-api-key
    and x-gateway-verified, is now dropped from every client request.
  • POST /bots no longer hands back the webhook signing secret it just stored (#1547,
    @DmitriyG228).
    The response was a verbatim copy of the stored row, so the secret travelled
    through the public gateway into caller logs and agent context. Rotate any webhook secret minted by
    an earlier version.
  • Boot refuses the published placeholder secrets (#1456, @DmitriyG228). See Upgrade notes.
  • agent-api validates the workspace repository host and pins git transports (#1539,
    @DmitriyG228).

Deployment

  • The agent surface is unchanged (#1553, @DmitriyG228). /agent/* keeps the seven routes it
    served at 0.12.26, now declared in core/agent/routes.v1.json like every other domain, with the
    compose default restored. A deployment that leaves AGENT_API_URL unset serves no agent surface
    and answers 404 there — at 0.12.26 the same state answered 403.
  • Dependency floors across the Python, pnpm and transcript-rendering lockfiles (#1541, #1542,
    #1544, @DmitriyG228).
  • The ASWF 2020 v2 CCLA is accepted as an alternative corporate instrument (#1378,
    @DmitriyG228).
    A company whose legal has already approved that shape does not need a bespoke
    one.
  • Carve manifest for the v0.12.26 train (#1418, @DmitriyG228).

Fixed

  • Asking for a Zoom bot through MCP could never work (#1631, @DmitriyG228).
    request_meeting_bot dropped the join URL for Zoom, and meeting-api refuses a Zoom request
    without one — every attempt came back 422. Meet, Teams and Jitsi were unaffected.
  • /.well-known/mcp-tools.json was served offline and absent live (@DmitriyG228) — the module's
    entrypoint guard sat above the route, so nothing below it ran in the process every deployment
    starts.
  • A person's settings move to identity, so one answer serves every service (#1456, @DmitriyG228) —
    see Upgrade notes.
  • One absence is said once, however many meetings ran into it (#1547, @DmitriyG228) — a deployment
    that does not run the agent domain no longer adds an identical queue item per completed meeting.
  • The friction sink never loses a report to a vocabulary word, and never to a missing one
    (@DmitriyG228).
  • GET /reactions answered 500 for every authenticated caller: the route shadowed the helper it
    called (@DmitriyG228).

Known limit — three things this release does not finish. The person-settings import is
operator-triggered
: settings move from the workspace file to identity, and an existing
deployment that does not run the one-shot migration starts everyone on the defaults (mail on,
clock in UTC) rather than on what they had. The agent-half flow tests are skipped when the agent
module is absent
, which is the shape OSS ships — those flows (meeting_prep, email_chat, the
desk pair) are not registered at all without an agent domain, so their suites prove nothing here
and are marked present-only. And Python dependency licences are still unasserted: the licence
gate resolves the npm tree, pip-licenses scanning is owed rather than done (ADR-0009 §
Python licence scanning), and the per-release SPDX carries those fields as NOASSERTION
alongside the Ubuntu packages the Lite final stage installs. The gate is green on an inventory
that does not actually assert those terms.

Upgrade notes (breaking)

Read these before upgrading a self-hosted deployment. Each one changes behaviour that a 0.12.26
deployment relies on.

  1. Generate a real INTERNAL_API_SECRET. Boot now refuses the literals this repo has published
    (vexa-internal-secret, lite-internal-secret, changeme, …) and stops naming the variable. Use
    openssl rand -hex 32 and set the same value on every service that talks to another. Vexa Lite
    mints a random one per boot and needs nothing set. VEXA_FLOWS_API_KEY has no default at all and
    refuses the same placeholders — flows-api will not start without it.
  2. Upgrade the gateway with, or before, the services behind it. The authority-header strip is now
    a prefix family rule. Anything that was reaching an internal tier by sending its own
    x-internal-* header through the public edge stops working — which is the point.
  3. MCP tool errors changed shape. A refusal arrives as reason / message / action_url /
    body, not as one sentence. Anything scraping the old text breaks. notices is a new key on five
    tools' results.
  4. /agent/* answers 404, not 403, when AGENT_API_URL is unset.
  5. Run the person-settings import. Timezone and the mail switches are read from admin-api, not
    from .settings.json in the workspace. Without the operator-triggered migration everyone reverts
    to defaults — anyone who had mail off starts receiving it again, in UTC.
  6. FLOWS_API_URL → VEXA_FLOWS_API_URL on admin-api. The old name is honoured for one release;
    the new one wins when both are set.
  7. X-Flows-Admin-Key → X-Flows-Operator-Key. Accepted for one release, with a warning once
    per process. The old name read as admin-api's token, which it never was.
  8. PROC_PENDING_GRACE_SEC is removed. No longer read; delete it from your .env.
  9. Copilot processed notes are no longer persisted after the bot stops. The durable notes pane
    and the schedule digest's notes flag stay empty once a meeting ends.
  10. Rotate any webhook signing secret minted before this release — earlier POST /bots responses
    returned it in the clear.
  11. Email lookups on the admin API are case-insensitive. Reconcile existing case-variant
    duplicate accounts before upgrading; which one resolves is otherwise plan-dependent.
  12. **flows expect...
Read more

v0.12.26

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 31 Aug 19:28
4ca323d

v0.12.26

Zoom

  • Per-participant speaker attribution (#1018, @jbschooley). Zoom transcripts are named per participant with a self-correcting vote, replacing the mixed-lane guess. Maintainer hardening on top: the deaf-capture guard restored on the per-track branch, typed observations on the Zoom lane, and the page-side name resolver extracted into a tested module with a Speaker A/B fallback and a self-exclusion backstop.
  • Better admission (@jbschooley). Browser-signal hardening lets the bot into Zoom meetings that previously refused it.
  • In-meeting popups are dismissed during the call (@jbschooley) — the mic advisory, the AI-Companion notice and the hardware-acceleration toast no longer sit over the meeting or the recording.

Known limit — the Zoom bot joins as a web client, so not every Zoom meeting can be recorded. Some Zoom accounts and meetings block automated browser clients outright; the bot is refused at the door there, ends as failed with reason zoom_requires_rtms, and no transcript is produced. This is a platform policy on the host's side, not a fault in the bot — those meetings need Zoom's Realtime Media Streams path, which this release does not ship (#706). Meetings that do admit a web client are unaffected.

Meetings

  • Join failures name themselves (#1075). A failed join now records a typed reason, the stage it failed at, whether the cause was ours or the platform's, and how long it took — instead of one opaque failed. Visible on the meeting record and through the API.
  • Text-only custom STT responses reach the transcript (#1148). OpenAI-compatible endpoints that return only text no longer have their output discarded for missing timestamps.
  • Voxtral / JSON-only transcription backends (#1342, @Cantfirmed).

Deployment

  • Noble base image for the bot, lite and full (#1011, @jbschooley).
  • DEFAULT_BOT_NAME is honoured without rebuilding the terminal (#1258).

Fixed

  • The probe's live-view listener re-sends its subscribe until acked (#1354) — it raced meeting-row creation and a refused subscribe was permanent.
  • The full bot image builds again on the new base: awscli is no longer in the noble archive, so AWS CLI v2 installs from the vendor package.

Images: vexaai/vexa-lite:v0.12.26 and the compose set (v012-runtime, v012-meeting-api, v012-gateway, v012-admin-api, v012-agent-api, v012-agent-worker, v012-mcp, v012-terminal, vexa-bot), published from the reviewed candidate packet releases/v0.12.26/candidate-images.json and validated by run 33421145816.

In production: this release is what vexa.ai runs, pinned as channel entry 0.12.23-estate-20260829-seq12 on 2026-08-31 and re-verified against the cluster after the pin.

v0.12.25

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 31 Aug 09:20
503fb1f

v0.12.25

Maintenance release: everything merged since v0.12.23 (v0.12.24's lite/compose images were never published — its tag predated the release-commit convention; this release closes that gap too).

Added

  • Meeting participants read — GET /meetings/{platform}/{native_meeting_id}/participants, rows labelled invite / speaker, owner-scoped, with an honest observed_roster: "not_recorded" marker (#451).
  • Teams passcode joins — numeric Teams id + passcode now builds the real …/meet/<id>?p=<passcode> URL; unknown aliases are a typed 422; the passcode stays out of stored URLs and logs (#892).
  • Sized spawned pods — bot and agent-worker pods declare CPU/memory requests + limits via runtime.workloadResources, so quota-controlled namespaces admit them (#1005).
  • MCP report_issue — an agent can file a ticket from inside its session, bound to the cluster's record of the same meeting (#1272).

Changed

  • One API home for interactive acts — POST …/acts per ADR-0035; speak/chat kept as aliases; screen/avatar retire from api.v1; the status page stops overstating speak (#1089).
  • Meetings list ordered by event time, active meetings pinned to the top (#1226).

Fixed

  • Capture-fault ≠ empty room — a bot whose audio capture breaks mid-meeting no longer ends a live meeting as completed(left_alone); it logs a suspected capture fault and attempts a restart (#1192).
  • Restart mid-leave no longer strands meetings in "stopping" (#1280).
  • Text-only STT hardening — the transcription client tolerates JSON-only/Voxtral-style engines (#1149, #1343).
  • Terminal honors DEFAULT_BOT_NAME without a rebuild (#1259).
  • Gate failures print their diagnostic — empty stdout Buffer no longer swallows the real error at 21 call sites (#1107).

Delivery evidence

Validated at station t25-01225 (vexa-stations: battery, row-0 image-ID proofs, live-meeting run, five promise-exercises, full-green lite probe S0–S8). Production runs this train as channel entry 0.12.23-estate-20260829-seq11 (cosign-signed, verifier verdict ELIGIBLE).

v0.12.24

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 29 Aug 20:15
6294996

Delivered to hosted production 2026-08-29 (channel entry 0.12.23-estate-20260829-seq10), station-validated on live meetings before release.

Fixed

  • Teams bots join with a separate passcode (#892, #1201). A numeric meeting ID + passcode now builds the join URL Teams actually uses; the passcode never appears in any log line. Proven live against a passcode-required meeting.
  • A broken audio capture no longer ends a live meeting as completed(left_alone) (#1192, #1195). Connected streams delivering nothing are treated as a capture fault: the bot holds the meeting, logs it, and attempts one capture restart. On platforms that keep a server-side mix alive (Teams), affected runs now end at the maxActiveMs ceiling rather than reporting a false success.
  • Restarting mid-leave no longer strands a meeting in "stopping" (#1280).
  • Every gate failure prints its diagnostic (#1107, #1114) — an empty stdout Buffer was swallowing the real error at all 21 call sites.

Added

  • Delete a completed meeting's artifacts (#116, #1293). Owners erase transcripts and recording artifacts from primary stores; the meeting row remains as a tombstoned usage record. Active meetings answer 409. This is what makes the published privacy promise true.
  • GET /meetings/{platform}/{native_meeting_id}/participants (#451, #1275) — who was in a meeting, each row labelled by source (invite / speaker), with observed_roster: "not_recorded" stating honestly what the core does not know.
  • Spawned bot and agent-worker Pods declare CPU/memory requests and limits (#1005, #1093) — quota-controlled and ResourceQuota/LimitRange namespaces now admit dynamically created Pods; sizing is configurable per profile.
  • MCP report_issue (#1272) — an agent can file a ticket from inside its own session; off until VEXA_TICKET_SINK_URL is set.
  • Interactive capabilities get one API home (#1089, ADR-0035) — POST /bots/{platform}/{native_meeting_id}/acts, with speak/chat kept as aliases.

Changed

  • GET /meetings orders by event time (#1226) — non-terminal meetings pinned on top; calendar-imported meetings sort where they happened, not where they were created.
  • The minio-init Job is pinnable and sized (#1321) — its client image is a value (pin by digest for reproducibility); the Job declares its own resources.

Docs

  • The meeting completion-reason taxonomy — all ten values, who owns each, and what a failed row does and does not tell you (#1284). Operability numbers with the commands that reproduce them (#1285). API-key provenance on the pages that carry snippets (#1301, #1302). "Air-gapped" corrected to self-hosted across the comparison pages (#1304). The MCP page linked from where agents actually look (#1345). 0.10 → 0.12 upgrade runbook (#1332).

v0.12.23 — Calendar auto-join discipline, and Teams CSRC transcription

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 20 Aug 11:15

v0.12.23 — Calendar auto-join discipline, and Teams CSRC transcription

Calendar-dispatched bots now follow a fixed, observable contract: dispatch at
start−2 min, wait up to 15 min in the lobby, retry genuine failures once per bounded
backoff inside the occurrence window, never re-arm a served or stopped meeting,
record under the same flag as manually started bots, and honour an explicit stop on
every path. Teams transcription routes the mixed audio into per-speaker windows from
the RTP transport's contributing sources; a speaker name stays provisional until the
roster has settled.

Calendar & scheduled bots

  • Multiple calendar feeds, managed from the dashboard: named, independently synced
    ICS connections; meetings deduplicate across feeds and keep every source visible
    (#1150).
  • Bots dispatch two minutes before the scheduled start and wait up to fifteen
    minutes to be admitted (#1210).
  • A failed auto-join retries on a bounded backoff, and the reason for the wait is
    readable on the meeting (#1200).
  • A meeting already served or stopped is never re-armed; only genuine failures
    retry, and only within their own occurrence window
    (#1211).
  • An explicit stop is stop: stopping a scheduled meeting cancels it before
    dispatch, a stop that races a starting bot leaves nothing running, and the stop
    is recorded as the outcome (#1212).
  • Calendar auto-joined meetings are recorded like manually started ones
    (#1217).

Teams

  • CSRC transport activity routes the mixed audio into per-speaker transcription
    windows with the same buffering contract as Google Meet
    (#499 — attribution work; the
    exactly-once oracle that issue asks for stays open).
  • A speaker name earned before the participant list is complete stays provisional
    and is corrected as evidence accumulates — no more locking one person's words
    under another's name (#1228).
  • Names resolve from Teams' stable data-tid attributes instead of hashed CSS
    classes; only the mainAudio mix is transcribed, and double-mirrored tracks are
    deduplicated (#1024).

Correctness & API surface

  • One bot per meeting, even under concurrent requests; a request for a live
    meeting adopts the running bot (#1185).
  • Self-hosted deployments fail closed when that guarantee cannot be enforced: the
    admin service refuses to start without the unique index
    (#1187).
  • API keys only do what their scope allows — scope is enforced on bot lifecycle
    and user-config routes (#1236).
  • Meeting responses ship owner-private fields (webhook URL, delivery config) to
    the owner and nothing sensitive to a transcript-share viewer
    (#1243,
    #1244).
  • Operator plumbing: admin identity by authoritative ID (#994), producer-observed
    service provenance on completed meetings (#984), optional external service
    authority (#988), signed system callback for terminal meeting facts (#992).

Infrastructure

  • Every deploy surface — Lite, Compose, Helm — now runs
    Valkey 8.1.9 instead of Redis
    (#653).
  • Release promotion copies registry manifest bytes exactly and reads back every
    frozen descriptor; nothing is rebuilt after the witness (#951).

Docs

New guides and honest-status pages: n8n (#1076), the
MCP server boundary (#1077), share-links and
interactive-bots status (#1078, #1086), hosted-runs-0.12 start-here (#954),
signal-driven redirects (#1111), and FunASR validation attributed to the upstream
maintainers who ran it (#928).

Getting it

Images are published multi-arch (linux/amd64 + linux/arm64) under
:v0.12.23, byte-identical to the witnessed release candidate
(v0.12.23-rc.21). Lite and Docker Compose build and run green at this source.

Upgrade notes

  • Self-hosted with an existing database: the admin service now refuses to
    start if the one-bot-per-meeting unique index is missing (#1187). Apply the
    shipped schema migration before rolling this version; a fresh install needs
    nothing.
  • The Redis→Valkey swap is transparent for stock deployments; custom images or
    pinned redis:* references need the new image name (#653).
  • The declared api.v1 surface has not been re-frozen for the routes added since
    0.12.18; the reconciliation is tracked in
    #1234.

Not claimed

  • A transport-contested phrase on Teams is currently published under both
    contributing tracks — detected and counted in pipeline health, not yet
    resolved or marked in the public transcript (#499, documented in-repo as
    deliberately unresolved).
  • Teams naming under heavy participant churn can still flap before settling;
    hysteresis and a fully deterministic CSRC→name path are tracked (#1229, #1230).
  • Jitsi: the alone-leave timer and admitted→active lag have known residuals (#1214).
  • A stop arriving before the meeting row exists is lost rather than remembered (#1213).
  • Zoom: not updated in this release.

Credits

Jacob Schooley (@jbschooley) — Teams speaker-name
resolution from stable data-tid, mainAudio-only transcription and track
dedup, from #1024.

The FunASR maintainers (@LauraGPT) — upstream
validation of the FunASR/SenseVoice custom-STT path
(#928).

v0.12.22 — Teams speaker attribution

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 12 Aug 13:27

v0.12.22 — Teams speaker attribution

Teams turns now carry the speaker's name. The bot reads who is audible from the
WebRTC transport's contributing sources, correlates that against the roster panel
and tile names, and attributes at word granularity rather than per turn.

Teams

  • Speaker names resolve from stable DOM attributes, guarded so a control label, a
    clock or a machine token can never become a name
    (#1119).
  • The audible-source signal comes from the RTP contributing-source list, so
    attribution follows the transport rather than the UI's animation, and stream
    selection is verified against voice energy.
  • Names are applied at word granularity. A name another track has already earned
    is treated as contamination, not as disagreement.
  • Only the mainAudio mix is transcribed; double-mirrored tracks are deduplicated,
    and a mix that is present but carries no sound is abandoned rather than
    transcribed as silence.
  • Unattributed speech is the number we are driving to zero. This release cuts it
    substantially and publishes what is left with an empty speaker rather than a
    guess, so the remaining gap stays visible release over release.

Transcript quality

  • Superseded pending drafts are retracted, and a confirmed turn's dangling tail is
    promoted on close, so dedup no longer loses speech.
  • Invented media-artifact text is suppressed, and every suppression is reported.
  • Overlap trim, gap reclaim, and a four-second cut on long turns.

Getting it

Images are published multi-arch (linux/amd64 + linux/arm64) under :v0.12.22.
Lite and Docker Compose deployments build and run green at this tag.

Not claimed

  • Zoom: not updated in this release — next target.
  • Roughly 4–7% of rows still publish unnamed under heavy crosstalk.

Credits

Jacob Schooley (@jbschooley) — transcript
retract/promote (6eff8c09, ba382219) and mainAudio dedup (dc740e52), from
#1024; also 2db27364.

Daniel Dormann (@danieldormann) — structural
Teams name resolver (0d0bdbda), from
#1121, and the report that opened
#1119.

v0.12.18 — dependable joins and truthful operator failures

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 24 Jul 12:26
1b62993

v0.12.18 — dependable joins and truthful operator failures

v0.12.18 tightens the paths that decide whether a meeting bot really joined,
is still alive, and failed for a reason an operator can act on. It also makes
the MCP front door stream correctly, fixes Lite startup, and removes several
false-success states from bot creation and deployment.

Meeting reliability

  • Google Meet lobby bots are no longer reaped at five minutes while still
    legitimately waiting for admission (#862).
  • Meet host denial wins over ambient reCAPTCHA and terminates with a typed,
    non-retryable reason (#840).
  • The browser locale is deterministic (en-US by default), with its resolved
    language recorded for diagnosis (#856). The English lobby path was walked
    live; arbitrary non-English joining is not claimed.
  • Teams no longer treats generic alert UI as evidence that the bot was
    removed; the staged bot stayed active for 207 seconds under the live removal
    monitor (#600).
  • Mixed-lane turns without a resolved participant name now use the stable
    Speaker label instead of leaking the internal seg_N cluster identifier
    (#890). Human name binding remains a later attribution-quality step.
  • Teams authentication redirects and non-admitted bot deaths now preserve
    their real causes instead of degrading into admission timeouts or
    reason: None (#915, #926). These external failure shapes are
    machine-proven; neither recurred during the release witness.
  • Stop during pre-admission now reaches the phase-aware withdraw path and
    cleans up the workload (#889). Meet's host-side knock can still persist;
    that distinct visible withdrawal gap remains
    #839.

Truthful APIs and operator behavior

  • A bot workload that never starts no longer yields false 201; POST /bots
    returns 502 with the cause and marks the meeting failed immediately (#718).
  • Invalid native meeting IDs containing URL/query characters fail at intake
    with 422; callers should use meeting_url and passcode (#892).
  • Meeting API remains available for durable reads during Redis loss and
    reports Redis degradation on /health; Redis-dependent Stop fails narrowly
    and retryably (#809).
  • Redis keyspace reconciliation is no longer on the ten-second collector hot
    loop, removing the known /health saturation amplifier (#893).
  • The webhook introspection capture is bounded, removing its unbounded
    in-process retention source (#803).

MCP, webhooks, and identity

  • MCP streamable HTTP is available through the authenticated gateway /mcp
    front door. Sessioned GET /mcp opens and keeps its SSE stream instead of
    being buffered into a timeout (#795, #921).
  • Admin token mint honors JSON scopes, keeps query compatibility, and refuses
    unsupported body fields loudly (#922).
  • Core now records recent webhook delivery outcomes in a per-user,
    secret-safe ledger exposed at GET /user/webhook/deliveries (#841 core
    half). The dashboard does not display that ledger yet; the consumer remains
    open in v0.12.20.

Deployment

  • Helm migrations use the same pinned image tag as the deployed release
    (#900).
  • Admin API retries transient database DNS/connect races with bounded backoff
    (#901).
  • Vexa Lite pins all service environments to Python 3.12, restoring reliable
    admin API startup and first-run key provisioning (#927).
  • The no-STT contract is documented truthfully: transcription is required by
    default and missing configuration fails loudly; capture-only is an explicit
    transcribe_enabled=false choice (#532).

Upgrading

From any v0.12.x release, pin IMAGE_TAG=v0.12.18. The guarded :v012
pointer moves only after the published image set, value gate, and release
witness are green. There is no database schema migration in this batch.

Behavior refinements to note:

  • token JSON bodies reject unknown fields with 422;
  • native meeting IDs carrying URL characters reject with 422;
  • bot workload start failure now returns 502, not 201;
  • /mcp and /user/webhook/deliveries are new gateway surfaces.

Artifact identity

The stable tags are same-byte aliases of frozen release candidates; they were
not rebuilt after validation. Production revision 103 and meetings 24740/24741
witnessed the stage2 Bot at
sha256:9442a44558fd48950208cbef40673cc7a0b2feb41f380964fc74a0e25bf18fae.
The public Bot below is the packet3 replacement: its runtime inputs are
tree-identical to the tagged source and its image/Compose validation passed,
but it was not deployed or human-witnessed on hosted stage or production.

image :v0.12.18 digest
vexaai/v012-admin-api sha256:49a8bd29268250cb37976c3170fde41c960f00d55f31ef4a42e17062b968b114
vexaai/v012-runtime sha256:653fcd1a8edbc925a174a55fe34863d414c56c8ef367a94b449f89a5a3a5c4e1
vexaai/v012-agent-worker sha256:d4c637ca1ca9c05ff6de852778299da665374ed9d59115088aa030d90212bf15
vexaai/v012-agent-api sha256:af4f2bf2008ed2c910904e82155e06d3b0e747d04d7c7ccb9f14208b17c49bae
vexaai/v012-meeting-api sha256:d1db225472d54f0b087e1b7a342c465b01b7e7f042372a730d3b32e0c6836f4a
vexaai/v012-gateway sha256:4b9bd9d6d33f4f2ec8df0b9530b02942482c0399ec71f39722fb06f48aae55df
vexaai/v012-mcp sha256:72158d83293ce48c1cfd5c9bd8e2762e72a2da63d962fdbdbf7fe9b09daa8755
vexaai/v012-terminal sha256:a668ad43e225364422b225939fad057bff33db8b5bb3d6c341e1b625c5bde008
vexaai/vexa-bot sha256:a7f8feae7870b722e3542fb7cb054ff7c092e62f4c5a6b6a3b63e52f8cd1fe47
vexaai/vexa-lite sha256:5d0b6f865afe726109bb326361b917a3d9f762d64abbeea0826744134162d051

Hosted production directly held the listed admin API, runtime, meeting API,
and gateway bytes. It held the stage2 Bot digest recorded above, not the
packet3 public Bot digest. Agent worker, agent API, MCP, Terminal, packet3 Bot,
and Lite are proven by published-candidate validation; they are not claimed as
hosted production workloads.

Known boundaries

  • Host-visible graceful lobby withdrawal remains #839.
  • Dashboard webhook Delivery History remains #841.
  • Teams auth-redirect and Zoom non-admission reason fixes have exact-head
    machine evidence but no live recurrence in the witness window.
  • Jitsi hosted meeting detail remains #937.
  • Real audio→words is witnessed live; the standalone machine wav→words oracle
    remains deferred.
  • Hosted transcription currently has a functional Cloudflare fallback, while
    primary health remains false and direct BBB was not exercised.
  • Hosted Account/API-key management currently has a webapp-to-Admin-API
    compatibility incident (#942,
    platform carry vexa-platform#127).
    It is not delivered or closed by this OSS release.

Credits

Contributors: Joseph Yaksich, Felix-Ayush, and Dmitry Grankin.

Reporter credit: Valerie Phoenix (#927).

Community PR validator: Dmitry Grankin.

Release witness: Dmitry Grankin, corroborated by dashboard, bot, meeting API,
Redis, Postgres, and Kubernetes evidence.

Delivery guarantees

# Guarantee Evidence
1 Users pull the bytes that were proved exact candidate map; stable alias digest equality
2 Compose, Lite, and Helm install paths work to their declared scope base-eight published-candidate validation plus bounded packet3 Bot/Lite validation
3 Real audio produces speaker-attributed words hosted stage and production human witness; standalone wav→words remains unclaimed
4 Fresh empty VM works not claimed; no fresh-VM leg was run
5 Existing behavior remains covered v0.10 compatibility, bot-spawn, Compose, Lite, Helm, and arm64 validation
6 Each image contains what its name promises image-identity validation
7 A human witnessed the hosted value stage2 hosted witness plus production meetings 24740/24741; packet3 Bot is validation-only
8 Every batch change has acceptance evidence 21-PR value map plus inherited #890 and exact-head checks
9 Notes state coverage boundaries this document
10 Moving pointers change only after validation release value/witness gates plus the release-promote environment

v0.12.16 — the community batch

Choose a tag to compare

@DmitriyG228 DmitriyG228 released this 22 Jul 00:17
e617d83

The community batch — 7 PRs from 5 external contributors, plus the Teams live-transcript fixes and reliability work. Baseline v0.12.15.

Highlights

Google Meet & bots

  • Leave-when-alone — a bot alone in a meeting now emits left_alone and leaves instead of burning capacity for up to 4h (#545, @m-tauqueer). Witnessed live on a real Google Meet.
  • Lite bot no longer echoes meeting audio back into the call (#819, @Ahmed-Tokyo). Witnessed live.
  • Redis first-use connect race ("Socket already opened") fixed via idempotent memoized connect (#820, @Ahmed-Tokyo).

Teams / mixed lane

  • Live transcripts render in real time — the mixed-lane mapper now stamps absolute_start_time, so Teams/Zoom transcripts paint live on the dashboard instead of only after a reload (#895).
  • Stable speaker labels — unattributed turns show a single "Speaker" instead of a new fake speaker per line (#890).

Transcription

  • Bring-your-own STT — any OpenAI-compatible endpoint, documented (#537, @LauraGPT).
  • Degraded STT reports why on the terminal lifecycle event instead of completing silently (#552).
  • Google Meet silence-hallucination suppression (ja/tr phrase lists + near-silent submit gate).

API & correctness

  • Malformed native_meeting_id → typed 422 at intake, not an asyncpg 500 (#843).
  • Meeting timestamps serialize as UTC (Z) so clients render local time correctly (#860, @jacobschooley).
  • Zoom URLs now accepted in the join dialog (#897); honest version surfacing on webapp + dashboard (#72).

Honest boundaries

  • left_alone does not yet fire on Zoom/Teams/Jitsi when the bot is alone from the start (readiness latches on first audio) — #866.
  • Community-contributed STT examples (FunASR/SenseVoice) are marked unwitnessed — #863.
  • Dashboard browser logout has a known gap — #899. Mixed-lane transcript quality (oversplit/hallucination) is the focus of v0.12.17 (#847).

Contributors: Mohammad Tauqueer, LauraGPT, Ahmed Tokyo, Jacob Schooley, Dmitriy Grankin.

Full changelog: https://docs.vexa.ai/changelog · Integration PR: #867