OpenCode V2 Is No Longer Just My Plan B

In May I wrote that Claude Code was my daily driver and OpenCode was my Plan B. That label aged quickly.

I meant it as a compliment. Plan B is the tool you’re glad you installed — the one you fall back to during an outage or a client engagement that won’t approve the supply chain. But Plan B also means second choice, and I listed three specific reasons it was second: a less-polished agent loop, a weaker subagent and verification story, and a smaller skills and integration ecosystem. Those were fair criticisms in May. The OpenCode 2.0 beta addresses all three head-on.

One thing up front, because it changes how you should read the rest of this: I have not run the V2 beta. I’ve used OpenCode V1 alongside Claude Code for months and I have opinions about it earned the normal way. Everything I say about V2 specifically comes from its official beta documentation and public release material, not from my terminal. I’m reading architecture, not reporting latency numbers.

First: there are two different things called “v2”

This trips people up constantly, so let’s separate them before anything else.

OpenCode 2.0 beta is the future core — the CLI, the server, the runtime. It installs from the next npm tag with npm install -g @opencode-ai/cli@next and runs as opencode2. It keeps its own separate beta data and ships a new server/client/plugin architecture. It is explicitly a beta, and the docs say so without softening it.

Desktop v2 is something else entirely: a redesigned desktop layout whose migration completed in stable OpenCode v1.18.0 on July 14, 2026, with stable at v1.18.3 as of today. The effort itself ran across the v1.17 and v1.18 series — browser-style tabs and per-window restoration, a rebuilt review panel with persistent file browsing, integrated terminal improvements, per-prompt model selection, and server-aware session routing all landed through v1.17.x, and v1.18 finished the job with the old/new layout transition setting, faster cold loads, timeline reconnect and backfill, and better WSL startup readiness.

Both are real. They’re not the same release, not on the same track, and there is no stable 2.0 on GitHub to point at. This post is about the 2.0 beta. I mention the desktop work only because it’s evidence that the product’s UX is maturing on a separate front — not because a tab bar is why V2 matters.

The thesis

V1 was a good multi-provider CLI. V2 is becoming an inspectable, programmable agent runtime.

That’s the whole argument, and the distinction matters more than any feature list: the first is a program you run, the second is infrastructure you build on.

Background subagents, and being able to see them

The V2 agents documentation describes two built-in primary agents — Build and Plan — and two built-in subagents, General and Explore. Custom agents can be declared as primary, subagent, or all. Subagents run in child sessions with fresh context, which is the correct design: the point of a subagent is that it burns its own context window and hands back a summary rather than polluting the main conversation.

A primary agent can invoke subagents in the foreground or the background. Visible subagents can be addressed directly with @ mentions. The public beta material highlights background subagents and shell commands as headline features, including a management view that lists what’s running and lets you background or kill it. That UI isn’t described in the beta docs yet — the session-background and shell-process endpoints behind it are, so the capability is real, but treat the interface details as provisional.

That last part is the one I care about, and it’s the least glamorous. Plenty of tools will happily spawn agents for you. Far fewer give the operator a lifecycle: what is running right now, and how do I stop it. Every agent harness I’ve used eventually produces the moment where something is clearly working on the wrong thing and your only recourse is Ctrl-C and hope. A UI that enumerates running work and offers a kill switch is not a flashy autonomy feature. It’s the difference between a system you can operate and a system you have to trust.

Permissions became policy instead of vibes

This is my favorite change, and it’s underrated.

V1 had a grouped permission object. V2 replaces it with an ordered array of rules. Each rule matches an action and a resource and carries an effect — allow, ask, or deny — and the last matching rule wins. Actions cover read, edit, shell, subagent, skill, web tools, MCP tools, and external-directory boundaries. Agent-specific rules append after the global rules, so an agent can refine policy without redefining it. Directories outside the workspace get their own permission boundary. Defaults treat environment files and out-of-workspace access cautiously. And a configured deny cannot be overridden by a saved interactive approval — you can’t click your way out of a policy you wrote down.

