Skip to main content
Source Code: src/gaia/cli.py
GAIA provides a comprehensive command-line interface (CLI) for interacting with AI models and agents. The CLI allows you to query models directly, manage chat sessions, and access various utilities without writing code.

Platform Support

Windows 11

Full GUI and CLI support

Linux

Full GUI and CLI support via source installation (Ubuntu/Debian)

Quick Start

  1. Follow the Quickstart to install GAIA
  2. Open PowerShell and run gaia to launch the Agent UI, or gaia --cli for terminal chat
GAIA starts Lemonade Server when it needs it, so there is nothing to launch first.

Top-Level Flags

Running gaia with no subcommand launches the Agent UI by default:
Examples:
gaia chat --ui continues to work as an alias. The Agent UI does not gate on your processor model — the Settings panel reports your processor, memory and model status for information only.

Agent step limit

Agents stop after a maximum number of reasoning/tool steps. The flagship GAIA agent (gaia chat, the Agent UI, the TUI) defaults to 150, enough for long desktop tasks such as reading a folder of datasheets and writing a costed BOM. Other agents default to 50. When 20, 10 and 5 steps remain, the agent is told so and asked to produce what the request asked for from what it already has, marking anything it couldn’t verify, rather than keep researching. An agent that runs out of steps before finishing still answers: it gets one more model call in which it can’t use tools, and reports what it found, what it couldn’t finish and why, and what you can do next. That summary is kept even when a requested file was never written; the missing file is listed after it. If that last call fails, you get the step-limit notice with the agent’s latest write-up and a line saying why the summary couldn’t be written. Set GAIA_AGENT_BUDGET_NOTICES=0 to turn the step-budget notices off. The same closing call runs when an agent is stopped for repeating one tool call with identical arguments (max_consecutive_repeats, default 4): the model gets one correction first, and if it asks for the same call again the turn ends — a run that finished its work and then kept re-checking it reports what it did instead of a “can’t confirm” notice. Repeats that failed or were refused get the same closing call, told what failed, and the failure is reported after the answer, so research done before the loop is kept. The same applies to one tool that keeps failing with different arguments, and to an agent that, having answered, starts work the request never asked for: the off-request call is not run, and a second one ends the turn with an answer to what you asked.
  • Per-invocation: pass --max-steps <n> to gaia chat (e.g. gaia chat --max-steps 80).
  • Fleet-wide: set the GAIA_AGENT_MAX_STEPS environment variable to change the default for every agent at once, without per-command flags.
Some skills intentionally need more headroom than the default (e.g. multi-file code generation). An invalid GAIA_AGENT_MAX_STEPS value (non-integer or ≤ 0) fails fast with an actionable error rather than silently capping agents.

Per-tool execution timeout

Each tool call is bounded so a hung tool (e.g. a stuck connector or network request) surfaces an actionable error instead of leaving the agent — and the UI — stuck indefinitely. The default is 180 seconds per tool. Time a tool spends waiting for you to answer a file-access prompt doesn’t count toward it, and neither does time the PC spends in S3 sleep or hibernation. Modern Standby counts as running time.
  • Fleet-wide: set GAIA_AGENT_TOOL_TIMEOUT (seconds) to change the default for every tool at once.
Tools that legitimately run longer opt out in code via @tool(timeout=...) — for example image generation, which may need to download a model on first use. A tool that takes its own timeout argument, like run_python and execute_python_file, is never abandoned before the time that call asked for plus 30 seconds. For relayed agent queries, the same variable also extends the SSE idle-read budget in the daemon relay, API host, and thin client. That transport budget remains at least 300 seconds and adds a 60-second grace period above a larger configured tool timeout so the sidecar can return its own timeout error. An invalid GAIA_AGENT_TOOL_TIMEOUT value (non-numeric or ≤ 0) fails fast with an actionable error rather than silently removing the guard.

Confirming dangerous tools

Shell commands, file writes, and an agent’s own destructive tools (email send / forward / permanent delete, calendar RSVP) prompt before they run:
The default is no — a bare Enter, Ctrl-C, or EOF denies. Where the call has a narrow enough scope, the prompt adds [a]lways for <scope> and names it — a command family such as pytest or git commit, one file, or one skill — and a stops asking about that scope until the command exits. It is never offered for a delete or for a command line GAIA cannot read. When GAIA is not attached to a terminal — output piped or redirected, a CI job, a host process — there is nobody to ask, so gated tools are denied with an actionable error rather than run silently. For an unattended run you trust, set GAIA_AUTO_APPROVE_TOOLS=1 in the launching environment:
A .env file cannot set this variable; it must come from the real process environment, so a project directory can never switch off your prompts.

Narrowing file access

The flagship agent can read anything under your home folder by default. To confine one run to particular folders — an evaluation run that must not read the graders’ answer keys, or a session you want kept to one project — set GAIA_ALLOWED_PATHS to absolute paths separated by ; on Windows and : elsewhere:
  • It only narrows. The agent’s scope becomes the overlap of its normal scope and this list. An entry wider than the normal scope (C:\, /) narrows nothing, and one outside it (another drive) is ignored with a warning — neither grants anything. If no entry overlaps, the agent can reach no files at all except its scratch folder. Out-of-scope paths are refused without an approval prompt, and full access does not lift that.
  • Every file tool honours it: reading, writing, listing, tree, name and content searches (including the Documents/Downloads sweep and deep search), indexing a document, code search and its index, the project map, saving downloads, capturing a skill from a local folder, and the path checks on shell commands. The agent’s own scratch folder stays writable.
  • It does not sandbox code. A run_python script or a shell command the agent runs can open any file your OS account can read. This setting confines GAIA’s tools, not arbitrary code — for real isolation, run the agent as a separate OS user, in a container, or in a VM, and keep answer keys where that account cannot read them.
  • It is read when the agent starts. gaia chat reads it directly, and gaia-tui passes it to the agent it launches. A Hub-installed flagship runs under the GAIA background service, so the variable must be set when gaia daemon start runs; restart the daemon to change it. The Agent UI server reads it when it starts.
An empty value, an empty entry, a relative path (~/x included) or a folder that does not exist stops the agent at startup with an error naming the bad entry; GAIA never falls back to your home folder. Unset the variable to get the default scope back.

Initialization

Init Command

New users start here! The gaia init command is the easiest way to get GAIA running.
Initialize GAIA with a single command: installs Lemonade Server and downloads required models.
Options: Available Profiles: The --profile flag accepts one of the following values (see src/gaia/cli.py for the canonical choices list):
A bare gaia init runs the gaia profile: Lemonade, the chat model, the RAG/memory embedder, and the flagship GAIA agent itself, installed from GAIA’s download server if it is missing. It does not install the terminal UI (gaia-tui) — get that with npx @amd-gaia/gaia, which fetches and verifies both binaries with no Python required.If the download server cannot be reached, gaia init still finishes the other steps, then reports the setup incomplete and exits 1, naming the URL and the error. Fix the network or proxy, then run gaia init again. gaia init does not upgrade an agent that is already installed.
Before it downloads anything, gaia init adds up what the models still need and checks it against the free space in Lemonade’s model store. When they don’t all fit, it stops before the first download with Not enough disk space, how much the models need, how much is free, and where, and exits 1. Each model fitting on its own is not enough. What an interrupted download already left there counts as downloaded, so a retry asks only for the rest. When GAIA can’t read that store (a remote server), a shortfall is a warning and the download goes ahead.

Progress events

