Repository navigation
fix(opencode): require OpenCode 2.2.3 and adopt its image tool contract - #1209
Merged
Merged
Conversation
Upstream anomalyco/opencode#60258 (shipped in 2.2.2) renamed the image tool to experimental_image_generate - the tool, its permission action, its job type and its completion notices' metadata.source - and turned it on by default: only experimental.image_generate: false disables it. CodeNomad's Images gallery matched the old name, so on 2.2.2+ it lost every generation entry, the Design agent asked for a tool that no longer existed, and Settings showed image generation as off while it was on. OpenCode 2.2.3 becomes both the minimum and the recommended runtime, and Images are implemented for its contract only: one IMAGE_TOOL constant drives the gallery and its proof, imageGenerationSelection reads unset as on, the Design agent and skills name the new tool, and older daemons take the existing below-minimum path. The 2.1.0 client/plugin/schema pins stay. Help in all ten locales, READMEs, CONTRIBUTING.md, dev-docs and the architecture skill describe the new minimum, its reason and default-on image generation; the setup minimum-reason string is updated in every locale. Fixtures follow the 2.2.3 transcript format, the activation fixture exercises the image tool instead of falling back to webfetch, and CI runs the image settings fixture the same way on both runtimes. Validation on the exact 2.2.3 CLI: Design companion/activation/content, image settings, session pruning with images, Code Mode jobs and the full native set; typecheck; server units; gallery, Design, update and UI tests; browser setup/settings tests; ten-locale help checks.
Contributor
Author
Gatekeeper reviewZERO FINDINGS (P0: 0, P1: 0, P2: 0, P3: 0). Round 1 found no product-code defects and one P3: the PR description still carried the contract review's initial recommendation (keep the 2.1.2 minimum, accept both image tool names), contradicting the implementation. The description now states the applied decision (2.2.3 as both minimum and recommendation, 2.2.3 image contract only) and lists the fix in this PR; round 2 confirmed it matches HEAD. Verified:
Follow-up outside this PR: fail-safe handling for |
This was referenced Oct 31, 2026
pascalandr
added a commit
that referenced
this pull request
Oct 31, 2026
## Summary - OpenCode **2.2.5** becomes the **recommended** (qualified) runtime. The **2.2.3 minimum** and the 2.1.0 `@opencode/client`/`plugin`/`schema` pins stay: neither 2.2.4 nor 2.2.5 changes a contract CodeNomad consumes. - `RECOMMENDED_OPENCODE_VERSION` is 2.2.5, and the reviewed-publication shortcut in `contractProfile` lists 2.2.3 and 2.2.5; other versions, 2.2.4 included, keep negotiating their authenticated schema. The CI `latest` lane already resolves the latest stable CLI. - Help (all ten locales), READMEs, `CONTRIBUTING.md`, `dev-docs/OPENCODE_COMPATIBILITY.md` and the architecture skill state two separate versions again: minimum 2.2.3 with its reason, recommendation 2.2.5. ## Contract review (v2.2.3 → v2.2.4 → v2.2.5; 31 + 19 commits) Reviewed from the exact tags `v2.2.3` (`082ac6281f`), `v2.2.4` (`45898d0f85`) and `v2.2.5` (`9bbb8ce18c`), the published npm packages, and an authenticated OpenAPI dump from each official CLI running in its own sandbox. The [changelog](https://opencode.ai/changelog) has full notes for 2.2.4 and 2.2.5 (both 30 October); everything below was also checked against the commit log and source. CodeNomad side: `origin/dev` `605820669`. **Headline:** neither release changes a contract CodeNomad consumes. 2.2.4 adds opt-in **artifacts** (seven `/api/experimental/artifacts` operations, four tools, a plugin hook), **renamable persistent terminals**, and moves background-job recovery markers from `kv` into a new **`job` table**. 2.2.5 is a bug-fix release; its one schema change is that `compaction.buffer` accepts a percentage such as `"20%"`. The image tool contract adopted in #1209 (`experimental_image_generate`, on by default) is unchanged through 2.2.5, and still nothing writes `session_part`. - **Authenticated OpenAPI:** 134 → **138** → 138 paths, 162 → **169** → 169 operations, schemas 351 → 362 → 363. - **Added operations (2.2.4):** `GET /api/experimental/artifacts/providers`, `GET|POST /api/experimental/artifacts`, `GET|PATCH|DELETE /api/experimental/artifacts/{artifactID}`, `GET /api/experimental/artifacts/{artifactID}/view/*` ([#60351](anomalyco/opencode#60351)). None removed in either release. - **Changed operations (2.2.4):** `GET /api/event`, where the `types` filter enum gains `persistent-pty.updated` and `artifact.updated` (94 → 96 types, none removed). `PUT /api/experimental/persistent-pty/{ptyID}` can also rename (`title`; `null` clears it), `size` becomes optional, and a 501 `PtyFeatureUnsupportedError` is returned while an older terminal service can't rename ([#60329](anomalyco/opencode#60329)). - **Changed operations (2.2.5):** none. - **Consumed schemas:** same family selector as the 2.2.3 review, 97 schemas at every version. - 2.2.3 → 2.2.4: 95 byte-identical. The two that change, `PersistentPty.Info` (+ `userTitle`, nullable, required) and `PersistentPty.UpdateInput` (`size` optional, + `title`), are both additive. - 2.2.4 → 2.2.5: all 97 byte-identical. - `V2EventEncoded`, `Session.*`, `Form.*`, `Permission.*`, `Prompt*`, `Event.*`, `Location.*`, PTY/Shell are byte-identical from 2.2.3 to 2.2.5. - **`Config.InfoEncoded`:** 2.2.4 adds `experimental.artifacts` (boolean, opt-in). 2.2.5 turns `compaction.buffer` into `Config.Compaction.Percent | integer` ([#60647](anomalyco/opencode#60647)). `experimental.image_generate`, `experimental.media_blob` and `experimental.schedules` are unchanged. - **Admission:** every predicate in `compatibility/negotiate.ts` (unchanged on origin/dev) holds on 2.2.3, 2.2.4 and 2.2.5. `contractProfile` lists only `2.2.3` as reviewed, so 2.2.4/2.2.5 are admitted through authenticated contract recognition. - **Instance proxy:** the allowlist is unchanged on origin/dev. No added or changed route is proxied: artifacts and persistent PTYs are outside it. The proxied `GET /api/config` returns the additive config fields above. - **Published declarations** (2.2.3 → 2.2.5): - **Client:** 185 → **192** → 192 methods. 2.2.4 adds `experimental.artifacts.{providers,list,create,get,update,delete,view}`, the artifact and `PersistentPtyUpdated` types, and `PtyFeatureUnsupportedError`. It removes `createPtyClient`/`PtyConnectInput` from `@opencode/client/solid/pty`, which CodeNomad doesn't import. 2.2.5 adds `ConfigCompactionPercent`. - **Schema:** 2.2.4 adds the `artifact` module and `PersistentPty.Updated`; 2.2.5 adds `config/compaction` `Percent`. - **Plugin:** 2.2.4 adds `effect/artifact` and `promise/artifact` and `ctx.experimental.artifact` (`transform`, `reload`). 2.2.5 has no export changes. - **Typecheck:** the server and UI typecheck with **0 errors** against the 2.2.4 and 2.2.5 client/schema/plugin/effect declarations. The pinned 2.1.0 control is also clean. - **Host modules:** the bundles' host imports are identical to the 2.2.3 review. A probe plugin in an isolated **2.2.4** and **2.2.5** daemon resolves every module and binding. `runtime-modules.ts` is unchanged from v2.2.3 to v2.2.5. - **Host Effect:** stays **4.0.6** (`bun.lock` sha512 identical; published `dependencies.effect` 4.0.6). - **Persistent PTY binary:** the 2.2.4 and 2.2.5 CLIs ship `@opencode/pty` **0.4.0** (string `opencode-pty 0.4.0` in the Windows binary; 2.2.3: `0.3.3`). `process-daemon-v0.4.0` adds only terminal renaming ([#60324](anomalyco/opencode#60324)): an additive `rename` request and `features: ["rename"]` with an unchanged `PROTOCOL_VERSION`. CodeNomad only lists persistent PTYs (worktree deletion guard) and reads `status`/`cwd`. - **Private native services:** the keys (`LocationServiceMap`, `Form`, `Permission`, `Location`, `Job`, `Database`, `Session`, `Bus`, `Shell`) and the members CodeNomad calls are unchanged. The changes are additive: `Job.cancelSession` (2.2.4), `Permission.allows` (2.2.5; never asks, used by `edit` to decide whether to quote file lines), and `Session.remove` now calls `jobs.cancelSession`. The Missions recurrence clock calls `Job.get/start/cancel`. Its Job metadata carries no `sessionID`, so deleting a session doesn't cancel it. - **Storage:** one migration, `20261030054400_job` (2.2.4, [#60491](anomalyco/opencode#60491)). It creates `job`, copies every `kv` row `job.background/%` into it and deletes those `kv` rows. 2.2.5 adds none. - CodeNomad reads only its own `kv` keys (Missions, pruning receipts and claims), never `job.background/*`. - Native migration 2.2.3 → 2.2.4 passes. Every 2.2.4/2.2.5 fixture DB inspected holds the `job` table, **0** `job.background/` rows and no leftover `schedule:leader` lease. - **`session_part`:** still **not written**. `history.ts`/`projector.ts` are unchanged from v2.2.3 to v2.2.5, the projector still only rewrites existing part rows, and every inspected 2.2.4/2.2.5 fixture DB has **0** rows. The forward risk from the 2.2.3 review stands (Recommendations). - **Behavior changes checked** (plain-language list in [What users will notice](#what-users-will-notice)): - **Image tool** (#1209 contract): `image-generate.ts` is unchanged from v2.2.3 to v2.2.5. 2.2.4 refactors restart recovery into one `restartMessage` but keeps `metadata.source: "experimental_image_generate"`, `jobID`, `state`, `path` and the `<image id state>` notice text. Design companion, activation, content and image settings pass natively on 2.2.4. - **Schedules** (2.2.4, [#60478](anomalyco/opencode#60478), then `da87c475a7`): the loop now runs in every server process, including `opencode serve`, and elects a leader through a `kv` lease `schedule:leader` (claimed at boot, released when there are no active schedules). Firing stays opt-in (`experimental.schedules === true`). CodeNomad uses neither OpenCode schedules nor that key; Missions recurrence uses its own storage and the Job service. - **Sessions:** deleting a session now cancels its running background jobs (shell, image, subagent) and drops their markers (2.2.4). That is the intended upstream cleanup; CodeNomad's session deletions (blank-session cleanup, temporary workspaces) get it automatically. - **Events:** `persistent-pty.updated` and `artifact.updated` reach CodeNomad's shared event relay. The relay routes generically (`instance-events.ts`), the 2.1.0 client's SSE reader doesn't validate types, and the UI handles neither type. The native relay suite passes on 2.2.4. - **Tools** (2.2.5): a model call to `bash` runs as `shell` with shell permissions, and the result carries a note ([#60687](anomalyco/opencode#60687)). CodeNomad already groups and renders `bash` and `shell` alike (`message-part-grouping.ts:48`, `tool-call/utils.ts`). `read`/`edit` failure texts are richer ([#60688](anomalyco/opencode#60688), [#60689](anomalyco/opencode#60689)). - **Compaction** (2.2.5): the prompt is capped at the context window when a catalog's input limit is larger ([#60660](anomalyco/opencode#60660)), and `compaction.buffer` may be `"N%"`. CodeNomad never reads or writes `compaction.buffer`. LiteLLM context errors now trigger compaction ([#60692](anomalyco/opencode#60692)). - **Reasoning replay** (2.2.5, [#60498](anomalyco/opencode#60498)): reasoning is no longer re-sent as plain text after a model switch, and interrupted or errored reasoning is dropped from the request. This changes provider requests only; stored transcripts and `Session.Message.*` are byte-identical. - **MCP** (2.2.5): stdio messages over 10 MiB no longer disconnect a server ([#60694](anomalyco/opencode#60694)), and an idle Location no longer restarts its MCP servers right after shutting down ([#60668](anomalyco/opencode#60668)). - **Providers:** a Console-supplied provider can now be overridden by user, project and remote config (2.2.4). The Claude Agent provider only replaces Claude Code's `/login` hint with `claude auth login` in its errors ([#60592](anomalyco/opencode#60592)); the guided setup ([#60603](anomalyco/opencode#60603)) is OpenCode Desktop only. The provider ID (`Provider.ID.claudeAgent`) and its use of Claude Code's own login are unchanged, so CodeNomad's `claude-agent` quota reader (#1214) is unaffected. - **Service lifecycle:** the CLI now adds `127.0.0.1,localhost,::1` to `NO_PROXY`/`no_proxy` for itself and its children ([#60441](anomalyco/opencode#60441)), so loopback traffic from daemon-hosted code, CodeNomad's plugins included, bypasses environment proxies. CodeNomad sets no proxy variables. - **Code Mode, Forms, permission prompts, media blobs:** no source changes from v2.2.3 to v2.2.5 (`packages/codemode` only bumps its version). The Code Mode run-to-completion regression passes on 2.2.4: `{ total: 2000, beforeAwait: 1000, count: 1000 }`. - **`@opencode/ai`:** one `ai` object for Promise and Effect ([#60516](anomalyco/opencode#60516); left out of the changelog by [#60631](anomalyco/opencode#60631)). CodeNomad doesn't import `@opencode/ai`. - Desktop/TUI-only changes (desktop terminals on persistent PTYs, base-branch and worktree shortcuts, quote sending, flat inputs, palette ordering) don't affect CodeNomad. One indirect effect follows. - **Worktree deletion:** OpenCode Desktop terminals now live as persistent PTYs that survive restarts ([#60346](anomalyco/opencode#60346)). CodeNomad's guard (`routes/worktrees.ts`, "Running persistent PTY blocks deletion: <id>") will therefore refuse more often for users who also open terminals in OpenCode Desktop. The behavior is correct, but the message names the PTY ID rather than the terminal's title. **Verdict:** - The **2.1.0 client/plugin pins can stay**. Bumping them would bring only artifacts, PTY renaming and a config type, none of which CodeNomad consumes. - The **2.2.3 minimum has no reason to move**. No consumed HTTP, plugin, private-service or storage contract broke in 2.2.4 or 2.2.5. - **Recommend 2.2.5.** It is admitted today through contract recognition. Recommending it is CodeNomad-side bookkeeping: `RECOMMENDED_OPENCODE_VERSION` (`runtime-support.ts`), the reviewed-version check in `contractProfile` (`compatibility/runtime.ts`), the version texts in docs and help, and CI's qualification target. This is subject to the full native suite on the exact 2.2.5 CLI, run separately. - **No CodeNomad bug found** in 2.2.3 → 2.2.5. Risks: the `session_part` forward risk (unchanged) and the more frequent worktree-deletion refusal (UX). **Not verified here:** - the full native suite on 2.2.5: only a host-module probe ran natively on 2.2.5, and everything else for 2.2.5 is source and OpenAPI review (Appendix I); - the 2.2.4 recurring Missions journey (`test-recurring-simple-native` ABCDGELFWQN), which was interrupted by a server restart; - the `kv` → `job` migration with a real pending background marker: only the empty case ran, and upstream `job.test.ts` covers the copy; - whether a `bash` call is stored as `bash` or `shell` (source suggests the model's name is kept; CodeNomad renders both); - artifacts, Claude Agent and MCP stdio framing at runtime (source only); - macOS/Linux. --- ## What users will notice **OpenCode 2.2.4** - **Persistent desktop terminals:** OpenCode Desktop terminals belong to the session, keep their scrollback across server restarts, stay in sync with the TUI and other windows, and can be renamed. Renamed terminals keep their names. - **Artifacts (opt-in):** with `"experimental": { "artifacts": true }`, agents can publish, update and serve HTML or static artifacts (`artifact_create`, `artifact_read`, `artifact_update`, `artifact_delete`) through a built-in local provider, which stores files under OpenCode's data folder, or a plugin-provided one. - **Schedules:** enabled schedules also fire under `opencode serve`, and only once when several OpenCode processes share a database. They stay off unless `experimental.schedules` is `true`. - **Claude Agent:** OpenCode Desktop guides installation, `claude auth login` and connection checking. Error messages now say `claude auth login` instead of `/login`. - **Config:** user, project and remote configuration can override Console-supplied provider names, limits, variants, settings and headers. - **Networking:** requests to the local OpenCode service no longer go through `HTTP(S)_PROXY`. - **Sessions:** deleting a session also stops its running background work, such as shells, image generations and subagents. - **Desktop/TUI:** base-branch shortcut, simpler location popover, faster Markdown streaming and various fixes. **OpenCode 2.2.5** - **Compaction:** `"compaction": { "buffer": "20%" }` keeps a share of each model's context free; token counts still work. Compaction no longer overfills models whose catalog input limit exceeds their context window, and LiteLLM context errors trigger compaction instead of stopping. - **Tools:** a model calling `bash` gets `shell` (with shell permissions) instead of an error. `read` names the missing folder, and `edit` shows the closest lines when `oldString` isn't found. - **Reasoning:** switching models or resuming an interrupted reply no longer resends reasoning as plain text, which some providers rejected. - **MCP:** large stdio messages (over 10 MiB) no longer disconnect a server, and idle locations don't immediately restart their MCP servers. - **Desktop/TUI:** worktree and base-branch shortcuts, a shell-mode toggle, sending a single quote from its popup, and MCP servers removed from the connect menu. ## Follow-ups (outside this PR) - Hardening for `session_part` (independent of this upgrade; 2.2.5 still writes no rows): make the direct `session_message.data.content` readers and the pruning transaction fail safe when `session_part` has rows for the target messages. - Optional UX: when the worktree deletion guard finds a running persistent PTY, name it by `userTitle ?? title` (2.2.4+) and say it may be an OpenCode Desktop terminal. - Any future reader of `compaction.buffer` must accept `"N%"` as well as a token count. ## Isolated Windows qualification Base: dev `605820669`; Node 25.2.1 (local; CI runs the pinned 24.20.0); exact official CLI **2.2.5**; locked client/plugin **2.1.0**; `CODENOMAD_NATIVE_FIXTURE_PARENT` outside the user profile. - **Native suites on 2.2.5, all passing:** Code Mode jobs, plugin runtime, automation, session pruning (history, navigation, media blobs, proxy, session images), Location/worktrees/event relay, permission receipts, Git init/degraded, Missions (contract, family control, native, trajectory) and all 11 recurring journeys, sessions (environment, fork, aside, interrupt, lifecycle, blank cleanup, compaction isolation, prompt skills), providers (usage, accounts, account selection), service usage, web search settings, plugin packages, output limit, Promise-tool failure, subagent depth, temporary workspaces, Design companion/activation/content, image settings, history migrations from 2.1.2/2.2.3/2.2.4, shared install of the 2.2.3 minimum. - **Installer suites after the bump:** upgrade (installed `opencode v2.2.5`) and legacy replacement (target `2.2.5`) install the new recommendation and pass. - **Builds and checks after the change:** server build, native plugin builds and root typecheck clean; server **2,840 passed, 9 skipped, 1 cancelled** (`index.test.ts` shutdown test timed out under full-suite load; **4/4** alone); runtime-support/transport/update **35/35**; browser `opencode-setup` **34/34**; ten-locale help **31/31**.
pascalandr
added a commit
that referenced
this pull request
Oct 31, 2026
…trols (#1224) ## Problem The opt-in `presence-native.test.ts` waits for the `mission_*` tools to leave the native tool list after the backend's presence lease ends. That never happens: tools register **once at plugin load, independently of presence** (`desktop-plugin.ts`), so scheduled passages keep them (`MISSIONS_RECURRING.md`). The test fails identically on OpenCode 2.2.0, 2.2.1, 2.2.2 and 2.2.3, and on every dev commit since registration moved to plugin load; it is unrelated to #1209. `MISSIONS.md` still said tools register through presence. ## Change (test and docs only) - The test now observes what presence actually controls: - the typed `codenomad.missions` RPC unregisters (only `rpc.unavailable`/`rpc.method_not_found` count as absent) and re-registers exactly once when the lease returns; - the tools stay registered; - the ordinary route refuses with the tools' own message; - the real provider request carries no `mission_*` tool without presence, and carries `mission_inspect` with it. - `MISSIONS.md`: the actual registration, and how to run the opt-in test. ## User impact None. Without presence, the context hook strips every `mission_*` tool from model requests and the executors refuse; the new test proves this natively. ## Validation - `presence-native` passes on isolated OpenCode 2.2.3, 2.2.2 and 2.2.0. - Red checks: it fails with presence retention always true, with the context hook keeping the tools, and with tools registered inside presence. - Server typecheck; 155 `opencode/missions` tests incl. missions-plugin/missions-reload; `build:missions`; `test-missions-native.mjs` on 2.2.3.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
experimental_image_generate, for the tool, its permission action, its job type and its completion notices'metadata.source, and the tool on by default (onlyexperimental.image_generate: falsedisables it). On 2.2.2+, the gallery lost every generation entry and the Design companion fixture timed out; 2.2.1 passed.IMAGE_TOOLdrives the gallery and its proof;imageGenerationSelectionreads unset as on; the Design agent and skills name the new tool; the activation fixture exercises the image tool instead of falling back to webfetch; the gallery fixture represents the 2.2.3 transcript format.native_contract). The 2.1.0 client/plugin/schema pins stay.CONTRIBUTING.md,dev-docs(compatibility, pruning, panel extensions, settings) and the architecture skill describe the new minimum, its reason and default-on image generation. The setup minimum-reason UI string is updated in all locales.Contract review (v2.2.1 → v2.2.2 → v2.2.3; 40 + 101 commits)
Reviewed from the exact tags
v2.2.1(3ebcf9a32c),v2.2.2(098aa7fd6e) andv2.2.3(082ac6281f), the published npm packages, and an authenticated OpenAPI dump from each official CLI running in its own sandbox. The changelog has entries for both 2.2.2 (29 October, highlights only) and 2.2.3 (30 October, full notes); everything below was also checked against the commit log and source.Headline: the HTTP contract CodeNomad consumes didn't change, but 2.2.2 renamed the image tool from
image_generatetoexperimental_image_generateand turned it on by default (#60258). CodeNomad's Images/Design features match the old name and the old default, so they break on 2.2.2 and later (see Design companion finding). Nothing else found affects CodeNomad.GET /api/model,GET /api/event,PUT /api/pty/{ptyID},POST /api/shell,GET /api/shell/{id}/output,POST /api/debug/heap-dumpandGET /api/experimental/migration/v1. All of these are representation-only: the Effect 4.0.6 JSON Schema output flattens nestedanyOf, bounds integers withmaximum: 9007199254740991(andminimumfor signed integers), and rewrites the numeric-string query pattern. The event-type and model-type enums are unchanged.POST /api/credential, which can now be refused by anintegration.connectpolicy.V2EventEncoded,Session.*,SessionStats.*,Form.*,Permission.*,Prompt*,Event.*,Location.*, PTY/Shell). Re-derived from that description it selects 104 schemas at 2.2.1 (the 2.2.1 file says 103), all byte-identical from 2.1.4 to 2.2.1.Form.Fields_1..3andSession.Message.Native_1..4collapse into their base schemas, which are byte-identical to the copies, leaving 97. Of these, 71 are byte-identical and 24 differ only in generator representation.Session.Info.time.busy, the start of the latest execution (#59910), and the input fieldPromptInput.FileAttachment.source(Prompt.FileSource, inline or uri; CodeNomad doesn't send it).V2EventEncoded,Session.Message.*content,Session.Inbox.*,Form.*semantics,Permission.*andEvent.SeqEncodedare unchanged.Config.InfoEncoded: no semantic change in 2.2.2 (representation only). 2.2.3 extends the managed-policyactionenum (top-level andexperimental.policies) withintegration.connect,mcp.use,skill.useandplugin.use(#60315); CodeNomad writes no policies. The meaning ofexperimental.image_generatechanged: unset now means on, and onlyfalseturns it off. The schema shape stayed the same and the OpenAPI carries no description, so no contract signal reveals it.whenbecomes optional;if/untilconditions (shell in 2.2.2, Code Mode in 2.2.3) are added; outcomes gainchecked/expired.compatibility/negotiate.ts(unchanged since the 2.2.1 review) holds on 2.2.1, 2.2.2 and 2.2.3.GET /api/model,PUT /api/pty/{id},POST /api/shellandGET /api/shell/{id}/output. Integer bounds and an equivalent numeric pattern accept everything CodeNomad sends.ScheduleCondition*;FormFields2is renamed toFormFields1, which is generated-type churn. 2.2.3 adds the Promise media helperswithMediaRefs,mediaHashFromUriandMediaRequestOptions(#60116) and persistent-PTY frame helpers insolid/pty. CodeNomad imports none of these.ShellCondition,CheckMode, …) and exportsSessionLegacy.part. 2.2.3 addsCodeCondition*andpolicy.Action.ComposerQuoteAnchor/ComposerFocusOptions. 2.2.3 has no export changes, but the optional peers@opentui/*become>=0.8.0(#60385). The publisheddependencies.effectmoves from 4.0.1 to 4.0.6 (2.2.2).pathsoverrides. The pinned 2.1.0 control is also clean. The harness now also remapseffectitself; the 2.2.1 harness skipped it because effect'sexportsare plain.jsstrings.@opencode/plugin(Plugin),/effect(Plugin,Rpc,Location),/promise/adapter(fromPromise),/rpc(Rpc),effectandeffect/encoding(Base64Url) from the host. A probe plugin in an isolated 2.2.3 daemon resolves every module and binding, as on 2.2.1.runtime-modules.tsis unchanged between the tags.@opencode/schema/@opencode/clientcode bundled into the Missions and pruning plugins now runs on host Effect 4.0.6. The plugin-runtime, pruning, Missions-contract, permission-receipts and image-settings native suites pass on 2.2.3.LocationServiceMap,Form,Permission,Location,Job,Database,Session,Bus) and the members CodeNomad calls are unchanged. The changes are additive:Permission.Unattended(a reference that denies unanswerable asks for schedule checks), aCorrectedError.messagegetter, andSessiondeciding fork media from the parent's location.session_part(new table),session_v2.time_busyand fourschedulecheck columns.session_part(#58915): history reads now prefersession_partrows oversession_message.data.contentfor assistant messages. The projector writes parts there only for messages that already have part rows, so new messages stay inline. Every 2.2.3 fixture DB inspected has 0session_partrows, and upstreamdev(1e28374e48, 30 October) has no writer either.session_message.data.contentreaders and the pruningUPDATE … json_set(data,'$.content',…)(session-pruning/transaction.ts) would silently miss or be shadowed by part rows. See Recommendations.executecall rows (#60480), and progress is throttled to at most one update per 100 ms (#60120). Programs see the same failure text as direct calls (#60109),fetchin Code Mode is now subject towebfetchpermissions (#59954),for…invisits inherited keys (#59933) andnullsurvives text encoding (#57639). The run-to-completion regression behind the 2.1.2 minimum passes on 2.2.3:{ total: 2000, beforeAwait: 1000, count: 1000 }.integration.usevsmcp.use/skill.use/plugin.use) is a changelog "breaking change" for managed deployments only.media_blobis on (#60118). The native fork suite passes on 2.2.3.@opencode-ai/pty0.3.1 to@opencode/pty0.3.3 (#60276).process-daemon-v0.3.3contains only the release commit after 0.3.2 (a version bump of the@opencode/pty-*binaries). v0.4.0 (terminal renaming, #60324) is not shipped in CLI 2.2.3, which pins 0.3.3. CodeNomad only lists persistent PTYs (worktree deletion guard), whose schema is unchanged apart from representation.@opencode/aibreaking changes (media moved to/experimental, onetool()): CodeNomad doesn't import@opencode/ai.Verdict:
Not verified here:
session_partreaders against a runtime that writes parts (no such runtime exists yet);Design companion finding
Symptom:
scripts/test-design-companion-native.mjs <cli>passes on 2.2.1 and fails on 2.2.2 and 2.2.3 withError: Timed out: series 1 generated and read. Reproduced independently in a scratch checkout oforigin/dev(f39642bc4), withCODENOMAD_NATIVE_FIXTURE_PARENToutside the home:What the step waits for (
test-design-companion-native.mjs:238-241): the fixture's loopback chat provider answers aRUN [...]turn with two tool calls{ tool: "image_generate", args: { prompt, path: ".codenomad/design/icon/<name>" } }. The step then waits until the transcript holds two synthetic notices withmetadata.source === "image_generate"(line 209) and tworeadparts of the generated files. No HTTP generate route, file watch or media blob is involved; it is the agent tool path.Cause: an intentional upstream contract change, not an upstream bug. In 2.2.2, #60258 (
d4e48f54da, "turn experimental_image_generate on by default") changedpackages/core/src/tool/plugin/image-generate.ts:export const name = "image_generate"→"experimental_image_generate". That one constant is the tool name, the permission action (assertions: ["experimental_image_generate", "edit", "read"]), the Jobtype, and the completion notice'smetadata.source.session/execution/restart.tsalso tags restart-recovery noticessource: "experimental_image_generate".if (!enabled || imageGenerate(entries) === false) return. An unsetexperimental.image_generatenow offers the tool; onlyfalseremoves it ("still honored for configs from when the tool was opt-in; permissions are the supported way to turn it off").experimental_image_generatewithout additional configuration".2.2.2 also wraps the notice text as
<image id="…" state="…">\n…\n</image>and addsidto the tool output (#60098). CodeNomad'sSaved image: (.+) \(parse still matches;jobID,stateandpathmetadata are unchanged.Evidence (fixture DBs copied read-only to
evidence/):So the product bug is real and independent of the fixture: on 2.2.2+ CodeNomad's Images gallery finds no generation entries. Images still appear through their
readparts, but without prompt, model, lineage or "proven" origin, andgalleryImageReferencerefuses them aschanged.CodeNomad code that assumes the 2.2.1 name or default (origin/dev):
session-pruning/assets-gallery.ts:56,139part.name === "image_generate"changedsession-pruning/assets-gallery-proof.ts:104,130source === "image_generate"opencode/image-settings.tsimageGenerationSelection(undefined) → false(doc: "Reads … as OpenCode does")images.status()returns"disabled"while the tool is offered; help step "Turn on image generation" (docs/help/en/extensions.md:73) is no longer neededopencode/design/opencode/agents/design.md,skills/design-core,design-logo,design-ui-mockupimage_generateexperimental_image_generate; the first call can fail with "No tool named …"scripts/test-design-companion-native.mjs:125,209,238,301image_generatescripts/test-design-activation-native.mjs:76,307image_generate; tool detection by that nameaskToolfalls back towebfetchon 2.2.2/2.2.3 (logged)dev-docs/SESSION_PRUNING_RPC.md,PANEL_EXTENSIONS.md,SETTINGS.mdimage_generateparts and opt-inUser permission rules written for action
image_generateno longer match on 2.2.2+; that is an OpenCode-side change for users. CodeNomad's help never names the permission action; the Design fixtures and agent useexperimental_image_generate.Fix in this PR (2.2.3 contract only, no fallback):
IMAGE_TOOL = "experimental_image_generate"constant drives the gallery and its proof; the gallery reads the 2.2.3 notice format.experimental_image_generate.imageGenerationSelectionreads an unsetexperimental.image_generateas on; onlyfalsedisables it. Settings → Images andimages.status()report it that way.Follow-up
session_part(independent of this upgrade, no 2.2.3 regression): make the direct readers and the pruning transaction fail safe whensession_parthas rows for the target messages.Isolated Windows qualification
Base: dev
f39642bc4; Node 25.2.1 (local; CI runs the pinned 24.20.0); exact official CLI 2.2.3; locked client/plugin 2.1.0;CODENOMAD_NATIVE_FIXTURE_PARENToutside the user profile for the Design fixtures.No tool named "image_generate" is currently available).{ total: 2000, beforeAwait: 1000, count: 1000 }.index.test.tsshutdown test timed out under full-suite load; 4/4 alone); gallery 107/107; Design 23/23; opencode-update 62 passed / 1 skipped; touched UI units 8/8; browseropencode-setup+websearch-settings38/38; native plugin build 2/2; ten-locale help 31/31.