If you’ve written firewall rules, IAM policies, or sudoers entries, you know this shape and why it beats the alternative. Ordered, last-match-wins rules are boring, auditable, and reviewable in a pull request. The alternative that most of this industry has settled for is asking a language model nicely to stay in its lane and hoping the system prompt holds. One of those is a control. The other is a wish.

Skills close the ecosystem gap

The ecosystem complaint from May was the one I expected to age worst, because ecosystems take years. V2’s answer is skills: Markdown instruction files advertised to the model and loaded on demand.

Project skills live in .opencode/skills, global ones in ~/.config/opencode/skills. The part that matters is the compatibility list — V2 also reads .claude/skills, ~/.claude/skills, .agents/skills, and ~/.agents/skills. Skills can ship scripts and reference material alongside the instructions, can be invoked explicitly as slash commands, and are governed by the same permission rules as everything else. Sources can be local directories or HTTP catalogs.

Two things stand out. First, skills are advertised by ID, name, and description rather than every skill body being stuffed into every prompt — the difference between a skills system that scales past a dozen entries and one that quietly eats your context window. Second, and more importantly: reading .claude/skills means the operational knowledge a team has already written down is portable. I have Hugo-aware skills, memory conventions, and deployment runbooks encoded for one harness. The prospect of not rewriting all of that to evaluate a different runtime is, practically speaking, the entire difference between “I’ll try it someday” and “I’ll try it this week.”

Portability is the whole point. After watching Anthropic restrict third-party access to Claude and break working setups overnight, the lesson I took was to never let a single vendor own a layer I can’t move. Skills that live in a directory I control, readable by more than one harness, are that lesson applied.

State management as a runtime responsibility

Long agent sessions rarely fail because the model got dumb. They fail because state got lost — context evaporated mid-task, or an edit went sideways and there was no clean way back.

V2 treats both as runtime problems. Snapshots are on by default and capture worktree state immediately before each model call and again after a cleanly completed step, using a separate internal Git object database. They do not create commits in your repository, move your branches, or intentionally touch your index — which is exactly the constraint I’d want, because an agent that quietly commits on my behalf is an agent I’d uninstall. /undo stages a conversation rollback and can restore the affected file changes; /redo puts the conversation and files back.

The docs are refreshingly blunt about the limits, and so should anyone recommending this be: it is best effort, and it does not reverse external side effects. Not databases, not services, not running processes, not network resources, not Git operations, not ignored build output, not files outside the active directory. If your agent ran a migration or pushed a branch, snapshots will not save you. This is undo for the worktree, not a time machine.

Compaction handles the other half. V2 builds structured checkpoints of older context while preserving durable earlier messages, estimates context pressure automatically and can compact before a model call rather than after failure, and can compact and retry once when a provider classifies an error as context overflow — a second overflow surfaces as a hard error rather than an infinite squeeze. The checkpoint captures objectives, important details, completed and active work, blockers, next moves, and relevant files.

That checkpoint schema is a design opinion, and it’s the right one — roughly what I’d write in a handover doc before going on holiday.

The API is the real story

Everything above is good. This is the part that changes what OpenCode is.

V2 exposes a documented HTTP API and a generated TypeScript client, published as @opencode-ai/client@next. The same server backs the TUI and the desktop app, and is documented for custom clients and for embedding in your own tooling. The surface is broad: sessions, durable history, event streams, models and providers, permissions, questions and forms, PTY sessions, shell process management, skills, MCP, filesystem access, snapshots and reverts, compaction, and VCS status and diff. Clients can create sessions, send prompts, switch model or agent mid-flight, interrupt work, wait for idle, and stream events. A Node service API can discover, start, and stop a compatible local background OpenCode service. Dax has described the API publicly as “very very good” and made a point of the bundled CLI client making it easy for a human — or another agent — to query.