With --progress-json, each event is one stdout line holding a JSON object with an event key. The lines between them are the usual human output; skip any line that is not such an object, and any event you don’t know.
The exit code still says whether setup succeeded.
gaia init picks the profile on the agent axis only. NPU users must still ask for the device axis explicitly with gaia init --profile npu, which swaps the GGUF models for their FLM equivalents — the default never switches your backend for you. It installs the same flagship agent as a bare gaia init, and fails the same way when the download server can’t be reached.
Only the gaia and npu profiles install an agent. The other model profiles set up models only; when no agent is installed yet, they say so and name the fix, gaia init.
The talk agent relies on the chat profile — there is no dedicated talk profile. The default gaia profile is a superset, so a bare gaia init covers it.
For the gaia, chat, rag and all profiles the chat model follows the hardware. Where Lemonade reports a GPU with enough memory for it — about 27 GB for models, its 23.3 GB of weights and vision projector plus the context cache at its smallest, 64K window, so a 64 GB+ Strix Halo or a 32 GB GPU, but not a 24 GB card or a CPU-only PC — gaia init also sets up Qwen3.6-35B-A3B-GGUF (a 23 GB Lemonade built-in MoE, run with thinking on, needs Lemonade v11.7.0 or newer) as the chat model, prints why, and records it as default_model. It must also fit the memory free when setup runs, so a PC busy with other apps keeps Gemma and says why: close them and re-run gaia init. If setup’s verify step fails, no default model is recorded. It loads with the largest window the memory holds, up to 262,144 tokens: 256K on a Strix Halo, about 88K on a 32 GB GPU (see Context length follows memory). Gemma is still downloaded, since vision and document-image extraction load it by name. Every smaller PC gets Gemma alone. A default_model you set yourself always wins and is downloaded instead of Gemma; questions about images then download Gemma (~6 GB) the first time you ask one, and gaia init says so. --check asks for the same models gaia init would download.
user.Qwen3.8-Flash-Next-GGUF (an 82 GB download, multimodal, thinking on) is available on a 128 GB Strix Halo and never auto-selected. It needs ~90 GB for models and Lemonade v2026.39.1 or newer (its llama.cpp cannot load the model otherwise; upgrade with gaia init --force-reinstall). Choose it with gaia config set default_model user.Qwen3.8-Flash-Next-GGUF, or pick it in the terminal UI’s model picker; Lemonade downloads it on first use. gaia init refuses it on a PC that cannot hold it and names the memory it needs. To measure both on your own PC, speed, tool calling and (with --tasks core) agent quality, run python util/compare_local_models.py from a GAIA checkout.
gpt-oss 20B (user.ggml-gpt-oss-20b-MXFP4-GGUF, a 12 GB download, ~15 GB for models) and gpt-oss 120B (gpt-oss-120b-mxfp-GGUF, 63 GB, ~70 GB for models, a 128 GB Strix Halo) are supported and never auto-selected. Choose one with gaia config set default_model <id>; see gpt-oss 20B and 120B.
gaia llm quick queries use default_model when set, else Gemma-4-E4B-it-GGUF.
Examples:
What It Does: GAIA runs its own Lemonade Server: a private copy under ~/.gaia/lemonade, on a port only GAIA uses, behind a generated API key. It needs no admin rights and never touches a Lemonade app you installed yourself.
  1. Starts Lemonade Server — downloads GAIA’s copy on first run (checksum-verified), then starts it. Re-running replaces an older copy or one that stopped answering.
  2. Downloads Models — pulls the models for the selected profile (the NPU profile also installs the FLM backend)
  3. Verifies Setup — tests each model with inference to detect corrupted downloads
Platform Support: GAIA’s own Lemonade Server runs on Windows and Linux (x64 and arm64) and on Apple Silicon Macs. Intel Macs have no upstream Lemonade build — point LEMONADE_BASE_URL at a Lemonade Server on another machine.
Corrupted Model Detection: gaia init verifies each model with a quick inference test. If a model fails verification (e.g., corrupted download), you’ll see instructions to manually delete and re-download it, or use gaia init --force-models to force re-download all models.

Install Command

Install individual GAIA components.
Options: Examples:
If a different version of Lemonade is already installed, you’ll be prompted to uninstall first.
The installer is downloaded to a file of its own in the temp folder and checked against its expected size and SHA-256 before it runs. It is deleted after a successful install. After a failed one it is kept, and its path is printed so you can retry by hand. If Windows Installer still holds the file, GAIA can’t delete it; it logs a warning with the path instead.

Uninstall Command

Tiered cleanup of GAIA components. By default, the OS-native uninstall (Add or Remove Programs, drag to Trash, apt remove) only removes the app — user data in ~/.gaia/ is preserved. This command lets you escalate cleanup as far as you want.
Options: Examples:
--purge permanently deletes your chat history, uploaded documents, what GAIA remembers about you (memory.db), its file and scratchpad indexes, goals, sessions, config.json, installed agents (~/.gaia/agents), caches, embedded Lemonade downloads, agent traces, and the daemon’s state in ~/.gaia/host — including its custody database (agent memory, sessions, and audit history). It also deletes GAIA’s sign-ins and API keys from the OS credential store (service gaia.connections) — only when it purges the default ~/.gaia, since that store is shared by every GAIA home. Use --purge --dry-run first to preview the purge tier; it lists only what exists. Pair other cleanup tiers with --dry-run to preview those instead.
--purge keeps ~/.gaia itself and what is not GAIA’s to delete: your connector setup (mcp_servers.json, connectors/, and the MCP server secrets that file names), your own skills in ~/.gaia/skills, and any file another tool put there. The plan and the end of the run list each kept entry. The desktop app’s ~/.local/bin/gaia shim (or $XDG_BIN_HOME/gaia) is removed only when it is the app’s exact wrapper around this GAIA home’s venv; any other gaia there is left alone and named.
--purge also undoes what the one-line installer set up. It stops a running GAIA daemon before deleting its state, removes gaia-tui and gaia-agent from ~/.gaia/bin, and removes the installer’s PATH entries: the # Added by GAIA installer block in ~/.zshrc, ~/.bashrc, ~/.bash_profile or ~/.profile, or the ~\.gaia\venv\Scripts and ~\.gaia\bin entries on the Windows user PATH. The rest of each file and every other PATH entry are left exactly as they were. If you edited the installer’s export PATH line by hand, it is kept and the command tells you to remove it yourself. A GAIA_HOME other than ~/.gaia leaves these PATH edits alone, because they still point at ~/.gaia. Open a new terminal afterwards so it stops using the old PATH.
GAIA_HOME moves the directory this command deletes into, and the resolved path is printed above the plan and in the --purge confirmation prompt. It must name a directory GAIA owns, and both --venv and --purge check this before deleting anything:
  • If it resolves to your home directory, a directory containing it, or a drive root, gaia uninstall refuses and exits 64 rather than deleting $GAIA_HOME/documents — which on Windows and macOS is your real Documents folder.
  • An existing $GAIA_HOME must also be named .gaia or hold something GAIA created (config.json, chat/, lemonade/, memory.db, …). A bare venv/ does not count, so neither tier will eat the virtualenv of a project directory that GAIA_HOME happens to point at. A $GAIA_HOME that does not exist yet is a no-op, not an error.

Kill Command

Stop running GAIA services.
Options: Examples:
--daemons stops only orphaned daemons: ones whose home folder or instance.json is gone, so no GAIA command can reach them (test runs used to leave these behind). The daemon of your own home is listed but never stopped; use gaia daemon stop for that. Daemons of other homes, such as a GAIA_HOME you set elsewhere, are listed and left running. Exits 1 if an orphaned daemon would not stop.
On Windows, --lemonade also kills orphaned llama-server.exe and lemonade-tray.exe processes.
--port only targets a socket that is listening on exactly that port — clients connected to it, and servers on a port that merely shares a prefix, are left alone. Before killing, it checks the owning process: GAIA’s servers run under a Python or Node interpreter, so a native service (a database, svchost, nginx) on a mistyped port gets a refusal message instead of a kill -9. Port-listing and forceful termination commands time out after five seconds and report inspection failures. If only some listeners are terminated, the command reports the killed, refused, and failed processes and exits 1. An empty port exits 0 because the requested stopped state already holds. --lemonade propagates failures from its port-based stop path. Both stop paths for the API server — gaia api stop and python -m gaia.api.app stop — use the same targeting.

Agent Command

Author, version, validate, and share GAIA agents.

Developer workflow: init, version, test

Scaffold a new agent package, bump its version, and run the quality gates that publishing requires. The scaffold mirrors the canonical hub package layout (hub/agents/email/python/). init and version are hidden from gaia agent -h but still work.
init scaffolds a package directory named after <name>:
  • Python: gaia-agent.yaml, pyproject.toml (with the gaia.agent entry point), a gaia_agent_<id>/ package (__init__.py + agent.py skeleton), tests/test_agent.py, and README.md.
  • C++: gaia-agent.yaml, CMakeLists.txt, src/agent.cpp, tests/, and README.md.
