Agent skill

Integrate Gaia

by amd in amd/gaia

A skill your agent uses when integrating the @amd-gaia/gaia npm package — running GAIA's flagship agent, or embedding its local sidecar into a Node, TypeScript, or Electron app.

MITAuto-check: warningsAI & LLM Engineering

Install Integrate Gaia

The automated check flagged lines worth reading first. See the safety section below.

skills CLI
$ npx skills add amd/gaia --skill integrate-gaia -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install amd/gaia integrate-gaia --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/amd/gaia.git skills-src && mkdir -p .claude/skills && cp -r skills-src/hub/agents/gaia/npm .claude/skills/integrate-gaia && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
integrate-gaia
GitHub stars
1.6k
Token cost
~9.7k tokens
SKILL.md length
4,923 words
Files
28
Skills in repo
44
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when integrating the @amd-gaia/gaia npm package — running GAIA's flagship agent, or embedding its local sidecar into a Node, TypeScript, or Electron app.

  • Works in 12 steps: Install → What npx @amd-gaia/gaia actually does → The integrity gate — it will stop you,… → …
  • Integrating the @amd-gaia/gaia npm package — running GAIAs flagship agent
  • SKILL.md covers 1. Install, 2. What npx @amd-gaia/gaia…, 3. The integrity gate — it… and 4. Platform coverage — the…, plus 13 more sections
  • Runs TypeScript scripts from its folder; calls npx, curl and npm; reaches amd-gaia.ai; needs GAIA_GAIA_SIDECAR_TOKEN

What it does

Integrate Gaia is an agent skill from amd/gaia. Use when integrating the @amd-gaia/gaia npm package — running GAIA's flagship agent, or embedding its local sidecar into a Node, TypeScript, or Electron app. Covers the one-command path, the SHA-256 integrity gate, platform coverage, starting the sidecar, the /v1/gaia/query SSE contract, and the gotchas that will bite you.

Its SKILL.md is about 9.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 29 other files (for example `CHANGELOG.md`, `README.md` and `SCORECARD.md`).

It sits in AI & LLM Engineering, covering Embeddings. It works with npm, Electron and TypeScript. The repository describes itself as: Build AI agents for your PC. The licence is MIT.

When your agent uses it

  • Integrating the @amd-gaia/gaia npm package — running GAIAs flagship agent
  • Embedding its local sidecar into a Node

Example prompts

  • “/integrate-gaia”

Requirements

  • Node.js
  • A credential in GAIA_GAIA_SIDECAR_TOKEN

Workflow steps

12 steps, taken from the step headings in SKILL.md.

  1. Install
  2. What npx @amd-gaia/gaia actually does
  3. The integrity gate — it will stop you, by design
  4. Platform coverage — the sidecar has two gaps
  5. Prerequisite — a local Lemonade server
  6. Start the sidecar
  7. Call POST /v1/gaia/query
  8. Over /v1/gaia/query, a gated tool asks — when you can answer
  9. File-access scope
  10. Skills — one always-on, the rest opt-in
  11. The project map — two things it costs you
  12. Ports

What it can do on your machine

Read from SKILL.md and the folder at commit 05fb50b. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships script files (TypeScript, from the files we listed), which the agent can run.

    Shell commands in SKILL.md call:

    • npx
    • curl
    • npm

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • amd-gaia.ai

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • GAIA_GAIA_SIDECAR_TOKEN

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Integrate Gaia loads about 9.7k tokens when it runs. Until then it costs about 85 tokens; SKILL.md has 4,923 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~85
When it runs · the whole SKILL.md, loaded when a task matches
~9.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check: warnings

