Repository navigation
feat(process-daemon): rename terminals - #60324
Conversation
There was a problem hiding this comment.
Looks good. I ran cargo test, cargo fmt --check and cargo clippy -D warnings in packages/process-daemon, all passing, including the new rename, OSC-precedence, creation-order and real-binary rename_is_advertised_and_served tests. I also checked that the TS client in packages/core/src/persistent-pty/daemon.ts still decodes the new fields and doesn't rely on list order.
Public API (the opencode-pty wire protocol, still v7):
- New request
rename { id, title: string | null }→Ok. - New field
TerminalInfo.user_title(always serialized,nullwhen unset). - New
features: string[]onPongand inservice.json. listnow returns terminals in creation order instead of by id.- An empty OSC title now falls back to the title given at creation instead of setting
"".
This is backwards compatible in both directions: the new fields default when missing, and clients are meant to check features before sending rename. No other concerns.
There was a problem hiding this comment.
The new commit looks correct. It sets the user title and recomputes the shown title under one lock, so a reader can no longer see the new user title next to a stale shown title. I reran cargo test, cargo fmt --check and cargo clippy -D warnings in packages/process-daemon, and all passed. The public API is the same as in my last review (rename request, TerminalInfo.user_title, features on ping/registration, list in creation order; backwards compatible). No new concerns.
Brings in 178 upstream commits (merge base 5f1f8d0, upstream tip 6b11312 "feat(process-daemon): rename terminals (anomalyco#60324)"). 15 files conflicted. None involved session move: since move was restored in 58fe982, upstream's move changes merge cleanly. Same rule as the last merge: keep fork behavior from PATCH.md, take upstream everywhere else. Conflict resolutions ==================== CI - .github/workflows/release.yml: kept the fork's datever release workflow. Upstream's pipeline only runs on anomalyco/opencode. Core - core/src/database/migration.gen.ts: the fork's 20261016185441_session_roots stays m50. Upstream's migrations follow in timestamp order as m51-m58, including the four new ones: schedule, session_part, session_time_busy, schedule_checks. - core/schema.json: both sides advanced the drizzle snapshot. The new snapshot id has prevIds [fork 8c8158eb..., upstream 0ac88673...]. Its DDL is upstream's plus the fork's `session.roots` column. `bun script/migration.ts --check` reports no pending changes. - core/src/plugin/internal.ts: kept the fork's CancelTool and upstream's new ScheduleTools next to each other. - core/src/session.ts: took upstream's fork publish, which now provides SessionBlob.ForkMedia (media copied as blobs based on the parent's location), and kept the fork's `destination` field so `session.fork({ directory })` still places the fork in another directory. - core/test/session-skill.test.ts: kept the fork's test setup (SessionRoots node, SessionStore replacement) and took upstream's renamed policy rule: `skill.use` on resource `effect` instead of `integration.use` on `skill:effect`. Plugin API - plugin/src/effect/session.ts and plugin/src/promise/session.ts: upstream moved `wait` and `instructions` out of SessionDomain into a Pick of the new ExperimentalSessionApi. Took that and kept `move` and the fork's `root` in SessionDomain. TUI - tui/src/config/index.tsx: kept the fork's `model_keywords` and took upstream's change that makes `cursor` always resolved (no longer optional). - tui/src/routes/session/dialog-message.tsx: upstream dropped the `setPrompt` prop in favor of usePromptRestore. Took that and kept the fork's `jump` prop, which shows "Jump to" only when opened from the timeline. - tui/src/routes/session/dialog-timeline.tsx: upstream's call without setPrompt, plus the fork's `jump`. - tui/src/ui/dialog-select.tsx: kept the fork's category `color` prop (provider colors) and took upstream's token change from `theme.text.faint` to `theme.text.muted` for the fallback. Generated files - packages/client/src/effect/api/api.ts, packages/client/src/promise/generated/types.ts, packages/protocol/openapi.json: regenerated with `bun run generate` in packages/protocol, packages/client and services/www, then formatted with prettier. The spec has both the move and root endpoints. Verification - bun install (new upstream dependencies) - bun turbo typecheck: 35/35 packages - oxlint: 0 errors (run without --fix) - bun script/migration.ts --check: no schema changes - make tests: tui 187, core 455, schema 7, sdk 3 pass, 0 fail
…ct (#1209) ## Summary - OpenCode **2.2.3** becomes both the **technical minimum** and the **recommended** runtime. The minimum moves because upstream [#60258](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#60480)), and progress is throttled to at most one update per 100 ms ([#60120](anomalyco/opencode#60120)). Programs see the same failure text as direct calls ([#60109](anomalyco/opencode#60109)), `fetch` in Code Mode is now subject to `webfetch` permissions ([#59954](anomalyco/opencode#59954)), `for…in` visits inherited keys ([#59933](anomalyco/opencode#59933)) and `null` survives text encoding ([#57639](anomalyco/opencode#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](anomalyco/opencode#60102)). Shell permission checks moved fully off tree-sitter and now check redirects, PowerShell method calls and dynamic directory changes ([#60332](anomalyco/opencode#60332), [#60376](anomalyco/opencode#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](anomalyco/opencode#60322)), and a persistent terminal with a missing cwd is rejected ([#60300](anomalyco/opencode#60300)). Forked and imported sessions store media as blobs when `media_blob` is on ([#60118](anomalyco/opencode#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](anomalyco/opencode#58782)). Plugin-supplied model variants are preserved ([#60097](anomalyco/opencode#60097)), and the dynamic provider npm fallback runs after external SDK hooks ([#60380](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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](anomalyco/opencode#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.
## 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**.
Issue for this PR
Closes #
Type of change
What does this PR do?
Adds terminal renaming to the PTY daemon, so a name the user sets survives the shell changing its own title. Today a terminal has one
title, and OSC title sequences overwrite it, so many prompts (oh-my-zsh, starship) would wipe a rename at the next prompt.rename { id, title }: sets a user title;nullor""clears it. It runs on the terminal's actor thread, so it's ordered with output.TerminalInfo.titlestays the title to show: the user title, else the shell's OSC title, else the title given at creation. Empty strings count as unset. A newuser_titlefield is always serialized (nullwhen unset).TitleChangedis sent only when the shown title changes, so an OSC change hidden behind a user title sends nothing.listreturns terminals in creation order instead of by their random id.service.jsonadvertisefeatures: ["rename"].PROTOCOL_VERSIONstays 7, so a running daemon is still adopted across a server upgrade (no terminals lost), and every server/daemon version pairing works: the new fields default when missing, and OpenCode will only sendrenameto a daemon that lists the feature.SPEC.mddocuments that rule.No version bump here; that's a separate release PR. Nothing in OpenCode uses this until it bumps
@opencode/pty.How did you verify your code works?
cargo fmt --check,cargo clippy --all-targets --all-features -- -D warningsandcargo testinpackages/process-daemon. New tests cover rename visible in list/snapshot/subscribe with oneTitleChanged, OSC titles not overriding a rename, clearing restoring the OSC title, empty titles, unknown ids, creation order, andfeaturesin ping andservice.json(against the real binary). Windows code was checked with clippy forx86_64-pc-windows-msvc; CI's Windows job runs the tests.Checklist
Added by the opencode bot (AI-generated).
Public API: adds the
renamerequest,TerminalInfo.user_titleandfeatureson ping/registration to the opencode-pty protocol v7;listnow in creation order (backwards compatible)