A safer LLM coding harness.
Every delimiter carries a cryptographic nonce and integrity hash, untrusted content is sanitised, and a role manager classifies untrusted inputs while segmenting benign tasks from risky agentic actions.
It pairs a safety-first architecture with multiple interaction modes (agent, plan, goal, and code for batching tool calls in a script) let you choose the right level of autonomy for the task. Inspiration for agents is taken from harnesses like Hermes, while the agentic loop and context management is designed to minimise token usage while maximising model alignment over longer sessions. This is not a model provider coding harness that incentivises token maxing, or a tool used as a gimmick "look! it has agency!". No, Belai holds models to sensible constraints and the result is a tool that leaves an impression of confidence and assurance.
brew install vulnetix/tap/belaiHomebrew also knows an unrelated cask called belai, so upgrade by the tapped
name to avoid the ambiguity:
brew upgrade --formula vulnetix/tap/belaiscoop bucket add vulnetix https://github.com/Vulnetix/scoop-bucket
scoop install belaicurl -fsSL https://raw.githubusercontent.com/Vulnetix/belai/main/install.sh | shThe script detects your platform, verifies the download against the release
checksums.txt, and refuses to install on a mismatch. It installs to
/usr/local/bin, falling back to ~/.local/bin when that is not writable.
# choose the directory or the version
curl -fsSL https://raw.githubusercontent.com/Vulnetix/belai/main/install.sh | sh -s -- --install-dir ~/.local/bin
curl -fsSL https://raw.githubusercontent.com/Vulnetix/belai/main/install.sh | sh -s -- --version v0.1.1Pre-built binaries for Linux, macOS, and Windows are available on the Releases page.
Release assets ship in four families. The default install (install.sh,
Homebrew, Scoop) is belai-bert-guardrails, which embeds the phase-1
prompt-saturation model so guardrails work with no provider or API key
configured. The others:
| Asset family | What is embedded | Approx size |
|---|---|---|
belai |
nothing (LLM sentinel only, exactly the pre-embed behaviour; voice input downloads its speech model on /voice download) |
~24 MB |
belai-bert-guardrails (default) |
phase-1 prompt-saturation model, and the speech model for voice input | ~77 MB |
belai-bert-guardrails-jailbreak |
phase-1 + phase-2 jailbreak model, and the speech model | ~517 MB |
belai-no-classifier |
the speech model only | ~56 MB |
Sizes are for Linux amd64; other platforms differ by a few megabytes. The speech
model is a 32 MB file that voice input runs in pure Go: every family except the
plain belai build carries it, so voice works with no download. Plain belai
stays small and fetches it only when you run /voice download.
install.sh accepts --variant (or BELAI_VARIANT) to pick one of
vanilla (the plain belai family), no-classifier, bert-guardrails, or
bert-guardrails-jailbreak. The on-disk
command name is belai regardless of variant, and a variant binary updates to
its own family.
At startup Belai compares its own version against the newest GitHub release.
When a newer one exists the banner says so and the belai panel prints the
upgrade command for how this binary was installed — the Homebrew tap, the
Scoop bucket, go install, or the installer script plus the release asset for
your OS and architecture. Nothing is downloaded or installed; you run the
command yourself.
The answer is cached for six hours, an unstamped source build never checks,
and the check is off with update_check: false in settings or
BELAI_NO_UPDATE_CHECK=1 for a single run.
Belai is pure Go with no cgo, so a clean build needs only a Go toolchain.
git clone https://github.com/vulnetix/belai.git
cd belai
go build -o belai ./cmd/belai
./belai -versionTo install it onto your PATH instead:
go install github.com/vulnetix/belai/cmd/belai@latestA binary built this way reports its version as dev. To stamp the real version — and to get the same flags the release builds use — install just and run just build or just install from a clone. See docs/development.md.
Run belai in a project directory to start the terminal UI:
cd ~/code/my-project
belaiThe first launch opens Getting started. A welcome page gives three ideas with the commands and keys for each, then it offers to install the Vulnetix CLI with Homebrew or Scoop (only if you pick Install). It then offers to create a Vulnetix account or log in, and once the CLI has credentials it turns on the Vulnetix AI Firewall and the Vulnetix MCP server. esc starts Belai from any page except the sign-up and login forms, where it goes back; /vulnetix setup or /welcome reopens it. Details are in docs/vulnetix.md.
Inside the UI, / opens slash-command autocomplete — /providers (the provider list) to manage providers, credentials and local models, /providers report to probe local model servers, /model to pick provider/model/effort for the agent and classifier roles, /settings to edit settings, /budgets to set token budgets per provider and model (also f1 then b), /caps to show the run caps (spend, tokens and wall time a session may use) and extend them (see docs/run-caps.md), /intel for session intelligence: plan limits, pace, trend, runway and usage over time (also f12 or f1 then i), /context to break down where the last request's tokens went against the model's window (see docs/token-budgets.md), /cache to show the prompt cache and set its lifetime (ttl 5m|1h) and the opt-in keep-alive pings (keepalive N|off; see docs/prompt-cache.md), /tts to read replies aloud (ctrl+b reads one, see docs/tts.md), /paste-image to attach the image on the clipboard (also ctrl+v, and @file.png attaches an image file), /permissions to edit tool rules, /prompts to manage the prompt library, /locate --dry-run to see which files explore locate may look at and where it would send its questions (see docs/jev-jobs.md), /kanban to view and manage the global kanban board (also f1 then t; see docs/kanban.md), /knowledge to browse the documents Belai has indexed, with their types, labels and topics, for this project, every project or the agents, and to search them as a model's Grep, Glob and Read would (also f1 then n; see docs/knowledge.md), /diff to browse the working tree's staged, unstaged and untracked changes read-only (see docs/diff.md), /processes (or /process) to manage supervised long-lived processes, /mode to set the operating mode (agent, plan, goal or auto), /handoff <plan file> to execute a plan file directly with no intent detection, /finish-review to show or switch the harness check of the working tree before a goal is accepted as complete (status, on, off; see docs/finish-review.md), /worktree to file a card a board agent works in its own git worktree and to list, update and drop the worktrees the harness made (new TITLE, list, update ID, drop ID; see docs/worktrees.md), /lint-loop to show or switch the per-edit lint loop, which runs the detected linter on each edited file (status, on, off; see docs/lint.md), /symbols to see the symbol index the Outline and Symbol tools serve: its size, where a name is declared, a file's declarations (refresh re-reads it; shown to you only; see docs/symbol-map.md), /init to have an agent draft a root AGENTS.md from the repository's detected facts when there is none (you approve the write; see docs/instruction-files.md), /patch-tolerance to show or set how loosely apply_patch matches hunks for this session (see docs/patch-tool.md), /todos, /profile, /agent to create, edit, and run background agents, /agents for the running agents, profiles, audit trail and fleet, /fleet to start (/fleet start NAME, /fleet crew NAME), stop and watch kanban worker agents (see docs/fleet.md), /crew for the crew control room, where a crew run's workers, cards, providers, scope of directories, faults and audit are watched and changed live (also f1 then o; see docs/fleet.md), /inbox for the cards waiting on you, where you approve or send back a flow's checkpoint (also f1 then w; see docs/kanban.md), /resume to resume a session by id or browse saved sessions, /tree to see the session as branches, continue from an earlier reply or prompt, or fork a branch into a new session (the file only grows; files on disk are not rolled back), /undo to put back the files your last prompt changed and /rewind to restore the code, the conversation or both to just before an earlier prompt, each after you confirm (see docs/checkpoints.md), /commit [message] to commit the files this session changed from a clean state, never the ones you had already edited and never a push (needs commit_mode ask or auto; see docs/commit-workflow.md), /lsp to manage language-server diagnostics, /execute and /refine to run or refine an extracted plan, /add-dir to add another directory to the workspace, /vulnetix (with review, configure, list, status, firewall, mcp and setup subcommands), /firewall to configure the AI Firewall — Vulnetix, the beta Fastly, Kong and AI Security Gateway adapters, or any custom proxy — and pick the active one (also f1 then f; see docs/firewall.md), /compact to summarise a long session into a new one (/compact <focus> steers the summary, /compact status shows the thresholds and the circuit breaker; see docs/compaction.md), /relay [direction] to draft a handoff note, review and edit it, then continue in a fresh session that begins from it (see docs/handoff.md), /review [pr N|branch --base REF] to review a change with a read-only model turn and a fixed rubric, and /review post to post the last pull request review after you confirm (see docs/review.md), /btw <question> to ask the model a side question that never joins the conversation or the session record (see docs/side-question.md), /clear (or /new, /reset) to start a fresh session, /yolo to turn guardrails and the ask gate off together (/yolo off restores the settings-file values; the hazard rails still ask), /hazards to list the commands and files that always ask, whatever a rule or ask off says (see docs/hazard-rails.md), the session controls /guardrails, /ask, /caveman, /effort, /autocommit, /reasoning, /tools, /decisions, /tests and /jev (plus /mode, /model <provider> <model> and /lsp on|off), which change this session only and take the same commands from a web session (see docs/session-controls.md), /export to write the current session (or another by id) as Markdown under .vulnetix/exports, /rename to name the session, /sync to show or change session sync to the Vulnetix website (on, off, backfill; see docs/session-sync.md), /gitsync to switch the rebase of the session's branch onto origin's default branch before turns, which is on by default (on, off, global on, global off; see docs/git-sync.md), /rc to set up and run remote control, which lets the Vulnetix website start and drive sessions on this machine (start, stop, status; see docs/remote-control.md), /trusted to list the directories you trust, which remote control offers, and revoke stale or temporary ones or trust a new one (also f1 then d), /voice to dictate into the composer with a hold-to-talk key (status, debug, on, off, download, push, listen, insert, submit, cleanup; see docs/voice.md), /skills to list installed skills, /commands to list your custom slash commands (a Markdown prompt template in ~/.vulnetix/belai/commands/<name>.md, or the project's .vulnetix/belai/commands/, runs as /<name> args and takes $ARGUMENTS and $1; a built-in command always wins; see docs/library-items.md), /sandbox to see what commands may touch, /egress to see which hosts the sandbox's egress proxy lets a command reach and which it refused, and to allow one for this session (allow HOST, revoke HOST; see docs/sandbox.md), /mcp to list MCP servers and their tools, and /plugin to list, enable, disable or remove plugins (install them with belai plugin install; see docs/plugins.md). /help lists every command and keyboard shortcut. The popup matches fuzzily (/pmt finds /prompts) and also offers saved prompts as /prompt:<name> (loads it into the composer), agent profiles as /agent:<name> (switches to agent mode with that profile) and saved processes as /process:<name> (starts it unless it is already running, then shows its status).
Operator safety controls live in the footer: guardrails: on|off (posture gates) and ask: on|off (the permission-ask gate). f3 toggles guardrails, f4 toggles ask, and when both are off the two chips collapse into a single gold YOLO. These are explicit opt-ins: turning them off is announced in the transcript and traced under BELAI_TRACE. Guardrails off sets every posture gate to ignore across every surface — the agent loop, inline !cmd, @file attachments, background agents and the CLI — and the classifier is then not called at all rather than called and ignored, so a turn costs no extra requests. Sanitising is not part of the switch: delimiter markup is stripped either way. Next to them the footer always states caveman: on|off, so the voice rewrite (f2) can never be on without saying so.
In the composer, ctrl+left and ctrl+right move the cursor by word, crossing into the neighbouring line at a line boundary, and home/end (fn+left/fn+right) jump to the ends of the line. A word stops at punctuation, so foo.bar is three hops and foo_bar is one.
Shortcuts use ctrl, shift and the function-key row — never alt. alt chords are unreliable across terminals, and under the kitty keyboard protocol a ctrl+alt+<key> press is indistinguishable from ctrl+<key> by the time it reaches the UI, so it could never have worked. The session toggles are f2 caveman, f3 guardrails, f4 ask, f5 cycle mode, and f6 cycle reasoning effort — all five from any screen — f7 saves the prompt to the library from the composer, and ctrl+s saves the hovered panel, overwrites/deletes a loaded library prompt, or saves the prompt. If your terminal eats a function key, /caveman, /guardrails, /ask, /mode and /effort do the same jobs.
In agent mode a strip above the prompt lists the agents that can carry your turns — your own profiles, the background-agent definitions (↻), and the built-ins (◈). tab moves the highlight, enter engages, ctrl+g starts a ↻ definition in the background instead, and typing @name filters the strip. The engaged agent shows in the footer chip and carries every turn until you pick another or (none). An agent with a display name and palette (the built-in ones have both) is shown by that name, and the interface takes on its colours; ctrl+p cycles the agents and re-themes live. It applies to agent mode only: plan and goal mode run Belai's own logic and cannot be steered by an agent.
shift+tab cycles the mode: agent, plan, goal, then auto, where no mode is held and the classifier picks the mode and intent for each prompt. /handoff <plan file> runs a plan file as a handoff without that detection step; prompt admission, attachment classification and every permission gate still apply. Goal mode is the one that keeps going: instead of stopping when the tool budget runs out, an evaluator checks whether the work advanced and grants another pass while it does, tracking a todo list in a panel above the prompt. It is stopped by a stall, not a counter — press esc (or ctrl+c outside the UI) to stop it and keep the partial result. Set resilience.max_passes if you want a hard ceiling. The rules are in docs/role-manager.md.
For a single answer without the UI:
belai -prompt "what does internal/run do?"
belai -provider anthropic -model claude-sonnet-4-5 -prompt "review this diff"| Flag | Meaning |
|---|---|
-prompt |
send one turn, print the reply, exit; -prompt -, or a piped standard input with no -prompt, reads the prompt from standard input (see docs/headless.md) |
-output-format |
with -prompt, how the run reports: text (the reply, default), json (one result object) or stream-json (one event per line, then the result); exit codes are 0 ok, 1 error, 2 usage, 3 run cap, 4 denied or could not ask, 5 schema failure (headless.output_format sets the default) |
-json-schema |
with -prompt, require the final answer to be JSON that satisfies the schema in this file (a subset: type, properties, required, enum, items, additionalProperties); one repair turn, then exit 5 |
-provider |
openai, anthropic, cloudflare-workers-ai, cloudflare-ai-gateway, openrouter, google-gemini, ollama, llama-server, github-copilot, huggingface, kiro, groq, deepseek, fireworks, mistral, together, xai, moonshot, minimax, alibaba, a custom name from settings.json, or a configured display label |
-model |
model id; each provider has a default |
-effort |
thinking-effort level: low, medium, or high |
-mode |
operating mode for -prompt: agent, plan, goal or code (default: classified from the prompt) |
-plan |
start in plan mode (read-only) |
-detect-mode |
report which operating mode the prompt selects |
-agent |
start a background agent by name in foreground mode |
-agent-create |
create an agent profile from a description and save to disk |
-export |
export a session by id or unique id prefix as Markdown and exit |
-caveman |
enable caveman voice rewrite for this run |
-guardrails |
posture guardrails (default on); -guardrails=false turns every gate off for this run |
-ask-permission |
the permission-ask gate (default on); -ask-permission=false resolves asks to allow |
-firewall |
route LLM traffic through the active AI Firewall (see /firewall) |
-tools |
enable tool execution (default on); -tools=false disables |
-defer-tools |
advertise core tools in full and load the rest on demand with ToolSearch (default on); -defer-tools=false sends every tool definition on every request |
-dangerously-yolo-everything |
ignore every posture gate |
-allow-unsafe-tool-result |
ignore unsafe tool results |
-allow-malformed-tool-result |
ignore malformed tool results |
-allow-unsafe-prompt |
ignore unsafe prompt classification |
-allow-malformed-prompt |
ignore malformed prompt classification |
-tool-call-mismatch |
abort|strip|ignore tool-call mismatches |
-allow-unpermitted-tools |
allow tool calls matching no permission rule (default) |
-allow-ask-without-tty |
ignore ask-without-tty blocks |
-allow-invalid-skills |
ignore invalid skill validation |
-allow-invalid-hooks |
ignore invalid hook validation |
-classifier-provider |
security-classifier provider (default: the main provider) |
-classifier-model |
security-classifier model (default: the main model) |
-classifier-effort |
security-classifier thinking effort (default: none) |
-classifier-kind |
security-classifier stack: llm, models, openrouter-decisions or systemone (jev is read as systemone); default: models when the binary embeds a model, else llm |
-classifier-phase1-model |
phase-1 prompt-saturation model id |
-classifier-phase1-source |
phase-1 source: embedded or huggingface |
-classifier-phase1-threshold |
phase-1 attack threshold (default 0.75) |
-classifier-phase2-model |
phase-2 jailbreak model id |
-classifier-phase2-source |
phase-2 source: embedded, huggingface, or disabled |
-classifier-phase2-threshold |
phase-2 attack threshold (default 0.75) |
-session-retention-days |
idle session retention in days (default 28) |
-no-prune |
never prune idle sessions |
-max-cost |
stop a run at this many US dollars of model spend (run_caps.max_cost_usd); the model is told to finalise at 90%, then the run ends unless you extend it; 0 is off |
-max-tokens |
stop a run at this many tokens (run_caps.max_tokens) |
-max-wall |
stop a run after this many minutes spent running turns (run_caps.max_wall_minutes); a -prompt run stopped by a cap exits with code 3 |
-no-transcript |
with -prompt, do not keep a session transcript of the run |
-no-finish-review |
do not run the finish review of the working tree before a goal is accepted as complete (finish_review.enabled in settings; BELAI_FINISH_REVIEW=0; /finish-review in the TUI; see docs/finish-review.md) |
-no-checkpoints |
keep no copies of files before an edit changes them, so /undo and /rewind have nothing to restore (checkpoints.enabled in settings; see docs/checkpoints.md) |
-lint-loop |
run the repository's detected linter on each file after an edit and return its findings on the tool result (lint_loop.enabled in settings; BELAI_LINT_LOOP=1; /lint-loop in the TUI; see docs/lint.md) |
-no-git-sync |
do not rebase the branch onto origin's default branch before a turn (git.sync in settings; /gitsync in the TUI) |
-resume, -r |
resume a session by id or unique id prefix in the interactive TUI; with -prompt, replay it and run the prompt as its next turn |
-continue, -c |
continue the most recent session for the current project |
-teleport |
continue a session of your account from another host, sandbox or the web: it opens here under a new id and the original keeps running (see docs/teleport.md) |
-teleport-push |
with -teleport, agree that the origin host may push the session's uncommitted and unpushed changes to the forge as one belai/teleport/<id> branch, which this host then fetches; without it they are sent as a patch and replayed here |
-teleport-ref |
with -teleport, check out this ref or commit instead of the one the session was at |
-verbose |
print mode and security decisions to stderr |
-usage-json |
with -prompt, write a JSON summary of the run's token usage (per role, per model, cache reads and writes, request composition) to a file on exit — see docs/benchmarks.md |
-version |
print the version and exit |
-trust-dir |
trust the current directory without prompting (grants the directory only, not its proposed workspace dirs) |
Belai starts the UI only when both stdin and stdout are a terminal, so it is safe in pipelines and CI.
Run belai -help for every command, a set of examples and the flags grouped by purpose. belai help <command> (or belai <command> -h) prints one command's own usage. Subcommands sit beside the flags: belai acp serves the Agent Client Protocol so an editor such as Zed can use Belai as its agent (docs/acp.md), belai agent lists, runs and manages background agents and fleet workers (docs/fleet.md), belai kanban reads and edits the global board (docs/kanban.md), belai review reviews the working tree, a branch or a pull request against a fixed rubric and prints findings the harness checked against the diff (docs/review.md), belai worktree files, lists, updates and removes the git worktrees the harness made for board agents (docs/worktrees.md), belai library scan searches this host for commands, skills, prompts, agents and instruction documents kept by Claude Code, Cursor, Codex and the other harnesses in the registry, and says what importing each would do (docs/agent-import.md), belai login kiro signs in to Kiro (docs/kiro.md), belai plugin installs and manages plugins (docs/plugins.md), and belai rc runs remote control so the Vulnetix website can start sessions here (docs/remote-control.md).
No provider configured? Belai defaults to OpenRouter's free router
(openrouter / openrouter/free). Sign up at https://openrouter.ai/, where a
one-time signup credit unlocks the free model, then store the key with
/providers (or export OPENROUTER_API_KEY). If exactly one other provider's
credentials resolve, Belai adopts that provider instead.
Set the API key for your provider and Belai picks it up:
| Provider | Environment |
|---|---|
openai |
OPENAI_API_KEY |
anthropic |
ANTHROPIC_API_KEY |
cloudflare-workers-ai |
CLOUDFLARE_API_KEY, CLOUDFLARE_ACCOUNT_ID |
cloudflare-ai-gateway |
CF_AIG_TOKEN, CF_ACCOUNT_ID (or CLOUDFLARE_ACCOUNT_ID). Optional: CF_AIG_URL |
openrouter |
OPENROUTER_API_KEY |
google-gemini |
GEMINI_API_KEY or GOOGLE_API_KEY |
ollama |
none (local; honours OLLAMA_HOST) |
llama-server |
none (local; honours BELAI_LLAMA_HOST, default http://localhost:8080/v1) |
github-copilot |
GITHUB_COPILOT_TOKEN or GH_TOKEN (OAuth, exchanged for a session token) |
huggingface |
HF_TOKEN or HUGGINGFACE_TOKEN |
kiro |
none: run belai login kiro (AWS Builder ID or IAM Identity Center sign-in; see Kiro). KIRO_LOGIN holds a stored login |
groq |
GROQ_API_KEY |
deepseek |
DEEPSEEK_API_KEY |
fireworks |
FIREWORKS_API_KEY |
mistral |
MISTRAL_API_KEY |
together |
TOGETHER_API_KEY |
xai |
XAI_API_KEY |
moonshot |
MOONSHOT_API_KEY; optional BELAI_MOONSHOT_BASE_URL for .cn |
minimax |
MINIMAX_API_KEY; optional BELAI_MINIMAX_BASE_URL for .cn |
alibaba |
DASHSCOPE_API_KEY or ALIBABA_API_KEY |
A custom provider defined in settings.json resolves its key from its
api_key_env variable or BELAI_<NAME>_API_KEY.
/providers in the UI opens the provider list where you can store them instead, in your host keychain or in ~/.vulnetix/belai/credentials.json. Belai resolves credentials from the environment first, then a project-local .vulnetix/belai/credentials.json, then the user file, then ~/.netrc, then the keychain — and tells you which one each value came from. A credential may be stored as the name of an environment variable rather than a value, which is how project credential files stay committable.
Pick a default provider without passing -provider every time by setting BELAI_PROVIDER.
For a custom state directory, set BELAI_HOME. To disable the TUI and keep the old one-shot behaviour, set BELAI_NO_TUI=1 or run in CI (CI is honoured). To stop Belai opening a web browser (it already skips this over SSH, without a graphical session, and in belai rc), set BELAI_NO_BROWSER=1.
Belai creates ~/.vulnetix/belai/ at mode 0700 and credential files at mode 0600. If you already have a ~/.belai/ directory, it is migrated automatically on the next run. We recommend adding .vulnetix/belai/ to your repository .gitignore — even though the project file only holds references, defence in depth is cheap.
Declared intent lives in settings.json; last-used runtime values live in
state.json. The global file is ~/.vulnetix/belai/settings.json (or
$BELAI_HOME/settings.json); the project file is
<workdir>/.vulnetix/settings.json. Precedence, lowest to highest:
defaults < state.json < global settings.json < project settings.json <
environment < CLI flags. The /settings browser shows the effective value and
its provenance for every key. From /model the agent provider/model/effort can
be written to session scope (state.json), global scope, or project
scope, following the same precedence rules.
| Key | Meaning |
|---|---|
provider |
default provider name |
model |
default model id |
effort |
default thinking effort: low, medium, high |
caveman |
toggle the caveman voice rewrite |
permissions |
structured allow / ask / deny tool rule arrays |
session_retention_days |
idle session retention (default 28) |
ui.banner / ui.status_bar |
TUI presentation toggles |
show_session_names |
show session names in the status bar (default on) |
update_check |
check GitHub for a newer Belai release at startup (default on) |
tests |
the post-end test pass: post_end (off default, goal, goal_plan, session), command (an argv that replaces the detected suites), scope (affected default or full), on_fail (fix default, diagnose or off), max_fix_passes (default 3), timeout_seconds (default 300), report (default on) and each_turn (default off; also runs the suites after a plain TUI agent turn that edited files). A completed goal, plan or session runs the suites, a passing run gets a fast-model report and a failing run a bounded diagnose-and-fix loop. A project may only turn it off (each_turn included) and lower the budgets, never on — see docs/testing.md |
defer_tools |
advertise the core tools in full and load the rest (native catalogue, cloud CLIs, repo and agent-store tools, MCP tools) on demand with ToolSearch, keeping every request small (default on; -defer-tools=false for one run). Deferred tools stay callable by name. ToolSearch also finds installed skills, and with a decision backend it ranks its matches and the request's likely tools and skills are ready before the model asks (see docs/jev-jobs.md) |
offload |
keep oversized tool output out of the context: a head-and-tail preview stays inline and ReadResult reads the rest; enabled (default on), threshold_tokens (default 4000), preview_tokens (default 1500) — see docs/context-offload.md |
web_fetch |
what a session keeps of the pages it fetched: cache (serve a repeat WebFetch from memory, still sanitised and classified; default on), cache_ttl_seconds (default 900), index (an in-memory index so SearchFetched can search earlier pages without fetching them again; default on) and index_tokens (default 60000) — see docs/web-fetch.md |
jev |
relevance jobs that use a decision backend: jobs.<job> switches (bash swap, compaction prune, tool selection and search, LSP triage, option order, explore locate, voice command, request scale and goal judge; default on, and only while a decision backend is the classifier, when /settings shows them) locate_previews and thresholds (the score cut-offs of the security gate and each job, defaults 0.10 allow and 0.90 deny); a project may only turn a job off, and cannot set thresholds — see docs/jev-jobs.md |
voice |
speech input to the composer: enabled (on by default when the speech model is built into the binary, as in every release family except the plain belai build; otherwise off), mode (push_to_talk default or listen), delivery (insert default or submit), cleanup (fast-model tidy-up streamed over the words as you speak, default on), log (voice notices and cleanup rows in the thread, default on), wake_word (act only on speech that starts with "Hey, Belay", needs listen, default off), commands (spoken keywords such as stop, option 2, submit, skip, approve and deny, plus Jev voice command, default on), key (f11 default; ctrl+space if your terminal keeps it) and device. /voice debug checks the microphone, shows a live level meter, the raw and tidied transcripts and a key event log. The speech model is built into release binaries and run as pure Go (a build without it downloads 32 MB after /voice download); a capture helper (parecord, arecord, ffmpeg or sox) supplies the audio. Per-user only: a project file cannot turn it on — see docs/voice.md |
tts |
read replies aloud with a player card in the thread: enabled and consented (off by default; /tts on is the agreement to send the text to Microsoft's read-aloud service), read_reports (read each turn's final reply, default off), voice (default en-US-AndrewMultilingualNeural), speed (0.5 to 3, default 1) and cache_mb (the local audio cache for replay, default 256, 0 off); ctrl+b reads the reply under the pointer or the last one, and saying "stop" stops it; the project layer cannot set it - see docs/tts.md |
knowledge |
sizes of the retrieval store behind agent profile documents and the project's .vulnetix output, which Grep, Glob and Read search by meaning: max_index_tokens (per profile, default 200000), max_project_tokens (the project, default 200000) and max_result_tokens (one search, default 3000); the project layer cannot set it - see docs/knowledge.md |
compaction |
when the context compacts and how much a prune keeps: threshold_pct (default 70), keep_recent_turns (6), keep_result_chars (300), keep_tool_results (2), and a circuit breaker for a failing summary, breaker_failures (3) and breaker_cooldown_passes (4). Your layers set any key in range; a project may only compact sooner or keep more. /compact <focus> steers one summary and /compact status shows the numbers and the breaker — see docs/compaction.md |
headless |
the contract of a -prompt run with its caller: output_format (text, json or stream-json, default text), max_stdin_bytes (a prompt read from standard input, default 1048576) and event_text_cap (bytes of text in one event, default 4096); the project layer cannot set it - see docs/headless.md |
finish_review |
the check of the working tree before a goal is accepted as complete: enabled (on), max_rounds (2, 0 to 5, how many times a completion is sent back) and scratch_patterns (extra scratch file names). Your layers and flags set any key; a project may only tighten it. /finish-review shows and switches it; see docs/finish-review.md |
prompt_cache |
the provider cache: ttl (5m default, or 1h), keepalive_max (pings per idle stretch, 0 to 100, default 0 = off) and keepalive_every_s (60 to 3500, default 270 with 5m, 3300 with 1h). Your layers only; the project layer cannot set it. /cache shows and changes it; see docs/prompt-cache.md |
commit_mode, commit_draft, commit_exclude |
the commit workflow: commit_mode (off default, ask or auto) commits the files a completed goal changed from a clean state, commit_draft (off) lets a model write the words of the message from file names and line counts, and commit_exclude lists path globs never committed. Your layers only for the first two; a project may add commit_exclude patterns, never remove one. auto_commit_per_task: true still means auto. /commit commits on request; see docs/commit-workflow.md |
checkpoints |
file checkpoints: enabled (on), max_per_session (50, 1 to 500), max_bytes (200 MiB, 1 MiB to 10 GiB) and retain_days (7, 1 to 365). Your layers and flags set any key; a project may only turn them off. /undo and /rewind restore; see docs/checkpoints.md |
symbol_index |
the symbol index behind the Outline and Symbol tools: enabled (on), max_files (2000, 100 to 10000), max_results (50, 1 to 200) and refresh_secs (60, 5 to 3600). Your layers set any key; a project may only turn it off. /symbols shows it; see docs/symbol-map.md |
lint_loop |
the per-edit lint loop: enabled (off), timeout_ms (10000, 1000 to 60000), max_attempts (3, 1 to 6) and max_rows (10, 1 to 20). Your layers and flags turn it on and set any key; a project may only turn it off or lower a number. /lint-loop shows and switches it; see docs/lint.md |
run_caps |
ceilings on one session: max_cost_usd, max_tokens and max_wall_minutes (all off by default), finalize_at (0.9, the share at which the model is told to wrap up) and max_extends (3, how many times you may extend a reached cap). Your layers and flags set any key; a project may only add a cap or lower one. /caps shows and extends them; see docs/run-caps.md |
instructions |
how AGENTS.md and CLAUDE.md reach agent turns: enabled (default on), nested (a file in a subdirectory arrives when a tool touches a path beneath it, default on), max_files (12), file_bytes (49152) and total_bytes (131072). Your layers set any key in range; a project may only turn a switch off or lower a number — see docs/instruction-files.md |
review |
/review and belai review, a code review turn: enabled (default on), max_diff_kb (400, clamped to 16 through 2048), post (ask default, deny or allow: whether a review may be posted to a pull request, and whether the command line may do it without a question) and rubric_file (your own extra criteria). Your layers set any key; a project may only turn the review off, lower max_diff_kb or set post to deny; see docs/review.md |
worktrees |
worktree sessions: setup and teardown (lists of argv arrays, none by default), timeout_seconds (120, 1 to 600), update_mode (off default, merge or rebase), keep_on_exit (default on) and network (deny default or allow). Every key is yours: a project file can only set update_mode to off. See docs/worktrees.md |
side |
/btw, a side question answered off the main history: enabled (default on), history_turns (6, clamped to 0 through 20) and max_reply_bytes (4096, clamped to 512 through 8192). Your layers set any key; a project may only turn /btw off or lower a limit; see docs/side-question.md |
handoff |
/relay, the reviewed handoff into a fresh session: enabled (default on), max_bytes (16384, clamped to 2048 through 65536) and include_goal (default on, gives the drafter the goal and todo list). Your layers set any key; a project may only turn /relay off, turn include_goal off or lower max_bytes; see docs/handoff.md |
edit |
tolerant (default on): Edit applies a whitespace-only relaxed match (line endings, trailing spaces and tabs, one consistent indentation shift) when exactly one place matches, and names the closest lines when none does. false matches byte for byte. A project may only turn it off — see docs/edit.md |
patch_tool, patch_tolerance |
apply_patch changes several files in one atomic call. patch_tool (default on) offers it; patch_tolerance is how loosely a hunk may match: strict, whitespace (default) or unicode (also folds curly quotes, dashes and wide spaces). A project may only turn the tool off or move toward strict. /patch-tolerance sets the level for one session — see docs/patch-tool.md |
kanban |
the global kanban board: tools, wrap-up, composer pane, /kanban and sync (default on; a project may only turn it off) — see docs/kanban.md |
agents |
fleet workers: enabled (default on), max_workers (default 4), factory_reserved_workers (slots only Software Factory dispatches may fill, default 0, global only), max_candidates (default 4, the ceiling on a profile's best-of-N kanban.candidates), publish (default on); a project may only turn them off or lower a cap |
token_budgets |
global only: token allowances per provider, model and scope (session, day, month) — see Token budgets |
ui.budget_cycle_seconds |
seconds the footer shows each budget of the selected model before cycling (default 10, minimum 2) |
ui.budget_warn |
print a warning line on each call to the selected model while one of its budgets is amber or red (default off) |
ui.intel |
session intelligence: the footer slot, the intel tab (f12) and /intel (default on) — see Token budgets |
ui.mac_key_hints |
add the Mac form after an F key in hints and /help, such as f9 (Fn+⏭) (unset follows the platform: on for macOS, off elsewhere; display only) |
ui.clipboard_images |
let ctrl+v and /paste-image attach an image from the clipboard (default on; a project file can turn it off, never on) — see Images |
ui.reply_cost |
print tokens in and out, the cache share and an estimated cost after each agent-role model call (default off; a project file can only turn it off) — see Context and reply cost |
intel.plan_limits |
global only: read provider rate-limit response headers into plan-limit readings (default on) |
context_windows |
per-model context-window overrides, in tokens |
providers |
custom provider profiles (see below) |
provider_labels |
display labels keyed by provider name (see below) |
allow_project_providers |
opt in to project-layer providers (default off) |
resilience.max_agents |
fan-out ceiling for explore subagents + background agents (default 15) |
resilience.plan_explore |
survey the repository before the first planning pass (default off) |
resilience.goal_explore |
survey a goal with references before its first pass (default off) |
Custom providers. A providers block defines a provider by name, with
base_url, api (openai-chat, openai-responses, or
anthropic-messages), optional auth (bearer, x-api-key, or cf-aig),
optional api_key_env, and a models catalogue. Secrets never live here; a
profile references the key via api_key_env or the credential backends.
Project-layer providers blocks are ignored unless the global settings set
allow_project_providers: true, because a hostile repo defining a provider is
an API-key exfiltration primitive.
A profile may also carry a kind (ollama, llama-server, or
openai-compatible) that templates it from a built-in descriptor, plus
protocol / host / port for the editor and the default display label.
base_url stays the authoritative wire value. A custom provider's API key is
always optional: with no key stored, Belai offers the provider when its
/models endpoint answers (the same liveness probe local servers get), so a
keyless self-hosted OpenAI-compatible endpoint works with no credential at
all. Add as many named instances as you need — a second Ollama, a second
llama-server — and give each a friendly name in provider_labels, which the
TUI shows in /providers, /model and the footer and accepts as a
-provider selector:
{
"providers": {
"ollama-gpu": {
"base_url": "http://localhost:11435/v1",
"api": "openai-chat",
"auth": "bearer",
"kind": "ollama",
"protocol": "http",
"host": "localhost",
"port": "11435"
}
},
"provider_labels": {
"ollama-gpu": "GPU Ollama"
}
}Permissions merge is a union, never a replacement. A project file can add rules but can never remove a rule you set globally, and a deny from either scope wins.
Bash permissions. Because Bash is a registered tool, permission rules
use the same Tool(spec) shape as other tools. Common rules include
Bash(git status *), Bash(git diff *), Bash(ls *), and Bash(echo *).
Under the read-only master switch Bash executes without a shell, so pipes,
redirections, and command substitution are rejected structurally. The legacy
flat-map form ("bash": "ask") is still accepted on read but files
self-upgrade to the structured form on first write.
Bash is unavailable in plan mode. Plan mode neither advertises nor
executes it, read-only or otherwise — investigation there goes through
Read, Grep, Glob, Cd, and the native read-only tools, whose argument
shapes are fixed.
What goes to the security classifier. Every tool result is sanitized.
Results whose content is arbitrary are classified on top of that: Bash (an
arbitrary command), WebFetch and WebSearch (text written off your
machine), and Read (a file's bytes). The tools whose output shape the
harness already knows — Grep, Glob, Write, Edit, and the native
read-only tools — are sanitized and promoted directly. See
docs/architecture.md.
Outbound requests identify themselves. Provider calls, WebFetch and
WebSearch send User-Agent: belai/<version> plus X-Belai-Session-Id,
X-Belai-Tool, X-Belai-Tool-Call-Id, X-Belai-Client-Version,
X-Belai-Client-Build and a W3C traceparent. Tool subprocesses get the
same identity as BELAI_* and TRACEPARENT environment variables. The
session id is sent as-is, so any site you fetch can see it. See
docs/architecture.md.
Path rules are relative to the working directory. Tools share one working
directory that starts at the session root and can move within it with Cd. A
path beginning with / means the session root; anything else is relative to
the current working directory. Nothing reaches outside the root either way —
a move changes how a path is spelled, never what it can reach — and the TUI
footer shows where the session currently is.
The TUI writes an append-only JSONL session per workdir under
~/.vulnetix/belai/sessions/. /compact never mutates the old file — it
writes a new session whose root entry links meta.parent_session to the old
id, and carries the old name forward. /clear starts a new session and leaves
the previous one on disk untouched.
Quitting (ctrl+d twice, /exit, or its alias /quit) prints a branded exit card below the
restored shell prompt: the session's display name, turn/duration/token facts,
its on-disk path, and the exact belai --resume <id> command that returns to
it. belai --continue (-c) reopens the most recent session for the current
project without remembering an id. belai -teleport <id> continues a session
that ran on another host, in a Pix Sandbox or on the web, in the checkout you
are in, under a new id (docs/teleport.md); every session
card on the Vulnetix website copies that command.
Highlights — full reference in docs/README.md:
- Architecture: delimiter/nonce integrity model, tool-result trust, TUI keybindings
- Role Manager: security classification, posture gates, operating-mode classification
- Resilience: error classification, provider retries, semantic repair
- Token budgets: per-model session/day/month budgets, the footer gauge, warnings, the usage ledger, and session intelligence (plan limits, pace, trend, runway, the
f12pane) - Development: prerequisites,
justrecipes, QA checklist, CI/release - Agent Profiles: reusable agent definitions, schema, background lifecycle, precedence
- Agent Stores: read-only context search with confinement guarantees
- Vulnetix: review scanners, the Vulnetix AI Firewall, project history
- AI Firewall: adapters (Vulnetix, Fastly ARC, Kong, AI Security Gateway, custom), modes, cards
- Firewall permissions: permissions you state in a prompt, offered as AI Firewall guardrails to apply
- Turn latency: what runs before the first token and between passes, and how classification stays cheap
- Nonce endpoint: provider/gateway
GET /v1/noncescontract - Image attachments (deferred): multimodal design considerations
- Marketing site: source for belai.vulnetix.com