BlaiseBlaise
Documentation
Runner and PlugsExplanation

Design alternatives

What was considered before plugs, and why each alternative lost.

These are the other designs considered for the runner, and why they were not chosen.

AlternativeWhy not
Keep scripting around the spawned commandIt cannot see or change the traffic between Blaise and the agent, has no cleanup, and tends to wrap the whole runner, which puts the token next to the agent.
Hooks on the typed callbacks of the ACP SDKThey miss methods the SDK does not call back for, such as initialize and extensions, and tie plugs to an agent on a local pipe. The runner proxies the raw message stream instead, so one mechanism sees everything.
Fork a process for each run instead of the CLIBreaks for installed packages and compiled binaries. The CLI starts itself with a hidden run command, which works the same everywhere.
Timers on the runner for idle and paused timeTwo sources of truth, and a dead runner enforces nothing. See Why time belongs to Blaise.
A snapshot of the configuration for each sessionNeeded to match stored state to plugs after a change. Stateless plugs and state keyed by id do the same without it.
A separate package for the authoring APITwo packages with one version line. The authoring API is part of @blaiseagent/runner.
Conditions in setup entries (if)Grows the entry syntax. Conditions live in plugs or in separate files that are included.
A string interpolation of our own${{ }} uses GitHub's expression library, whose syntax people already know.
Keeping the agent process across a pauseNeeds a detached supervisor on the host. A resume starts the agent again and loads the conversation with session/load. It fits the same API and is left for later.

How the chosen design works: How plugs wrap a session.