v0.12.27
mufeedvh/code2promptv0.12.27Sep 7, 2026by DmitriyG228
Upgrading?
AI Summary
A major release introducing comprehensive MCP capabilities, a new 'Flows' system for event queuing and scheduling, and searchable meeting transcripts. It also enhances security by stripping specific authority headers and improves agent ownership of meetings.
Key Highlights
- MCP agents can now perform all Vexa tasks, including friction reporting and standing notices
- New 'Flows' API allows chaining bots and agents with Python-coded steps and global scope
- Meetings are now searchable via full-text search and agents can annotate/own meetings
- Security improvements strip authority headers by family and no longer return webhook secrets
- Zoom vanity hostnames are now recognized and supported
Breaking Changes
- Generate a real `INTERNAL_API_SECRET` (placeholder secrets are now refused)
- Settings move from workspace files to identity, requiring operator-triggered migration
- Agent surface returns 404 if `AGENT_API_URL` is unset (previously 403)
- Previous webhook secrets must be rotated as they are no longer returned
New Features
- Flows API, worker, and mailbox services
- Full-text search for transcripts
- Meeting annotation and filtering by metadata
- Agent ownership of meetings via deal tagging
Full Release Notes
## 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](https://docs.vexa.ai/flows/overview).
- **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` 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](https://github.com/Vexa-ai/vexa/actions/runs/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.
Related Repositories
A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io
An open-source remote desktop application designed for self-hosting, as an alternative to TeamViewer.
Build smaller, faster, and more secure desktop and mobile applications with a web frontend.
π RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video.
Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one