opencode-v2

GitHub

提供 OpenCode 2.x 架构、API 路由、事件模型及插件机制的详细参考,辅助解决版本差异、集成调试及代码修复。

.agents/skills/opencode-v2/SKILL.md openchamber/openchamber

触发场景

OpenCode 2.x 相关 bug 排查 查询 OpenCode API 路由与事件结构 理解 OpenCode 插件与客户端集成逻辑

安装

npx skills add openchamber/openchamber --skill opencode-v2 -g -y
更多选项

非标准路径

npx skills add https://github.com/openchamber/openchamber/tree/main/.agents/skills/opencode-v2 -g -y

不安装直接使用

npx skills use openchamber/openchamber@opencode-v2

指定 Agent (Claude Code)

npx skills add openchamber/openchamber --skill opencode-v2 -a claude-code -g -y

安装 repo 全部 skill

npx skills add openchamber/openchamber --all -g -y

预览 repo 内 skill

npx skills add openchamber/openchamber --list

SKILL.md

Frontmatter
{
    "name": "opencode-v2",
    "description": "Load for any work that touches OpenCode, including its routes, events, message or session shapes, plugins, the pinned CLI\/client version, \"what's new in OpenCode 2.x\", or a bug that looks like OpenCode behaving differently than OpenChamber expects."
}

OpenCode 2.x in this repository

A bug report that mentions 1.x behaviour (/session without /api, message.updated events, auth.json, @opencode-ai/sdk) describes the old world. Answer from the 2.x code.

