GitHub Docs

The Agent Panel is the core “describe a task, the agent plans and executes it” surface in VibeCoder/VibeAIChat. It owns the agent loop UX — prompt, plan, tool calls, approval gates, partial-completion checkpoints, and parallel-chunk execution via git-worktree isolation.

This page covers the desktop panel. The chat-tab side of agent invocation (per-tab agent loop toggle) is documented in docs/chat-tabs. The cross-cutting agent runtime lives in vibecoder/crates/vibe-ai/src/agent.rs.


What you can do

  1. Type a task. Free-form natural language — “Add a /health endpoint to src/server.ts”, “Refactor the auth module to use the new token store”, etc.
  2. Pick an approval policy:
    • Suggest — every tool call requires explicit user approval.
    • Auto-Edit — file writes auto-approve; bash and other side-effecting tools still need approval.
    • Full Auto — all tools auto-approve. The Turbo toggle is a one-click shortcut to this mode.
  3. Optionally enable Parallel mode — splits the task into independent chunks (up to 4) and runs each on its own git worktree branch, then merges back. Falls back to sequential execution on non-git workspaces.
  4. Press Run (⌘/Ctrl+Enter) — the agent starts streaming.

Approval modes

Mode Wire value Who decides
Ask once per session suggest You, for the first change
Read-only read-only Nobody — writes, shell and sub-agents are blocked
Auto-edit auto-edit File edits run; shell commands ask you
Reviewed reviewed (also review, auto-review) A reviewer, then you for anything it is unsure about
Autonomous full-auto Nobody

auto has always meant full-auto and still does; the reviewed mode deliberately uses a different name so no existing setting changes meaning.

How Reviewed decides

  1. Reads run unreviewed. Reading, listing, searching and fetching are not risky.
  2. A deterministic screen runs first, with no model. Commands that delete the filesystem root or home, format or overwrite a disk, fork-bomb or shut the machine down are refused. Commands that may be legitimate but are your call — sudo, piping a download into a shell, force-pushing, git reset --hard, recursive rm, dropping data — and writes outside the workspace or to credential files (.env, .ssh, .aws, *.pem) ask you. A model is never consulted about these, so it cannot be talked into approving them.
  3. Everything else goes to a reviewer model. It is the provider you picked in the toolbar — no provider is hard-coded. It sees only your task and the proposed action, never the agent’s reasoning or what it has read, because an injected instruction would sit there. It answers approve, deny or escalate.
  4. It fails closed. If the reviewer errors, takes longer than 20 s, gives an answer that cannot be read, or gives two competing answers, the action asks you. With no reviewer configured, the same. It is never treated as an approval.
  5. Some things are never the reviewer’s to waive. A tool your admin policy gates, and a hard-gated connector (Payments), still ask you.

A denied action is reported back to the agent as refused, with the reviewer’s reason, so it can choose another way.

A local decision model reviews first

Ollama publishes small decision models (classification, routing, scoring). When one is installed, Reviewed asks it before the toolbar model: it is fast, free and private. The chain is:

Step Outcome
Deterministic screen Settles the obvious cases; no model is asked.
Local decision model (5 s) approve / deny is final. escalate goes to you.
Toolbar model (20 s) Asked only if the decision model is missing, not running, errors, times out or answers unreadably. The reason shown says so, e.g. [the chat model answered because the local decider … was unavailable (…)].
You If every step failed, or any step was unsure.

A denial is never appealed to the next model. If the decision model is the same model as the toolbar one it is asked once.

Setting: the ProfileStore global key review_model — unset or auto uses the first installed of nimble-v3, nimble, strands-decider (tev1 is excluded: it has only Ollama’s decision capability and the server rejects chat for it); off skips it; a model name (nimble:9b) uses exactly that model, and if it is not installed the toolbar model reviews alone (no substitute). Nothing needs configuring: with no decision model installed, behaviour is unchanged.

Not verified: how well any of these models judge real actions or whether they follow the JSON answer format. If one does not, its answers are unreadable and the toolbar model takes over, which is safe but slower. Models that need a typed state schema (kev, clef) are not used yet.

Where it works:

Surface How
VibeCoder Enter during an agent run (steer_agent_task).
Daemon POST /agent/{session_id}/steer with {"text": "..."}: 200 delivered, 400 empty, 409 no run in progress. A delivered message is also written to the session log as a user event, so stream clients show it and a replay keeps it.
Agent SDK agent.steer(text, sessionId?) returns {delivered: true} or {delivered: false, reason: 'no-run' \| 'empty'}; other failures throw.
VS Code Command “VibeCLI: Message Running Agent”, for a run started with “Run Agent Task”.
JetBrains While a run streams, the Run button becomes “↩ Send” and the input messages the run.
Flutter ApiClient.steerAgent. No screen calls it yet: the app has no live agent view.
VibeCLI Type a line and press Enter while an agent run streams (Unix terminals only). The approval prompt reads through the same reader, so a y answers the prompt and is not sent to the agent.

