Skip to content

Agents

VibePod manages each agent as a Docker or Podman container. Credentials and config are persisted on your host — at ~/.config/vibepod/agents/<agent>/ for the default profile — and mounted into the container on every run, so you only need to authenticate once. To keep several credential sets per agent (subscription vs. API key vs. Ollama, stored under ~/.config/vibepod/profiles/<name>/), see Credential Profiles.

Supported Agents

Agent Provider Shortcut Image
claude Anthropic vp c vibepod/claude:latest
gemini Google vp g vibepod/gemini:latest
opencode OpenAI vp o vibepod/opencode:latest
devstral (alias: vibe) Mistral vp d vibepod/devstral:latest
auggie Augment Code vp a vibepod/auggie:latest
copilot GitHub vp p vibepod/copilot:latest
codex OpenAI vp x vibepod/codex:latest
pi Earendil vp pi vibepod/pi:latest
agy (Antigravity) Google vp n vibepod/agy:latest
tau Hugging Face vp t vibepod/tau:latest
jcode 1jehuang vp j vibepod/jcode:latest
freebuff CodebuffAI vp fb vibepod/freebuff:latest
qwen Qwen (Alibaba) vp q vibepod/qwen:latest
dsh (DeepSeek Harness) DeepSeek vp ds vibepod/dsh:latest

Alias note: vp run vibe resolves to vp run devstral, vp run qwen-cli resolves to vp run qwen, and vp run deepseek / vp run deepseek-harness resolve to vp run dsh.

DeepSeek Harness (dsh) — Web UI agent

dsh is Web-UI-first: vp run dsh starts the harness's browser UI and prints its URL (default http://127.0.0.1:3080, published loopback-only on the host). The terminal stays attached to the container logs; Ctrl+C stops it. Sessions, profiles, plugins, and credentials persist in the agent config dir (~/.config/vibepod/agents/dsh/, mounted as the container's home).

DeepSeek Harness Web UI served by vp run dsh

Developer preview: upstream warns of compatibility-breaking changes. The default vibepod/dsh image pins an exact harness (npm) version — the :latest docker tag refers to the image build, not the harness inside. VibePod prints a preview notice on every run.

First-run setup (one time): the Web UI needs a workspace and a model before the first session; both persist in the agent config dir.

  1. Open the printed URL and click the workspace path in the top bar (or Choose workspace). Navigate to /workspace — the project mount — via the root breadcrumb or the path editor, and select it. Don't use the new-folder input: /workspace already exists, and the input only accepts a single folder name. Every project is mounted at /workspace, so this registration carries over to all of them.
  2. Configure a model under Settings → Models (see below).

Auth: enter a DeepSeek API key in the Web UI (Settings → Models) — it persists across restarts — or pass it from the host:

vp run dsh -e DEEPSEEK_API_KEY=$DEEPSEEK_API_KEY

Other providers: no DeepSeek account is required. Any OpenAI-compatible endpoint works — in Settings → Models pick the openai provider, set a custom base URL (e.g. a local Ollama server), fill in any non-empty token if the endpoint doesn't check one, and enter the model name manually.