Read that list as an operator rather than a user. Wait for idle and stream events are how you put an agent in a pipeline. Durable history is how you audit what it did last Tuesday. Shell process management and snapshots are how you contain it. Start and stop a local background service is how you run it on a box with no terminal attached.

V1 looked like an application. V2 looks like infrastructure. That’s a category change, and it’s the reason “Plan B” stopped fitting — you don’t call the thing you can embed, automate, observe, and script your emergency fallback.

Provider neutrality, now with a serious harness

None of this replaces the original reason to care. The product still leads with more than 75 model providers through Models.dev including local ones, multi-session operation, LSP integration, terminal and desktop and IDE surfaces, ChatGPT Plus/Pro and GitHub Copilot login where officially available, and a privacy-first stance in which OpenCode itself stores no code or context. That last claim has an obvious asterisk worth stating plainly: your model provider still receives whatever you send it, under their policy, not OpenCode’s.

Provider neutrality was always the pitch. In May it came bundled with a harness that was merely fine, which is why Claude Code won my day. The V2 argument is that you no longer have to make that trade. Neutrality plus a serious runtime is a different product than neutrality plus a decent CLI, and it’s the combination — not the desktop redesign — that makes this worth your attention.

What’s still rough

I’d be doing the exact thing I complain about if I wrote 2,000 enthusiastic words and buried the caveats.

This is a beta. Not a stable 2.0, not a release you should treat as one. The docs warn that beta data may be wiped and that features, APIs, configuration, and plugin APIs may change. V1 plugins do not work in V2 during the beta. Three areas are documented as intentionally breaking: the plugin API, the server API and client contracts, and TUI configuration moving to a global cli.json (migrated for you on first startup, at least). Compatibility is the intent everywhere else — V2 reads your existing V1 configuration and translates V1-shaped config in memory rather than rewriting your files, which is a genuinely considerate touch — but “intended compatible” and “verified compatible” are different words, and beta regressions are normal.

Installation is also thin: the beta is package-manager-only — npm, Bun, pnpm, or Yarn. No curl installer, no Homebrew, no standalone binaries, no Chocolatey, Scoop, Docker, or mise path yet. Smaller edges show too: custom commands still accept a subtask field and currently ignore it, running in the current session instead of spawning a child. And plugin and SDK contracts are still being finalized ahead of stable 2.0, so anything you build against them now is built on sand you were warned about.

The architecture is impressive. Production stability is unproven. Those statements are both true and neither cancels the other, and I’d rather say both than pretend the first one settles it.

So: try it now if you build agent tooling, care about provider neutrality, want permissions you can actually read, or can absorb beta breakage. Wait if you depend on V1 plugins, need stable automation contracts, or can’t tolerate a local data reset. The nice part is that this isn’t a migration decision at all — opencode and opencode2 coexist during the beta, so evaluating V2 doesn’t require replacing or uninstalling V1. That’s also the only responsible way to do it. I’ve argued before that changing tools too often is its own tax, and nothing here justifies moving critical workflows onto a beta because the architecture reads well.

Where this leaves the Plan B label

Claude Code is still my daily driver, and I want to be precise about why, because “OpenCode got better” is not the same claim as “Claude Code got worse.” It didn’t. The harness is mature, Opus is co-tuned with it, and that pairing still produces results I haven’t matched elsewhere. Nothing in the V2 docs changes that.

What changed is the shape of the alternative. In May, OpenCode was the tool I kept installed for the day Plan A wasn’t available. The 2.0 beta is something else: a provider-neutral runtime with inspectable permissions, portable skills, real state management, and an API that makes it embeddable. Beta caveats fully intact, that is not a fallback. That’s a peer.

I’m going to keep running Claude Code tomorrow. But “Plan B” was a description of a gap, and the gap is closing faster than I expected it to.