Where it does not apply: VibeDesk and VibeAIChat start every run with mode: "chat", a single reply with no steps, so there is no step to steer into; the daemon answers 409 for them. The Apple Watch and Wear OS apps do not start /agent runs. VibeCLI on Windows is not steerable from the keyboard (the reader polls a Unix file descriptor).

Verified: the screen, the prompt, the answer parsing, the fail-closed paths and the loop’s handling of every verdict are tested. Not verified: how well any real model judges actions — the tests use scripted reviewers — and the approval prompt does not yet show you why the reviewer escalated.

Messaging an agent while it works

Type and press Enter while the agent is running and the message goes into the run: the agent reads it at the start of its next step, once, framed as “the user sent this while you were working”, and carries on. It does not start a second run and it does not wait for the run to finish.

  • Only while an agent run is active in this tab. In a plain chat turn the message is queued and sent when the answer ends (see chat queueing); with nothing running it is an ordinary send.
  • Not kept for later. If the run ended between your keypress and the send, the message is queued and runs next. The backend refuses a steer when no run is active rather than holding it for some later, unrelated run.
  • Text only. Attachments cannot be steered; the composer says so.
  • Read at the next step, not mid-step. A long model call or tool call finishes first. A message that arrives during the run’s final step is never read.
  • Stop discards it. Stopping a run closes the channel; anything not yet read goes with it.

Backend: AgentLoop::with_steering reads the channel, SteeringSlot (vibe-ai/src/agent_steer.rs) connects it to the host, and VibeCoder exposes it as the steer_agent_task command. Where it works: VibeCoder’s agent, and the daemon over HTTP: POST /agent/{session_id}/steer with {"text": "..."} (authenticated like every /agent route) returns 200 when delivered, 400 for an empty message, and 409 when that session has no run in progress. Where it does not yet: no client calls the daemon route — VibeDesk, mobile, watch, VS Code and the SDK each need a button and an API-client method.

Verified: the loop delivers a message sent during a tool call at the next step, in order and exactly once; the slot refuses sends with no run, with an empty message, and after the run ends; the chat steers during an agent run, queues during a plain chat turn, and falls back to the queue when the run has just ended. The daemon route is covered by four HTTP tests (401, 409, 200 with the loop receiving the trimmed text, 400); the registry, the SDK, the JetBrains and Flutter outcome mapping and the CLI keyboard reader each have their own tests. Not verified: the event written to the session log on delivery (no test reaches the job manager), the VS Code command (the extension has no test runner), the JetBrains panel and the CLI’s interleaving of typed text with streamed output in a real terminal, the live app, a real daemon run steered end to end, and a real model acting on a steered instruction.

Streaming UI

While the agent is running, the panel shows:

  • Step feed (role=log) — one card per tool call with the summary line, expandable output (collapses past 600 chars), and a copy button. Auto-scrolls.
  • Streaming card with a TTFT metric (milliseconds to first token) and a live tokens-per-second counter — useful for diagnosing slow providers or local model setups.
  • Approval prompt (role=alertdialog) when a tool needs explicit consent. Non-destructive approvals auto-focus the Approve button so keyboard users can hit Enter; destructive approvals require an explicit pointer click.

Status states

State UI signal
idle Run button enabled, empty state visible
running Streaming card + step feed populated; Stop button visible
complete Reset button visible
partial Resume button visible (resumes from last checkpoint) + Reset
error Retry button (re-runs the task from the start; the step feed stays on screen for reference) + Reset

Status changes are announced via a screen-reader-only aria-live="polite" region — AT users hear “Agent is running” / “Agent task complete” without visual cues.


Parallel mode + worktree isolation

When Parallel is on, the daemon splits the task plan into up to 4 independent chunks. Each chunk runs in its own ephemeral git worktree on a temporary branch; on completion, the branches are squash-merged back into the source branch. This is the only way to avoid clobbering the working tree when multiple agents are editing the same workspace concurrently.

The panel shows the active isolation mode as soon as the daemon decides:

Badge Meaning
🛡 Worktree Isolated Each chunk has its own branch. Conflicts surface as merge errors per chunk.
📋 Sequential Mode Non-git workspace — chunks run one at a time on the same tree.

If neither shows, the daemon hasn’t reported yet (very early in startup).


Resume from checkpoint