Use --layout hub when authoring a package for this repo’s hub/agents/ tree, which nests the implementation under a language directory (hub/agents/<id>/python/). Standalone packages outside the repo want the default flat layout. version bumps the SemVer in gaia-agent.yaml and keeps pyproject.toml / __init__.py in sync. test runs quality gates in two modes: The publish workflow requires --lint to pass; --live is recommended but not enforced. Examples:

Lifecycle: configure, health, status

Manage an installed agent: set per-agent configuration, verify it loads, and inspect its state. Configuration is persisted under ~/.gaia/agents/<id>/config.json and survives restarts.
configure writes per-agent settings (e.g. a preferred model). --set values are JSON-decoded when possible (--set temperature=0.2 stores a number, --set verbose=true a boolean), otherwise kept as strings. Settings merge into the existing config by default; pass --replace to overwrite it wholesale, or --show to print the current config without changing it. health verifies that an installed agent actually loads — its registration resolves and its entry point imports. It reports one of healthy, degraded (loads but something optional is off, e.g. a corrupt config), error (a required piece fails to load), or not_installed. status aggregates installed version, health, config summary, and source for one agent — or every discovered agent when <id> is omitted. Exit codes: with an explicit <id>, both commands exit 1 when that agent’s health is error or not_installed, 0 otherwise — so scripts can gate on a single agent without parsing table output. The all-agents form (gaia agent status with no <id>) always exits 0; an absent agent shows up as a row, not a failure of the listing itself. --json emits machine-readable output instead of the table/text form, with log lines routed to stderr so stdout stays pure JSON for | jq. health --json prints one object (HealthStatus.to_dict()); status --json prints one object for a single <id>, or a JSON list of objects for the all-agents form. Examples:

Distribution: pack, publish, login

