## Summary
- OpenCode **2.2.3** becomes both the **technical minimum** and the
**recommended** runtime. The minimum moves because upstream
[#60258](https://github.com/anomalyco/opencode/pull/60258) (shipped in
2.2.2) changed the image tool contract that CodeNomad's Images gallery,
Design agent and Images settings consume: one name,
`experimental_image_generate`, for the tool, its permission action, its
job type and its completion notices' `metadata.source`, and the tool on
by default (only `experimental.image_generate: false` disables it). On
2.2.2+, the gallery lost every generation entry and the Design companion
fixture timed out; 2.2.1 passed.
- Images are implemented for the 2.2.3 contract only, with no fallback
to the old name: `IMAGE_TOOL` drives the gallery and its proof;
`imageGenerationSelection` reads 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.
- Older daemons (2.1.x, 2.2.1, 2.2.2) go through the existing
below-minimum path (`native_contract`). The 2.1.0 client/plugin/schema
pins stay.
- Help (all ten locales), READMEs, `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`) and `v2.2.3` (`082ac6281f`), 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 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_generate` to
`experimental_image_generate` and **turned it on by default**
([#60258](https://github.com/anomalyco/opencode/pull/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](#design-companion-finding)). Nothing else found affects
CodeNomad.
- **Authenticated OpenAPI:** **134 paths and 162 operations** on all
three, the same operation set. Schemas 355 → 350 → 351.
- **Added/removed operations:** none in either release.
- **Changed operations (2.2.2):** `GET /api/model`, `GET /api/event`,
`PUT /api/pty/{ptyID}`, `POST /api/shell`, `GET /api/shell/{id}/output`,
`POST /api/debug/heap-dump` and `GET /api/experimental/migration/v1`.
All of these are **representation-only**: the Effect 4.0.6 JSON Schema
output flattens nested `anyOf`, bounds integers with `maximum:
9007199254740991` (and `minimum` for signed integers), and rewrites the
numeric-string query pattern. The event-type and model-type enums are
unchanged.
- **Changed operations (2.2.3):** only the description of `POST
/api/credential`, which can now be refused by an `integration.connect`
policy.
- **Consumed schemas:** the same family selector as the 2.2.1 review
(`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.
- In 2.2.2 the seven alias copies `Form.Fields_1..3` and
`Session.Message.Native_1..4` collapse 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.
- Two changes are additive and optional: `Session.Info.time.busy`, the
start of the latest execution
([#59910](https://github.com/anomalyco/opencode/pull/59910)), and the
input field `PromptInput.FileAttachment.source` (`Prompt.FileSource`,
inline or uri; CodeNomad doesn't send it).
- `V2EventEncoded`, `Session.Message.*` content, `Session.Inbox.*`,
`Form.*` semantics, `Permission.*` and `Event.SeqEncoded` are unchanged.
- From 2.2.2 to 2.2.3 the family is byte-identical.
- **`Config.InfoEncoded`:** no semantic change in 2.2.2 (representation
only). 2.2.3 extends the managed-policy `action` enum (top-level and
`experimental.policies`) with `integration.connect`, `mcp.use`,
`skill.use` and `plugin.use`
([#60315](https://github.com/anomalyco/opencode/pull/60315)); CodeNomad
writes no policies. The *meaning* of `experimental.image_generate`
changed: unset now means on, and only `false` turns it off. The schema
shape stayed the same and the OpenAPI carries no description, so no
contract signal reveals it.
- **Schedules (not consumed on dev):** `when` becomes optional;
`if`/`until` conditions (shell in 2.2.2, Code Mode in 2.2.3) are added;
outcomes gain `checked`/`expired`.
- **Admission:** every predicate in `compatibility/negotiate.ts`
(unchanged since the 2.2.1 review) holds on 2.2.1, 2.2.2 and 2.2.3.
- **Instance proxy:** no new routes and an unchanged allowlist. Proxied
routes whose representation changed: `GET /api/model`, `PUT
/api/pty/{id}`, `POST /api/shell` and `GET /api/shell/{id}/output`.
Integer bounds and an equivalent numeric pattern accept everything
CodeNomad sends.
- **Published declarations** (2.2.1 → 2.2.3):
- **Client:** 185 methods on all three; the method tree is identical.
2.2.2 adds the generated types `ScheduleCondition*`; `FormFields2` is
renamed to `FormFields1`, which is generated-type churn. 2.2.3 adds the
Promise media helpers `withMediaRefs`, `mediaHashFromUri` and
`MediaRequestOptions`
([#60116](https://github.com/anomalyco/opencode/pull/60116)) and
persistent-PTY frame helpers in `solid/pty`. CodeNomad imports none of
these.
- **Schema:** 2.2.2 adds schedule conditions (`ShellCondition`,
`CheckMode`, …) and exports `SessionLegacy.part`. 2.2.3 adds
`CodeCondition*` and `policy.Action`.
- **Plugin:** 2.2.2 adds the GUI
`ComposerQuoteAnchor`/`ComposerFocusOptions`. 2.2.3 has no export
changes, but the optional peers `@opentui/*` become `>=0.8.0`
([#60385](https://github.com/anomalyco/opencode/pull/60385)). The
published `dependencies.effect` moves from **4.0.1 to 4.0.6** (2.2.2).
- **Typecheck:** the server and UI typecheck with **0 errors** against
the 2.2.2 and 2.2.3 client/schema/plugin/effect declarations through
scratch `paths` overrides. The pinned 2.1.0 control is also clean. The
harness now also remaps `effect` itself; the 2.2.1 harness skipped it
because effect's `exports` are plain `.js` strings.
- **Host modules:** the three daemon-loaded bundles still import
`@opencode/plugin` (`Plugin`), `/effect` (`Plugin`, `Rpc`, `Location`),
`/promise/adapter` (`fromPromise`), `/rpc` (`Rpc`), `effect` and
`effect/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.ts` is unchanged between the tags.
- **Host Effect: 4.0.1 → 4.0.6** (2.2.2,
[#59630](https://github.com/anomalyco/opencode/pull/59630)). The 2.1.0
`@opencode/schema`/`@opencode/client` code 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.
- **Private native services:** the keys (`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), a `CorrectedError.message` getter, and `Session`
deciding fork media from the parent's location.
- **Storage** (three migrations): `session_part` (new table),
`session_v2.time_busy` and four `schedule` check columns.
- **`session_part`**
([#58915](https://github.com/anomalyco/opencode/pull/58915)): history
reads now *prefer* `session_part` rows over
`session_message.data.content` for 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 **0**
`session_part` rows, and upstream `dev` (`1e28374e48`, 30 October) has
no writer either.
- The tables CodeNomad reads directly keep their shape.
- **Forward risk:** when a release starts writing parts, CodeNomad's
direct `session_message.data.content` readers and the pruning `UPDATE …
json_set(data,'$.content',…)` (`session-pruning/transaction.ts`) would
silently miss or be shadowed by part rows. See Recommendations.
- **Behavior changes checked:**
- **Image generation** (2.2.2): renamed and on by default. See the
[dedicated section](#design-companion-finding).
- **Code Mode:** inner tool progress no longer replaces the `execute`
call rows ([#60480](https://github.com/anomalyco/opencode/pull/60480)),
and progress is throttled to at most one update per 100 ms
([#60120](https://github.com/anomalyco/opencode/pull/60120)). Programs
see the same failure text as direct calls
([#60109](https://github.com/anomalyco/opencode/pull/60109)), `fetch` in
Code Mode is now subject to `webfetch` permissions
([#59954](https://github.com/anomalyco/opencode/pull/59954)), `for…in`
visits inherited keys
([#59933](https://github.com/anomalyco/opencode/pull/59933)) and `null`
survives text encoding
([#57639](https://github.com/anomalyco/opencode/pull/57639)). The
run-to-completion regression behind the 2.1.2 minimum passes on 2.2.3:
`{ total: 2000, beforeAwait: 1000, count: 1000 }`.
- **Permissions and MCP:** permission reasons and feedback are kept in
MCP tool failures
([#60102](https://github.com/anomalyco/opencode/pull/60102)). Shell
permission checks moved fully off tree-sitter and now check redirects,
PowerShell method calls and dynamic directory changes
([#60332](https://github.com/anomalyco/opencode/pull/60332),
[#60376](https://github.com/anomalyco/opencode/pull/60376)), so some
shell commands may now ask where they didn't. The managed-policy split
(`integration.use` vs `mcp.use`/`skill.use`/`plugin.use`) is a changelog
"breaking change" for managed deployments only.
- **Sessions:** deleting a session removes its persistent terminals
([#60322](https://github.com/anomalyco/opencode/pull/60322)), and a
persistent terminal with a missing cwd is rejected
([#60300](https://github.com/anomalyco/opencode/pull/60300)). Forked and
imported sessions store media as blobs when `media_blob` is on
([#60118](https://github.com/anomalyco/opencode/pull/60118)). The native
fork suite passes on 2.2.3.
- **Providers:** a built-in **Claude Agent** provider uses the user's
Claude Code login
([#58782](https://github.com/anomalyco/opencode/pull/58782)).
Plugin-supplied model variants are preserved
([#60097](https://github.com/anomalyco/opencode/pull/60097)), and the
dynamic provider npm fallback runs after external SDK hooks
([#60380](https://github.com/anomalyco/opencode/pull/60380)). These are
source-reviewed and touch no consumed contract.
- **Persistent PTY binary** (the process daemon): 2.2.2 switches from
`@opencode-ai/pty` 0.3.1 to `@opencode/pty` **0.3.3**
([#60276](https://github.com/anomalyco/opencode/pull/60276)).
`process-daemon-v0.3.3` contains only the release commit after 0.3.2 (a
version bump of the `@opencode/pty-*` binaries). **v0.4.0** (terminal
renaming, [#60324](https://github.com/anomalyco/opencode/pull/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.
- **Client fix not inherited:** the 2.2.3 client evicts missing sessions
on 404 ([#60459](https://github.com/anomalyco/opencode/pull/60459)).
CodeNomad stays on the 2.1.0 client.
- **`@opencode/ai` breaking changes** (media moved to `/experimental`,
one `tool()`): CodeNomad doesn't import `@opencode/ai`.
- Desktop/TUI-only changes (timeline performance, side-panel tabs,
spellcheck, question sounds, OpenTUI 0.8) don't affect CodeNomad.
**Verdict:**
- The **2.1.0 client/plugin pins stay**. Bumping them would bring
nothing CodeNomad consumes.
- No consumed HTTP/plugin contract broke; the breaking change is the
image tool contract of #60258, consumed by the Images gallery, the
Design agent and the Images settings.
- **Decision applied in this PR:** 2.2.3 becomes both the minimum and
the recommendation, and Images are implemented for the 2.2.3 contract
only (see Summary). This replaces the review's initial suggestion to
keep the 2.1.2 minimum and accept both tool names.
**Not verified here:**
- the full native suite matrix on 2.2.2/2.2.3; only the runs in Appendix
I were performed;
- the forward-compatibility of the `session_part` readers against a
runtime that writes parts (no such runtime exists yet);
- the Claude Agent provider and shell-permission changes at runtime
(source only);
- macOS/Linux.
---
## 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 with `Error: Timed out: series 1
generated and read`. Reproduced independently in a scratch checkout of
`origin/dev` (`f39642bc4`), with `CODENOMAD_NATIVE_FIXTURE_PARENT`
outside the home:
```
test-design-companion-native 2.2.1 exit=0 PASS AC-16, AC-52
test-design-companion-native 2.2.2 exit=1 Timed out: series 1 generated and read
test-design-companion-native 2.2.3 exit=1 Timed out: series 1 generated and read
```
**What the step waits for**
(`test-design-companion-native.mjs:238-241`): the fixture's loopback
chat provider answers a `RUN [...]` 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 with `metadata.source === "image_generate"` (line 209) and two
`read` parts 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](https://github.com/anomalyco/opencode/pull/60258)
(`d4e48f54da`, "turn experimental_image_generate on by default") changed
`packages/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 Job `type`, and the completion notice's
`metadata.source`. `session/execution/restart.ts` also tags
restart-recovery notices `source: "experimental_image_generate"`.
- The opt-in became default-on: `if (!enabled || imageGenerate(entries)
=== false) return`. An unset `experimental.image_generate` now offers
the tool; only `false` removes it ("still honored for configs from when
the tool was opt-in; permissions are the supported way to turn it off").
- The 2.2.2 changelog states it: "agents can now use
`experimental_image_generate` without additional configuration".
2.2.2 also wraps the notice text as `<image id="…"
state="…">\n…\n</image>` and adds `id` to the tool output
([#60098](https://github.com/anomalyco/opencode/pull/60098)).
CodeNomad's `Saved image: (.+) \(` parse still matches; `jobID`, `state`
and `path` metadata are unchanged.
**Evidence** (fixture DBs copied read-only to `evidence/`):
```
2.2.3, original fixture (calls "image_generate"):
seq 5 tool image_generate error "No tool named \"image_generate\" is currently available. Please use a tool from the available tool list." (x2)
-> no image job, no notice, no read: the wait times out.
2.2.3, fixture with the tool name changed (scratch copy; CodeNomad server code unmodified):
seq 5 tool experimental_image_generate completed {path:…/icon-minimal-a, model:"fiximg/image", jobID:"image_18cfb59…"}
seq 18 synthetic {"source":"experimental_image_generate","jobID":"image_18cfb59…","state":"completed",…}
<image id="image_18cfb59…" state="completed"> | Image request finished. Saved image: …
seq 20 tool read completed (x2) … seq 40/48/49 refinement
-> files, image calls and reads all correct, then
AssertionError: generations.length 0 !== 3 (test line 260: gallery "generation" entries)
```
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 `read` parts, but without prompt, model, lineage or
"proven" origin, and `galleryImageReference` refuses them as `changed`.
**CodeNomad code that assumes the 2.2.1 name or default** (origin/dev):
| Where | Assumption | Effect on 2.2.2+ |
| --- | --- | --- |
| `session-pruning/assets-gallery.ts:56,139` | `part.name ===
"image_generate"` | no generation entries; references refused `changed`
|
| `session-pruning/assets-gallery-proof.ts:104,130` | tool name and
notice `source === "image_generate"` | no calls or notices, so no
provenance proof |
| `opencode/image-settings.ts` `imageGenerationSelection(undefined) →
false` (doc: "Reads … as OpenCode does") | unset means off | Settings →
Images shows **Off** and `images.status()` returns `"disabled"` while
the tool is offered; help step "Turn on image generation"
(`docs/help/en/extensions.md:73`) is no longer needed |
| `opencode/design/opencode/agents/design.md`, `skills/design-core`,
`design-logo`, `design-ui-mockup` | the agent is told to call
`image_generate` | the model sees only `experimental_image_generate`;
the first call can fail with "No tool named …" |
| `scripts/test-design-companion-native.mjs:125,209,238,301` | fixture
calls/filters `image_generate` | red on 2.2.2+ |
| `scripts/test-design-activation-native.mjs:76,307` | ask rule on
action `image_generate`; tool detection by that name | **passes but
loses coverage**: `askTool` falls back to `webfetch` on 2.2.2/2.2.3
(logged) |
| `dev-docs/SESSION_PRUNING_RPC.md`, `PANEL_EXTENSIONS.md`,
`SETTINGS.md` | describe `image_generate` parts and opt-in | out of date
for 2.2.2+ |
User permission rules written for action `image_generate` no 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
use `experimental_image_generate`.
**Fix in this PR** (2.2.3 contract only, no fallback):
1. One `IMAGE_TOOL = "experimental_image_generate"` constant drives the
gallery and its proof; the gallery reads the 2.2.3 notice format.
2. The native fixtures use the new tool; the activation fixture
exercises it (default on) instead of falling back to webfetch.
3. The Design agent and skills name `experimental_image_generate`.
4. `imageGenerationSelection` reads an unset
`experimental.image_generate` as on; only `false` disables it. Settings
→ Images and `images.status()` report it that way.
## Follow-up
- Hardening for `session_part` (independent of this upgrade, no 2.2.3
regression): make the direct readers and the pruning transaction fail
safe when `session_part` has 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_PARENT` outside the user profile for the
Design fixtures.
- **Before the image fix (recommendation-only change):** every native
suite passed on 2.2.3 except the Design companion end to end, which
passes on 2.2.1 and times out on 2.2.2/2.2.3 at "series 1 generated and
read" (`No tool named "image_generate" is currently available`).
- **After this change, on 2.2.3:** Design companion (3 files, 2 series,
all proven, lineage), Design activation (image tool offered to Design
and Build, default on), Design content, image settings (default on,
Off→On hot reload), session pruning including session images/media, Code
Mode jobs `{ total: 2000, beforeAwait: 1000, count: 1000 }`.
- **Full native set on 2.2.3 (before the image fix, unaffected areas):**
plugin runtime, automation, 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, prompt skills,
providers, service usage, web search, plugin packages, output limit,
Promise-tool failure, subagent depth, temporary workspaces), migrations
from 2.1.2/2.2.1/2.2.2, install/upgrade/legacy replacement.
- **Units and checks after the change:** root and server typecheck
clean; server **2,825 passed, 9 skipped, 1 cancelled** (`index.test.ts`
shutdown 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; browser `opencode-setup` + `websearch-settings` 38/38; native
plugin build 2/2; ten-locale help 31/31.
13 KiB
Panel extensions
Browse installed and online extensions
- Open Customize right panel in the right-panel tab bar.
- Expand Extensions below the separator.
- Use Search extensions to search the single list of installed addons and the official online catalogue. Hover or focus an entry for its metadata.
The disclosure starts collapsed each time the customization popup opens. Installed and online entries are combined by extension ID; there is no separate manager window or installed/online mode. Browsing catalogue metadata does not download or execute the author's panel code. The catalogue is maintained in CodeNomad Extensions; inclusion and claimed author names are not signatures or proof of trust.
If the catalogue is unavailable, installed extensions and Install from ZIP… remain available. Refresh to retry discovery. Collapsing or dismissing the popup abandons its pending review/confirmation UI; it does not silently install anything.
Review and install disabled
- Choose Install… on a catalogue entry, or Install from ZIP… for a package you obtained separately.
- Review the package ID, author, repository, version, API/permissions and SHA-256 digest. Expand technical details when needed.
- Verify the publisher and, where available, compare the digest through a trusted channel.
- Only if you trust the code, select I trust this package and its author, then Install disabled.
Inspection downloads/revalidates the selected package; author code is not executed by inspection. A digest identifies exact bytes, not the author's identity. Catalogue packages come from the exact declared GitHub release asset, not an arbitrary URL. Manual packages are ZIPs containing only manifest.json and self-contained panel.html; invalid or unsupported packages are rejected.
Installation needs no OpenCode restart and never grants activation automatically. A failed validation, conflict or storage operation does not authorize a retry with different bytes. Reinspect the package before a new installation attempt.
Activate and understand the scope
- Return to the addon row above the Extensions disclosure in Customize right panel. Installed disabled addons remain in that list.
- Select its checkbox to activate it. Clear that same checkbox to disable it.
That checkbox is the only activation control: there is no extra manager switch or per-window visibility gate. Activation is configuration-wide across projects, not per folder. On a remote connection it affects the server's installed extension store, not just your viewing device.
The extension store is panel-extensions/ beside the selected CodeNomad configuration. Separate desktop data profiles do not isolate extensions when those profiles use the same configuration directory. Conversely, opening a different configuration is not a grant to run an addon installed elsewhere. See Desktop windows and data for the distinction.
An enabled panel can mount when selected; hiding it can dispose its temporary UI state. Disabling revokes its activation. Reset in customization only restores built-in layout and does not enable, disable or uninstall extensions.
Replace or remove
- Use Change version… or inspect another ZIP with the same ID to replace an installed package.
- Review the new digest and permissions, acknowledge trust, then install it disabled.
- Reactivate through the normal checkbox only after deciding to trust the replacement.
- To uninstall, use Remove… and Confirm removal after reviewing the target.
Every replacement revokes activation across all projects, even if the permission list is unchanged, and clears the panel's own saved settings. Validation/conflict/storage failure preserves the installed package. There are no silent extension updates. Removal deletes the package, its activation settings and its saved panel settings, not OpenCode sessions or project files. To recover from unwanted code, disable it first; reinstall only a verified trusted package.
Context, asset consent and sandbox limits
| Contract | What activation permits |
|---|---|
API 1 / session.context |
Current session identifier (or none), interface language and appearance. No transcript text or filesystem access. |
API 2 / session.assets.read plus context |
Explicitly reviewed permission to list bounded current-session tool-result image/attachment metadata and read supported embedded bytes. It is not general transcript, file or URL access. |
API 3 / repository.workItems.read plus context |
Explicitly reviewed permission to list bounded pages of the project repository's issues and pull requests on GitHub, GitLab or Gitea/Forgejo, and to open one of them in the browser. It is not file, credential or general network access. |
| API 4 / context plus one or more of the permissions below | The review lists one sentence per declared permission; activation grants all of them together, and a replacement revokes them all. |
session.images.read |
Read the images returned by the read tool in the displayed conversation, with generation status and lineage when they can be verified. |
session.images.prompts (needs session.images.read) |
Read the prompt and model of each image generated in the project's Images conversation. |
composer.prefill |
Add text to the message box of the displayed conversation. It never sends a message. |
images.conversation |
Open or start this project's Images conversation. |
opencode.designAgent |
Adds CodeNomad's Design agent and its image skills to OpenCode while this extension is enabled. |
panel.preferences |
Remember this panel's own settings. |
Asset access follows the currently mounted host/session, not an author-selected session. Local/remote references may be listed as unavailable; the asset API does not fetch arbitrary remote URLs or open local files. Switching sessions, disconnecting or revoking consent invalidates old access.
With API 4, image reading follows the displayed conversation in the same way, and only shows the images the agent read back into it. An extension with session.images.prompts can read the prompts you or the agent wrote for images in an Images conversation, and could pass them on despite the sandbox, so grant it only to authors you trust with them. Text added by composer.prefill stays in the message box until you edit it or select Send message; it never starts with a command, never ends with an open @ mention, and cites image paths as plain text. Undoing with Ctrl+Z (Cmd+Z on macOS) in the message box does not remove it. Saved panel settings belong to this CodeNomad profile, at most 4 KiB per extension; disabling an extension keeps them.
Work items come from the project folder's upstream remote, else origin. CodeNomad reads them with the GitHub, GitLab or Gitea CLI you already signed in to (gh, glab, tea), or anonymously for public repositories, and stores no token; the extension sees only the repository name and the items. Anonymous reads cover github.com, gitlab.com, codeberg.org and gitea.com; a self-hosted forge is read only through its CLI signed in to it. Opening an item asks CodeNomad to open that item's page on the same forge in your browser, at most once a second; the extension cannot open other addresses.
Author code runs in a restricted iframe, not in the trusted application renderer. It receives no native/OpenCode bridge, credential access, general RPC, forms, popups or downloads; network APIs and external resources are restricted. Extensions are UI addons, not OpenCode plugins.
The sandbox is not process/CPU isolation or a promise that approved code is harmless. A hostile extension can disrupt its renderer. Install only trusted authors, do not provide secrets, and disable/remove an addon if it behaves unexpectedly.
Create images with the Design extension
The Design extension is a light table for the images you create with CodeNomad's Design agent in a project's Images conversation. It shows the images, prepares requests in the message box and leaves every generation to the messages you send.
- Connect a provider that offers an image model. Image generation is on unless it was turned off in Settings → Providers → Images (Image generation and storage).
- In Customize right panel → Extensions, install Design from the catalogue (Review and install disabled) after reviewing its permissions (Context, asset consent and sandbox limits), then select its checkbox (Activate and understand the scope).
- Open the Design panel and choose Open the Images conversation.
While the extension is enabled, CodeNomad adds its Design agent and image skills to OpenCode. Your own design agent and your own design-* skills always take precedence; when you supply your own design agent, the panel shows Your own Design agent is used. The panel also says when image generation, an image model or the Design agent is missing.
Images conversation
Each project folder or worktree has its own Images conversation, titled Images. Open the Images conversation finds it, or creates it with the Design agent and the model CodeNomad picks for a new conversation with that agent, then shows it with the focus in the message box; opening sends nothing. An existing Images conversation keeps its own model. New Images conversation starts another one with an empty light table, and the panel suggests it once a conversation holds many images; Open then shows the newest one. Archived and deleted Images conversations are skipped.
Describe what you need and send it: the agent asks a few framing questions, says how many images it will generate, generates them, reads each one back and reviews it. Images are saved in .codenomad/design/ of the project folder. CodeNomad adds that folder to the repository's local Git exclusions (.git/info/exclude), leaving .gitignore untouched; when it cannot, the panel shows Images in this location may appear in Git. Undoing a turn removes its images from the light table but keeps their files, and an image the agent overwrote cannot be recovered. The agent asks for images of about 1K pixels, yet providers may return larger ones, and several large images at once can use a lot of memory in the shared OpenCode service.
If the Design agent is missing from an Images conversation that uses it, the message box shows The Design agent is not available and keeps sending blocked: choose Use the default agent, or enable the Design extension again. If the conversation's chat model cannot receive images, the panel warns that the agent can't critique them visually; the images it reads back still appear on the light table.
Light table
The Light table shows the images the agent read back into the displayed conversation, newest first, grouped by reply. Selecting an image shows it larger with Small sizes previews at 16, 32 and 64 px; Compare puts two images side by side, and View full size opens a viewer. In a narrow panel, widen it to compare. Lineage (Made from, Used for), and in the Images conversation the prompt and model, appear only when CodeNomad can verify which generation produced the image; otherwise the image keeps its pixels and reads origin not verifiable. Generations lists each image generation with its state; follow its progress in the conversation. In another conversation, the panel shows that conversation's read-back images without prompts or requests and offers to open the Images conversation.
In the Images conversation, Start from (a recipe such as an app icon or a logo), Refine, Variants, Apply direction, Save to project…, Ask the agent to show it and Retry only add editable text to the message box: the panel never sends a message or generates an image. Actions about a chosen image cite its path relative to the project, and only images of the project with a verifiable origin can be cited. Edit the request, then send it yourself. Save to project… asks the agent to copy the image to the destination you type after the request. Images per turn (1 to 4, 3 by default) is included in requests that generate new images and kept in the panel's saved settings. On a phone, the panel floats and closes when it adds a request.
Use the Design agent without CodeNomad
The Design agent and skills are ordinary OpenCode files. To use them in the OpenCode TUI or another client without CodeNomad, copy the contents of the Design content folder into a project's .opencode/ folder or your global OpenCode configuration folder (~/.config/opencode/), keeping NOTICE.md and the licence beside them. Then select the design agent; it loads its skills by name, and other agents use them only when you name them.
The Design agent and skills adapt material from Open Design (Apache-2.0) and taste-skill (MIT); NOTICE.md lists each adapted file. The Design extension reuses code from the MCP Assets extension (MIT).