Where the boundary lives

  • packages/ui/src/lib/opencode/client.ts — every official OpenCode call the shared UI makes; projection.ts turns wire shapes into the OpenChamber domain model in model.ts; events.ts translates wire events; plugins.ts translates the experimental plugin routes; session-stats.ts translates the experimental session.stats usage route; websearch.ts translates web search (providers, the websearch config choice, keys, the tool's text result and its first-use consent form). Rendering and stores read the domain model; fix a missing field there, never with a shim.
  • packages/web/server/lib/opencode/proxy.js forwards /api/* as-is (2.x serves under /api itself) and folds OpenChamber-owned session state into the records it serves. env-runtime.js launches opencode serve.
  • Plugins OpenChamber generates for OpenCode: plugin-spec.js and agent-tool/runtime.js, declared through the watched <dataDir>/opencode.managed.json (OPENCODE_CONFIG), so a settings change applies without a restart. Only the binary, port and external toggle restart.

Every directory-scoped read starts a location

On 2.x a read through the location middleware builds that directory's location, and the build starts every configured local MCP server for it. The location then lives until an hour without session events. So each read names a directory, and only one the user is working in:

  • Which routes: agent, plugin, model, provider, integration, mcp, usage, form, permission request list, fs, command, skill, rpc, pty, shell, reference, vcs, websearch, config, location (protocol/src/api.ts lists the groups with locationMiddleware). Session routes resolve the session's own location; GET /api/session, /api/session/active, /api/credential and, since 2.2.0, /api/project are global and start nothing. Since 2.2.0 the session update (except generating a title), fork and agent and model switches no longer build the session's location either.
  • How the directory travels: the x-opencode-directory header, percent-encoded, or a location[directory] query. A ?directory= query is ignored.
  • A read without one answers for OpenCode's own working directory, the user's home for a managed OpenCode, and starts a fleet there. The UI reads through opencodeClient with the current directory; server code with no directory of its own uses the lifecycle's getDefaultOpenCodeDirectory(), the last-used directory it warmed at startup.
  • Fan-out is the failure: a loop over every project, worktree or store directory starts one fleet each. A refresh after a catalog event re-reads only the directories the events named; they are already running.

A report of processes multiplying, memory climbing with MCP servers enabled, or MCP servers starting in projects nobody opened: reproduce it with references/mcp-spawn-probe.md before reading code.

Workarounds for what 2.x cannot do

Each exists because 2.x has no route for it. When a tag adds the route, the workaround goes and the record comes from OpenCode.

  • Archive entries an older OpenChamber kept: session-archive.js pushes archive-store.js entries to OpenCode after start (packages/web/server/lib/opencode/DOCUMENTATION.md, session archive state).
  • 1.x sessions created after the one-shot migration: v1-migration-topup.js rewinds the migration cursor before a managed start, only when no revisited session has 2.x activity.
  • Error bodies: the generated client drops the body of an HTTP status a route does not declare, so session update/delete/archive report the status without OpenCode's message or log ref.

Open asks upstream (OpenCode Slack): declaring 500 bodies on session mutations; provider.list and agent.list waiting for plugin activation the way model.list does since 2.0.27 (done in 2.2.0; the cold-catalog recovery in useConfigStore can go); built-in provider plugins setting attribution headers only when the client has not set its own, so a client can name itself to a provider (today some plugins overwrite provider-level headers). Dropped: an import route for missing 1.x sessions (the top-up workaround is enough). Check the newest tag before re-asking.

Watched upstream issues, checked on every new tag (status with gh issue view <n> --repo anomalyco/opencode, then the tag's diff):

  • #59605: generate/* runs in the ~/.config/opencode location since #44773, so each call starts a second copy of every local MCP server. Until it is fixed, Jev stays off OpenCode's generate/decisions; once it is, Jev calls can move to that route (routing/DOCUMENTATION.md).
  • #59606: PredicateQuestion has no criteria, so the Jev features that ask yes/no questions with true/false criteria (session-work/questions.js, session-goal/audit.js) cannot move to generate/decisions without re-measured wording.

Behaviour to expect

Verified against live 2.x servers; re-check on a newer tag before relying on a gap.

  • A cold location's catalog is not authoritative. The first provider/model read for a directory not started yet answers an empty list, then a partial one without plugin providers, and the full list about two seconds later, announced by provider.updated / model.updated with that location.directory. Recovery rides on those events (markConfigCatalogStale). Since 2.0.27 model.list and model.default wait up to 5 seconds for the location's plugins; provider.list and agent.list still answer early, so the recovery stays.
  • Plugin providers exist only in the running OpenCode. Nothing about them reaches opencode.json or auth.json; /api/provider is the only view, and it strips options.fetch, so nothing tells a directly callable provider from one that only works through OpenCode. Without a zen login OpenCode sets options.apiKey = "public" on zen and trims it to free models: those run on OpenCode's infrastructure and are called only through OpenCode.
  • A session whose directory was deleted still reads, but location-scoped requests answer 404 LocationNotFoundError. STATUS_BY_TAG does not map it to 404 on purpose: fetchPermission reads 404 as "settled", which would let auto-accept fail open. POST /api/session/:id/move works on such a session.
  • A background shell or a subagent has no clean cancel. shell.remove kills the process but hands the agent a Shell.NotFoundError; interrupting a subagent's child session (no job-cancel route) reports "Subagent cancelled". Agents relaunch either, so stopBackgroundShell and stopSubagent post a cancellation note to the agent first.
  • opencode run --agent X uses the default model, not the agent's: pass -m provider/model#variant in batch runs.
  • A scratch opencode serve started from the app's shell answers 401: the shell inherits the desktop's OPENCODE_PASSWORD, which wins over OPENCODE_SERVER_PASSWORD. Start it with env -u OPENCODE_PASSWORD (Basic auth user opencode).

Sources of truth

  • Reference checkout ~/projects/opencode, branch origin/dev and its v2.x.y tags (origin/v2 is abandoned; upstream PRs target dev). Maintainer machine only: where the path is absent, report it as unavailable and answer from the pins below. Server behaviour: packages/core/src, HTTP surface: packages/server/src/handlers/*, wire types: packages/schema/src, packages/protocol/src/groups.
  • Minimum supported version: MINIMUM_OPENCODE_VERSION in packages/web/server/lib/opencode/compatibility.js; raise it when OpenChamber starts depending on a route a newer tag added.
  • Pinned version: opencodeCli.version in packages/electron/package.json (the bundled binary) and @opencode/client / @opencode/schema in the root, ui, web and vscode manifests, plus @opencode/cli@ in the Dockerfile. They move together, including opencode.url in flake.nix (see the bump steps).

"What's new in OpenCode 2.x?"

Answer from the diff. Done when every API-facing change between the pinned tag and the newest tag is classified.

  1. In the reference checkout: git fetch origin --tags, pinned = opencodeCli.version, newest = git tag -l 'v2.*' | sort -V | tail -1, then git diff --stat vPINNED..vNEWEST -- packages/schema/src packages/protocol/src packages/server/src packages/client packages/plugin/src. Ignore packages/tui, packages/app, packages/web.
  2. Classify each change to a route, event, schema or plugin hook: breaks us (name the consuming OpenChamber file), fixes a workaround (name the one above that can go and what the user gains), closes an open ask, or neutral.
  3. Check every watched upstream issue above against the new tag.
  4. Report in that order with the maintainer's decisions explicit: remove, adopt, still ask, and which watched issue the tag closes.

Bumping the pinned OpenCode

Move every pin to the same tag and check out that tag in the reference checkout (git checkout vX.Y.Z in ~/projects/opencode), bun install, then bun run type-check (it covers packages/ui, packages/web and packages/vscode); the isolated ui suites; web vitest; vscode tests. A new message type or event needs a case in model.ts and events.ts before it renders. Verify @opencode/cli@<tag> exists on npm: the desktop packaging and the Docker image install it.

Set opencode.url in flake.nix to the same vX.Y.Z tag and run nix flake update opencode from the OpenChamber root. Include flake.nix and flake.lock in the bump. OpenChamber's Bun cache derives from bun.lock through bun2nix; OpenCode's dependency hashes come from the pinned upstream source. There is no hash-baking workflow to repair a mismatched pin after merge. Run nix build --no-link .#openchamber .#openchamber-desktop; nix-eval CI also evaluates and builds both on x86_64-linux, aarch64-linux and aarch64-darwin. If Nix is unavailable locally, report that and require those CI jobs to pass.

版本历史

  • 6cd8058 当前 2026-11-03 06:04

    新增对目录中间件、MCP 服务器启动逻辑及会话状态变更的详细说明;修正了关于 Zen 路由故障的描述。

  • bf977d2 2026-10-19 13:04

    记录 model 路由等待插件激活的行为变化,以及 OpenCode 2.0.27 版本更新说明。

  • 1f0004f 2026-09-28 05:11

同 Skill 集合

.agents/skills/changelog-authoring/SKILL.md
.agents/skills/clack-cli-patterns/SKILL.md
.agents/skills/communication-style/SKILL.md
.agents/skills/desktop-shell/SKILL.md
.agents/skills/drag-to-reorder/SKILL.md
.agents/skills/enterprise-boundary/SKILL.md
.agents/skills/isolated-space-boundary/SKILL.md
.agents/skills/locale-ui-patterns/SKILL.md
.agents/skills/openchamber-change-discipline/SKILL.md
.agents/skills/pr-review/SKILL.md
.agents/skills/relay-transport/SKILL.md
.agents/skills/rtl-layout/SKILL.md
.agents/skills/serve-sim/SKILL.md
.agents/skills/settings-ui-patterns/SKILL.md
.agents/skills/sync-state-invariants/SKILL.md
.agents/skills/theme-system/SKILL.md
.agents/skills/triage-issues/SKILL.md
.agents/skills/triage-prs/SKILL.md
.agents/skills/ui-api-decoupling/SKILL.md
.agents/skills/update-changelog/SKILL.md
.agents/skills/writing-for-agents/SKILL.md
.agents/skills/performance-engineering/SKILL.md

元信息

文件数
0
版本
6cd8058
Hash
0faf15c9
收录时间
2026-09-28 05:11

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.156.0. UTC+08:00, 2026-11-06 11:18
浙ICP备14020137号-1