How we reviewed this: we ran it in python:3.13-slim with --network none, pointing api.typesafe.ai at a self-signed TLS stand-in, using the project’s own Ableton stub. We made no Jev calls.
What it does
A macOS daemon that turns a spoken phrase — Japanese or English — into an Ableton Live action. “パッドをちょっといい感じにして” nudges the Pad track’s volume; “make the bass louder” does the obvious thing. A local regex phrase table handles what it can, and only the leftovers go to Jev as a set of multiple-choice questions whose options are the actual tracks, devices and parameters in your session.
The shape is right. Asking a decision model to pick from this session’s real track list is much better than asking a language model to invent a command, and the answers map onto a whitelist of Remote Script calls rather than arbitrary code.
The local-first part is not decoration. In a twenty-command session on a three-track project, thirteen commands never touched the network.
What a request contains
intent.py:401:
state = {
"utterance": utterance,
"tracks": [
{
"i": track.index,
"name": track.name,
"volume": track.volume_display,
"pan": track.pan,
"mute": track.mute,
"solo": track.solo,
}
for track in snapshot.tracks
],
"tempo": snapshot.tempo,
"playing": snapshot.playing,
"scenes": [{"i": scene.index, "name": scene.name} for scene in snapshot.scenes],
}
Plus, as question criteria: every device name including third-party plugins, every candidate parameter with its current value, clip names, scene names, return bus names, and your own aliases file. Nothing is redacted and nothing is truncated — the only limits are counts (250 clips, 250 devices, 250 parameters, 250 scenes, 26 returns).
File paths are the one thing held back: Track.path and Device.path exist in the snapshot and build_request never reads them. That looks deliberate and it is the right call.
Whether the rest matters is a judgement about your own work. Track names and clip names in an unreleased project are the names of songs and ideas; the device list is a fingerprint of which plugins you own. It is not secrets in the credential sense, and the README does not hide that a session summary is sent. But “adjust the pad” transmitting your whole arrangement is worth knowing before you leave it running.
The request grows with the session. Serialising build_request at three sizes:
| session |
bytes |
questions |
| 3 tracks, 1 device, 1 param, 1 clip |
16,601 |
19 |
| 16 tracks, 2 devices, 8 params, 4 clips |
48,117 |
58 |
| 40 tracks, 3 devices, 12 params, 8 clips |
157,285 |
130 |
At Jev’s input pricing a 40-track session is a few hundredths of a cent per phrase, so this is not expensive. It is unbounded, though: no cap, no budget, no rate limit, and no cache, so the same phrase twice is billed twice. A chained command costs one request per clause, up to four.
Google is there but switched off
There is a second model in the tree, and it is not OpenRouter — it is Gemini, used to rewrite ambiguous phrases. daemon.py:588:
# The public build does not use slow LLM rewriting for ambiguous requests.
# Enable it only for experiments with LIVE_JEV_LLM=1. Otherwise return the clarification unchanged.
if self.llm_key is None and os.environ.get("LIVE_JEV_LLM", "0") == "1":
self.llm_key = read_key("GEMINI_API_KEY")
Off unless you opt in, with the comment explaining why. That is how an optional third party should be wired.
It won’t start from some directories
bridge_client.py:29:
for candidate in (here.parent / "codex-live-bridge", here.parents[1]):
If the checkout sits one level below the filesystem root, here.parents[1] does not exist. We copied the repo to /app and imported it:
File "/usr/local/lib/python3.13/pathlib/_local.py", line 49, in __getitem__
raise IndexError(idx)
IndexError: 1
From /x/y/app it imports fine. This runs at module scope, so there is no way around it, and containers are exactly where a repo ends up at /app. On a Mac, where this is meant to run, you will never see it.
Verdict
MIT, 36 stars, one day old. python -m unittest discover gives 326 tests, 1 skipped, 0 failures with no network and no Ableton — the stub in tests/support.py is a real stateful fake, not a mock that agrees with everything, and the single skip is the author’s own marker on a script not in the public copy. That is a genuinely good test suite.
And there is no CI. Zero workflows, no .github/ directory. 326 tests nobody runs automatically.
The key handling is mostly careful — Keychain on the Swift side, never written to disk from Python, no key in the logs — with one habit worth flagging: read_key scans .zshenv, .zprofile, .zshrc, .bash_profile, .bashrc and .profile line by line for an export. The reason is good (apps launched from Finder don’t inherit your shell environment) but it means the daemon reads six of your dotfiles at startup.
We’ve reported the path crash, the missing CI and the unbounded payload growth. If you make music in Ableton and don’t mind your session structure leaving the machine, this is the most fun thing in the directory.
For the same idea pointed at a browser, see jev-browser.
See how it compares with other tools in Best Jev tools, tested hands-on.
Review updated Sep 20, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.