Dev page Metrics fixes and a working Claude skill install.How to upgrade:
- Upgrade unmute:
brew upgrade --cask slng-ai/tap/unmute. - If you use Claude, run
unmute skill install --agent claudeagain. You do not need to compile your agents again.
- Metrics view: each reply now sits on the caller turn it answers. Before, a reply was shown on the turn before it, and the last turn showed ”, ”. This affected typed calls and mic calls on LiveKit.
- Metrics view: when you open a turn, its parts are now listed in the order they started. Before, they were grouped by type. A tool turn now reads the way it ran: LLM, tool, filler speech, LLM, tool, reply.
unmute skill install --agent claudenow also writes the shared skill files under.agents/skills/. Before, it wrote only the.claudepointers, and they pointed at files that did not exist.
A new dev page with metrics, state and text chat, plus runtime fixes.How to upgrade:
- Upgrade unmute:
brew upgrade --cask slng-ai/tap/unmute. - Compile your agents again. The generated Pipecat and LiveKit
projects changed:
call_state.py,pyproject.tomland the LiveKitagent.py.
- The
unmute devpage has a new look, in the style of SLNG Studio. It has a top bar, a call bar, a transcript with a speaker column, and a side panel with call details. - New Metrics view. It shows p90 tiles and one row per caller turn, with the time for each part: STT, LLM, TTS and tools. Open a turn to see each part on its own row. Latency by model is grouped by STT, LLM, TTS, and tools and handoffs.
- Reply latency colours: green up to 900ms, yellow up to 1200ms, orange above. Red now means failed only. Turns with tool calls are not rated. A legend explains the colours.
- Live call state. Every save to
Stateshows as a row in the conversation, with what set it and the value before. A new State panel lists every field fromstate.pywith its current value. - Type to the agent. During a call, a text box under the conversation sends a line as the caller’s turn. A new Text only button starts a call with no microphone.
- A start screen shows progress while the agent starts. If the start fails, it shows the first error and a link to Logs.
unmute dev --target pipecatno longer fails at startup for a package withknowledge:on Linux. Generated projects now tell uv to copy packages, not hard-link them.- A LiveKit worker behind
HTTPS_PROXYnow connects to its server directly whenNO_PROXYlists the server. Before, the proxy answered 405 and the worker gave up. - Pipecat reply latency is now linked to its reply. Before, every Pipecat turn in Metrics showed ”, ”.
- On LiveKit, a hang-up during a task is now recorded as
cancelled, notfailed. - The docs changelog workflow works again. It now opens a pull request after each release.
- New
examples/salon-concierge-unoptimized. All three salon examples now use the same model, tools and turn-taking. You can compare their token use directly. salon-concierge uses 36 to 45% fewer tokens than the unoptimized one. scripts/text_run_livekit.pynow prints token usage.
build/<target>/dev.log now show state
values. These can include tool results and caller details. They stay
on your machine.Run unmute skill install to refresh the bundled assistant guidance.Full notes and downloadsSLNG deploy fixes.How to upgrade:
- Upgrade voiceai:
brew upgrade slng-ai/tap/voiceai. Deploy needs voiceai 0.1.22 or later. - Upgrade unmute:
brew upgrade --cask slng-ai/tap/unmute. - Compile and deploy your SLNG agents again.
- end_call: the caller hears one goodbye, not two. The
announce:on end_call is now the goodbye line itself. - Inbound calls no longer fail when a prompt uses a call fact, such as
from_number. - A package that also targets LiveKit or Pipecat no longer fails to
deploy because of
world_part. - Deploy works again after api.slng.ai went offline.
- A dry run no longer refuses an MCP server whose snapshot is old.
- Deploy warns you when inbound phone calls are not ready.
- The deploy report drops
organisation(report_version 2).
unmute skill install to refresh the bundled assistant guidance.Full notes and downloadsCall state in state.py, and tasks on SLNG managed agents.How to upgrade:
- Upgrade voiceai:
brew upgrade slng-ai/tap/voiceai. Deploying a task that lists tools needs voiceai 0.1.20 or later. - Upgrade unmute:
brew upgrade --cask slng-ai/tap/unmute. - Install uv (https://docs.astral.sh/uv/) if a package has variables. unmute now reads state.py with it. The first run needs network.
- Run
unmute validateon each package. Every old form is refused with its file, its line and where it moved. The fixes are below. - Run
unmute compileand deploy.
-
The call state is a Pydantic model in state.py. Why: types,
defaults and descriptions are now real Python that Pydantic checks,
not a type grammar inside YAML. How: move each variable’s
type:,default:anddescription:to a field onclass State(BaseModel)in<package>/state.py.variables:in agent.yaml is now a list. It keeps only the values that needsource:orconfirm:.The old form fails with:variables: is a list now. Types, defaults and descriptions live on State in state.py. -
shapes:is gone. Why: a shape is a type, and types now live in state.py. How: write each shape as aBaseModelclass in state.py and use it as a field type on State. -
Templates name a saved value as
{{state.x}}. Why: the name now says where the value lives, a field on State in state.py. How: change{{x}}to{{state.x}}in the greeting, prompts, toolinject:, webhookpath:and prefetchargs:.{{$NAME}}is unchanged. The old form fails with:{{x}} names a value on State; write {{state.x}}. -
A step must return every value its
assign:names. Why: every field that starts empty is now| None, so| Nonecan no longer mean “the step may leave this out”. How: add?to a value the step may skip:- callback_time: result.callback_time?. Lists (x+:) stay optional. Nothing fails at validate, so read eachassign:once. -
Pipecat needs
params.world_parton SLNG speech. Why: pipecat-slng 0.6.0 made it required, and a Pipecat bot without it failed at start. How: addworld_part: eu-north(or your region) underparams:on each SLNGlistenandspeakbinding.unmute validaterefuses a Pipecat package without it. LiveKit is unchanged. The SLNG target dropsworld_part, so one package can target Pipecat and SLNG.
-
Tasks and task groups on SLNG managed agents. Why: SLNG managed
agents now run tasks. How: write tasks the same way as for LiveKit
and Pipecat, and add
slngto the targets. A tool that only a task uses is attached hidden from the main agent.unmute validatenames what SLNG still refuses: a model per task,finish:rules,history: summary, a group ending in anything butreturn, and handoffs. -
Typed values on SLNG. A value a task saves, or one the model records
with
source: conversation, can be a phone number, email, date, time,Literal, list or model. A value the dispatch fills stays plain text. -
Logfire tracing on LiveKit and Pipecat:
tracing.provider: logfirewithLOGFIRE_TOKEN. Langfuse and Coval settings are unchanged. LiveKit no longer installs the langfuse SDK, and its traces drop the empty wrapper rows.
- Generated Python passes its own ruff and ty rules. Each project carries them in pyproject.toml.
- Each code target writes a small project instead of one big file: settings.py, session.py, agents.py, call.py, utils/, and prompts in prompts/*.md. Twilio splits app.py the same way. The entry file keeps its name (bot.py, agent.py, app.py), so deploy commands do not change. If you edited a generated file by hand, find your edit in the new layout.
- The saved call state is a real Pydantic model at runtime. A saved model holds a model, not a dict. Call-start values and carrier facts now go through the same check as every other save.
Voice agents on Twilio, with SLNG optimization by default.Highlights:
- Twilio is now a compilation target. Turn your agent spec into a standalone FastAPI application for inbound ConversationRelay calls. Twilio handles speech recognition and synthesis; your app manages conversation history, tools, and streamed model responses.
-
Start with
unmute init my-agent --target twilio. The starter uses SLNG Context Router with OpenAI upstream to reuse eligible responses and send other requests to the model. Direct OpenAI and Gemini bindings are also supported. -
Host the generated app wherever you choose, then connect your number
with
unmute deploy --target twilio. Deployment checks the hosted build and signed webhook, saves the previous route, and updates the number. Use--dry-runto check before making changes. - The generated app includes signature validation, streamed replies, interruption handling, local tools, end_call, health checks, and graceful shutdown. Custom Python logic and post-session TwiML let you control what happens during and after a conversation.
- A first-call walkthrough covers Twilio account setup, getting a number, compiling locally, publishing the app, and connecting it. Hosting examples show container services, a VM, and a laptop tunnel.
- The shipped example was verified on a deployed application with a real phone call through SLNG, including an opening-hours tool call and a clean hangup.
unmute skill install to refresh the bundled assistant guidance.Full notes and downloadsPipecat steps that start on the caller’s words, one trace per call, and prompts checked against replayed calls.Highlights:
- Pipecat: a task step now opens on an instruction. It speaks its opening line first, then gives the model an instruction that points at what the caller already said. Before, the request ended on the model’s own sentence. The model then asked again for details it had, sometimes invented a value, and sometimes returned nothing. That last case left the caller in silence. Without any instruction, a step acted on a request before asking for a yes.
- Pipecat tracing: two calls on one worker stay apart. Per-call state lives in a context variable now. A warm worker no longer files one call’s spans under another call’s session.
-
LiveKit tracing: one call is one trace again. The event loop monitor
sent an
event_loop_blockedspan with no parent when the loop stalled for more than 100 ms. That span could split a call into two traces. It is no longer exported. - Every Pipecat build answers the Twilio route of the runner. A browser-only example can now take a simulated phone call too.
-
{{$NAME}}in a prompt or greeting now failsunmute validateon every target. Onslngit used to pass validation and then fail at push, because the backend removed org variables on 2026-09-02. The error says where a Vault token still works: an MCP server URL. A tool still names its secret by bare name inauth:. - The salon prompts were fixed where replayed calls showed them wrong. The agent asks for a number it does not have instead of inventing one. It reads back a number given in pieces once, whole. It runs verification before it records a complaint, so it never says it saved something it did not. It no longer offers a transfer nobody asked for.
-
make sim TEST_SET=<id>runs one Coval test set against the examples running on your laptop, on both LiveKit and Pipecat. It exits 1 when a call did not finish, the agent never spoke, or a metric said NO. Seeutils/coval_sim/README.md. -
scripts/text_run_pipecat.pydrives a compiled Pipecat bot through scripted caller lines. It uses the real model and the local tools, with no audio. It takes the same flags astext_run_livekit.py.
- Recompile. The Pipecat bot template and both tracing templates changed.
-
If a prompt or greeting uses
{{$NAME}}, move the value into a package variable ({{name}}) or into the MCP server URL. It was already refused at push onslng. Nowvalidatesays so first. -
Run
unmute skill installagain. The skill’s package and deploy references changed with the Vault token rule.
Task groups that speak on Pipecat, a booking turn that costs one request less, and a docs tree that stopped rotting.Highlights:
-
Task groups work on Pipecat. A step’s opening line was emitted as a
Flows
tts_saypre-action, and a pre-action holds the node until the frame behind it reaches the worker sink, a frame this worker queues does not move until the tool call building the flow returns. The first node of every flow waited on itself: nothing spoken, nothing asked of the model, the caller left hanging. It is a queuedTTSSpeakFramenow, so nothing waits.opening: listenwas broken the same way and is fixed by the same change. Two runtime smokes hold it; both time out on the old template. -
The salon’s
bookgroup is back, and a booking turn spends one model request fewer.manage_bookingloses itswhen:, so the group is the only way in, andskip_when_confirmed:decides verification again. That is the general shape, now written down at /build/orchestration/task-groups and in the skill: two tasks an agent chooses between spend a request on the choice, and that request speaks nothing and does nothing. A group is chosen once, and every step after the first opens by itself. -
The Vertex bridge emits
HttpOptionswith the enum.tyreads a plain dict asdict[str, str]and refuses"COLLECTION"where aResourceScopebelongs, which is what turned the salon’s static check red. Oneconst, so Pipecat gets it too, and the request on the wire is the same string it was. -
docs/is one file.GATES.md,HARNESS_TEST.mdandSELF_VERIFY.mdeach restated something the code, the tests or CLAUDE.md already said, so they rotted against it instead of holding it.ARCHITECTURE.mdstays, it is the one file that describes the system boundary, the four compiler stages and each target’s runtime topology in one place, minus the interactive console. The allow-list test now refuses any other tracked file underdocs/, so a second one stays a deliberate decision.
- Recompile. The Pipecat template changed, and a package built before this still has a first node that waits on itself.
-
Run
unmute skill installagain. The skill now says to reach for a group before two tasks with awhen:each, and says why. -
Nothing in
agent.yamlhas to change. If you split a group into separate tasks to work around the silence, put the group back.
A skill for the push, curated capabilities from the package, and telephony you can read.Highlights:
-
A third skill,
unmute-deploy. Pushing is a different job from building, done at a different time and often by somebody who did not write the package. It carries the command loop, what each of the two dry runs checks, how to read the detach list, and what a push replaces against what it leaves alone. That last list is the fourteen fields of the compiled body, held to the code both ways. -
unmute deployattaches a curated capability named by aslng:reference. It used to refuse, send the author to the dashboard, and the next push detached what the dashboard attached.voiceai agents pushaccepted the same body all along, so the refusal was ours: three gates, and the organisation-only scope filter alone was enough to stop a curated record, which is global. -
current_datetimeanduser_phone_numberjoin the prebuilt registry, now four ids, andinject:pins the settings each one takes. A package can pin a timezone the curated tool cannot carry.announce:is legal on a prebuilt on SLNG. The registry and its settings are written down at /build/tools/prebuilt. -
A tool that ends the conversation waits for its own
announce:line.end_callhangs up the moment it runs, so a goodbye spoken alongside it was cut off mid-word. Every other announcement still runs alongside the tool. -
Seven refusals found by deploying one package: a
{{placeholder}}for asource: conversationvariable (it reached SLNG as a fieldless 422), an omittedprovider:under a manifest, a prebuilt whose id is not its file’s name, one name held at two scopes, and a placeholder in a prompt now names its line. The deploy preview compares every setting the package pins, so pinning a timezone no longer reads as deleting it. -
telephony-setup.shis gone. It called barelk, which has no project flag, so on a machine pointed at another project it created the trunk and the dispatch rule in the wrong account and printed success. Compile still writes the two JSON files; the runbook prints the two commands that send them, each naming its project. The Twilio and LiveKit pages are rewritten in step order, with a symptom table, a teardown and a what-to-redo table. - The salon concierge sounds like a person. Nine passages read like incident reports written to the model on every turn; every rule stayed, every incident went to the README. Behind it, the realism guidance now puts a frequency budget on each technique: at most one filler in a turn, none in most, and a clean grammatical sentence is no longer labelled bad.
-
The company contract is
manifest.yamlnow, in a package and in the saved library. A package still holding the old extensionlessmanifestis refused by name. A library saved before this keeps loading. -
Run
unmute skill installagain. The bundle writes three skills. -
If you ever ran
telephony-setup.sh, check which LiveKit project holds the inbound trunk and the dispatch rule before you dial. -
Recompile to pick up the goodbye before the hangup and the new
refusals. Nothing in
agent.yamlhas to change.
Plain init, a skill for company rules, and templates in their own repo.Highlights:
-
unmute init <name>writes six files and asks nothing again. A saved default manifest was routing every new package into guided setup, so a new agent could not be created without answering for a contract nobody had asked about.--from-manifestis now the one way a contract reaches a new package. -
A second skill,
unmute-manifest, writes the company contract. An assistant cannot run the picker, so it does the part that is words: ask what the company allows and write the YAML.unmute manifest create <name> --file draft.yamlsaves it, validates before it saves, and writes nothing when it refuses. - The salon concierge thinks with Gemini 3.1 Flash-Lite on Vertex in eu, at thinking_level MINIMAL to keep the first token fast. The old OpenAI block stays commented in the file, so switching back is one edit.
- Templates live in slng-ai/unmute-templates. The single copy kept here is gone, and the README, CONTRIBUTING.md and the skill a coding agent reads point at that repo and say what a template carries.
-
Replace
unmute init <name> --manifest <name>withunmute init <name> --from-manifestand pick from the list. The--draftflag is gone, along with the assistant draft flow it served. Theunmuteconsole andmanifest usekeep the default. -
Run
unmute skill installagain to pick upunmute-manifest. The bundle now writes two skills.
Alternating announce lines, a reworked salon, and steps that don’t repeat.Highlights:
- announce: takes a list. The caller hears one of those lines each time it fires, never the one that site used last, so a flow entered twice in a call does not sound like a recording. A single line still works and needs no change.
- A task speaks its announce as the step is entered, whichever way the step was reached. On a tool the line sits in the tool body and fires once per call, so two calls in one turn played it twice.
- A step the caller has not spoken since is refused instead of run again, on LiveKit and Pipecat. A finished result with no new caller turn above it is the model re-reading its own work.
- LiveKit sessions retry the model once instead of four times. The default sleeps 4.1 seconds before it gives up.
- Rework the salon concierge: seven tools instead of eleven, two plain tasks instead of a task group, and the saved booking now carries the sentence the agent reads out loud.
- Fix a LiveKit call arriving as two Langfuse traces, and name a tool call after its tool on both targets.
- Recompile to pick up the refusal and the retry cap. Nothing in agent.yaml has to change.
- A task group still compiles but speaks nothing on Pipecat. Use plain tasks there until that is fixed.
Speech-to-speech agents, better manifests, and clearer deployment guides.Highlights:
- Add realtime and live architectures on LiveKit and Pipecat. Run speech-to-speech agents, or pair a realtime model with a separate speech synthesizer. Existing packages default to cascade.
- Add pharmacy-refills and takeaway-orders examples with local tools and knowledge lookup.
- Improve the manifest editor and create agents from saved company rules with unmute init —manifest or —from-manifest.
- Fix LiveKit tool inputs, Pipecat task history, delegated tool timing in the browser, and the Homebrew postflight warning.
- Explain architecture choices, workflow limits, and deployment steps for LiveKit Cloud, Pipecat Cloud, and SLNG.
- Set the LiveKit framework version to 1.8.1 and Pipecat to 1.10.0 in targets.yaml.
- Realtime and live currently support single-agent browser calls. Keep cascade for tasks, saved state, tracing, and phone routes.
Google inference locations on LiveKit and Pipecat.Highlights:
- Choose Google Vertex inference locations with params.location. US, EU, individual regions and explicit global routing now select the corresponding Google endpoint on both LiveKit and Pipecat.
- Keep GOOGLE_API_KEY authentication and provider parameters, including thinking_config. Existing EU routing and Gemini Developer API configurations continue to work.
- Follow Google’s endpoint rules without a compiler region allowlist. Unsupported locations and unavailable models fail at the requested endpoint, with no fallback to global or another region.
- Verify real US requests through both generated native clients. Regression checks cover endpoint selection, parameter forwarding, mixed locations, Developer API behavior and provider errors.
- Update the model guide and coding-agent skill reference with location examples and routing behavior.
Reusable company rules, native Gemini, and unified regions.Highlights:
- Reuse company rules with unmute manifest. Guide new agents through allowed models, languages, regions, targets and tools. Each package carries its own manifest for offline validation and compilation.
- Run native Gemini on LiveKit and Pipecat, including Vertex EU access with an API key. EU requests use the regional endpoint with no global fallback.
- Use one set of 13 SLNG regions across deployment, speech and the Context Router. Listening, thinking and speaking now share params.world_part.
- Build with clearer guides: a first-task walkthrough, complete field references, practical credential setup and example troubleshooting. New checks catch broken links, invalid MDX and missing field docs.
- Replace the Context Router’s params.world_part_override with params.world_part. Choose a current region such as eu-west or us-east; the old eu, us, india and indonesia values are refused.
- Update SLNG deployment_region values any, eu-central and ap-south to a supported region. LiveKit now rejects unknown worker regions and media region groups used as deployment regions.
Regional speech, live transcripts, and typed contacts.Highlights:
- Choose where SLNG speech requests go. Set params.world_part on each SLNG listen or speak model to select one of 13 regional gateways, including eu-north, eu-west, us-east and us-west. Unmute generates the matching endpoint on both LiveKit and Pipecat. Gateway location and worker deployment region are configured separately.
- See every turn in unmute dev. Streaming transcripts show the caller’s words and the agent’s reply as they arrive. Model requests, tool calls and handoffs get their own rows, with per-request latency and audio timings to help explain where the wait comes from.
- Collect typed contact details on LiveKit and Pipecat. EmailStr checks and normalizes email addresses before saving them. NameEmail keeps a name and address together, with dotted references for either field. Invalid values leave saved state untouched and tell the model what to correct.
- Deploy SLNG tools by name. Reference published hosted tools and named MCP tools without pulling a mirror first. Deployment checks published contracts, injected arguments and required credentials before pushing. A dry run previews the versions and attachment changes.
- Record conversation values and send SMS on SLNG. The new source: conversation lets the model save values during a call. The send_sms builtin sends a message to the caller’s number on phone calls, with the sender fixed by the package.
- Two new examples to build from. customer-intake shows typed details, caller confirmation and saved values passed into a local tool. hotel-concierge replaces slng-support with hosted lookups, MCP web search, conversation memory and SMS summaries.
- Run local dev sessions side by side. Busy default ports are selected automatically, LiveKit stacks are isolated, and shutdown cleans up the session. Transcript handling, worker startup and Pipecat hangups also get fixes.
- Updated dependencies and clearer guides. pipecat-slng moves to 0.5.2, generated Python supports ty 0.0.40, and the docs expand SLNG deployment, typed state, regional model settings and latency measurement.
- For SLNG listen and speak models, replace params.world_part_override and params.region_override with params.world_part and a specific gateway, such as eu-north. Legacy values na, eu and ap are refused. Omit world_part to keep the default endpoint. SLNG think models keep the Context Router’s existing world_part_override setting.
- SLNG deployment requires voiceai support for checked, resolved pushes (verified with 0.1.18). Validation and compilation remain offline.
Simpler SLNG deploys, live transcripts, and a hotel concierge.Highlights:
- Reference SLNG hosted tools and MCP tools by name. Deployment checks published bindings and credentials, previews attachment changes, and pushes only after checks pass.
- Follow conversations as they happen in unmute dev, with streaming transcripts, tool activity, and per-request latency.
- Meet hotel-concierge: a new SLNG example combining hosted tools, web search, conversation memory, and SMS summaries.
- Improve local worker startup, transcript handling, and graceful Pipecat hangups, with regression coverage for LiveKit task handoffs.
- Update pipecat-slng to 0.5.2 and keep generated Python compatible with ty 0.0.40.
- Expand the SLNG deployment walkthrough and clarify tool setup, testing, and latency measurement.
Typed state, simpler tasks, and portable hosted tools.Highlights:
- Typed session state on LiveKit and Pipecat. Declare reusable shapes, lists, optional values and fixed choices. Task finish arguments come from the variables they assign, so each type is declared once. Results are validated together before any value is saved.
- Explicit context sharing. Prompts read saved values with {{variable}} or {{variable.field}}. Tasks restore the owner’s earlier context and return a completion status; saved results reach only prompts that name them. List assignments can append records across several tasks.
- Simpler task definitions. Tasks live inside the agent that owns them, with their trigger, announcement and assignments in one place. Other agents can reuse a task by name.
-
Hosted SLNG tools. The new
slng:block references an existing tool, andunmute pullsaves its definition and hash into the package. Validation and compilation check the committed mirror offline. Supported mirrors also run in generated LiveKit and Pipecat projects. - Langfuse v4 tracing on both code targets. Each call stays in one trace, with the full conversation on its root and both sides of each exchange on a turn span. Tool spans carry their names, arguments and results; session IDs and trace names reach every observation.
- More reliable conversations. Pipecat settles task tool calls before returning to the owner, starts replies after empty-history handoffs, and removes tool calls together with their replies when trimming history. Salon examples preserve verified identity and booking details across changes. A new text harness exercises generated LiveKit agents with real models and local tools.
- Clearer CLI output and docs. Commands print what they did and what needs fixing; compile details live in compile-report.json. New guides cover state design, context sharing and verification. Shared OpenAI bindings can select LiveKit’s Responses API; Pipecat warns and drops the options it cannot use.
-
Replace agent
model:andvoice:selectors withthink:andspeak:. Task model overrides also usethink:. -
Move top-level task definitions into an agent’s
tasks:list. Replacedelegates:withtasks:ortask_groups:attachments, moving triggers and announcements onto the task or group. -
Replace task
result:schemas with typed variables and taskassign:. Writeassign:and toolinject:as lists of single-key entries. -
Remove
requires:and handoffcontext.variables:. Put step ordering in prompts and reference saved values explicitly. Tools still refuse missing or unconfirmed injected values. -
Omitted task and handoff history now means
messages. Usefullwhen the receiver needs earlier tool calls and results. -
Move package-level
timezone:onto each clock pre-fetch entry. Replace toolread_only:with an explicitwrites: falseorwrites: trueon each tool pre-fetch entry. -
SLNG deployment no longer creates tools from
local:orwebhook:. Create the tool on SLNG, reference it withslng:, rununmute pull, and commit the generated mirror. Local and webhook tools remain supported on LiveKit and Pipecat.
Pre-fetch known facts, and a deploy that checks the account first.Highlights:
prefetch:resolves known facts before the greeting. A package declares an ordered list that reads three sources:clock:(a date under the package’stimezone:),source:(a call fact the carrier supplies) andtool:(one read-only lookup, keyed on a value an earlier entry assigned). Each entry gets a 2s budget and cannot raise: a skip leaves the variable on its default and the existing guard runs the step that asks.confirm:marks a value the caller has not agreed to yet, so it renders in no prompt but its confirming step’s and satisfies no gate until it is settled.announce:on a delegate covers the entry gap, spoken after the guard so a refused step stays silent.unmute dev --source from_number=...seeds a call fact into the browser loop, which has no carrier. Salon is rebuilt around it andget_current_dateis gone. A package declaring none of the new fields emits byte-identical output.unmute deploychecks the account before it writes anything. It asks the organisation what it already has, compares it with what the package needs, and reports every gap in one pass with the line that asked for it and the one action that fixes it. Checked: everybuiltin:tool, every MCP server and exposed server tool, and every vault secret and variable including whether it holds a value. A code or webhook tool is never reported missing because the push creates it. Secrets are filled by handing the prompt tovoiceai secret create, so no value enters unmute or reaches argv. A refused run leaves build/ and the account untouched. Addsunmute resourcesand--call.mcp:is core on slng. voiceai 0.1.16 resolves a reference by server name and copies each tool’s schema hash from the platform’s own snapshot, so nothing connects to the server; unmute warned the opposite and told authors anmcp:package could not deploy at all. Both were wrong. Addsmcp.serverfor a platform name carrying a dash or a space. Proven on a live deploy.- An agent declares what it can do in
tools:,delegates:,handoffs:andescalations:. The block a thing is written in is its kind, sokind:andcontrols:are gone. A strict re-spelling: every package compiles byte for byte identical on every target. - Tracing: salon-concierge moves to Langfuse, and
scripts/read_langfuse_trace.pyreads aunmute devcall back out of it, transcript, tool calls and per-span latency, newest trace by default. Coval self-verification is push-based and is unaffected. - Docs: two new pre-fetch pages (the primitive, and how to decide what to pre-fetch), one page that explains the shape of a package before Orchestration needs it, and our own measured costs are out of the reader-facing pages.
controls:andkind:are removed with no alias. An old package fails as an unknown key, with file, line and column.
One deploy command, deployment names that stop colliding, and honest Coval traces.Highlights:
unmute deploy [package-dir]validates, compiles and pushes a package to SLNG in one command. It shells out tovoiceai agents push --json, so the push contract stays in one released binary. A refused push relays every problem with the fix line and the dashboard page. Credentials come from SLNG_API_KEY, then VOICEAI_API_KEY, then a storedvoiceai loginprofile, read from the package’s own .env, and the organisation is printed every run.- Deployments are now named after the package, not the target. Before this,
every package deployed as
slng,livekitorpipecat, so a second package replaced the first one’s live agent and two LiveKit packages split one worker’s dispatch.agent.yamlnow requiresname:, and a deployment is that name joined to its target. This also fixesunmute dev, which minted a browser token for a room nothing ever joined. - Coval traces are per call again. Pipecat Cloud serves many calls from one
warm container, and every call after the first was filed under the first
call’s conversation with nothing logged either way. Correlation now resets on
every call, late spans are dropped instead of misfiled, local runs label
themselves
<name>-local, and one accurate log line replaces the message that claimed no trace was exported when one was. - Pipecat support raised to 1.8.0, with pipecat-slng >= 0.5.1. 0.5.0 imported a private name that 1.8.0 renamed, which broke every emitted bot at import.
- The salon concierge example sounds like a person. Prompts now separate the speech contract from the personality, leave numbers, money and times in plain written form so the voice engine normalizes them, and drop the second acknowledgment that talked over each tool’s announce line. Three defects came off a live call, and the evidence sits next to each rule. The skill teaches the same shape.
- Docs: a runbook for the second deploy, including the routing records a rename strands, on every target’s own page.
agent.yamlmust carry aname:. A package without one is refused with the shape and an example.- Deployed identities change name, so the first deploy after this upgrade creates a new agent and leaves the old one live. Delete the old one.
Compile to SLNG, knowledge search, and turn timing you can set.Highlights:
- New
slngtarget: compile a package straight to the SLNG platform. - Knowledge bases: point a tool at your own documents - txt, md or pdf files.
- Turn timing is yours to set.
endpointing_delayis the silence floor,paceis the ceiling, andsemantic_endpointingcan switch the turn model off. - Pipecat phone calls are faster and quieter:
warm_instanceskeeps containers warm, and the agent no longer interrupts its own greeting on speakerphone. - Faster turns on both targets, from pipecat-slng 0.5.0 and the quicker SLNG route.
- Context Router sends model settings per request, and
prompt_suffixadds one line to every prompt in a package. - Task groups: a step that returns can carry its own prerequisites.
- Console rebuilt on Bubble Tea and Lip Gloss, with one owner for colour.
- Versioned docs, a full carrier setup guide, and a changelog that writes itself.
- The
vapianddeepgramtargets are retired. deepgram is still a model vendor. - Local telephony testing is gone. Test phone calls on a deployed agent.
Per-site Context Router cache scopes.Fixes a cache collision where agents sharing one authored agent_id could be
served each other’s cached replies. The router keys its cache on the last
exchange with no system prompt, so in a multi-agent package a specialist’s
opening turn could get the concierge’s cached line.Highlights:
- Every prompt site now gets its own cache scope derived from the authored id: “<id>:<agent>”, “<id>:task.<name>”, “<id>:summary”. Authoring is unchanged: still one agent_id per package.
- LiveKit sets the header per request in an emitted _slng_llm_node, since constructor-level extra_headers would replace per-request values.
- Pipecat switches scope on task entry and restores the owner’s scope on every exit path: finish, next step, transfers, terminal node, and error rollback.
- New gates: one scope per prompt site on every target, no constructor extra_headers on LiveKit, scope restore on each Pipecat exit path, and the name pattern excludes the scope separator.
A phone call that works, a dev loop that shows the numbers, and a faster LiveKit turn.Highlights:Local telephony you can test end to end
The (pipecat, sip) inbound route compiled and passed its goldens but could
not answer a phone. Six defects, each found by placing a real call against
the local SIP plane: no telephony.py for the containers uvicorn CMD, no
route telling the bot about a call, telephony-setup.sh emitted without the
JSON files it reads, a two-phase startup that created trunk records against
a server that was not running, a greeting that waited on an RTVI handshake
no phone call fires, and a hangup handler installed on an event the
transport does not have. Cold transfer now reads the participant identity
off the room instead of passing a sid the platform answers 404 for.
salon-concierge ships a pipecat_sip target on the same trunk as its livekit
target, so the docs are one command again instead of “copy this other
example and edit two files”. Three telephony pages merged into one
dev/local-telephony page with redirects, and first-phone-call is now
inbound-calls to pair with outbound-calls. Two test gates got honest: the
live-call banner test no longer reads the real PATH for sox, and the
barge-in gate reads its counters under the lock that writes them, so it
stops failing a call where the barge-in actually worked.Metrics, logs, and tool timings in the dev UI
Every emitted project now carries dev_metrics.py and prints one framed JSON
line per turn to stdout. No collector, no exporter, nothing over the
network. internal/devmetrics owns the lines shape and is the only decoder.
The producers stay inert unless UNMUTE_DEV_METRICS is set, and the artifact
is byte-identical with the switch on and off. The switch now also reaches
the containers that run an agent, which is why the LiveKit half shipped
wired correctly and never switched on.
The dev page gained a logs view fed by server-sent events, and the listener
now binds before the target starts, so a failed container build is visible
instead of invisible on exactly the runs whose output you need. Output
produced before the browser loads is replayed on connect, and Last-EventID
is honoured so a reconnect resumes rather than repeats.
Each agent turn carries its own timing line: end to end, time to first byte
per service, and how long the reply took to stream. Every tool the turn
called gets its own row above the reply it delayed, which is where it
happened in the timeline. Handoffs are no longer reported as tools: a
LiveKit delegate that does not return until its whole flow finishes was
reporting as a single 51-second tool call. An unreported value renders as a
dash and never as 0.00.
The transcript shows one row per spoken turn instead of one per recognizer
fragment, which was nine rows for one sentence on gradium/stt:default. The
mic control states which of three things is true, M toggles it, and holding
SPACE talks, so barge-in can be tested deliberately. New docs page under
Develop and test explains where the time goes in a voice turn, with field
meanings read from pipecat 1.7.0 and livekit-agents 1.6.10 themselves.Coval as a first-class tracing integration
tracing: provider: coval compiles to a project that sends Covals canonical
spans on both code targets, with no correlation code written by the package
author. Both of Covals correlation routes are used: a call Coval placed
exports against the simulation ID on the call, and any other call is
registered as a Coval conversation when it ends and exports against the
conversation ID that comes back. That second route is what puts a local
unmute dev run in Covals Trace Search. Spans are held, capped, and flushed
once an ID arrives, so nothing before correlation is lost.
The two targets reach the span tree differently, because the frameworks
differ. Pipecat already nests its spans the way Coval wants, so they are
renamed in flight. LiveKits shape cannot be fixed by renaming, since a
spans parent is fixed when it starts, so the LiveKit module leaves
LiveKits own OpenTelemetry off and builds Covals tree from session
events, using only numbers LiveKit measured itself. Each llm span carries
the prompt that round actually ran on, under the same attribute names on
both targets. A number that was never measured is left off rather than sent
as zero.LiveKit turn latency, measured across three calls per setting
A session of live latency work on salon-concierge, with every claim taken
from three calls rather than one, because two calls on an identical build
differed by 1.1s of mean silence.
- The transcriber, not any endpointing number, was buying a two-second penalty per turn: a turn whose transcription_delay reached 1.0s got the 2.5s max_delay instead of the 0.58s floor. deepgram/nova:3 finalises in 0.159s mean against gradiums 0.999s, with the same words in every transcript.
- endpointing_delay now sets the silence window it always claimed to. On LiveKit it renders as the prewarmed Silero VADs min_silence_duration instead of an endpointing min_delay that could never fire before the VAD reported end of speech, so every authored value under 0.55s used to be silently inert. Under 250ms is now a compile error, not a first-call crash. Unset stays byte-identical and no golden moved.
- Warm TTS standby stays, with the claim corrected: it is worth about 40ms of caller-visible time, not the 610ms first reported, plus a real floor improvement from 221ms to 58ms on the best segment. The earlier number was the websocket handshake, which was already off the callers path.
- Control hops came out of the turn. Verification asks for a phone number only, booking is one task instead of a three-step group, and finish calls in a scripted booking drop from 7 to 2. Tool state moved from SQLite to memory, so get_current_date went from 245ms to 7.7us. llm ttfb over ten samples: 1.212s to 0.895s. Authored prompt text is down 61%.
- A delegated task no longer answers its parents in-flight call. LiveKit injects the still-running delegate call and a placeholder result into the context the task generates from, so the caller heard an apology for a failure that had not happened. The strip keys on the SDKs own lk_running_placeholder marker. Scripted bookings went from eleven or twelve agent turns to seven.
- announce: is a new optional scalar on a webhook or local tool file: one fixed sentence the agent speaks as the tool starts. The wait is not the tool, which returns in 1 to 14ms here, it is the second LLM round trip and the TTS after it. Works on both code drivers, including tools listed on a Pipecat task, which a wrong capability claim had refused.
- docs-site gained a top-level Optimization group and the skill gained references/latency.md, both carrying the seven settings that looked promising and were not, with reasons, so nobody repeats that day.
- A locked-down step can hand the caller back instead of refusing. Every task prompt now ends with the compilers own escape, and every generated finish takes a reserved optional unserved_request that the owning agent is told to read.
- A receiving agent gets its handoffs back after the opening turn. The loop guard used LiveKits IGNORE_ON_ENTER, whose filter leaks into everything the opening reply starts, so one call offered the booking specialist a single tool for ten turns.
- The pipecat delegate history snapshot is a deep copy, so a history entry that is not a plain dict survives the snapshot and restore round trip.
- The (pipecat, carrier-websocket, *) routes stop teaching a Pipecat Cloud deploy they have no manifest for, and carry the self-hosted section they were missing.
- Firecrawl MCP is fully out of both salon examples, gradium STT is in the catalog, and the examples are down to five: four structural packages plus salon-concierge, which keeps the only shipped telephony route.
- The SLNG marks are replaced with the Unmute brand.
Fix for local dev and docs update.Full notes and downloads
Hardened orchestration, production-ready examples, and regional deployments.Highlights:
- Hardens task orchestration across LiveKit and Pipecat, including shared task results, response handling, and session isolation.
- Strengthens telephony and cold transfers with validated phone routes, call-scoped environment variables, idle-resume handling, and safer local cleanup.
- Makes Pipecat startup and observability safer with ordered workers, process-safe Langfuse and MCP tracing, and clearer default logging.
- Adds the full salon concierge example with browser and telephony flows, booking tools, runtime date handling, and release smoke gates.
- Adds a regional speech infrastructure example and deployment guidance for choosing provider regions.
- Fixes scaffold environment keys and dotenv loading.
- Makes the public docs easier to navigate and aligns task, tool, onboarding, and installed-skill guidance.
Upgraded runtimes, verified examples, and clearer docs.Highlights:
- Upgrades and pins LiveKit Agents 1.6.10 and pipecat-ai 1.7.0, while removing deprecated run modes.
- Restructures the public documentation into a clearer user journey and corrects CLI, deployment, telephony, secrets, variables, and schema guidance.
- Makes the CLI easier to use from an agent folder, defaults new packages to LiveKit, and improves phone-route setup guidance.
- Completes a fresh live sweep of every supported example across browser, outbound, inbound, SIP, Twilio, tasks, handoffs, and transfers.
- Fixes task-context restoration, repeated delegation, LiveKit handoff loops, outbound call-start variables, LiveKit SIP dispatch authorization, and Pipecat Twilio cold-transfer shutdown.
- Makes the outbound reminder self-contained and removes the unverified Pipecat Daily transfer example.
Unmute 0.2.0 makes the first agent run reliable, adds coding-agent
support, and opens Windows installation.Highlights
- Update through Homebrew on macOS or install through Scoop on Windows.
- Install the bundled coding-agent skill with
unmute skill install. - LiveKit and Pipecat examples now complete real provider-backed conversations.
- Agent handoffs are more reliable.
brew install slng-ai/tap/unmute works.v0.1.1 published every archive but never pushed the cask. The tap upload was
still switched off, and a skipped step does not fail a run, so the release went
green with nothing to install. This turns it on.Nothing in the CLI changed. On an archive or a clone of v0.1.1 you are not
missing a fix.winget install slng.unmute is still closed and will say so.Full notes and downloadsThe first release of Unmute, a command line compiler for voice agents.You write a small package of YAML and Markdown that says who the agent is, which
models it uses, and which tools it can call. Unmute turns that into a real Python
project for the orchestrator you picked. The project it writes is yours: pinned
dependencies, a Dockerfile, a runbook, and no dependency on Unmute at runtime.
Unmute compiles ahead of time and is never in the call path.What is in this releaseFour commands. Archives are on the release page, each holding the binary, the LICENSE and the
README, alongside a signed
init scaffolds a package, validate checks it against its
targets and names the file and the line when a field is wrong, compile writes
one project per target under build/, and dev compiles, runs the agent locally
and lets you talk to it in the browser. Add --console and you talk over the
terminal mic and speaker with no Docker in the way. validate prints one status
line per target, and every refusal names the fix.Two code targets, one agent. The same package compiles for Pipecat and for
LiveKit. Nothing in agent.yaml mentions either one. Where a runtime cannot run
a model as defined, targets.yaml overrides that single entry by name and the
agent itself does not change.Models named once. Every listen, think, speak and turn model is declared once
and referenced by name. Point brain at a different model and every agent using
it follows. A provider catalogue turns each binding into code, so the emitted
service carries its own import, its dependency pin and its required environment
variable, and a provider with no slot on a framework fails by name instead of
quietly emitting the wrong class. Pipecat covers anthropic, assemblyai, cartesia,
deepgram, deepseek, elevenlabs, google, gradium, groq, inworld, mistral, openai,
openrouter, qwen, rime, sarvam, slng, soniox and speechmatics. LiveKit covers the
same ground through its native per vendor plugins, plus aws, azure and gemini. An
unknown vendor is still legal as a custom OpenAI compatible endpoint, and
provider: local runs the Silero turn detector on your own machine.Multi-agent, tasks and task groups. Any number of agents, with
agent_transfer between them and history you control per transfer (full,
messages, last n, summary or reset). Delegates run a single task or a task group
and return where the contract says they return. Task groups can chain, hand over
or end. A task can name its own model, so one stage can be cheaper or stronger
than the rest of the conversation. Both drivers lower all of this to native
primitives: Pipecat Flows on the owning agent, and LiveKit AgentTask sequences
with typed session state.Tools, four ways. A webhook tool with bearer or API key auth. A local Python
handler in tools/<name>.py, copied in beside the agent. An MCP server as a tool
source on both code targets, with optional transport and an optional list of the
server tools to offer. And prebuilt tools from the registry, end_call in v1,
with an optional closing line. inject adds values to a tool call that the model
never sees and cannot overwrite, which is where a user id or a captured slot
rides along.Variables and secrets. Typed input variables, usable in templates, injected
into tool calls and captured back out of the conversation. secrets lists every
environment name the author wrote, and the compiler cross checks the secrets your
local handlers actually read. Every *_env field is a name and never a value, so
a pasted URL or key fails validation instead of landing in the spec. Seed a
variable for a local session with unmute dev --var name=value, the stand in for
a real dispatch payload.Telephony. Real phone calls on the routes each platform ships: LiveKit SIP
with a Twilio trunk, the LiveKit Twilio connector, the Pipecat carrier WebSocket
route, and Pipecat on Daily, either with a Daily number or with your own carrier.
unmute dev --telephony runs the resolved route end to end from your laptop:
it brings up the routes Compose graph, opens a managed tunnel, points the Twilio
webhook at it and writes the local LiveKit SIP trunk records, so a first phone
call takes no manual setup. --to places an outbound test call, and
--public-url or --no-webhook hand the carrier callback back to you. Local
runs need no cloud account, only a carrier account and what runs on your machine.Human transfer. Cold and warm handoffs written as cold: and warm: blocks,
with their parameters in the shape block. Each route uses the platforms own
primitive and the generated code never owns the audio path: SIP REFER on LiveKit
SIP, Dailys room reroute on both Daily number forms, and a carrier markup
replacement on the Pipecat carrier stream. Warm transfer lands on LiveKit SIP,
with your briefing and the transcript going to the person who picks up. Where a
platform has no primitive, validate refuses and names the routes that work.
Generated transfer tools log which control fired, and a cold transfer refuses in
words when the session is not a phone call.Tracing. A tracing block that lowers to each platforms native
integration, with Langfuse examples on both targets.Secrets that match reality. Compiled output carries a .env.example holding
exactly the variables that agent needs, and the generated project checks them at
startup instead of failing mid call.A runbook per build. Each build/<target>/ gets its own README.md,
Dockerfile, compose.dev.yaml, the platform deploy file, and a
compile-report.json recording what was resolved, which catalogue entry produced
each service and when that entry was last checked against upstream docs.Examples that run. examples/ holds packages that each compile for both
targets: salon-support needs nothing but a browser and two keys,
twilio-telephony-hello places a real phone call, multi-task, task-groups
and subagents cover staged conversations, mcp-example wires an MCP server,
and three transfer examples cover the three mechanisms.Getting itOne static binary for darwin, linux and windows, on amd64 and arm64.checksums.txt and one SBOM per archive.
unmute --version reports its own commit and build date.MIT licensed.Full notes and downloadsOne tag push releases unmuteFull notes and downloads