Headless one-shot (uses dsh's headless profile, no server, prints the final answer):

vp task create dsh "summarize this repository"

Changing the host port: override the published port in your config; the container-side port stays 3081 (the image's internal forwarder):

agents:
  dsh:
    ports:
      - "127.0.0.1:3090:3081"

For a one-off run, the -p/--publish flag replaces the configured list instead:

vp run dsh -p 127.0.0.1:3090:3081

A host port of 0 (e.g. 127.0.0.1:0:3081) lets the Docker daemon pick a free port — useful for running dsh in several projects at once; the printed Web UI URL shows the assigned port.

dsh's browser-trust fence expects the canonical authority; on a non-default host port also pass vp run dsh -- --trusted-host 127.0.0.1:3090 (with the actual port when the daemon assigned one).

Customizing: dsh composes profiles from patch layers. Edit ~/.config/vibepod/agents/dsh/.dsh/cordis.patch.yml on the host — changes hot-reload into the running server.

First run & authentication

Start any agent for the first time with vp run <agent>. The container will prompt you to authenticate (browser OAuth, API key entry, or device flow depending on the provider). Once authenticated, credentials are written to the persisted config directory and reused on subsequent runs.

Auto-pulling the latest image

VibePod automatically pulls the latest image for an agent before every run and shows pull progress while layers download. This ensures you always start with the most up-to-date container without manual intervention.

To disable auto-pull globally:

auto_pull: false

Or for a specific agent only:

agents:
  devstral:
    auto_pull: false

Per-agent auto_pull takes precedence over the global setting. For example, you can disable it globally but keep it on for a specific agent:

auto_pull: false          # skip pull by default
agents:
  claude:
    auto_pull: true       # except claude — always pull

You can also force a one-off pull via the CLI flag regardless of config:

vp run claude --pull

The resolution order is: --pull flag > per-agent auto_pull > global auto_pull.

Overriding the image

You can point VibePod at a custom image via an environment variable:

VP_IMAGE_CLAUDE=myorg/my-claude:dev vp run claude

Or permanently via your global config:

agents:
  claude:
    image: myorg/my-claude:dev

Image customization workflows

VibePod has a fixed set of supported agent IDs (claude, gemini, opencode, devstral, auggie, copilot, codex, pi, agy, tau, jcode, freebuff, qwen, dsh). The CLI also supports the aliases vibe (→ devstral), qwen-cli (→ qwen), and deepseek / deepseek-harness (→ dsh). Image customization means changing the image used for one of those IDs.

1. Extend an existing image for an agent

Example: add tools to the default Claude image.

  1. Create a Dockerfile that extends the current base image.
# Dockerfile.claude
FROM vibepod/claude:latest

# Add project-specific utilities.
RUN apt-get update \
  && apt-get install -y --no-install-recommends ripgrep jq \
  && rm -rf /var/lib/apt/lists/*
  1. Build and tag the derived image.
docker build -f Dockerfile.claude -t myorg/claude-container:with-tools .
  1. Run with the new image (one-off) or set it in config (persistent).
# one-off
VP_IMAGE_CLAUDE=myorg/claude-container:with-tools vp run claude
# ~/.config/vibepod/config.yaml (or .vibepod/config.yaml)
agents:
  claude:
    image: myorg/claude-container:with-tools

2. Add a new image for an agent

Example: point opencode at a newly published internal image.

  1. Build/publish your image to a registry (for example registry.example.com/team/opencode:2026-03-01).
  2. Attach that image to the target agent in config.
agents:
  opencode:
    image: registry.example.com/team/opencode:2026-03-01
  1. Start the agent.
vp run opencode

You can also test quickly without editing config:

VP_IMAGE_OPENCODE=registry.example.com/team/opencode:2026-03-01 vp run opencode

Passing environment variables

Use -e / --env to inject variables at runtime:

vp run claude -e MY_VAR=value -e ANOTHER=123

Persistent per-agent env vars can also be set in config:

agents:
  claude:
    env:
      MY_VAR: value

Passing arguments to the agent

Any extra arguments after the agent name are appended to the agent command inside the container:

vp run <agent> <agent-args>

Use -- before agent flags so VibePod does not parse them as its own options:

vp run <agent> -- <agent-flag> <value>

For concrete syntax, check the agent's own CLI help. For example, Claude and Codex both accept model flags, but their exact flag names and values differ.

Init scripts before startup

Use agents.<agent>.init to run shell commands in the container before the agent launches. This is useful for installing extra tools in a custom image workflow.

agents:
  codex:
    init:
      - apt-get update
      - apt-get install -y ripgrep jq

The init commands run on every vp run for that agent and must be idempotent.

IKWID mode (--ikwid)

Use --ikwid to enable each agent's built-in auto-approval / permission-skip mode when supported.

Agent --ikwid appended args
claude --dangerously-skip-permissions
gemini --approval-mode=yolo
devstral --auto-approve
copilot --yolo
codex --dangerously-bypass-approvals-and-sandbox
pi --approve
agy --dangerously-skip-permissions
opencode Not supported
auggie Not supported
tau Not supported
jcode Not supported
freebuff Not supported
qwen --approval-mode=yolo
dsh Not supported

Example:

vp run codex --ikwid

Pasting images (--paste-images)

Agents that accept pasted screenshots need access to your clipboard, which lives on the host X server. Use --paste-images to forward it:

vp run claude --paste-images

VibePod mounts the X11 socket (/tmp/.X11-unix), passes your DISPLAY, and prepares an X authority cookie for the container. Host cookies are bound to the host's hostname, which the container does not share, so VibePod rewrites the cookie to the wildcard address family and mounts it read-only at /tmp/.vibepod-xauth. Without that rewrite the X server answers with "Authorization required, but no authorization protocol specified".

Requirements and caveats:

  • DISPLAY must be set; otherwise VibePod warns and starts without forwarding.
  • The xauth binary must be available on the host, otherwise the cookie cannot be prepared and clipboard access may be rejected.
  • X11 only (including XWayland). There is no Wayland-native or macOS clipboard forwarding.
  • Forwarding the X socket lets the container talk to your X server; only use it with agents you trust.

Detached mode

Use -d / --detach to start an agent container in the background without attaching your terminal. The agent process starts immediately inside the container — -d only controls whether VibePod attaches your terminal to it.

Basic usage

vp run claude -d
# ✓ Started vibepod-claude-a1b2c3d4

The command prints the container name and returns immediately. You can also find it later with:

vp list --running

Interacting with a detached container

The agent is already running inside the container. You can exec into it to inspect state, install extra tools, or interact with the agent alongside its running process:

docker exec -it vibepod-claude-a1b2c3d4 bash

Use the container name printed by vp run -d or shown in vp list.

Tip

If you need to run setup commands before the agent launches, use agents.<agent>.init or extend the base image instead — these run inside the container before the agent process starts.

Managing detached containers

Check running agents:

vp list              # shows running + configured agents
vp list --running    # shows only running agents
vp list --json       # machine-readable output

When the current project has overlays, vp list adds a Project Overlays table naming the overlay image each agent resolves to and whether it is already built.

Stop a specific agent, a single container, or all agents:

vp stop claude                       # stop every container for the `claude` agent
vp stop vibepod-claude-a1b2c3d4      # stop one specific container (from `vp list`)
vp stop claude -f                    # force stop immediately
vp stop --all                        # stop every VibePod container

The argument is resolved as an agent name/shortcut first; anything else is looked up as a container name or ID. Only VibePod-managed containers can be stopped this way.

Caveats

  • auto_remove (default: true) — By default, containers are automatically removed when they stop. This means you cannot restart a stopped detached container; you need to vp run again. Set auto_remove: false in your configuration if you want stopped containers to persist.
  • Session logging — Sessions started with --detach are not recorded in the VibePod session log since VibePod does not capture the interactive I/O. If you need session logging, run without --detach.

Task mode (headless)

Run an agent non-interactively as a background task. Claude calls this "headless mode"; codex and auggie expose equivalent flags. VibePod wraps them in a uniform vp task command.

vp task create claude "Summarize the README"
# ✓ Task started: 9f1e8a20b4c14e2f89d7f6a1c3e42b99
#   container: vibepod-claude-abcdef12
#   follow:    vp task logs 9f1e8a20b4c1 --follow

The command starts a detached container, writes a row to ~/.config/vibepod/tasks.db, and returns a task id. The container is not auto-removed, so you can inspect logs and exit status after it finishes.

Task mode applies a finite timeout by default: 2 hours. Override it per task with --timeout 30m, --timeout 4h, or another s/m/h duration. Use --timeout none only when you explicitly want an unlimited background task.

Supported agents (v1)

Agent Invocation inside container
claude claude -p "<prompt>"
codex codex exec "<prompt>"
auggie auggie --print "<prompt>"
tau tau -p "<prompt>"
jcode jcode run "<prompt>"
qwen qwen -p "<prompt>"
dsh dsh --profile headless "<prompt>"

Other agents error with a clear message; support can be added by setting headless_prefix (or headless_command for agents whose one-shot invocation differs from their interactive command) on their AgentSpec.

Managing tasks

vp task list                     # recent tasks + container status
vp task list --agent claude      # filter
vp task list --json              # machine-readable

vp task logs <id>                # dump captured stdout/stderr
vp task logs <id> --follow       # stream

vp task status <id>              # state, exit code, timestamps
vp task cancel <id>              # gracefully stop a running task, keeping logs
vp task rm <id>                  # remove task + its (stopped) container
vp task rm <id> -f               # kill running container before removing
vp task rm --all                 # remove all stopped/finished tasks
vp task rm --all -f              # kill running task containers and remove all tasks

Task ids can be abbreviated to any unique prefix (e.g., the first 12 chars shown by vp task list).

Passing agent flags

Anything after -- is forwarded to the agent's command after the prompt, matching the CLI's documented form (claude -p "..." --allowedTools ...):

vp task create claude "review staged changes" -- --output-format json
vp task create --timeout 30m claude "run the fast audit"
vp task create --timeout none claude "wait for the external job"

Auto-approval for automation

Headless tasks typically need to run without permission prompts. Pass --ikwid to apply the agent's documented auto-approve flag (e.g., --dangerously-skip-permissions for Claude):

vp task create claude "fix the failing test in auth.py" --ikwid

Codex task authentication

codex exec reuses saved Codex login state from the mounted config directory when available. For automation and smoke tests, pass CODEX_API_KEY explicitly:

vp task create -e CODEX_API_KEY="$OPENAI_API_KEY" --ikwid codex \
  "Print exactly TASK_OK_CODEX and exit."

For compatibility, Codex task mode also maps an explicitly supplied OPENAI_API_KEY to CODEX_API_KEY when CODEX_API_KEY is not already set.

Caveats

  • No --resume in v1. Task mode starts a fresh session every time. Use the agent's own resume flag via passthrough (-- --resume <session_id> for Claude) if you need continuity.
  • LLM config model flag is skipped in task mode. llm.base_url / llm.api_key / llm.model are still injected as env vars, but the --model-style CLI flag is not appended — different agents place it differently relative to subcommands. Pass an explicit model via --.
  • Default timeout: 2 hours. On timeout, VibePod gracefully stops the container and records the task as failed. Pass --timeout none to opt out for one task.
  • Task containers are not auto-removed. Run vp task rm <id> when you're done inspecting one task, or vp task rm --all -f to clean up every recorded task and its container.

Reattaching a terminal

Closing the terminal window that runs vp run does not stop the container — the agent keeps running in the background under Docker. This is by design: the container's lifecycle is tied to Docker, not to your shell. Use it as a feature when you want to keep a long-running session alive across terminal restarts.

To rejoin a running container:

vp list --running       # find the container name
vp attach <container>   # reattach your terminal

If exactly one managed container is running you can omit the name:

vp attach

vp attach only works for containers that are already running and managed by VibePod. When you are done, close the terminal to leave it running, or stop it explicitly with vp stop <container>, vp stop <agent>, or vp stop --all.

Resume hints after a session ends

Several agents print their own resume instruction when they exit — Claude, for example:

Resume this session with:
claude --resume 39bf1a93-ea1f-4b0a-a894-0a662e5a1d4e

When VibePod recognizes such a hint in the session output (supported for Claude, Codex, Pi, Copilot, Jcode, and Freebuff), it prints the equivalent VibePod command after the agent exits:

Resume this session with:
  vp run claude -- --resume 39bf1a93-ea1f-4b0a-a894-0a662e5a1d4e

The suggested command wraps the agent's own resume arguments in passthrough form (after --), so it works because each agent's session state lives in the persisted config mount. If no hint is recognized, exiting behaves as before and nothing extra is printed.

Connecting to a Docker Compose network

When your workspace contains a docker-compose.yml or compose.yml, VibePod detects it and offers to connect the agent container to an existing network so it can reach your running services.

You can also specify the network explicitly:

vp run claude --network my-compose-network

Individual agents

Claude (Anthropic)

vp run claude   # or: vp c

Credentials are stored in ~/.config/vibepod/agents/claude/. On first run, Claude's interactive setup will guide you through API key configuration.

Claude Code has a known upstream bug where OAuth access tokens (~8 h TTL) are not automatically refreshed from disk, forcing users to run /login roughly once per day. See Why this workaround exists below for the full bug history and links.

VibePod works around this by storing a ~1-year long-lived token on the host and injecting it as CLAUDE_CODE_OAUTH_TOKEN on every run. This sidesteps the refresh path entirely.

This is an official authentication method

claude setup-token and the CLAUDE_CODE_OAUTH_TOKEN environment variable are both documented by Anthropic as a supported authentication path for CI pipelines, scripts, and other environments where an interactive browser login isn't available. See the official Claude Code authentication docs and the claude-code-action setup guide. VibePod just automates the storage and injection.

One-time setup:

vp run claude setup-token

This starts the container with claude setup-token, which opens Anthropic's OAuth flow in your browser. After you authorise, the container prints a token. VibePod then prompts you to paste it and saves it to:

~/.config/vibepod/agents/claude/oauth-token   (mode 0600)

Subsequent runs:

vp run claude

VibePod detects the stored token and injects CLAUDE_CODE_OAUTH_TOKEN automatically. Look for Using stored Claude OAuth token in the startup output to confirm.

Precedence (first match wins):

  1. -e ANTHROPIC_API_KEY=... or -e CLAUDE_CODE_OAUTH_TOKEN=... passed on the CLI
  2. ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN set in your per-agent env: config
  3. Stored oauth-token file
  4. Interactive OAuth via .credentials.json (subject to the refresh bug)

Verifying the token is stored:

vp doctor claude

Shows credentials state, stored-token presence and mtime, and which auth mode the next run will use. You can also inspect the file directly:

ls -l ~/.config/vibepod/agents/claude/oauth-token
# or to view contents (treat as a secret — do not share):
nano ~/.config/vibepod/agents/claude/oauth-token

Verifying the token works:

vp run claude -p "say ok"

-p runs Claude Code in headless mode — one API call, one response. If you see "ok", the token is valid.

Caveats:

  • The long-lived token is inference-only — it cannot establish Remote Control sessions (steering a container from claude.ai/code or the mobile app).
  • claude setup-token requires a Pro, Max, Team, or Enterprise plan. Console (pay-per-token) accounts should use ANTHROPIC_API_KEY instead.
  • The token rotates roughly once a year. When it expires, just run vp run claude setup-token again.

Using an API key instead

If you're on a Console (pay-per-token) account, set ANTHROPIC_API_KEY and skip the setup-token flow entirely:

vp run claude -e ANTHROPIC_API_KEY=sk-ant-...

Or permanently in config:

agents:
  claude:
    env:
      ANTHROPIC_API_KEY: sk-ant-...

Diagnostics

vp doctor claude is the first tool to reach for when auth misbehaves. It reports:

  • .credentials.json — file owner/mode, expiresAt, presence of refreshToken, scopes, subscription type
  • .claude.json — mtime cross-check
  • Stored long-lived token state
  • Which host env vars (ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CONFIG_DIR) are set
  • Effective auth mode — what the next vp run claude will actually use

Exit codes: 0 healthy, 1 config dir missing, 2 OAuth token expired (useful in scripts).

Why this workaround exists

The root cause is in Claude Code itself, not in VibePod. The OAuth refreshToken is stored in .credentials.json but never used: the access token is loaded from disk, sent as-is until it 401s, and nothing is written back when a refresh would have succeeded. The bug affects native Linux, WSL, macOS, and every container-based deployment equally.

Community forensics (#33995 comment):

Set expiresAt in ~/.claude/.credentials.json to Date.now() to force expiry. Send a message — Claude processes it successfully, meaning the in-memory token refresh worked. Check ~/.claude/.credentials.json afterward — file was never written. Conclusion: refreshOAuthToken succeeds and returns new tokens, but the credential store's update() is never called (or silently fails) after a successful refresh. The new token lives only in memory. Next session launch reads the stale expired token from disk and requires re-login.

The community-validated workaround (#24317 comment) is exactly what VibePod implements:

I worked around this using claude setup-token and then feeding it in as the CLAUDE_CODE_OAUTH_TOKEN environment variable. It skips all the "OAuth tokens invalidating each other", but has the downside that it doesn't allow /usage.

claude setup-token itself is an officially supported Claude Code authentication path, documented for exactly this kind of non-interactive deployment. See Anthropic's authentication guide and the claude-code-action setup guide — the same mechanism used by Anthropic's own GitHub Action.

Upstream tracking issues — core bug (access-token not refreshed from disk):

# Status Summary
#50743 open · has repro · area:auth Newest and cleanest repro on headless Linux — refreshToken ignored
#42904 closed as duplicate Canonical "daily re-login required for subscription users" report
#40985 open · stale "Auth tokens expire too frequently" — confirms ~8 h TTL
#33995 closed not-planned Best technical forensics (quoted above); proves write-back is the broken step
#21765 closed not-planned First clear statement: "Claude Code doesn't use refresh tokens to get new access tokens"
#12447 open OAuth expiry disrupts autonomous workflows; refresh token handling needed
#37402 open --print / automation mode also affected

Multi-session race condition (why a shared .credentials.json across simultaneous sessions makes things worse):

# Status Summary
#24317 open · has repro · 18 comments Canonical thread; documents refresh-token rotation and single-use semantics
#48786 closed as dup of #24317 Independent reproduction
#27933 closed Early race-condition report
#45129 closed as dup Agent worktree subprocesses hit this constantly

Container / headless specifically:

# Status Summary
#22066 closed as duplicate OAuth authentication not persisting in Docker
#34917 closed OAuth "Redirect URI not supported" in headless/Docker
#34141 closed Claude Code ignores ANTHROPIC_API_KEY when OAuth redirect fails in devcontainers
#7100 closed not-planned Request for official headless-auth documentation
#22992 open Feature request: RFC 8628 device-code flow for headless

Proxy / Cloudflare interaction (relevant if you run vibepod behind the built-in mitmproxy):

# Status Summary
#47754 open · area:auth · platform:linux Cloudflare WAF blocks OAuth token refresh from headless Linux servers
#33269 open Cloudflare challenge race during auth login / setup-token

Anthropic's posture: most reports are auto-closed as duplicates by a bot; the core issues (#21765, #33995) were closed as "not planned." A changelog line for Claude Code v2.1.44 mentioned "Fixed auth refresh errors" but users report the same behaviour on every later version (v2.1.62, 2.1.74, 2.1.116 observed). No committed fix has landed as of this writing.

Gemini (Google)

vp run gemini   # or: vp g

OpenCode (OpenAI)

vp run opencode   # or: vp o

Credentials and settings are persisted to ~/.config/vibepod/agents/opencode/ on the host and mounted at /config inside the container. In addition, the XDG data and config directories (/root/.local/share/opencode and /root/.config/opencode) are bind-mounted from the host so that authentication state is preserved across container restarts.

Devstral / Vibe (Mistral)

vp run devstral   # or: vp d
vp run vibe       # alias of devstral

Note

Devstral runs under your host user (uid:gid) and requires the linux/amd64 platform. On Apple Silicon, Docker's Rosetta emulation is used automatically.

Auggie (Augment Code)

vp run auggie   # or: vp a

Copilot (GitHub)

vp run copilot   # or: vp p

Codex (OpenAI)

vp run codex   # or: vp x

For browser OAuth login, run:

vp run codex login

Codex binds its OAuth callback server to 127.0.0.1:1455 inside the container. During codex login, VibePod publishes the expected host callback port and enables a small container forwarder so the host browser can complete the redirect back to Codex.

Device-code and API-key flows do not need the browser callback, so they skip the forwarder:

vp run codex login --device-auth
vp run codex login --with-api-key

Pi (Earendil)

vp run pi   # or: vp pi

Pi runs the Pi coding agent from Earendil in the same isolated VibePod container workflow. Credentials and configuration are persisted under ~/.config/vibepod/agents/pi/.

Pi has a per-project trust/approval system: before loading project-local extensions and settings, it asks you to trust the project once. Inside VibePod:

  • Interactive: run /trust to save the decision. It is written to PI_CODING_AGENT_DIR (/config/.pi/agent/trust.json), which lives inside the persisted config mount, so the trust survives container restarts.
  • --ikwid: appends --approve, trusting project-local files for that run (see IKWID mode).
  • Non-interactive modes (-p, --mode json, --mode rpc) show no prompt and fall back to defaultProjectTrust in /config/.pi/agent/settings.json (ask | always | never). Pre-seed this file if you need unattended runs to trust projects automatically.

Local models (Ollama, LM Studio, Lemonade, llama.cpp)

Pi supports custom OpenAI-compatible providers through a models.json file in its agent directory. Inside the container that directory is PI_CODING_AGENT_DIR (/config/.pi/agent/), which is part of the persisted config mount — so you create the file on the host and it is picked up on every run:

vp config path
# Global:  ~/.config/vibepod/config.yaml

The agent directory lives next to that config file:

~/.config/vibepod/agents/pi/.pi/agent/models.json

Point each provider's baseUrl at host.docker.internal so the container can reach the server running on your host (VibePod maps this hostname on Linux, macOS, and Windows automatically):

{
  "providers": {
    "ollama": {
      "baseUrl": "http://host.docker.internal:11434/v1",
      "api": "openai-completions",
      "apiKey": "ollama",
      "models": [
        { "id": "qwen3.6:35b" },
        { "id": "gemma4:12b" }
      ]
    },
    "lmstudio": {
      "baseUrl": "http://host.docker.internal:1234/v1",
      "api": "openai-completions",
      "apiKey": "lmstudio",
      "models": [
        { "id": "poolside/laguna-s-2.1" }
      ]
    },
    "lemonade": {
      "baseUrl": "http://host.docker.internal:13305/v1",
      "api": "openai-completions",
      "apiKey": "lemonade",
      "models": [
        { "id": "Devstral-Small-2507-GGUF:latest" }
      ]
    }
  }
}

The api / baseUrl / models fields are documented in Pi's models and llama.cpp guides. The apiKey value is arbitrary for local servers that don't check it. Each model id must match an identifier your server actually exposes (GET /v1/models lists them) — the ids above are examples, replace them with the models you have installed. Start Pi as usual (vp run pi) and pick the model with the /model command — providers from models.json appear alongside the built-in ones, and the file is re-read every time you open /model, so you can edit it mid-session.

Note that adding models.json does not change which model Pi starts with: the startup model comes from the defaultModel setting in /config/.pi/agent/settings.json (on the host: ~/.config/vibepod/agents/pi/.pi/agent/settings.json). Select the local model via /model, or set defaultModel explicitly, so an existing configuration does not silently keep sending requests to a remote metered provider.

Linux: expose the server to Docker

On Linux, host.docker.internal resolves to the Docker bridge gateway (typically 172.17.0.1), so a server bound to 127.0.0.1 — the default for Ollama, LM Studio, and Lemonade — refuses connections from the container. Bind it to the bridge gateway IP (preferred when only VibePod needs access) or to 0.0.0.0, e.g.:

OLLAMA_HOST=0.0.0.0 ollama serve
LEMONADE_HOST=0.0.0.0 lemonade-server serve

In LM Studio, enable Serve on Local Network in the server settings, and turn on Require Authentication there (then use the generated API token as the provider's apiKey in models.json). Ollama and Lemonade have no built-in authentication, so when binding those to 0.0.0.0 restrict access with firewall rules instead. For systemd-managed services, set the variable via sudo systemctl edit <service> and restart. See Using OSS models for the full Ollama systemd walkthrough and the security implications of binding to 0.0.0.0.

On macOS and Windows (Docker Desktop), host.docker.internal reaches the host's loopback interface directly, so the default 127.0.0.1 binding works without changes.

Agy / Antigravity (Google)

vp run agy   # or: vp n

Agy runs Google's Antigravity CLI in the same isolated VibePod container workflow. Credentials and configuration are persisted under ~/.config/vibepod/agents/agy/ and mounted at /home/agy inside the container.

Use --ikwid to append Agy's permission-skip flag:

vp run agy --ikwid

Tau (Hugging Face)

vp run tau   # or: vp t

Tau is Hugging Face's minimalist, Pi-inspired terminal coding agent, written in Python. It is the first non-Node agent in the VibePod matrix. Credentials and configuration are persisted under ~/.config/vibepod/agents/tau/, mounted at /config inside the container, where Tau finds them as ~/.tau/:

Path in container Contents
/config/.tau/credentials.json Provider credentials written by /login (mode 0600)
/config/.tau/providers.json Default provider, per-provider settings, scoped models
/config/.tau/catalog.toml Your own providers and models, overlaid on Tau's built-in catalog
/config/.tau/sessions/ Durable JSONL session history (resume and branching)

Authentication. Start Tau and run the /login slash command:

/login              # pick a provider interactively
/login openai
/login openai-codex # OpenAI Codex subscription auth
/model              # choose a model

Tau supports OpenAI, Anthropic, the Codex subscription, OpenRouter, Hugging Face, and any custom OpenAI-compatible endpoint. Saved credentials take precedence over environment variables and survive container restarts because they live in the persisted mount.

API keys can also be injected instead of using /login:

agents:
  tau:
    env:
      OPENAI_API_KEY: sk-...
      HF_TOKEN: hf_...

Or per run: vp run tau -e OPENAI_API_KEY=sk-....

Custom models. Drop a catalog.toml into ~/.config/vibepod/agents/tau/.tau/catalog.toml on the host to add providers and models; it is overlaid on Tau's bundled catalog (scalar fields replace, models merge with your entries first). Tau deliberately ignores project-level .tau/catalog.toml, so a repository cannot redirect your model traffic.

Proxy and TLS. Tau uses httpx, not Node. VibePod's proxy env applies as usual: HTTP_PROXY/HTTPS_PROXY route traffic through vibepod-proxy, and SSL_CERT_FILE points at the mounted mitmproxy CA, which httpx honors for its verification context. Streaming responses pass through unchanged.

Non-interactive mode. Tau's print mode works with both vp run and task mode:

vp run tau -p "explain this repo"
vp task create tau "Summarize the README"

Skills. Tau scans ~/.agents/skills/, so skills installed via vp skills are mounted at /config/.agents/skills/<id> and picked up automatically.

Tau has no auto-approval flag, so --ikwid is not supported for it.

Jcode (1jehuang)

vp run jcode   # or: vp j

Jcode is a resource-focused terminal coding agent written in Rust, distributed as a single static binary. It supports Anthropic, OpenAI (including the Codex subscription), Gemini, GitHub Copilot, OpenRouter, Azure, Ollama, LM Studio, and custom OpenAI-compatible endpoints. Credentials and configuration are persisted under ~/.config/vibepod/agents/jcode/, mounted at /config inside the container, where Jcode finds them as ~/.jcode/ and ~/.config/jcode/:

Path in container Contents
/config/.jcode/config.toml Main configuration (display, providers, timeouts)
/config/.jcode/auth.json Claude/account credentials written by jcode login (OpenAI tokens go to openai-auth.json)
/config/.jcode/mcp.json Global MCP server configuration
/config/.jcode/sessions/ Session history (resume with jcode --resume <name>)
/config/.config/jcode/ Provider env files for custom endpoints (API keys)

Authentication. Run jcode login inside the container:

vp run jcode -- login --provider claude                # Anthropic OAuth / subscription
vp run jcode -- login --provider anthropic-api         # Anthropic API key
vp run jcode -- login --provider openai --no-browser   # headless: prints the auth URL

OAuth-style providers support --no-browser (alias --headless) so the auth URL is printed instead of a browser being launched — open the URL on the host, then paste the callback URL (or code) back into the container prompt, since the container's localhost callback listener is not reachable from the host browser. Saved credentials survive container restarts because they live in the persisted mount.

API keys can also be injected instead of jcode login:

agents:
  jcode:
    env:
      ANTHROPIC_API_KEY: sk-ant-...
      OPENAI_API_KEY: sk-...

Or per run: vp run jcode -e ANTHROPIC_API_KEY=sk-ant-....

Proxy and TLS. Jcode's HTTP client (reqwest with rustls) honors HTTP_PROXY/HTTPS_PROXY and loads extra CA certificates from SSL_CERT_FILE, both of which VibePod sets when the proxy is enabled, so traffic routes through vibepod-proxy with the mounted mitmproxy CA.

Non-interactive mode. Jcode's run subcommand works with both vp run and task mode:

vp run jcode run "explain this repo"
vp task create jcode "Summarize the README"

Skills. Jcode scans ~/.agents/skills/, so skills installed via vp skills are mounted at /config/.agents/skills/<id> and picked up automatically.

VibePod sets JCODE_NO_AUTO_UPDATE=1 so the pinned binary in the image is never self-updated at runtime. Jcode has no auto-approval flag (its safety system asks for permission in-session), so --ikwid is not supported for it.

Freebuff (CodebuffAI)

vp run freebuff   # or: vp fb

Freebuff is a free AI coding agent built on the Codebuff platform. Its npm package is a thin Node launcher that downloads a compiled glibc binary on first run, so the vibepod/freebuff image is glibc-based (Debian Node), not Alpine. Credentials and configuration are persisted under ~/.config/vibepod/agents/freebuff/, mounted at /freebuff inside the container, where the image symlinks ~/.config/manicode to /freebuff:

Path in container Contents
/freebuff/settings.json Freebuff settings
/freebuff/analytics-id.json Analytics ID
/freebuff/projects/<cwd>/chats/<ts>/log.jsonl Chat history

Authentication. Freebuff authenticates through its own interactive login flow:

vp run freebuff login

On first run without a saved session, Freebuff prints "Not authenticated" and prompts you to press ENTER to log in. Saved credentials survive container restarts because they live in the persisted mount.

Resuming a session. Pass --continue (optionally with a conversation id) through the CLI:

vp run freebuff -- --continue
vp run freebuff -- --continue <conversation-id>

Use -- so VibePod does not parse the agent's own flags.

Headless / task mode. Freebuff has no non-interactive print mode, so vp task create freebuff ... is not supported.

Skills. Freebuff scans ~/.agents/skills/, so skills installed via vp skills are mounted at /config/.agents/skills/<id> and picked up automatically.

IKWID mode. Freebuff is a managed TUI with no auto-approval or permission-skip flag, so --ikwid is not supported for it.

Qwen Code (Qwen)

vp run qwen   # or: vp q
vp run qwen-cli   # alias of qwen

Qwen Code is Alibaba's open-source terminal coding agent (npm @qwen-code/qwen-code, binary qwen, Node 22+). It is Claude-Code-inspired and supports OpenAI-, Anthropic- and Gemini-compatible endpoints, Qwen models, and any custom provider. Credentials and configuration are persisted under ~/.config/vibepod/agents/qwen/, mounted at /qwen inside the container, where the image symlinks ~/.qwen to /qwen:

Path in container Contents
/qwen/settings.json Settings, model providers, auth selection
/qwen/.env Env-file overrides (API keys, base URLs)
/qwen/skills/ Personal skills (SKILL.md folders)
/qwen/ Memory, session history, commands

Authentication. Run Qwen Code and use its /auth command to connect an Alibaba Cloud Coding Plan, a third-party API key, or a custom endpoint:

vp run qwen
# then inside the session: /auth

Saved credentials survive container restarts because they live in the persisted mount. API keys can also be injected instead of /auth:

agents:
  qwen:
    env:
      OPENAI_API_KEY: sk-...
      OPENAI_BASE_URL: https://dashscope.aliyuncs.com/compatible-mode/v1
      OPENAI_MODEL: qwen3-coder-plus

Or per run: vp run qwen -e OPENAI_API_KEY=sk-.... Qwen Code honors the standard OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL (alias QWEN_MODEL), ANTHROPIC_* and GEMINI_API_KEY env vars for its provider protocols.

Non-interactive mode. Qwen Code's headless mode works with both vp run and task mode:

vp run qwen -p "explain this repo"
vp task create qwen "Summarize the README"

Skills. Qwen Code scans personal ~/.qwen/skills/ (symlinked to /qwen/skills/), so skills installed via vp skills are mounted at /qwen/skills/<id> and picked up automatically. Project-level skills in <workspace>/.qwen/skills/ work without extra setup because the workspace is already mounted.

IKWID mode. Qwen Code auto-approves all tool calls in YOLO mode, which --ikwid enables via --approval-mode=yolo:

vp run qwen --ikwid
vp task create qwen "fix the failing test" --ikwid