The automated check found patterns that need a careful read before installing.

  • NoteMentions a .env fileSKILL.md:238
    _NO_DOTENV=1` stops the sidecar loading `.env` files.
  • WarningMentions a credentials file (SSH keys, cloud or package-manager tokens)SKILL.md:455
    it.** Reads refuse secrets — `.env`, `id_rsa`, `credentials.json`, `.netrc`,
  • WarningMentions a credentials file (SSH keys, cloud or package-manager tokens)SKILL.md:456
    `.pem`/`.key`, and everything under `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.kube` —

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from amd/gaia at commit 05fb50b, republished under its MIT licence (© amd). 4,923 words, ~9,656 tokens.

Download SKILL.mdSave it as .claude/skills/integrate-gaia/SKILL.md (or your agent's skills folder). This skill also uses 27 other files; get the full folder from GitHub.
name
integrate-gaia
description
Use when integrating the @amd-gaia/gaia npm package — running GAIA's flagship agent, or embedding its local sidecar into a Node, TypeScript, or Electron app. Covers the one-command path, the SHA-256 integrity gate, platform coverage, starting the sidecar, the /v1/gaia/query SSE contract, and the gotchas that will bite you.

Integrating @amd-gaia/gaia

@amd-gaia/gaia delivers two binaries and owns their process lifecycle: the frozen agent sidecar (gaia-agent) and the Go terminal UI (gaia-tui). It ships no agent logic of its own, and it builds neither binary at install time — both are published artifacts it downloads and verifies. The terminal UI is the published terminal-hub component, the same binary a full GAIA install runs as gaia tui, so its behaviour cannot differ from that one. Everything runs on the local machine against a local model server — nothing you type or index leaves it.

Two ways in:

  • npx @amd-gaia/gaia — fetch, verify, launch the terminal UI. What a human runs.
  • The programmatic exports — fetch, spawn the sidecar, drive POST /v1/gaia/query yourself. What you use when embedding GAIA in an app.

This file is NOT one of the agent's own skills. It is the integration playbook: how you wire this package into an app. The agent separately loads Agent Skills into its own prompt at runtime from gaia_agent/skills/<name>/SKILL.md. Same filename, different artifact — don't ship this one as an agent skill. See Skills.

1. Install

bash
npx @amd-gaia/gaia          # no install step; fetches what it needs on first run
npm install @amd-gaia/gaia  # when you want the programmatic exports

The package is ESM-only ("type": "module") and needs Node 18+ for the built-in fetch. Use import, not require; from CommonJS use await import("@amd-gaia/gaia").

@amd-gaia/gaia publishes with this release — it is not on npm yet. Until the release tag lands, npx @amd-gaia/gaia will not resolve. Run the agent from a source checkout in the meantime (see the guide).

2. What npx @amd-gaia/gaia actually does

  1. Resolves the host platform key (`${process.platform}-${process.arch}`).
  2. Reads binaries.lock.json, the checksum manifest published with this exact package version. Each component records its own hub lane, version, artifact name and hash — they do not share a base URL.
  3. Downloads both binaries, each from its own lane, and SHA-256 verifies each against the lock.
  4. Installs the sidecar into ~/.gaia/agents/gaia/ and the TUI into ~/.gaia/npm-cache/gaia-<version>/.
  5. Writes ~/.gaia/agents/gaia/.installed — the record the daemon and the TUI both read to decide the sidecar is installed. Written on a cache hit too, so an install staged by an earlier release repairs itself.
  6. Execs the TUI, whose exit code becomes ours.

run deliberately does not spawn a sidecar. The TUI reaches agents through the GAIA daemon's relay and never holds a sidecar token, and the daemon is what spawns and supervises the sidecar — from exactly the directory step 4 wrote to. A second sidecar started here would only fight the daemon's own for port 8141. Use gaia serve when you want to own the process.

Other commands: gaia fetch (download + verify, print JSON, exit), gaia serve (sidecar alone), gaia version (per-component version, source URL, and platform matrix). Anything after a bare -- goes to the TUI verbatim.

Where each binary comes from
ComponentHub laneArtifact names
sidecaragents/gaia/<agentVersion>/gaia-agent-<platformKey>[.exe]
tuiagents/terminal-hub/<componentVersion>/gaia-<goPlatform>[.exe]

Two things follow from that, and both bite if you assume otherwise:

  • The two components version independently. lock.agentVersion is this package's version; components.tui.componentVersion is the terminal-hub release it consumes. They will not match.
  • The TUI's artifact names use win-x64 / win-arm64, not win32-*. Platform keys stay in Node's namespace (process.platform says win32); only the filename crosses over. Never build a TUI URL by interpolating a platform key — read filename from the lock entry.

3. The integrity gate — it will stop you, by design

The SHA-256 check is this package's security boundary, and there is no flag, env var, or option that relaxes it.

  • Bytes are hashed in memory and compared before anything is written to the cache path. A mismatch raises IntegrityError naming expected vs actual and leaves nothing on disk.
  • A placeholder hash blocks the fetch before any network call — between releases every sha256 in the lock is PENDING-replace-with-real-sha256, and a value that is all zeros or contains PENDING (case-insensitive) is treated as a placeholder. You get a PlatformError, not a download.
  • A cache hit re-hashes the on-disk file. A cached binary whose bytes drifted is re-downloaded, not reused.

If you need to run against a locally built binary, build it and point startSidecar / runTui at it directly. The fetcher will not be talked into it.

4. Platform coverage — the sidecar has two gaps

terminal-hub publishes the TUI for all six targets. The sidecar is a PyInstaller freeze built on the platform it targets, and there is no arm64 Linux and no arm64 Windows sidecar build.

Platform keySidecarTUI
win32-x64yesyes
darwin-arm64yesyes
darwin-x64yesyes
linux-x64yesyes
linux-arm64noyes
win32-arm64noyes

Resolving the sidecar on those two keys raises PlatformError naming the platform and the supported set. It is not silently skipped, and the TUI is never launched with no agent behind it. npx @amd-gaia/gaia version prints the matrix for the version you have.

5. Prerequisite — a local Lemonade server

The agent thinks with a model hosted by Lemonade Server, which this package does not install. Required before any query succeeds:

  1. Lemonade 10.2.0 or newer, running. GAIA's daemon starts and supervises one, so gaia daemon start is normally all that is needed (gaia init also installs and starts it on first run). Qwen3.8 Flash Next needs v2026.39.1 or newer, the version gaia init installs; older servers cannot load it.
  2. The machine's default chat model downloaded — run gaia init. It picks Qwen3.6-35B-A3B-GGUF where a GPU holds it (~27 GB for models, weights plus context cache — a 64 GB+ Strix Halo or a 32 GB GPU) and Gemma-4-E4B-it-GGUF everywhere else, CPU-only PCs included, and records the pick as default_model in ~/.gaia/config.json. On 128 GB-class PCs the user may switch to the multimodal user.Qwen3.8-Flash-Next-GGUF with gaia config set default_model. model.id below names whichever this machine uses. gaia download takes no model argument — naming one makes it exit 2. To pull a single model instead, repeat the command GET /v1/gaia/init gives you: it names the Lemonade client this machine actually has. Do not invent one.

Do not guess — ask the sidecar. GET /v1/gaia/init is a read-only preflight (it never pulls or loads) that probes Lemonade, compares its version to the floor, and checks the model is present:

bash
curl -s http://127.0.0.1:8141/v1/gaia/init

It answers 200 when ready and 503 when not, with the same body shape either way — so branch on .ready and render .hint, never on the status code alone:

jsonc
{
  "ready": false,
  "lemonade": { "reachable": false, "base_url": "…", "version": null,
                "min_version": "10.2.0", "compatible": null },
  "model":    { "id": "Gemma-4-E4B-it-GGUF", "present": false,
                "loadable": null, "ctx_size": null },
  // The manual fallback ("Otherwise: …") is resolved for the host's Lemonade
  // install (tray app, macOS app, systemd service, or CLI) — render it verbatim.
  "hint": "Local Lemonade Server is not reachable at …. GAIA starts it automatically — run `gaia daemon start` if the background service is not running. Otherwise: Start the Lemonade app from Applications, then retry. Or set LEMONADE_BASE_URL to a running server. See https://amd-gaia.ai/docs/guides/gaia."
}

lemonade.compatible: null is indeterminate, not a pass — the version could not be parsed. Render it as unknown.

GET /health is liveness only. A green /health means the REST surface is up; it says nothing about whether a query will work.

6. Start the sidecar

ts
import { fetchAll, startSidecar, shutdown } from "@amd-gaia/gaia";

// Fetch + SHA-256 verify both binaries. Build step, not per request.
const { sidecar, tui } = await fetchAll();

// Spawn -> poll /health -> check the contract apiVersion, in one call.
const proc = await startSidecar({ binaryPath: sidecar.binaryPath });  // port 8141

// ... drive proc.baseUrl ...

await shutdown(proc);   // tree-kill; auto-cleanup also reaps on exit
  • fetchAll(opts?) returns { sidecar, tui, lock }. Each result carries binaryPath, platformKey, sha256, cached, url. For one component use fetchBinary({ component: "sidecar" | "tui", outDir }).

  • startSidecar throws if the binary can't start, never becomes healthy (HealthTimeoutError, 60 s default — a cold one-file build unpacks first), or reports an apiVersion whose major differs from this package's (VersionMismatchError). On any failure it shuts the sidecar down before rethrowing, so a failed start never leaks a process.

  • Tree-kill is not optional. The frozen sidecar spawns a child uvicorn process that child.kill() on the parent does not reap, which leaves port 8141 bound. shutdown kills the group (POSIX SIGTERM to -pid, escalating to SIGKILL; Windows taskkill /T /F). autoCleanup (default true) also reaps on exit, SIGINT/SIGTERM/SIGHUP, uncaughtException, and unhandledRejection. A SIGKILL of your process is the one case nothing in-process can catch.

  • Mint a caller token, or you are running the sidecar unauthenticated. The sidecar requires Authorization: Bearer <token> on every /v1/gaia/* request, and skips the check only when neither token env var is set — dev mode, which is what startSidecar gives you, because this package mints nothing. Loopback binding is not the boundary: this agent has shell and file tools. Mint your own and pass it through the env option (StartOptions extends SpawnOptions, so it merges over process.env):

    ts
    import { randomBytes } from "node:crypto";
    const token = randomBytes(32).toString("hex");
    const proc = await startSidecar({
      binaryPath: sidecar.binaryPath,
      env: { GAIA_GAIA_SIDECAR_TOKEN: token },   // or ..._TOKEN_FILE, a 0600 path
    });

    Then send authorization: "Bearer " + token on every /v1/gaia/* call. /health, /version, and /v1/gaia/version are exempt, so startSidecar's own health-and-version handshake works either way. If you did not spawn the sidecar — you are talking to one the GAIA daemon started — the token is the daemon's, delivered to the sidecar as GAIA_GAIA_SIDECAR_TOKEN_FILE (a 0600 file path); read it from there, don't invent one. A 401 whose detail names both env vars means you sent the wrong token or none.

    The programs the agent starts (shell commands, MCP servers, CLI installs and sign-ins, native hub agents, media tools) never inherit this token or GAIA's other internal credentials. To withhold your own variables from them too, list the names in GAIA_CHILD_ENV_DENY (comma or space separated); GAIA_NO_DOTENV=1 stops the sidecar loading .env files.

Or skip the code entirely and let the CLI own it:

bash
npx @amd-gaia/gaia serve --port 8141
curl http://127.0.0.1:8141/health

7. Call POST /v1/gaia/query

This is the whole agent surface. There is no typed query client in this package — call it with plain fetch. Contract version 2.15; the stream is text/event-stream terminated by exactly one final or error.

Request body (extra: "forbid" — an unknown field is a 422, not ignored):

FieldRequiredNotes
queryyesNon-empty.
run_idyesYou mint it, and it must be a UUID (non-UUID → 422). It is the cancel handle, valid from the instant the request is sent.
contextyesTranscript slice, pushed in the body — may be [], never absent. Each item { role, content }; role ∈ user / assistant / system / tool.
session_idnoContract ≥ 2.12. Pass it. The agent persists its indexed-document set per session — without it, it forgets a document between the turn that indexed it and the next question. 1–128 characters from A-Z a-z 0-9 . _ - (a UUID works); anything else is a 400.
can_answer_questionsnoSet false for one-shot / batch runs so the agent resolves ambiguity itself instead of parking on a question nobody can see.
modelnoOverrides the model id. On a retained session_id a different model is switched in place (contract ≥ 2.14), keeping the conversation and any loaded skills; a switch that fails is a 409 and leaves the session on its previous model.
providerno"lemonade" (the default backend) or "claude", which sends the conversation to Anthropic's API instead of the local server. Anything else is a 400. When you name it, model must agree: under "claude" it must be a claude-* id, under "lemonade" it must not be. A mismatch is a 400, on a new session or an existing one. Omit it and the model decides instead — a claude-* id alone reaches Anthropic, any other id runs locally — so there is nothing to mismatch. On a retained session_id, naming a different provider switches the session to it in place, onto model or, without one, the provider's default model; omitting both leaves the session where it is.
max_stepsno≥ 1.
ts
import { randomUUID } from "node:crypto";

const runId = randomUUID();
const res = await fetch(`${proc.baseUrl}/v1/gaia/query`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    accept: "text/event-stream",
    authorization: `Bearer ${token}`,   // required unless the sidecar is in dev mode — §6
  },
  body: JSON.stringify({
    query: "Summarize the PDFs in ~/Documents/reports",
    run_id: runId,
    context: [],
    session_id: "s1",
    can_answer_questions: false,
  }),
});

const reader = res.body!.getReader();
const dec = new TextDecoder();
let buf = "";
outer: for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += dec.decode(value, { stream: true });
  let i: number;
  while ((i = buf.indexOf("\n\n")) >= 0) {
    const frame = buf.slice(0, i);
    buf = buf.slice(i + 2);
    const line = frame.split("\n").find((l) => l.startsWith("data: "));
    if (!line) continue;                  // ":" frames are keepalive comments
    const ev = JSON.parse(line.slice(6));
    switch (ev.type) {
      case "status":      console.log(ev.message); break;
      case "token":       process.stdout.write(ev.delta); break;
      case "tool_call":   console.log(`→ ${ev.tool}`, ev.args); break;
      case "tool_result": console.log(`← ${ev.tool}`, ev.data); break;
      case "needs_confirmation": break;   // see §8 — a refusal follows
      case "needs_input": /* answer it — see below */ break;
      case "final":       console.log(ev.answer); break outer;   // terminal
      case "error":       console.error(ev.detail); break outer; // terminal
      default:            console.warn("unsupported event", ev); // future additive type
    }
  }
}

The canonical event shapes, as emitted:

EventShape
status{ type, message, phase?, words?, chars? } — progress and reasoning narration. phase (loading_model, downloading_model, reading, reasoning, tool_call) names what the model is doing; show it as the live line, not as narration
token{ type, delta } — answer text to append
tool_call{ type, tool, args }
tool_result{ type, tool, data, render? }
needs_confirmation{ type, run_id, action, summary, confirm_id?, always_scope? } — answer it, or it ends the run; see §8
needs_input{ type, run_id, request_id, question, options[], allow_free_text, sensitive, respond_url, timeout_seconds? }
final{ type, answer, usage? } — terminal
error{ type, detail, status } — terminal, surface detail verbatim

Rules a client must respect:

  • An idle run emits : keepalive SSE comments every 10 s. Skip lines that aren't data: and reset your read-idle timer on them — a long tool call is not a dead stream.
  • Never treat stream close without a terminal event as success. The server guarantees one; a close without one means something broke on your side.
  • Answer needs_input, don't restart. The run is parked on the same stream. POST /v1/gaia/query/{run_id}/respond with { request_id, response } and keep reading the existing stream — a fresh /query POST abandons the paused run. Unknown run → 404; a request_id that is no longer pending → 409 (both loud, never a silent drop). Render each option's description, and mask the input when sensitive is set.
  • Cancel with POST /v1/gaia/query/{run_id}/cancel. It returns { run_id, cancelled } — an unknown id reports cancelled: false with a 200, not a 404, because a cancel racing a normal completion is expected. Dropping the HTTP connection also cancels the run.
  • Add to a running turn with POST /v1/gaia/query/{run_id}/followup (contract ≥ 2.15, body { text }). The run is not interrupted and no second turn starts: the agent folds the text in at its next step boundary and answers it alongside what it was already doing. Unknown run → 404, an agent that cannot take one → 409; both are loud, so hold the message rather than telling the user it was sent. /query is stateless, so put a delivered follow-up into the next turn's context yourself, between that turn's question and its answer.

8. Over /v1/gaia/query, a gated tool asks — when you can answer

Read this before you design a workflow around it. This section is about the HTTP surface — the agent's other transport can collect an approval; see SPEC §5.5.

Twelve of the agent's 95 tools mutate the machine and need explicit approval before they run. Nine sit in the base TOOLS_REQUIRING_CONFIRMATION set — write_file, edit_file, run_shell_command, wait_for_condition, which re-runs a shell command until it succeeds, execute_python_file, run_python, notify_desktop, which spawns a PowerShell child on Windows to draw the notification, and install_cli / sign_in_cli, which install software and sign a CLI in to the user's account — and the flagship adds three of its own (CONFIRMATION_REQUIRED_TOOLS): install_skill, capture_skill, and remove_skill, because installing or capturing a skill writes third-party content under ~/.gaia/skills and removing one deletes it. A capture that does land is additionally code-inert: its instructions load, but any tools.py/scripts stay unregistered until a human runs gaia skill promote <name> in a terminal. Everything else — reading, indexing, querying, web fetching, memory, and the read-only check_cli_setup — runs without asking.

The eight live-browser tools are gated by a hook, not a set, so a name alone does not tell you whether a call will ask. browser_login always asks. browser_click / browser_type ask when the call acts inside a session the run signed in to, or when the target element's label reads irreversible, and run unasked on the open web. The hook fails closed: if the browser cannot be asked what state it is in, the call is treated as authenticated and prompts. Design a workflow around the approval path, not around a fixed tool list.

Contract ≥ 2.14 can answer one. Send a session_id and leave can_answer_questions unset (or true). The stream emits needs_confirmation carrying a confirm_id and then stays open while the agent waits:

data: {"type":"needs_confirmation","run_id":"…","action":"run_shell_command","summary":"python -m pytest -q tests/","confirm_id":"…","always_scope":"pytest","risk":"execute"}

Answer it on the same run, then keep reading the stream:

POST /v1/gaia/query/{run_id}/tool_decision
{ "decision": "allow" | "deny" | "always" | "timeout", "confirm_id": "…" }

summary is the command or call itself, shown once — your UI supplies the question. risk says what the call does (read, write, execute, destructive); label by it rather than by tool name, since run_shell_command is pytest on one call and rm -rf on the next. always grants the family named in always_scope for the rest of the session (pytest covers every spelling of a test run) — say so in your UI, because it stops asking; no always_scope means don't offer it. If your prompt expires unanswered, send timeout, not deny: it still refuses, but the agent tells the user the request timed out instead of claiming they said no. Unknown run → 404; a prompt that is no longer pending → 409; any other decision → 422. All loud, never a silent drop. Send confirm_id: without it a late answer resolves whichever prompt replaced the one it was typed against.

To stop being asked for a whole session:

POST /v1/gaia/sessions/{session_id}/bypass
{ "enabled": true }

It applies to the very next gated tool, including one in a turn already running, and an unknown session is a 404 rather than a new one. A malformed session_id is a 400, same as on /query.

That bypass stops the prompts; it does not open the shell. The stdio transport's --bypass-permissions does both — it runs gated tools unasked and lifts the shell tool's guardrails (redirection and the other shell-only operators, the read-only binary allowlist, the rate limit). That second half is arbitrary code execution, appropriate for one local parent on a private pipe and not for a bound socket, so it stays on stdio: HTTP sessions never lift the shell gates, and the request body rejects unknown fields so a client cannot ask. See SPEC §5.5.

A run nobody can answer is still refused. With can_answer_questions: false, or with no session_id, the server emits needs_confirmation, follows it immediately with a terminal final saying it stopped before running the action, and cancels the run:

data: {"type":"final","answer":"I stopped before running 'write_file' because it needs your explicit approval, and this streaming surface cannot collect that yet. …"}

That is deny-by-default, not an oversight: parking a batch run on a prompt nobody will ever see reads as a hang. So a one-shot integration cannot run those nine tools — pass a session_id and answer, or perform the mutation from your own code and let the agent do the reading and reasoning.

9. File-access scope

The agent's file, document, and data tools are confined to a set of allowed paths, and the default is the user's home directory. That is the honest scope for a personal document agent, and it is still a real boundary — system directories, program files, and other users' homes are refused, with the check run against the resolved path so a symlink out of scope doesn't slip through.

Being in scope is not the same as being safe, and two denylists apply inside it. Reads refuse secrets — .env, id_rsa, credentials.json, .netrc, .pem/.key, and everything under ~/.ssh, ~/.aws, ~/.gnupg, ~/.kube — even in an allowed directory. Writes additionally refuse anything that executes on its own: shell startup files, PowerShell profiles, ~/.config/autostart/*, systemd user units, LaunchAgents, and a repo's .git/ (hooks and config). Both come back as a structured error naming the file and the reason, so do not plan an integration around reading a credential file or editing a shell rc — perform those from your own code.

The agent also gets its own scratch directory for throwaway scripts and intermediate files, so they stay out of the user's project. It is created per agent under the system temp dir, deleted when the agent closes, and is the only part of the temp dir the agent may use.

In 0.1.1 narrowing it is a construction-time setting only. The packaged sidecar exposes no flag or env var for allowed_paths (its CLI accepts only --host and --port), so restricting the scope means embedding GaiaAgent in your own Python process:

python
from gaia_agent.agent import GaiaAgent, GaiaAgentConfig

agent = GaiaAgent(config=GaiaAgentConfig(allowed_paths=["/home/me/Documents"]))
Show full SKILL.md (1,894 more words)Show less

10. Skills — one always-on, the rest opt-in

The agent is built to host Agent Skills (short playbooks loaded into its own prompt, grouped into named sets, one set active per launch), and its bundled skill directory is the highest-precedence discovery root.

Loaded skills are not all resident every turn: each turn the agent embeds the query against every loaded skill's description and renders only the matching bodies in full — the rest collapse to a one-line menu entry, and the model (or the user) re-activates one by calling load_skill on it again. GAIA_DYNAMIC_SKILLS=0 disables the per-turn selection (every loaded body renders every turn); GAIA_DYNAMIC_SKILLS_TAU=<float> overrides the match threshold. Manifest skills: entries are always-on and never collapse, and a turn with only always-on skills loaded embeds nothing. If the embedder is unavailable, selection disables itself for the session and every body renders — capability is never silently lost to a failed match.

One skill ships enabled: gaia-voice. It is a manifest skills: entry, so it is always on, always rendered in full, and paid on every LLM call of every turn — budget for it. It is not a task recipe but the agent's honesty floor: do not claim work you did not do, do not present empty output as a result, do not substitute a near-miss and report success. Those failures corrupt an answer whatever the task is, which is why it cannot live in an opt-in bundle. It declares no tools, and its body measures 790 tokens (tiktoken cl100k).

document-extract ships bundled but not enabled. gaia-voice routes a request for every item in a document to it, and it drives extract_document_items and save_extracted_items so a long transcript yields a complete, source-quoted inventory rather than a summary. A skill turns that inventorying on only by listing extract_document_items in tools_required; wording such as "find every call site" in another skill does not.

No skill set loads. gaia-agent.yaml ships its skill_sets: and default_skill_set: blocks commented out — following the email agent's precedent, because loading several skill bodies into every prompt costs tokens and no eval has measured that trade for this agent yet. Re-enabling is uncommenting two blocks; no code change.

So today there is nothing for GAIA_SKILL_SET to select — leave it unset; any value stops the agent at startup (the sidecar exits non-zero before binding its port). Once a release declares sets, GAIA_SKILL_SET is the selection channel for the packaged sidecar (its CLI accepts only --host and --port), and an undeclared name stops startup naming the valid sets rather than falling back to a default. Beyond gaia-voice, do not design around a skill being on by default.

11. The project map — two things it costs you

When the agent's working directory is a code repository, every task starts with a project map in the system prompt: the root, the directory shape, the likely entry points, which commands are installed, and the three platform differences that change command syntax. It exists so the agent stops burning round trips on "no such file" and "command not found".

Two consequences an integrator needs to plan for:

  • Up to 600 prompt tokens, every turn. That is the enforced ceiling (1.8% of the NPU profile's 32K window), not a typical value — budget it alongside gaia-voice's 790.
  • An embedding pass on the first semantic code search. If the repo has no code index, the first search_code_index builds it, then searches. On a large monorepo that one search takes minutes of local embedding; a task that never searches pays nothing. GAIA_PROJECT_MAP_AUTO_INDEX=1 builds it in the background at task start instead.

The sidecar's CLI accepts only --host and --port, so pointing the map at a specific project means GAIA_PROJECT_ROOT=/path/to/repo in its environment, or GaiaAgentConfig(project_root=...) when embedding. A directory that is neither a VCS checkout nor holds a recognised manifest gets no map — that is the designed answer, not a failure.

The same root check also decides whether the shell rides along: when it resolves to a repository, run_shell_command is offered on every turn instead of only when semantic selection guesses the request sounds like a shell request. No repository, no change.

12. Ports

ServicePort
Agent sidecar8141 on 127.0.0.1
GAIA daemonassigned at start, recorded in ~/.gaia/host/instance.json

Port 4001 is reserved repo-wide: spawnSidecar throws a RangeError and gaia serve --port 4001 exits 2. Both services bind loopback only — this agent speaks for the user's documents and memory and has no business on a LAN interface.

13. Running in a server or long-lived app

  • fetchAll / fetchBinary are a build step, not per request — network plus a full SHA-256 hash of a large artifact. Run once at install time.
  • resolveSidecarPath / resolveTuiPath are startup, not per request. They re-hash the binary against binaries.lock.json before handing back a path that gets spawned, so they cost a full read of a large file. Resolve once and keep the path. { verify: false } skips the check for a binary you built yourself.
  • Spawn once at boot and hold the Sidecar handle for the process lifetime. Never per request.
  • Low concurrency. One local Lemonade model slot, so parallel queries serialize. Cap inflight runs.
  • The package does not restart a crashed sidecar. It reaps one; supervision is the daemon's job (or yours).
  • DEBUG=gaia puts download, spawn, and sidecar output on stderr. stdout belongs to the TUI once exec'd, and to machine-readable JSON for fetch / version — never write diagnostics there.

Every failure throws a typed error extending GaiaError, so instanceof GaiaError catches any of ours: IntegrityError, PlatformError, HealthTimeoutError, VersionMismatchError, BinaryNotFoundError, HttpError. There is no silent null.

Gotchas — read before debugging

  • /health green ≠ ready. It never touches the model server. Use GET /v1/gaia/init and branch on .ready; it returns 503 with a full body and a hint, not an empty error.
  • A terminal error whose detail starts "Local Lemonade Server is not reachable" means Lemonade isn't running or isn't reachable — not a bug in this package. Start it, or set LEMONADE_BASE_URL.
  • needs_confirmation is followed by a refusal and the run ends. See §8. The nine gated tools are unreachable over /query — the agent itself can run them on a transport that can prompt (SPEC §5.5).
  • A placeholder hash in binaries.lock.json blocks the fetch before any network call. Between releases that is the expected state — it is not a broken install, and there is no override.
  • No linux-arm64 / win32-arm64 sidecar. The TUI has both. A PlatformError on those hosts is the design, not a missing artifact.
  • A 401 from /v1/gaia/* is the caller-auth token, not a bug. The sidecar requires Authorization: Bearer <token>; /health, /version, and /v1/gaia/version are exempt, which is why a green health check sits happily in front of a 401 on /query. It skips the check only in dev mode — neither token env var set — which is what spawnSidecar and gaia serve produce, because this package mints nothing. Do not treat loopback binding as the boundary; mint a token and pass it (§6).
  • run_id must be a UUID, and unknown fields in the request body are a 422 — the model forbids extras. Typos don't get ignored.
  • gaia run needs the Python gaia CLI on PATH — the TUI shells out to it to start the daemon. So the TUI doesn't re-invoke our own npm shim, the child's PATH is rewritten: a directory holding nothing but our shim (an npx temp dir) is dropped, and a shared bin directory is moved to the end instead of removed, so the python3 / lemonade-server / real gaia beside it stay reachable. If the Python CLI isn't installed anywhere, the daemon never comes up. It must also be 0.25.0+: an older core's daemon starts fine but cannot run this agent, which reads as a UI with a dead agent rather than as a version problem.
  • The TUI is installed as gaia-tui, never gaia — the terminal-hub artifact is called gaia-<platform>, and a file named gaia in a cache directory would shadow the npm bin shim. The lock's filename and executable differ for that reason. Don't rename it back.
  • The TUI comes from a lane this package doesn't publish. If a fetch 404s on the TUI but not the sidecar, the pinned terminal-hub version is the thing to check — gaia version prints it and its base URL.
  • ESM-only. require("@amd-gaia/gaia") fails; use import or dynamic import().

Verify the integration

Green path, in order:

bash
npx @amd-gaia/gaia version          # per-component version + source URL + matrix
npx @amd-gaia/gaia fetch            # JSON: one entry per binary with its sha256
npx @amd-gaia/gaia serve --port 8141

Against a lock that still carries PENDING-… hashes, fetch is expected to fail with a PlatformError before any download — that is the gate working, not a broken install. Only a published release has real hashes.

Then, in another terminal:

bash
curl -s http://127.0.0.1:8141/health          # {"status":"ok","service":"gaia-agent-gaia"}
curl -s http://127.0.0.1:8141/version         # {"apiVersion":"2.15","agentVersion":"0.1.1"}
curl -s http://127.0.0.1:8141/v1/gaia/init    # 200 + "ready":true, or 503 + a "hint"
curl -N -X POST http://127.0.0.1:8141/v1/gaia/query \
  -H 'content-type: application/json' \
  -d '{"query":"What can you do?","run_id":"00000000-0000-4000-8000-000000000001","context":[],"can_answer_questions":false}'

A healthy run streams status / token events and ends with one final. If /v1/gaia/init is 503, fix what its hint names and retry — the rest of your integration is fine.

That /query call carries no Authorization header because gaia serve starts the sidecar in dev mode. Against one started with a token, add -H "authorization: Bearer $TOKEN" — a 401 here and a green /health is that and nothing else (§6).

A 503 from /query itself is a different condition: every retained session slot is busy and none is idle enough to evict (SPEC §5.2). Do NOT loop on /v1/gaia/init — it will report ready. Wait for a running turn to finish (or close an idle session) and retry the same /query.

Three more refusals are yours to avoid, each naming its fix in detail (SPEC §5.2 has the reasoning):

  • 409 — the run_id is still in flight. You mint it, so mint a fresh UUID per request; reusing one would leave the earlier run with no way to be cancelled.
  • 409 — the model switch itself failed (a missing Claude credential, an unknown local model). The session stays on its previous model, so this is a failed request rather than a broken session; detail names the reason.
  • 400 — the Host header is absent or empty. The loopback check fails closed, so omitting the header is refused rather than served. Send Host: 127.0.0.1:<port>; every real HTTP client already does.

For the full wire contract, lock schema, exit codes, and timeout table, see SPEC.md. For the user-facing overview, see README.md and https://amd-gaia.ai/docs/guides/gaia.

TUI inference providers

The stdio TUI supports /provider for Local, Fireworks AI, and AMD LLM Gateway. Keys are entered in a masked field and sent directly to local Lemonade's runtime auth API, never as agent queries. /model lists supported discovered cloud and downloaded local models; /model fireworks.gemma-4-31b-it selects Gemma 4 31B IT when available. Cloud chat sends conversation history to the selected provider; embeddings remain on Lemonade. The status event names the actual provider and marks remote inference. This is a TUI/stdio capability, not an HTTP query command.

Developer engineering mode

Available only when GAIA was started with --developer-mode (or GAIA_DEVELOPER_MODE=1). If the engineering tools are missing, say that developer mode is off; don't route around it with shell or file tools. When describing a handoff, claim only what happened:

  • Context was shared only after the user approved that exact snapshot in the prompt. Approval can't be remembered, so each share or append asks again.
  • Opening the coding app never creates or submits a task, and for Codex it doesn't prefill a prompt either. Never say the context was "posted" or "sent" to Codex; give the user the directory and the prompt to paste.
  • Preview and test results come from the coding app. Report them as app-reported, not as checks GAIA ran.
  • Revoking stops future reads; it cannot recall data already delivered.

See the usage guide.

© amd, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 27 other files in hub/agents/gaia/npm of amd/gaia.

  • SKILL.md
  • .gitignore
  • CHANGELOG.md
  • LICENSE
  • README.md
  • SCORECARD.md
  • SPEC.md
  • binaries.lock.json
  • package-lock.json
  • package.json
  • src/cli.ts
  • src/errors.ts
  • src/fetch.ts
  • src/index.ts
  • src/lifecycle.ts
  • src/logger.ts
  • src/platform.ts
  • src/url.ts
  • test/cli.test.ts
  • … and 9 more

Open the folder on GitHubat commit 05fb50b

Compare with similar skills

Integrate Gaia next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Integrate Gaia compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Integrate Gaia this skillamd/gaia1.6k—~9.7kAutomated safety check: WarnMIT
Getting Startedlive-codes/livecodes1.5k—~1.6kAutomated safety check: PassMIT
Golem Add LLM TSgolemcloud/golem1.5k—~1.3kAutomated safety check: PassCustom licence
Duel Agents2aronS/Duel-Agents745—~228Automated safety check: PassMIT
Duel Agents2aronS/Duel-Agents745—~298Automated safety check: NotesMIT
Gemini API Best Practicestakeshy/obsidian-gemini-helper117—~1.1kAutomated safety check: PassMIT

Similar skills

  • Getting Started

    live-codes/livecodes

    Quick start for standalone app at livecodes.io, embedding playgrounds with CDN or npm, and self-hosting basics.

    1.5k GitHub stars~1.6k tokensUpdated today
    AI & LLM EngineeringAuto-check passed
  • Golem Add LLM TS

    golemcloud/golem

    Adding LLM and AI capabilities to a TypeScript Golem agent. An agent skill from golemcloud/golem.

    1.5k GitHub stars~1.3k tokensUpdated today
    AI & LLM EngineeringAuto-check passed
  • Duel Agents

    2aronS/Duel-Agents

    Use Duel Agents model routing in Cursor. An agent skill from 2aronS/Duel-Agents.

    745 GitHub stars~228 tokensUpdated 3 mo ago
    AI & LLM EngineeringAuto-check passed
  • Duel Agents

    2aronS/Duel-Agents

    Configure and use Duel Agents model routing in Claude Code. An agent skill from 2aronS/Duel-Agents.

    745 GitHub stars~298 tokensUpdated 3 mo ago
    AI & LLM EngineeringAuto-check: notes
  • Gemini API Best Practices

    takeshy/obsidian-gemini-helper

    Reviews and corrects Gemini API integration code against Google's published practices, covering SDK choice, safety settings, finishReason checks, tools, streaming and thinking.

    117 GitHub stars~1.1k tokensUpdated yesterday
    AI & LLM EngineeringAuto-check passed
  • Frontmcp Extensibility

    agentfront/frontmcp

    A skill your agent uses when extending FrontMCP beyond the core SDK by integrating external npm packages, libraries, or third-party services into providers and tools.

    146 GitHub stars~3.2k tokensUpdated yesterday
    AI & LLM EngineeringAuto-check passed

More from amd/gaia

All 44 skills in this repo
  • Adds a release eval scorecard to a GAIA hub agent by writing a harness adapter, running a real eval, and wiring the result into the agent's README and release gate.

    1.6k GitHub stars~2.6k tokensUpdated today
    Auto-check passed
  • Walks through releasing a GAIA sidecar agent as a frozen binary plus npm client through the tag-triggered Agent Hub CI pipeline, with a human gate before publishing.

    1.6k GitHub stars~3.6k tokensUpdated today
    Auto-check passed
  • Mines local Claude Code session transcripts with a deterministic Python pipeline to show what the agent is actually used for, how often it fails and what it costs.

    1.6k GitHub stars~2.3k tokensUpdated today
    Auto-check passed
  • Benchmarks AMD's GAIA agent against Claude Code and across models on quality, honesty, steps, tokens, time and real cost, using gaia eval tasks.

    1.6k GitHub stars~1.8k tokensUpdated today
    Auto-check passed
  • Guides safe code changes by finding the right file with grep or semantic search, reading before editing, reproducing bugs first, and proving a fix with a real test run.

    1.6k GitHub stars~2.1k tokensUpdated today
    Auto-check passed
  • Walks through scaffolding, writing and testing a new GAIA agent as a Python class with the SDK, from the base Agent subclass to registered tool methods.

    1.6k GitHub stars~1.5k tokensUpdated today
    Auto-check passed

Questions about Integrate Gaia

What does Integrate Gaia do?

A skill your agent uses when integrating the @amd-gaia/gaia npm package — running GAIA's flagship agent, or embedding its local sidecar into a Node, TypeScript, or Electron app. Integrate Gaia is an agent skill from amd/gaia. Use when integrating the @amd-gaia/gaia npm package — running GAIA's flagship agent, or embedding its local sidecar into a Node, TypeScript, or Electron app.

When should I use Integrate Gaia?

Integrate Gaia fits situations like: integrating the @amd-gaia/gaia npm package — running GAIAs flagship agent; embedding its local sidecar into a Node.

How do I install Integrate Gaia in Claude Code?

Run `npx skills add amd/gaia --skill integrate-gaia -a claude-code`. Or copy the skill folder (hub/agents/gaia/npm in amd/gaia) into .claude/skills/integrate-gaia in your project. Claude Code loads it when a task matches its description.

How do I install Integrate Gaia in Codex?

Run `npx skills add amd/gaia --skill integrate-gaia -a codex`. Or copy the skill folder (hub/agents/gaia/npm in amd/gaia) into .agents/skills/integrate-gaia in your project. Codex loads it when a task matches its description.

Can I use Integrate Gaia in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add amd/gaia --skill integrate-gaia -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/integrate-gaia, .gemini/skills/integrate-gaia, .github/skills/integrate-gaia and .opencode/skills/integrate-gaia in your project.

What does Integrate Gaia need to run?

Going by SKILL.md and its folder, Integrate Gaia needs TypeScript for the scripts in its folder, the command-line tools its instructions call (npx, curl and npm) and credentials named GAIA_GAIA_SIDECAR_TOKEN. Our summary lists: Node.js; A credential in GAIA_GAIA_SIDECAR_TOKEN.

Does Integrate Gaia access the network?

SKILL.md names 1 domain. In commands or code: amd-gaia.ai; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.

Is Integrate Gaia safe to install?

Our automated static check of SKILL.md flagged 2 warning(s): mentions a credentials file (ssh keys, cloud or package-manager tokens). Read the flagged lines before installing; the check is not a guarantee either way.

What licence does Integrate Gaia use?

Integrate Gaia is published under the MIT licence (from the LICENSE file in the skill folder). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Integrate Gaia use?

About 9.7k tokens (SKILL.md is roughly 39k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Integrate Gaia?

Skills that share tags, products or a category with Integrate Gaia: Getting Started (live-codes/livecodes, 1.5k stars), Golem Add LLM TS (golemcloud/golem, 1.5k stars), Duel Agents (2aronS/Duel-Agents, 745 stars) and Duel Agents (2aronS/Duel-Agents, 745 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Integrate Gaia?

amd (a GitHub organization) maintains it in amd/gaia, which has 1,580 GitHub stars. The repository holds 44 skills in this directory. The repository was last updated on October 8, 2026.

Source: amd/gaia on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.