Releases: tetrixdev/ai-bridge
Release list
v0.25.0 — a turn's input stays open until the assistant has read its prompt
The input no longer closes before the turn has begun. When an earlier turn on the same session was stopped while a background task it started was still running, the CLI on resume first answers that leftover task notification. That answer is a "stamped" result. The bridge took it as "the main assistant is idle" and closed the turn's input before the assistant had even read its opening message. The turn then ran on, sometimes for half an hour, and every message sent to it was refused with turn_ending.
Now a turn's input stays open, and main_state stays working, until the opening message has been taken in: its echo has arrived, or an unstamped result has (for a CLI that does not echo). A stamped result before that is still recorded, but no longer ends anything.
input_closed. The bridge now says when it closes a turn's input while the turn keeps running, instead of revealing it only by refusing:
{"type":"stream","request_id":"…","event":"input_closed","data":{"reason":"idle"}}After it, a turn_input is answered turn_ending. It is advertised as hello.input_closed: true, and servers must accept reasons other than idle. Additive; no protocol bump.
Foreground helpers. A message accepted while the main assistant is blocked on a foreground helper is written at once and read after the helper's call returns. This is now tested against a captured turn and documented in PROTOCOL.md.
Servers on laravel-ai-bridge 0.16.0+ or Engram can move machines to this with desired_bridge_version: "0.25.0".
v0.24.1 — self-update fetches one at a time, and repairs a half-installed npx cache
Found while testing 0.24.0 end to end, minutes after it was released. Two npx installs of the same version ran at once on one machine and left half a package in npx's shared cache. Every later run of that version failed with ERR_MODULE_NOT_FOUND.
That was safe, because nothing restarts onto a version that fails its --version run, but the bridge stayed stuck. Several bridges on one machine told to update at the same moment is exactly that overlap.
- One fetch at a time per machine. The lock lives in
~/.cache/ai-bridge/, and only its holder releases it. A bridge waits up to 5 minutes for its turn; a lock older than 10 minutes counts as stale. - Repair. When the
--versionrun fails with a missing module inside an npx cache directory that holds the target version, that directory is removed and the fetch tried once more. A directory holding any other version is never touched. - The fetch never throws. A lock or removal failure comes back as a failed fetch, and the updater retries it with the usual backoff.
0.24.0 bridges self-update to this as soon as a server asks for it.
v0.24.0 — a bridge follows the version its server asks for
Self-update. A server can name the bridge version every machine should run, as desired_bridge_version in its welcome. A bridge running as a systemd service that can restart it onto a pinned version follows it by itself, upgrade or downgrade:
- It fetches the version and runs it once with
--version. If that fails, it stays on what it runs and retries after 5 min, doubling, up to 6 h. - It waits until nothing is in progress: no turn, upload, file transfer or app call.
- It pins
AI_BRIDGE_VERSIONin its env file, disconnects cleanly, and exits with code 75. systemd starts the new version.
It says whether it will follow as hello.self_update.
- Only acts on a unit that will pick the pin up. That means
Restart=alwaysoron-failure, anExecStartthat runs@tetrixdev/ai-bridge@${AI_BRIDGE_VERSION}, and exactly one writable env file settingAI_BRIDGE_VERSION. The bridge finds all of this by asking systemd about its own unit;AI_BRIDGE_ENV_FILEoverrides the file. Anywhere else — a terminal, an unpinned unit, macOS, Windows — it only logs. - Strict semver only. No ranges, tags, URLs or commands. Never below 0.24.0.
- Loop protection. A restart that comes back on the old version backs off: 5 min, doubling, up to 24 h.
- Opt-out.
--no-self-updateorAI_BRIDGE_SELF_UPDATE=0. ai-bridge installwrites the pinned form on Linux. It also takes--allow-native,--local-toolsand--no-self-update.
Not covered: a new version that crashes before it runs this code (the --version run makes that rare), and macOS / Windows, where you update by reinstalling with the version you want.
Moving a 0.23.x machine onto it, once. In a pinned unit's env file, set AI_BRIDGE_VERSION=0.24.0 and restart the unit. For an ai-bridge install service, run npx -y @tetrixdev/ai-bridge@0.24.0 install … again with its server and token. See README, "Following the server's version".
Additive, no protocol bump. The server side is laravel-ai-bridge 0.16.0 (AI_BRIDGE_DESIRED_VERSION).
v0.23.0 — a background command that keeps writing output no longer gets its turn stopped as silent
A growing background command counts as activity. The CLI writes nothing to the stream while a main-assistant background shell command (local_bash) runs. So a turn waiting on one that was visibly making progress, such as a release watcher printing a line every 30 s, was stopped as silence_timeout_exceeded after 900 s, with all the work done. While such a command runs, the bridge now looks at the output file the CLI named in its reply to the call: every 30 s at the default bound, or a quarter of the bound when it is shorter. Growth since the last look resets the silence clock.
- Only growth counts, never "a command is running". A command that has gone quiet, or never ends, still runs into the bound, and the turn is stopped as before.
- The path is read from the CLI's reply, never built. If the reply stops naming it, nothing is watched and behaviour is unchanged.
- Sub-agents were already covered by their own progress frames. The 900 s default is unchanged.
Input-turn addendum. It now says what counts as progress: the agent's own messages, a background command that keeps writing output, and a subagent that keeps working. That replaces "no output at all", which the agent read as including its task's own log lines.
No protocol change; a server needs nothing new. Also bumps fast-uri and ip-address, both transitive, past moderate advisories.
v0.22.0 — an app request can use a second linked item
Another linked item, for one request. Engram links vault items to an app per person, and a role may take several ("many": true), one of them the default. The default is still what fill carries and what the backend's environment holds (ENGRAM_<ROLE>_<FIELD>). A request that names another linked item now arrives with use: the bridge opens it like the fill and hands it to the backend on that one request's line as vault, never in the environment, so the process is not restarted, no other request sees it, and its sealed values are scrubbed from that answer. A use role the call does not fill is refused.
Advertised as app_items in hello. Additive, no protocol bump: a server must not send use to a bridge that did not advertise it. Engram needs 0.22.0 only for this feature; the default item works on 0.21.0. See PROTOCOL.md.
v0.21.0 — Engram app backends run on your machine
App backends. With --local-tools, the bridge runs the backend of an Engram app on the machine of the person using it: one long-running node --permission process per app version, built from the server's files (fetched by sha256 from the server's own origin, verified, cached), able to read only its own working copy and the folders its manifest lists. Started on the first request, stopped after 10 minutes idle, restarted after a crash. Requests arrive as app_call frames; the process speaks JSON lines on stdin/stdout, so nothing listens on a port. Vault fields are filled as ENGRAM_<ROLE>_<FIELD> and sealed values scrubbed from answers.
Advertised as app_backends in hello. Servers that do not send app_call are unaffected. See PROTOCOL.md.
v0.20.0 — the bridge leaves the system prompt alone
When a server sends no system_prompt, the bridge no longer substitutes its one-sentence stand-in ("You are an AI assistant… reply in plain prose") in isolated and workspace. The CLI keeps its own default prompt instead; the lifecycle addendum (bridge_prompt) is unchanged.
Behaviour change: a server that relied on the stand-in and sends no system_prompt now gets the CLI's default prompt. Send your own system_prompt to set the assistant's voice.
v0.19.0 — local tools fill roles from vault items
A sealed secret reaches the machine again. The bridge opens the vault's current wrap (context-bound, signature required), strips the vault's padding, and matches secrets by id. Engram's cross-repo test now runs the browser vault code against this bridge's code.
Roles are filled from vault items. A resolved call carries each role's item: plain fields, and for each sealed field the secret id and its space. The tool gets ENGRAM_<ROLE>_<FIELD>; only sealed values are scrubbed from output. Values may come from several spaces in one call.
Also removes a node_modules symlink committed by mistake in v0.18.0's history.
Needs an Engram with vault items (feat/vault-items); local tools are unaffected for servers that do not use them.
v0.18.0 — files go straight to the machine
Person uploads land on the machine. A file picked in a chat streams from the browser through the server into <working folder>/file-uploads/, verified by size and sha256, never overwriting, with a .gitignore so nothing is committed by accident. Advertised as file_uploads in hello.
Files are handed back by id, never by path. The bridge serves only files it recorded itself (received uploads, and files the assistant handed back with attach_file), re-checked at serve time, with a single Range passed through for seeking and resuming. The older path-based attachment_read is refused.
See PROTOCOL.md, "Person uploads" and "Handing files back". Servers that do not use these frames are unaffected.
v0.17.0 — helpers you can see, and messages into a running turn
Two things a server can now show while a turn runs: which helper did what, and a message delivered into a turn that is still going.
Helper visibility (#39)
parent_tool_use_idonblock_startandtool_result: a helper's (sub-agent's) text, thinking, tool calls and results name theAgentcall that spawned it, so a server can group them under it. Absent for the main assistant.- New stream event
task:started,progress,heartbeat,updated,finishedfor each helper or background shell task, with description, what it is doing now, usage and elapsed time.done, a streamerrororcancelledends every task of the request. done.subagent_stats, the CLI's own summary, passed through.
Turn input (#40)
- Opt in per turn with
ai_request.options.accepts_input: true; the ack saysinput_open: truewhen the turn takes messages. Advertised asturn_input: trueinhello. turn_input/turn_input_ack; the stream saysuser_inputwhen the CLI read a message andmain_state(working/idle) as the assistant works.- Unread messages come back in
cancelled.pending_inputs,done.pending_inputsand, after a reconnect,bridge_disconnected.pending_inputs. A refusal while the old CLI is still stopping isturn_ending: hold until the request ends before starting another. - A failed input turn is reported like any other (
session_lostwith usage, not a bareprovider_error).
All additive: older servers ignore the new events and never opt in. Verified end to end against a live Engram.