gaia agent pack, gaia agent publish and gaia agent login are maintainer release commands. They are hidden from gaia -h and not documented here.
Installing a published agent
The two publish targets back two install paths, and both register the agent the same way — via the gaia.agent entry point, so the registry discovers it automatically:
From PyPI (pip)
PyPI publishing for gaia-agent-* wheels is currently paused (tracked by #2240), so the command above doesn’t resolve yet. Until publishing lands, install an agent straight from this repo:
From source (works today)
The download-server path fetches the published, SHA-verified artifact into ~/.gaia/agents/<id>/ (backed by gaia.hub.installer). Install headlessly with:
gaia agent list shows what is available to install so you know which id to pass. An installed agent shows the version on disk with an [installed] marker, plus the catalog’s latest version when the two differ. This is the supported way to provision a sidecar agent on a machine with no UI.
gaia agent install installs in-process. gaia hub install performs the same install through the daemon, which additionally stops that agent’s running sidecar first and keeps it stopped for the whole download — the install directory is the sidecar’s own binary cache. Reach for gaia hub when the daemon supervises the agent, and gaia agent when it does not (or when the daemon extras are not installed). Both enforce the same --trust gate and the same SHA-256 verification.
Running a sidecar agent starts its sidecar automatically on first use; gaia daemon start-agent <id> is an optional pre-flight to launch and health-check it early.

Sharing: export, import

Export every custom agent installed under ~/.gaia/agents/ into a single .zip bundle, and import a bundle produced on another machine.
Export options: Import options: Examples:
Exported bundles contain your agent source files as-is. Any API keys or credentials present in agent.py will be included. Review bundles before sharing.
Importing a bundle runs third-party Python code on your machine. gaia agent import shows the agent IDs in the bundle and requires explicit y/yes confirmation (or --yes) before proceeding.

Core Commands

LLM Direct Query

The fastest way to interact with AI models - no server management required.
Options: Examples:
The lemonade server must be running. If not available, the command will provide instructions on how to start it.

Chat Command

Talk to the flagship GAIA agent (the same agent the TUI and Agent UI run): start an interactive conversation, or send a single message and exit.
Modes:
  • No message: Starts interactive chat session
  • Message provided: Sends single message and exits
Output: the answer goes to stdout; the device banner, model-load progress, warnings and errors go to stderr, so gaia chat -q "…" > answer.txt captures just the answer. INFO diagnostics are written only to the log file (~/.gaia/gaia.log, or the path in GAIA_AGENT_LOG); --debug also shows them on stderr. Options: Exit codes: --query exits 0 when the agent answered, 1 when the query or the agent failed, and 130 when interrupted with Ctrl+C — so gaia chat --query "..." && next-step only runs next-step on success. Examples:
Interactive Commands: During a chat session, use these special commands:

Prompt Command

Send a single prompt to a GAIA agent.
Options: With --stats, the command exits 1 if the stats cannot be fetched after the answer has printed. Examples:

Specialized Agents

Talk Command

Voice Interaction

Speech-to-speech conversation with optional document Q&A
Options: Speech recognition (Whisper) and voice output (Kokoro) run in the same Lemonade Server as the chat model, at --base-url; the first run downloads both models through it. The microphone is muted while GAIA speaks. Say “stop” or press Ctrl+C to end the session; gaia talk exits non-zero if the voice loop, the microphone or Lemonade’s transcription fails. Examples:
→ Full Voice Interaction Guide

Telegram Command

Telegram Adapter

Bridge a Telegram bot to GAIA so you can chat with your agents from Telegram.
Subcommands & options: → Telegram Adapter Guide

Slack Command

Slack

Drive the flagship GAIA agent from a Slack direct message, over Socket Mode.
Subcommands & options: → Slack Guide

Email Command

Email triage

The standalone email triage agent for Gmail or Outlook, with all body inference running locally on Lemonade. Needs the Google or Microsoft connector. The flagship agent reads mail too, through its own tools.
gaia email autonomy takes status, trust, set-level, pause, resume, kill and run. → Email Guide

API Server

API Server

OpenAI-compatible REST API for VSCode and IDE integrations

Quick Start

  1. Start GAIA API server — it brings up Lemonade through the daemon, at the context size your device profile pins:
  1. Test the server:

Commands

Options:
  • --host - Server host (default: localhost)
  • --port - Server port (default: 8080)
  • --debug - Enable debug logging
  • --show-prompts - Log the prompts sent to the LLM for every request (useful for debugging)
  • --streaming - Stream tokens to clients via SSE (OpenAI-style)
  • --step-through - Pause between agent steps for manual inspection (development aid)
Examples:Foreground:
With debug logging:
Custom host/port. Binding beyond loopback is refused unless GAIA_API_KEY is set, since the agent loop would otherwise be open to the network (Authentication):
Streaming mode with prompt logging:
→ Full API Server Documentation

MCP Client

MCP Client

Connect GAIA agents to external MCP servers
Configure MCP servers that your agents can connect to. Servers are saved to ~/.gaia/mcp_servers.json by default ($GAIA_HOME/mcp_servers.json when GAIA_HOME is set), or to a custom config file using --config.

Commands

Managing MCP servers (add / remove)

gaia mcp add and gaia mcp remove were removed in #977 — MCP servers are now configured through the connectors framework. Run gaia connectors --help for the current commands. gaia mcp list (below) still lists configured servers.

gaia mcp list

List all configured MCP servers.
Options:
  • --config PATH - Custom config file path (default: ~/.gaia/mcp_servers.json)
Example:

gaia mcp tools

List tools available from a configured MCP server.
Arguments:
  • <server-name> - Name of the server to query
Options:
  • --config PATH - Custom config file path (default: ~/.gaia/mcp_servers.json)
Example:

gaia mcp test-client

Test connection to a configured MCP server.
Arguments:
  • <server-name> - Name of the server to test
Options:
  • --config PATH - Custom config file path (default: ~/.gaia/mcp_servers.json)
Example:
→ Full MCP Client Guide

MCP Bridge

MCP Bridge

Expose GAIA agents as MCP servers
The MCP Bridge allows other applications to use GAIA agents as MCP servers.

Quick Start

Install MCP support:
Start MCP bridge:
Test basic functionality:

Commands

gaia mcp start options

Browser origins
A web page can reach http://localhost:8765 from the user’s own browser, so the bridge refuses any request whose Origin is not loopback (localhost, 127.0.0.1, [::1]) with 403, and never answers Access-Control-Allow-Origin: *. Clients that send no Origin — curl, MCP clients, n8n — are unaffected. To allow a browser app on another origin, list it in GAIA_MCP_ALLOWED_ORIGINS (comma-separated).
Concurrent requests and limits
Each request runs on its own thread, so /health and /status answer while a /llm or /chat call waits on a busy model. Calls to the same tool take turns: /chat shares one conversation. A queued caller waits for the call ahead of it, which can run 15 minutes or more, so give clients such as n8n a generous HTTP timeout. A client that stops sending its headers or body for 30 s gets 408. A request body over 1 MiB gets 413; raise the limit with GAIA_MCP_MAX_BODY_BYTES (bytes). A request with any Transfer-Encoding (chunked or other) gets 411; send a plain Content-Length body instead.
Authentication
The bridge is unauthenticated by default. Anything that can reach the port can enumerate and invoke its tools, which is why --host defaults to localhost. Pass --auth-token before exposing it anywhere else:
Prefer the environment variable over --auth-token <token>: on Linux and macOS command-line arguments are visible to any local user via ps. Clients then send the token on every call:
GET /health stays public so container and orchestrator liveness probes keep working; it reports only liveness plus agent/tool counts. Every other endpoint — /status, /tools, /chat, /llm, and the JSON-RPC endpoint — requires the token. gaia mcp status, gaia mcp test, and gaia mcp agent accept --auth-token (also defaulting to $GAIA_MCP_AUTH_TOKEN) so they can reach a protected bridge.
The bridge speaks plain HTTP. A bearer token stops anonymous callers but does not encrypt traffic — put it behind a TLS-terminating reverse proxy, or keep it on loopback, before sending a token across an untrusted network.

gaia mcp test options

gaia mcp agent

Pass a natural-language request and, optionally, a --domain (e.g. atlassian, gaia, all) and free-form --context string to steer the orchestrator.

gaia mcp serve options

Wraps the Agent UI backend’s REST API as MCP tools, so an MCP client (Claude Code, for example) can chat through the same backend that powers the browser UI — conversations and tool activity show up there in real time. The Agent UI backend (gaia chat --ui) must already be running. Over HTTP, any request carrying a browser Origin header is refused with 403, so a web page can’t drive the server. A non-loopback --host is refused unless --allow-remote is given with a token, sent by clients as Authorization: Bearer <token>. See Agent UI MCP Server for the tools that run a turn unattended.

gaia mcp tui options

Wraps the TUI control API as MCP tools, so an assistant can navigate and type in the TUI session you are watching. The TUI must already be running with gaia tui --control; the server finds it through ~/.gaia/tui/control.json rather than a fixed port. → Full TUI Control MCP Guide → Full MCP Integration Guide

Connectors

Connectors

OAuth providers, MCP-server connectors, and per-agent scope grants
Manage external connectors (OAuth providers like Google/GitHub, MCP servers, API tokens) and control which agents may use them. Configure a connector once, then grant individual agents the scopes they need.
Subcommands: Examples:
The --device flag on connect uses the RFC 8628 device-code flow instead of the browser redirect — required for Microsoft zero-setup sign-in (no Azure app registration or loopback redirect URI). Which authority a Microsoft connector signs in against is fixed by the connector itself (microsoft → consumers, microsoft_work → organizations) — to restrict microsoft_work to one Entra tenant, set its optional Directory (tenant) ID setup field instead of an environment variable; see the Microsoft connector guide. → Connectors Guide · → Connectors SDK

AMD LLM Gateway

Runs GAIA agents on gateway-hosted models — larger on-prem models and frontier models — alongside local inference. Requires Lemonade Server 11.8.0+. You enter your token once. Lemonade holds it in memory and forgets it on restart, so GAIA keeps a copy in your OS credential store (DPAPI, Keychain, SecretService) and replays it — encrypted at rest, never in a GAIA file.
Gateway models are namespaced amd.<model> and work anywhere a local model does. LEMONADE_AMD_API_KEY in Lemonade’s environment still takes precedence over a stored token; if both are set, Lemonade says so rather than silently choosing. When a gateway model answers HTTP 404, GAIA records it. status and models then mark it unavailable, and chat, llm, and prompt warn when it is your saved default. GAIA never switches models for you; pick another with gaia gateway use <id>. → LLM Gateway Guide

Configuration

GAIA keeps a small persistent config at ~/.gaia/config.json (override the location with the GAIA_CONFIG_DIR / GAIA_CONFIG_FILE environment variables). Use it to set a default model once instead of passing --model on every command.

Full access

By default the agent asks before it runs a shell command or writes a file. Full access stops the asking: every gated tool runs without a prompt, and the shell guardrails come off — output redirection works, and the read-only command list is replaced by a developer set (python, pytest, node, npm, make, go, cargo, gh, git, …) that can run arbitrary code. git is unrestricted, including push and history rewrites. It is the only mode in which commands GAIA otherwise refuses outright (a credential print such as gh auth token, an encoded PowerShell command) can run. Full access is one of three permission modes. Shift+Tab in the TUI cycles them, and the status bar shows the one that is on: Ways to turn full access on — each is deliberate: Turn it on every time you launch gaia-tui with:
The TUI shows a warning banner on every frame while it is on, and names this setting as the reason. To turn it off: Full access needs an agent the TUI launches itself. A Hub-installed flagship runs through the GAIA background service, which doesn’t support it yet: --full-access is refused there, and a saved full_access leaves prompts on with a note in the chat saying so. This is the only thing that turns full access on without someone asking for it on that launch, so it is read only from your own ~/.gaia/config.json — a project-local .env or a checked-in config file can never switch off your confirmation prompts.

Default model precedence

For gaia chat, gaia llm, and gaia prompt, the model is resolved highest-wins:
  1. An explicit --model <id> flag
  2. default_model from ~/.gaia/config.json
  3. The command’s built-in default (DEFAULT_MODEL_NAME)
The Agent UI resolves a new session’s model the same way: an explicit pick in the UI wins, then default_model, then the built-in floor. So gaia config set default_model <id> also changes what a brand-new “New Chat” lands on, not just the CLI. So gaia config set default_model <id> lets you skip --model entirely, while --model still overrides it for a single run. For gaia chat, passing an explicit --device selects a device-specific model and takes precedence over the config default.
A missing config file is fine — GAIA falls back to built-in defaults. A corrupt config file (invalid JSON) fails loudly with the file path and how to recover, rather than silently reverting to defaults.

Using a custom config file

Point GAIA at a config file anywhere on disk — handy for per-project configs or keeping work and personal defaults separate. Two equivalent ways:
When both are given, --config wins over GAIA_CONFIG_FILE, which wins over the default ~/.gaia/config.json.

Model Management

Download Command

Download all models required for GAIA agents with streaming progress.
Options: Without --host or --port, gaia download uses the same Lemonade Server as the rest of GAIA: LEMONADE_BASE_URL if set, otherwise GAIA’s own Lemonade on whatever port it started on (gaia lemonade embedded status shows it). --host on its own targets Lemonade’s standard port (13305) on that host. Available Agents: chat, talk, rag, vlm, minimal, mcp Examples: List all models:
List models for specific agent:
Download all models:
Download for specific agent:
Example Output:

Pulling a single model

gaia download takes no model name; it fetches every model an agent needs. To pull one model, use the client that ships with your Lemonade install — on a current Lemonade that is lemonade pull MODEL_NAME. When a GAIA command finds a model missing, its error names the exact command for your machine, so you never have to guess which client you have.

Embedded Lemonade

GAIA can run its own private Lemonade Server instead of the machine-wide install. Nothing else on the machine can reach it: it binds a port of its own and requires an API key GAIA generates, so two apps never fight over one server. This is the self-contained path — GAIA downloads the server itself, no installer and no admin rights. gaia init installs and starts it; a Lemonade you installed yourself is left alone unless you choose to use it (see Use a Lemonade you already run).
The API key changes on every start, and requests without it get a 401. The port stays the same from one start to the next unless another program has taken it, in which case a free one is picked and remembered instead. start writes them to a file you source rather than printing the key, so the secret never lands in terminal scrollback, shell history or a CI log:
The file sets LEMONADE_BASE_URL and LEMONADE_API_KEY, is owner-readable only, and is deleted on stop — credentials naming a dead port are worse than none, since whatever grabs that port next would inherit the trust. On Windows it is env.ps1, loaded with . $HOME\.gaia\lemonade\env.ps1.
GAIA agents find the embedded instance on their own through its state file. The env.sh / env.ps1 file is for other tools.

Use a Lemonade you already run

Point GAIA at a Lemonade Server you run yourself instead of its own. GAIA then installs and starts no Lemonade at all; if yours is down, the error says so and how to start it. GAIA never changes that server’s setup: no cloud providers or keys, no backends, no saved load options, and no unloading or deleting your models. The install guide covers what GAIA does on your server. gaia config set lemonade_mode gaia is refused, with nothing changed, until GAIA’s own server is installed; gaia init --gaia-lemonade installs it and switches.
LEMONADE_BASE_URL wins over the saved setting.

Embedded commands

start options: install options:

Inference backends

The downloaded server is small (~5 MB) and ships no inference backend — those are fetched separately, and only the ones your hardware needs: GAIA’s default models (Gemma-4-E4B-it-GGUF, embeddinggemma-300m) run on the llamacpp backends, so CPU + Vulkan is enough for the common case:

Where it stores things

Everything lives under ~/.gaia/lemonade (or $GAIA_HOME/lemonade): Downloaded models are not duplicated: the embedded server reads the same Hugging Face cache as every other Lemonade install, so models you already have are picked up immediately.
cache/ is deliberately shared across versions. Backends run from 141 MB to 4.3 GB, so a Lemonade version bump re-downloads the 5 MB server, not the backends. gaia uninstall --purge and gaia lemonade embedded uninstall remove this directory, including those downloaded backends.
An instance you start here is still GAIA’s: a running daemon takes it over, stops it with gaia daemon stop, and stops it after the idle timeout like any other — see GAIA’s Lemonade Server. After a Lemonade version bump, stop the old instance before starting the new one — start refuses rather than silently leaving the previous version serving:

Evaluation Commands

Evaluation Framework

Systematic testing, benchmarking, and model comparison
Tools for:
  • Agent eval benchmark (scenario-based, end-to-end)
  • Auto-fixing failures with Claude Code
  • Report generation
  • Performance-log visualization
Quick Examples: Run the agent eval benchmark:
Generate a report:
gaia report is registered but not currently dispatched (#3488) — it prints top-level help and exits 0 instead of generating a report.
Visualize llama.cpp performance logs:
Benchmark code search on real repositories (recall vs git grep, scale, robustness; no LLM):
Benchmark memory over months of chats (changing facts, forgets, lessons, planted content):
→ Full Evaluation Guide

Agent Eval

Agent Eval Benchmark

Scenario-based end-to-end testing of the GAIA Agent UI
Run automated multi-turn conversation tests against the live Agent UI. The eval agent (Claude Code) simulates user personas, drives conversations via MCP, and judges every response across 7 scoring dimensions.
Options: Examples:
The eval agent requires Claude Code CLI (claude command), an Anthropic API key, and the Agent UI backend running. See the Agent Eval Guide for full setup instructions.
→ Full Agent Eval Guide · → Scenario Authoring · → CI/CD Integration

Code Benchmark

Each task ships a project and a test suite; the agent is asked to change it and the suite decides pass/fail — no LLM judge. A TUI must already be running with a control port, since this drives that session so the run is visible.

Retrieval Benchmark

Scores RAG retrieval (recall@k, MRR) separately from answer accuracy, on real documents with labelled evidence, and measures indexing and query cost as the corpus grows. See Retrieval Benchmark.

Flagship Agent Tasks

Run an agent on real coding, data and Q&A tasks and score what the finished project does. CI gates on passes, tested changes, quality, honesty, tokens, steps and runtime, against main’s committed results.
Harness × model comparison. The same tasks, fixtures, time limit and judge, against more than one agent — so a difference in the table is a difference between the agents, not between their conditions:
SWE-bench Verified. The swebench suite builds its tasks from real upstream issues, and grading runs the official harness in Docker. run grades as it goes; pass --no-evaluate to capture the predictions now and grade them later on a machine that has Docker:
→ How tasks are scored and gated · → Comparing harnesses and models

Email Throughput Benchmark

Measure end-to-end email-triage throughput (tokens/sec), time-to-first-token, and pipeline latency for an on-device model. Direct-drives the email agent over the committed synthetic corpus and harvests metrics from the agent’s per-step stats. The committed bar is ≥10 tok/s (snappy-UX stretch ~30 tok/s); the benchmark is non-gating — a miss is reported, not failed.
Options:
Runs from a GAIA repo checkout (it drives the synthetic corpus in tests/fixtures/email/) and needs Lemonade serving the target model. Run at most one gaia eval process at a time against a single Lemonade server.

Performance Visualization

Plot llama.cpp server performance metrics from one or more log files. Plots are saved as images; pass --show to also display them interactively.
Options: Examples:

Memory

Agent Memory Guide

Persistent second brain — remembers facts, preferences, and workflows across sessions
Manage agent memory: run day-zero onboarding and view memory statistics.

Commands

Show aggregate memory statistics.
Output includes:
  • Knowledge entries by category (fact, preference, error, skill) and context
  • Search-vector coverage, and how many entries are Without vector (saved while the embedding model was unavailable, so only keyword recall finds them) with how to re-embed them
  • Conversation count and session history
  • Tool call success rates and error counts
  • Upcoming and overdue time-sensitive items
  • Database size
→ Full Memory Guide · → Memory SDK Reference

Skills

Agent Skills Spec

Portable SKILL.md capabilities — instructions, optional typed tools, and resources an agent loads only when relevant
Author and manage agent skills. A skill is a directory whose only required file is SKILL.md; GAIA implements the Agent Skills open standard, so a Claude Code skill loads unchanged.
Skills are discovered from three roots, highest precedence first:

Subcommands

A skill shadowed by a higher-precedence copy of the same name is reported on stderr rather than hidden. A skill directory that fails to parse is listed with its error and the command exits 4. One under the read-only claude-import roots belongs to another tool, so it is listed as a warning (import_errors in --json) and does not change the exit code.

Security tiers at install

Three tiers set the install-time permission ceiling. A skill’s front matter states the tier it wants; its signature decides the tier it gets. The effective tier is min(claimed, attested), so no unsigned skill installs as verified — the claim is collapsed rather than trusted. Imported and captured skills (gaia skill import, capture_skill) always land experimental. The ceiling is checked again whenever a skill loads or is promoted, against the tier it landed at, so editing SKILL.md afterwards cannot raise its tier or its grants.
v1 skills are instruction + connector-bridged only. A skill declaring a local-capability permission (filesystem, shell, database, desktop, env) is refused at every tier, including verified — the sandbox that would contain it is deferred (#1019). A verified stamp is a provenance claim, not a runtime sandbox.

skill-lock.json

Hub installs are tracked in ~/.gaia/skills/skill-lock.json. Discovery answers what is on disk; the lock answers what was requested, what resolved, what the artifact and the installed files hashed to, who signed it, and which tier it was installed at after the ceiling was applied:
gaia skill install writes source: "hub" entries, and gaia skill import writes source: "imported" ones held at experimental. A skill from create has no provenance to record; gaia skill lock --relock may record it as source: "local", which claims exactly the origin it has. Relock never raises the tier of a hub, imported, or captured entry. content_digest covers the installed directory (after the tier re-stamp), not the downloaded archive. That is what makes post-install tampering detectable: gaia skill lock compares against it, and a community / verified skill whose files no longer match refuses to load — those bytes are not the ones the signature covered, so the tier badge no longer means anything. An experimental or locally-authored skill warns and keeps working; nothing attested it, and editing a skill you wrote is the normal workflow.
skill-lock.json has no integrity protection of its own — anyone who can write to it can rewrite a digest. It defends against tampering with the skill directory (a swapped bundle, an edited manifest, a downgraded version), which until now was entirely silent.
Examples:
Exit codes: 0 success · 2 missing/unknown subcommand · 3 skill not found · 4 invalid skill (bad manifest, name collision without --force, or a GAIA discovery root containing a malformed skill — a malformed claude-import skill is only a warning).

Loading a skill into an agent

Skills are never globally active — an agent loads one explicitly, and its tools register under a <skill-name>/<tool> namespace:
Or declare them in the agent’s gaia-agent.yaml and write no Python at all:
Every Agent subclass resolves this block at construction, so a custom agent under ~/.gaia/agents/<id>/ consumes an installed hub skill by dropping a gaia-agent.yaml beside its agent.py — no code change, no registration call. Resolution mirrors agent dependencies:: highest installed version satisfying the range, tool providers ordered before their consumers, and fail-loud on a conflict or a dependency cycle. A missing required: true skill raises with the gaia skill install command that fixes it; a missing optional one is logged and skipped. Set AUTOLOAD_DECLARED_SKILLS = False on the class to opt out and call load_declared_skills() yourself.
A skill declaring a local-capability permission is refused, not sandboxed. network and mcp permissions bridge to the connector grant model and load fine. filesystem, shell, database, desktop, and env need the permission sandbox, which is deferred (#1019) — until then load_skill fails with an actionable error rather than loading the skill unenforced.
search / install / remove / publish shipped with the marketplace (#2467); gaia skill migrate (OpenClaw / Hermes import) shipped with #692; skill_sets: is #2466.
→ Agent Skills Spec · → Skill Format Reference

Schedule

Run a prompt on a recurring cron schedule and route its output to a sink. Schedules are stored in ~/.gaia/schedules.toml (hand-editable). The daemon action runs a long-lived scheduler that fires each enabled schedule when due. The running daemon reloads schedules every second, so adding, pausing, resuming, removing, or editing a schedule does not require a restart. Pausing or removing a schedule prevents future runs; it does not interrupt a run already in progress. A run that comes due while the daemon is running but the machine is asleep or busy still fires once the daemon catches up, as long as that is within an hour of the due time; several missed runs collapse into one. A run more than an hour late, or one that comes due while the same schedule’s previous run is still going, is skipped and logged at WARNING with the schedule name. A run that came due while the daemon itself was not running is not replayed on restart. Runs that never happened are not silent: the ones a sleep skipped or collapsed, and the ones due while the daemon was not running, are counted on the schedule. gaia schedule show prints the total, missed: 3 runs in all while the PC was off or asleep, or the schedule daemon wasn't running (latest due …), and gaia schedule list adds missed=3. Runs due while a schedule was paused are not counted. The total is kept until the schedule is removed; past 100,000 in one stretch it reads “at least”, and the latest due time is still exact. Times are shown in local time. Only one gaia schedule daemon can run per schedule store. A second one exits immediately with an error instead of firing every schedule twice. The daemon holds ~/.gaia/schedules.lock while it runs; the OS releases it when the process exits, even after a crash. On Windows that error names no pid — Windows refuses another process’s read of the locked byte, so the holder is unknowable.

Subcommands

add Options

Examples:

Sinks

A sink decides where each scheduled run’s output goes. Set it with --sink on add (default stdout). add rejects a sink that could never deliver (an unknown name, telegram without --to, bare file without a path, or notification on Windows) and stores nothing. The Telegram bot token is only checked when a run delivers, because the daemon reads it from its own environment. Telegram example:
--skill is not yet implemented — gaia schedule add --skill is rejected with an error at add time. The skills runtime has shipped (#888), but the scheduler is not wired to it yet. Use --prompt for now.

Utility Commands

Stats Command

View performance statistics from the most recent model run.
Exits 0 and says so when no run has recorded stats yet. If the stats cannot be fetched — for example, the LLM server is unreachable — it prints the cause to stderr and exits 1.

Test Commands

Run various tests for development and troubleshooting.
Test Types:
  • tts-preprocessing - Test TTS text preprocessing
  • tts-streaming - Test TTS streaming playback
  • tts-audio-file - Test TTS audio file generation
Options:
  • --test-text - Text to use for TTS tests
  • --output-audio-file - Output file path (default: output.wav)
Examples:Test preprocessing:
Test streaming:
Generate audio file:

YouTube Utilities

Download transcripts from YouTube videos.
Options:
  • --download-transcript - YouTube URL to download transcript from
  • --output-path - Output file path (defaults to transcript_.txt)
Example:

Daemon Command

Manage the headless custody daemon — the always-on, single-instance background process that supervises agent sidecars (spawn, health, tree-kill; email is the first) and that later Agent UI v2 phases (/host/v1/* custody, the model-slot broker, the scheduler clock) mount into. The Agent UI backend is a daemon client: it asks the daemon to ensure a sidecar and talks to the sidecar directly, so closing the UI leaves the sidecar running — stopping is a daemon command. The daemon also serves a streaming data plane: ANY /v1/<agent>/* (e.g. POST /v1/email/query) is reverse-proxied to that agent’s running sidecar, SSE relayed unbuffered, with cancel propagated on client disconnect and a synthetic terminal error event if the sidecar crashes mid-stream. Clients authenticate every route — control plane and data plane alike — with the daemon client token from ~/.gaia/host/instance.json; the per-sidecar bearer is injected by the daemon and never leaves it for relayed traffic. Requires the daemon extras (fastapi/uvicorn/psutil) — install with pip install "amd-gaia[api]" (or [ui], which adds the Agent UI’s RAG stack on top). The one-line installer already does this. Running it on a base install fails loudly with that instruction.

GAIA’s Lemonade Server

The daemon owns GAIA’s own Lemonade Server (the one gaia init installs). It starts it when it comes up, and again whenever a GAIA app or command needs it, so it is back after a reboot without running gaia init. It also frees the server’s memory when GAIA is not in use:
  • Closing the last GAIA app unloads every model. The Agent UI (desktop app or npx @amd-gaia/agent-ui) and gaia-tui register with the daemon while they run. When the last one exits, and no gaia command or sidecar agent is still using the server, the daemon unloads all of its models. The server stays up, so the next launch only reloads the model. An app that is killed rather than closed is noticed within about five seconds.
  • Every GAIA process holds a lease while it uses the server. Each client of the server registers on its own: apps, gaia commands, sidecar agents, and a process under another GAIA_HOME whose LEMONADE_BASE_URL names this server (it registers with this daemon too). A lease is renewed every 30 seconds and dropped when its process exits or stops renewing for two minutes.
  • No lease and no request for 15 minutes stops the server. The daemon also reads the server’s own activity, so a request from a process it does not know about restarts the clock, and nothing is stopped or unloaded while a request is in flight. Fifteen minutes balances freeing GPU memory for other apps against the 20 to 60 seconds a cold model load costs. Change it with gaia config set lemonade_idle_timeout_minutes <minutes>, or turn it off with 0.
  • A stopped server comes back on the next request. A GAIA process that finds the server gone mid-request has the daemon start it again and sends the request once more, so the turn still answers. A stream already under way is not replayed.
  • A server that is briefly not answering is waited for. A request, a /model switch, or a CLI command such as gaia llm that finds GAIA’s server unreachable shows “Reconnecting to the model server…” and waits up to 60 seconds from finding it gone while the daemon brings it back, then carries on. A new agent session waits the same way while it starts, but only logs the message. If the server is not back by then, the request fails with an error that says so and points at gaia daemon status. A Lemonade you run yourself, or one LEMONADE_BASE_URL names, is never waited on.
  • Closing a TUI session never stops the server other sessions use. On Windows the daemon and GAIA’s server leave the session’s process group when its agent starts them, so they keep running for every other session.
  • gaia daemon stop stops the server, whoever started it, including the one first-time setup starts. If the daemon is not running, it still stops GAIA’s server.
  • A server a crashed daemon left behind is taken over by the next daemon while it still answers, and stopped and replaced when it does not.
Before stopping anything, GAIA checks that the process is its own server: the lemond binary under ~/.gaia/lemonade/dist/, on the recorded port, with the recorded API key. A process that reused the recorded pid, or a Lemonade you run yourself, is never killed. If GAIA’s server crashes, or is killed without its child processes, its model servers (llama-server and the like) can keep running and holding their memory. GAIA stops them the next time it starts its server, or when it finds the server gone. It stops a process only when it runs from ~/.gaia/lemonade/cache/bin and the process that started it has exited. A tool from that folder that you run yourself, such as llama-bench, is left alone while the shell that started it is open. The daemon never installs Lemonade, never restarts a server that is slow to answer while its owner is running (it may be loading a model), and never replaces one running a different version. Those need gaia init. With LEMONADE_BASE_URL set, or lemonade_mode set to external, it leaves Lemonade alone entirely: no unload, no idle stop, no gaia daemon stop. Switching to your own server stops GAIA’s; switching back starts it again.

Model-slot serialization

Lemonade is single-tenant per model slot: a second process loading a different model evicts the first. The daemon therefore runs a model-slot broker that hands out one lease at a time, so model loads queue instead of racing. When a daemon is running, gaia commands and the Agent UI backend attach to it automatically and take a lease around each model load and request. Interactive chat turns take priority over background work (memory extraction, embedder warm-ups, autonomous briefs). A request that has to wait is queued, not failed:
  • It waits for as long as the request ahead of it holds the slot. The holder renews its lease every 15 seconds. One that stops renewing for a minute (it exited) is dropped and the queue moves on. One that keeps renewing past the time its own request is allowed (its request timeout, the two-minute retry window, and 15 minutes for a model load) is wedged, and loses the slot too.
  • Background work (memory extraction, document indexing) that has waited ten minutes goes ahead of newer chat turns, so it is never starved.
  • While an Agent UI turn waits, it shows Waiting for the model — <who> is using it with its place in line and seconds waited, updated every two seconds, and the wait never counts toward the turn’s idle stop. A gaia command prints ⏳ once. The gaia agent’s /query stream, which the terminal UI reads, carries the same wait as a model_queue status (see docs/spec/agent-ui-query-sse-contract.md §4.4).
  • Pressing Stop in the Agent UI, cancelling a /query run, pressing Ctrl+C, or closing the app takes the request out of the queue, so the slot never goes to a caller that has left. Indexing a turn started keeps going after the turn ends.
  • To cap the wait, set GAIA_MODEL_LEASE_WAIT_S to a number of seconds. Past it, the request fails with where it stood in line and who held the model.
  • If the daemon stops sending queue updates for 30 seconds, the request fails and names the daemon as the cause.
With no daemon running, nothing changes: there are no sidecars, nothing else holds the slot, and loads proceed directly. Attaching never starts a daemon. If the daemon is running but unreachable, a model load fails loudly rather than doing a direct load that would evict another process’s model — check gaia daemon status. A daemon left running at an incompatible version is reported as a warning and loads proceed unbrokered; gaia daemon restart restores serialization. A daemon that is running but slow to answer is not killed for it. GAIA gives it 10 more seconds, then reports it busy: commands fail with an error naming its pid, and gaia daemon status shows running, but not answering. Its status check waits on nothing else, so a daemon that has not answered it for two minutes, across probes no more than a minute apart, is hung: the next gaia daemon start (or any command that starts the daemon) replaces it, and gaia daemon restart does so at once. A daemon whose port refuses connections, answers as something else, or whose pid now belongs to another program is replaced right away. An exiting app does not wait on a busy daemon.
A daemon also shuts down on its own, logging why, within a few seconds of its home folder or instance.json being deleted, since no GAIA command can reach it after that. A daemon for any home other than ~/.gaia (a scratch GAIA_HOME, say) also shuts down after 120 minutes with no client connected, no sidecar busy and no scheduled job pending, and the next GAIA command starts a fresh one. Set GAIA_DAEMON_IDLE_EXIT_MINUTES to change that limit, or 0 to keep it running. The daemon for ~/.gaia never idles out. gaia kill --daemons finds and stops any that are still left. Daemon HTTP surface (the daemon serves no OpenAPI schema, so this is the contract; every route needs Authorization: Bearer <token> from ~/.gaia/host/instance.json):

Hub Command

gaia hub is hidden from gaia -h but still works. Use it for scripted installs of extra agents such as email from GAIA’s download server (https://hub.amd-gaia.ai). The flagship GAIA agent comes from gaia init, not from here.
Agents install into ~/.gaia/agents/<id>/. Every artifact is checked against its SHA-256 before it is written; a mismatch fails the install and leaves nothing behind. Install and uninstall stop a running sidecar first, and two installs of the same agent never race (the second gets a clean “already in progress” error). Requires the daemon extras (pip install "amd-gaia[api]"). A Python agent’s install waits up to 5 minutes when another install holds uv’s package cache (GAIA sets UV_LOCK_TIMEOUT for uv, keeping a shorter value of your own). While it waits, the install status reads “waiting for another install to finish”; if the cache is still taken, it stops with an error saying so. An install that does no work (no CPU or disk activity) for 900 seconds is stopped as stuck (GAIA_HUB_PIP_TIMEOUT changes that window), and one still running after 2 hours is stopped however busy it looks (GAIA_HUB_PIP_MAX_SECONDS changes that ceiling). Either way, everything the install started is stopped and the install is rolled back. Any agent outside the verified security tier runs third-party code on your machine, so the daemon refuses it with HTTP 403 until you pass --trust. The error shows the agent’s publisher and security tier and names the --trust re-run.
gaia agent install <id> does the same install in-process, without the daemon. Prefer gaia hub when the daemon supervises the agent (today: email).

Terminal UI (gaia tui)

gaia tui is the Go/Bubble Tea terminal client: it boots straight into the flagship gaia agent’s chat, behind a readiness gate. Building from source the binary is tui/bin/gaia-tui (cd tui && make build).
The binary accepts a leading tui word and drops it, so gaia tui status and gaia status are the same command. Running the source build directly, both ./tui/bin/gaia-tui tui status and ./tui/bin/gaia-tui status work.
Options accepted by every gaia tui command: There is no list, install, uninstall, or hub subcommand — the TUI does not install agents. Installing the email sidecar is the Python CLI’s job (gaia hub install email --trust, see Hub Command).

run --query is a real one-shot

--query does not open the alt screen. The answer goes to stdout, progress to stderr, and the exit code is 0 on a terminal answer, 1 on an error, and 3 when a confirmation gate held back a destructive action — so gaia tui run email --query "…" > answer.txt captures exactly the answer:
It never waits on something that is not there. Before the query is sent, a one-shot over the daemon transport (the agents the background service supervises) runs the same readiness check the interactive launch’s gate runs — background service, sidecar, local model server, model, and for email the mailbox — and if one of them is not ready it prints that row and its remedy to stderr and exits non-zero. A stopped local model server is started through the daemon before readiness is decided; this cold-start check can take up to 450 seconds (for the flagship that includes loading its models once, to prove they work). Nothing is sent to the agent until it is ready, and the gate screen is never rendered: a script has nobody to press a key. The flagship runs as a local subprocess and is checked the same way, just with its own three-row gate (agent binary, Lemonade, models) instead of the daemon’s five. If startup fails, stderr names the failing readiness row, the recovery action, and the relevant log. A stopped server alone is no longer an immediate refusal. The turn itself is bounded — 15 minutes by default, --timeout 90s / --timeout 2h to change it — so an agent that accepts the query and then stops answering is reported with what to read next instead of hanging a CI job until the job’s own timeout kills it. There is no “wait forever”: --timeout 0 is refused. Interactive gaia tui run <id> is unaffected — a person can read what arrives and press ctrl+c.

Readiness gate keys

In chat, /provider opens the same provider panel with masked key entry and a searchable model list. See AI providers for credentials, the recommended Fireworks models, and gateway settings.

Waiting on a consequential readiness problem

Most readiness rows that cannot be verified are named on screen and the launch continues — the sidecar itself does not treat them as fatal (an unadvertised Lemonade version, for instance). A handful are consequential enough to hold instead: right now, a model loaded into a smaller context window than this machine’s profile expects, since a document-sized request against it comes back context_length_exceeded rather than an answer. When that happens the screen waits for enter instead of proceeding on its own, and the notice lists every such row at once if more than one applies.

Theme (light vs dark terminals)

The TUI asks the terminal for its background colour on startup and picks a light or dark palette to match, so text stays readable either way. Over SSH, inside tmux, or in a CI log where the terminal never answers that query, force a mode instead of guessing:
GAIA_TUI_THEME also accepts auto (the default) to re-enable detection.

TUI Control API

The terminal UI (gaia tui, the Go/Bubble Tea binary) can expose a loopback HTTP API that drives the session you are looking at — not a headless copy. An assistant navigates, types, and drives the readiness gate while the frames render in your terminal in real time. It is how the TUI gets tested without a human at the keyboard. Off by default. Bound to 127.0.0.1 only, and every request needs a bearer token.
These are persistent flags on the TUI binary’s root command, so they apply to the bare launch, run, and chat alike. Building from source, that binary is tui/bin/gaia-tui (cd tui && make build).
On start the TUI writes ~/.gaia/tui/control.json (mode 0600) holding the pid, port, and token, and removes it on exit. Clients find the TUI by reading that file — the token is never printed to the terminal. A client must verify both that the recorded pid is alive and that /control/v1/status answers with a matching pid before trusting the port: after a crash the file can point at a port some unrelated process now owns.
Anything that can read ~/.gaia/tui/control.json can drive your TUI — the same trust boundary as ~/.gaia/host/instance.json for the daemon. Leave --control off unless you want a session driven.
Endpoints (all under /control/v1, all requiring Authorization: Bearer <token>): Errors come back as {"error": {"code", "message", "hint"}}; a /wait timeout is HTTP 408 and carries the screen it saw. Two response details matter when driving this:
  • settled on /keys, /text, and /resize. The call returns only once every injected key has been consumed and redrawn, so reading /screen straight after is race-free. settled: false means the model was still busy — the input is queued, so re-read rather than assume it was ignored. It does not promise the consequences are done: pressing enter to launch an agent is consumed immediately, while the view switch it kicks off lands later. Use /wait for anything asynchronous.
  • HTTP 503 not_running when the TUI’s event loop is not consuming input — it is still starting, or the user has quit. Bubble Tea silently drops messages in that window, so the API refuses instead of reporting a success for keys that went nowhere. Reads (/status, /screen, /frames) keep working.
esc is not universally “go back”: on the readiness gate it quits (there is no screen behind it), in an idle chat it clears the composer, and mid-turn it cancels the turn. A driver should read view and act on that rather than assume what esc does. Most callers should not speak this API directly — the TUI MCP server wraps it as MCP tools.

TUI Event Trace

--trace writes down what the agent actually did, so a wrong answer can be diagnosed from a file instead of from a screenshot. Every event the TUI receives is appended to a JSONL file — one JSON object per line, in arrival order, with tool calls carrying their arguments and tool results carrying their payloads.
The path is printed on startup (trace → …). An unwritable path is a startup error, not a warning: a trace that is silently not being written looks exactly like an agent that did nothing.
The path must be attached — --trace=run.jsonl, not --trace run.jsonl. A spaced value is read as a command name (the flag is legal with no value, so it never consumes the next argument); the TUI says so and names the working form.
Each line wraps the event with the two things the payload itself does not carry — when it arrived, and where it sits in the run:
A frame the TUI could not parse is still recorded — that is the frame a trace is most wanted for. It carries unparsed (the raw text) instead of event, or unparsed_b64 when the bytes are not valid UTF-8, since encoding those as a JSON string would replace each bad byte with U+FFFD and lose what went wrong. A reader must check all three keys. Events are flushed one at a time, so a run that crashes mid-turn still leaves everything it received. --trace and --dev are independent: --dev chooses what is rendered on screen, --trace chooses what is written to disk. Use both together to watch a run and keep it.
This records the agent’s externally visible behaviour — tool calls, arguments, results, errors, and timings. It does not capture prompt size or token accounting, which exist only in the agent’s own per-turn recorder (GAIA_TURN_LOG).
A trace contains verbatim tool results — file contents, shell output, email, whatever the agent read on your behalf. The file is written 0600 under a 0700 directory, but attaching one to a bug report shares all of it. Read it before you send it.
A launch that fails before the agent says anything (a refused port, an unmet precondition) removes its own empty trace rather than leaving a 0-byte file behind. ~/.gaia/traces is never pruned otherwise — delete old runs yourself, or remove them all with gaia uninstall --purge.

Cache Command

Inspect or clear GAIA’s on-disk caches (document-Q7 metadata, chat history, context7 library docs, etc.). Caches live under ~/.gaia/cache/ (Windows: %LOCALAPPDATA%/gaia/cache/).
Actions: Flags for clear: Examples:

Knowledge Command

Web research via Tavily, with SQLite result caching, a per-session credit budget, and an automatic keyless DuckDuckGo fallback when the mcp-tavily connector isn’t configured. See the Tavily connector.
Actions: Options: Examples:
search automatically degrades to DuckDuckGo when the mcp-tavily connector isn’t configured; extract requires Tavily and raises an actionable error otherwise. Configure the connector with gaia connectors configure mcp-tavily --set TAVILY_API_KEY=tvly-....

Kill Command

Terminate processes running on specific ports.
Options: Examples:
This command will:
  • Find the process ID (PID) bound to the specified port
  • Forcefully terminate that process
  • Report killed, refused, and failed listeners; exit 1 on any refusal or failure. An empty port is a successful no-op (exit 0). Listing and forceful termination commands each have a five-second timeout.

Diagnostics Command

Bundle system info and logs into a tarball for bug reports.
Options: Examples:
The bundle includes:
  • System info snapshot: OS version (uname -a and distro on Linux, sw_vers on macOS, ver on Windows), relevant env vars with secrets redacted, the pid and port of the GAIA daemon, GAIA’s Lemonade and the Agent UI backend with what listens on them, and all TCP listeners (ss -tlnp on Linux, lsof -iTCP -sTCP:LISTEN on macOS, netstat -ano on Windows)
  • ~/.gaia/electron-install-state.json (no chat content)
  • Log files, omitted with --no-logs: ~/.gaia/gaia.log, the Electron logs (electron-install.log, electron-main.log, electron-updater.log), ~/.gaia/logs/*.log, the daemon log (~/.gaia/host/daemon.log), GAIA’s Lemonade log (~/.gaia/lemonade/lemond.log) and agent sidecar logs (~/.gaia/agents/*/logs/*.log), each with its rotated copies (gaia.log.1, …). A log over 50 MB is bundled as its last 50 MB, named <file>.tail.
API keys and tokens in GAIA’s state files (lemonade/state.json, host/instance.json) are never bundled.

Global Options

Most commands support these options. They come from a shared parent parser, so despite the name they are not accepted by gaia agent, gaia cache, gaia config, gaia connectors, gaia daemon, gaia diagnostics, gaia engineering, gaia gateway, gaia hub, gaia init, gaia knowledge, gaia lemonade, gaia memory or gaia skill — passing one there fails with unrecognized arguments.
On commands with a nested subcommand, put these before the subcommand — gaia eval --no-lemonade-check agent, not gaia eval agent --no-lemonade-check — because the nested parsers don’t inherit the shared parent. gaia mcp start is the one exception: it declares them itself, so there they must come after (gaia mcp start --no-lemonade-check).
Backend flags are accepted only by the commands that use them, and any other command rejects them with a usage error (exit code 2):

Troubleshooting

Connection errors mean the model server isn’t up. The daemon owns it, so check there first, then start it:
If it still won’t come up, gaia daemon logs --lines 100 has the reason — most often that Lemonade isn’t installed, which gaia init fixes.
Check available system memory (16GB+ recommended)Verify model compatibility:
Pre-download models:
Install additional models: See Features Guide
List available devices:
Verify microphone permissions in Windows settingsTry different audio device indices if default doesn’t work
For optimal NPU performance:
  • Disable discrete GPUs in Device Manager
  • Ensure NPU drivers are up to date
  • Monitor system resources during execution
For more help, see:

See Also

Voice Interaction

Speech-to-speech conversation

API Server

OpenAI-compatible REST API

MCP Integration

Model Context Protocol

Evaluation Framework

Testing and benchmarking

Agent Memory

Persistent memory across sessions

Agent Skills

Portable SKILL.md capabilities


Developer engineering

gaia engineering --developer-mode manages explicit Claude Code/Codex handoffs — snapshot-only sharing of GAIA context, gated behind --developer-mode and a fresh consent prompt on every share. Normal mode exposes none of this. See the developer engineering guide for the full workflow.

Subcommands

gaia mcp engineering --developer-mode --backend <claude|codex> starts its local stdio bridge with the pairing credential supplied through its generated MCP configuration. Normal mode cannot start this bridge. gaia-tui --developer-mode enables the flagship skill and sharing tools; --dev remains diagnostic output only.