github Vexa-ai/vexa v0.12.27
v0.12.27 — the full MCP, flows, and searchable meetings

4 hours ago

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_URLVEXA_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-KeyX-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 expects a database that has never held its tables. The engine creates them with
    CREATE … IF NOT EXISTS and ships no migration runner.

Images (eleven — vexaai/v012-flows joins the release set with this version): vexaai/v012-admin-api@sha256:4c702354384eafe3a933cd537a106b7c15067cfd368a5f6da1a6e675e7e03e04, vexaai/v012-runtime@sha256:a1f6448fbb380b9433364e8b572ec25a12aa4f274bf403f1d83a89cbf5812f2d, vexaai/v012-agent-worker@sha256:a1120b24765ff6b1c86c5f9ddee35b5e71fdbb9543a23b918b278373ad705022, vexaai/v012-agent-api@sha256:6eb37574b33aab233aabbe5907e06e106bae44a403e5df781a436da8201a928d, vexaai/v012-meeting-api@sha256:4e9c201f656788755458fd6f2b9fbf3bfe0722d5b17d4cebd82b874b5aee98e1, vexaai/v012-gateway@sha256:2e76319812f28adbb6bc328ac0a7484d3826924a9d38a9dd9599dd190fbb0f19, vexaai/v012-mcp@sha256:4b2dbc08023ff424f40453d61a8f4d38a72de7eba1e5aaad1e428c309f63d338, vexaai/v012-terminal@sha256:8293350ff70008674032de67c682091eb8b87110412d2a4a1f3c4ed10cb1b09f, vexaai/vexa-bot@sha256:423c487aadb81514c71c9786758a3a730acbf5495e9593930da50c7442f158c1, vexaai/vexa-lite@sha256:945628e54d843cf6286a823ca8e226f2b3c48eb948ad2f895e64eb867b7a0d55, vexaai/v012-flows@sha256:e040c0de48924e78440e9cc4e71ed986471f101f99fbe5a5bd091af7e59996fa, published from the reviewed candidate packet
releases/v0.12.27/candidate-images.json and validated by run 34059966981.

In production: this release is what vexa.ai runs. Pinned on the internal channel — the witnessed
packet as entry 35, and entry 36 carrying the same eleven OSS digests plus the release-version pin —
and re-verified against the cluster after each pin. All four platforms were walked live on the
witnessed packet before the tag was cut.

Don't miss a new vexa release

NewReleases is sending notifications on new releases.