When the agent partially completes a multi-step plan and exits early (timeout, max-steps reached, or stop-by-user), it writes a checkpoint to <workspace>/.vibe/agent-runs/<task-id>.json. The Resume button reads the most recent checkpoint and continues from where it left off — completed steps are preserved, only the remaining plan steps are re-issued.

Retry (after error) is similar but re-runs the original task without consulting a checkpoint — useful when the failure was transient (network blip, rate limit) rather than a partial-progress situation.


/health declaration

features.agent_panel:

{
  "available": true,
  "transport": "tauri-desktop",
  "requires": "providers.configured_count > 0",
  "approval_policies": ["suggest", "auto-edit", "full-auto"],
  "parallel_isolation": "git-worktree"
}

available is false if no providers are configured — the panel still renders but shows “Select an AI provider in the header first.” Cross-client gating reads available; clients that want to know which approval policies the daemon honors read the array (the daemon validates approval_policy strings against this list).


Observability

Backend operations emit structured tracing events under vibecody::agent:

RUST_LOG=vibecody::agent=info vibecli serve

Events:

INFO vibecody::agent: agent.task.start
  provider=ollama approval_policy=auto-edit task_len=124 tab_id=(global)

WARN vibecody::agent: agent.task.provider_unavailable provider=anthropic
INFO vibecody::agent: agent.approval.respond approved=true tool=write_file
INFO vibecody::agent: agent.task.stop was_running=true

Task contents are NEVER logged — only the length, provider, and approval policy. Tool arguments and outputs are never logged. Operator dashboards aggregate these to spot abuse patterns (long-running tasks, rejection-heavy users) without seeing user prompts.


Accessibility

  • Status announcements via sr-only aria-live="polite" region (e.g., “Agent is running” → “Agent task complete”).
  • Step feed uses role="log" aria-live="polite" — AT users hear each step incrementally.
  • Turbo and Parallel toggles expose aria-pressed reflecting state, plus verbose aria-label (“Turbo Mode on — full-auto approvals enabled”).
  • Approval prompt is role="alertdialog" aria-live="assertive" with aria-labelledby / aria-describedby wiring — AT users get the same urgency signal as a sighted user seeing the bordered card.
  • Auto-focus on Approve button (non-destructive only) — destructive actions force an explicit pointer click to defend against muscle-memory Enters.
  • All status badges (worktree isolation, error, completion) are static, visible, and color-contrast-checked.

Cross-client behaviour

Client Agent UI
VibeCoder / VibeAIChat Full panel
VibeMobile Single-task agent screen, no parallel mode
VibeWatch Active-agent indicator; can stop but not start
IDE plugins Implicit — extensions invoke the agent via start_agent_task directly
Agent SDK Programmatic — same start_agent_task Tauri command as the panel uses

The daemon is the single source of truth for the agent loop. All clients send start_agent_task and subscribe to the per-tab event stream (agent:{tab_id}:{base}) or the global stream (agent:{base}).


Troubleshooting

“Select an AI provider in the header first”

No provider configured. Open Settings → Providers (or the toolbar dropdown) and add at least one API key. The panel inherits provider availability from the global selection.

“Agent stopped by user”

Emitted when the Stop button is clicked. The current tool call (if any) is aborted; pending approvals are dropped. The agent task id is marked cancelled in the dashboard. To resume from here, click Retry — but note Retry restarts the task from scratch, not from where it stopped.

“Worktree isolation failed: not a git repo”

Parallel mode requires a git workspace. The daemon falls back to sequential mode — the badge changes from 🛡 to 📋. To re-enable worktree isolation, run git init (or open a directory that’s already a git repo).

Partial completions don’t show a Resume button

The checkpoint write happens at end-of-task. If the agent crashes hard (panic, OOM) before writing the checkpoint, there’s nothing to resume. Use Retry instead.

“Linter ERRORS - agent must fix before proceeding”

After a write_file step, the panel auto-runs a linter against the changed file. If the linter reports errors, a synthetic step appears in the feed (step number N.5) with the diagnostic. The agent reads this in its next turn and is expected to fix the errors before claiming completion.


  • Agent runtime: vibecoder/crates/vibe-ai/src/agent.rs — the core loop (Plan → Act → Verify → Done)
  • Source: vibecoder/src/components/AgentPanel.tsx (~750 LOC) · backend vibecoder/src-tauri/src/commands.rs (start_agent_task, stop_agent_task, respond_to_agent_approval, resume_agent_task)
  • Tests: vibecoder/src/components/__tests__/AgentPanel.bdd.test.tsx (25 BDD scenarios)
  • Sandbox: docs/sandbox — every agent shell tool call goes through the Tier-0 sandbox