Repository navigation
fix(ai): stop demoting unsigned and interrupted reasoning to text - #60498
Conversation
There was a problem hiding this comment.
I found one regression worth fixing before merge. Nothing else blocks it.
The fix behaves as described. I compiled requests with unsigned reasoning on the base and on this branch, and ran the changed packages/ai provider tests, the packages/core session runner tests and typecheck:tsc (all pass). On the base, unsigned reasoning became text. Here, recognised signed families drop it and open-weight models get native unsigned thinking.
Public API: adds requireSignature?: boolean to Model.Compatibility and Provider.Compatibility in @opencode/schema, the OpenAPI spec and the generated client types. Adding it is backwards compatible. The default changes, though: for model IDs that match none of the listed families, an unset value used to mean "require a signature" (turn into text) and now means "send unsigned thinking".
Concern: the new default depends on the model ID matching a name pattern. Claude reached through an ID that doesn't contain claude now gets unsigned thinking blocks where it used to get text, which Anthropic answers with HTTP 400. See the inline comment.
| // Closed reasoning families sign or encrypt their thinking blocks; open-weight | ||
| // Messages and Converse implementations replay unsigned reasoning text. | ||
| export const requireThinkingSignature = (model: LLMRequest["model"]) => { | ||
| if (model.compatibility?.requireSignature !== undefined) return model.compatibility.requireSignature |
There was a problem hiding this comment.
The default now depends on the model ID matching a name pattern. When it doesn't match, we send unsigned thinking. That's a regression for Claude reached through an ID without claude in it. I checked two cases:
- A Bedrock application inference profile ARN (
arn:aws:bedrock:…:application-inference-profile/<id>) now lowers foreign reasoning toreasoningTextwith no signature. - A gateway alias such as
sonnet-latestbehind a LiteLLM-style proxy (notapi.anthropic.com) now gets{ type: "thinking", signature: "" }.
Both used to become text, and both now hit the 400s this PR is meant to avoid. The user has to find and set requireSignature themselves. Could unknown IDs keep a safe fallback, for example dropping instead of sending unsigned thinking unless the provider or model opts in? Or could the ARN case be detected (via the inference profile's underlying model)? Either way, I'd add a test for the ARN case.
There was a problem hiding this comment.
The new commit fixes my earlier point. Bedrock application inference profile ARNs and the Claude aliases (sonnet, opus, haiku, duo-chat-*) are now treated as signed, so their unsigned reasoning is dropped instead of being sent. Both cases have tests now. The affected packages/ai provider tests and typecheck:tsc pass. The public API is unchanged since my last review (requireSignature is still the only addition, and it is backwards compatible). I have no new concerns.
b1fd9db to
8b40946
Compare
There was a problem hiding this comment.
The rewrite settles my earlier point. Model IDs outside the allowlist now default to requiring a signature, and Bedrock Converse drops all unsigned reasoning, so Claude reached through a profile ARN or an alias like sonnet-latest can't get unsigned thinking blocks any more. The prefix: true change in Mistral only behaves differently when a trailing message with nothing to send is skipped, and the new test covers that. I re-ran the changed packages/ai provider tests and the packages/core session runner tests, and ran typecheck:tsc in both packages; all pass. The public API is unchanged: the schema, OpenAPI and client changes from the first version are gone. I have no new concerns.
8b40946 to
60755d6
Compare
There was a problem hiding this comment.
The rebase broke two of the tests this PR adds: they still call LLM.request, which dev renamed to ai.request. Both fail with ReferenceError: LLM is not defined (see inline). The rest of packages/ai/test/provider and the packages/core session runner tests pass, as does typecheck:tsc in packages/ai. The only other new change is that Gemini and Interactions now add skip_thought_signature_validator to unsigned tool calls on every model, and the updated tests cover it. My earlier point stays settled. The public API is unchanged.
packages/ai/test/provider/mistral-chat.test.ts:181: Same rebase problem:LLM.requestthrowsReferenceError: LLM is not definedhere. It should beai.request.
| expect(prepared.body.previous_interaction_id).toBeUndefined() | ||
|
|
||
| const unsigned = yield* compileRequest( | ||
| LLM.request({ |
There was a problem hiding this comment.
After the rebase, LLM isn't imported here any more (dev renamed it to ai), so this test fails with ReferenceError: LLM is not defined. Changing this to ai.request fixes it.
60755d6 to
37cabe9
Compare
There was a problem hiding this comment.
The two tests that broke in the rebase now use ai.request and pass. All of packages/ai/test/provider passes, as does typecheck:tsc in packages/ai. The one other new change is in google-interactions.ts: a signed reasoning step now counts like a signed tool call, so unsigned tool calls after it no longer get skip_thought_signature_validator. The public API is unchanged. I have no new concerns.
test(ai): update Gemini rich tool failure test for upstream anomalyco#60498
## 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**.
* fix(core): have agents ask before generating an image nobody asked for (anomalyco#60256) * release: v2.2.1 (anomalyco#60113) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com> * feat(core): turn experimental_image_generate on by default (anomalyco#60258) * feat(app): open the worktree picker from the new session branch (anomalyco#60263) * feat(app): redesign the todo pill and share the composer pill styling (anomalyco#59979) * fix: type the Go and ChatGPT usage limits (anomalyco#57897) Co-authored-by: Aiden Cline <aidenpcline@gmail.com> * chore(core): use @opencode/pty 0.3.3 (anomalyco#60276) * fix: show the Go usage limit's reset time, Go page link and a composer tray (anomalyco#60284) Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> * fix(tui): preserve captured attachments and source paths (anomalyco#60222) * perf(app): keep each tab's timeline when switching directories (anomalyco#59168) * fix(tui): prevent truncateMiddle overflow at small widths (anomalyco#60287) * fix(core): reject persistent terminals with a missing cwd (anomalyco#60300) * feat(core): gate scheduled prompts with shell checks (anomalyco#60137) * fix(session-ui): name a pending steer's model only when it changes (anomalyco#59310) Co-authored-by: vimtor <36263538+vimtor@users.noreply.github.com> * feat: link live system status from site footers (anomalyco#60223) * feat(go): add Go Teams plan and limits to /go (anomalyco#60308) * fix(tui): match the empty dialog search cursor to the highlighted row (anomalyco#60310) * feat(core): integration.connect and integration.use policies (anomalyco#60315) * fix(core): send execute progress at most every 100 ms (anomalyco#60120) * fix(core): keep permission reasons and feedback in MCP tool failures (anomalyco#60102) * release: v2.2.2 (anomalyco#60337) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> * fix(app): keep question options and submit controls reachable on mobile (anomalyco#60335) * fix(tui): resolve default cursor style across config reloads (anomalyco#60302) * fix(console): polish Go plan cards and audience switch (anomalyco#60345) * fix(tui): keep # in file search base when not followed by a line range (anomalyco#60336) * feat(core): add Code Mode checks with static tool scanning to schedules (anomalyco#60140) * refactor(core): finish moving shell permission checks off tree-sitter (anomalyco#60332) * fix(core): fail schedule creation when a code check throws and pause invalid checks immediately (anomalyco#60349) * feat(tui): load stored images by reference, like desktop (anomalyco#60116) * fix(tui): highlight PHP code blocks without opening tag (anomalyco#60359) * fix(app): keep inbox scroll position when opening a session (anomalyco#60356) * chore(console): move console and stats to released solid-start (anomalyco#60077) * feat(app): add question notifications and sounds (anomalyco#60358) Co-authored-by: Sayuri Nekomiya <contactme@awsl.rip> * fix(core): remove a session's terminals when it is deleted (anomalyco#60322) * fix(app): preserve command selection under a stationary pointer (anomalyco#60368) * fix(app): preserve simultaneous request notifications (anomalyco#60378) * feat(app): allow selecting question answer text (anomalyco#60372) * chore(release): announce releases in Discord again (anomalyco#60371) * feat(process-daemon): rename terminals (anomalyco#60324) * chore(process-daemon): release 0.4.0 (anomalyco#60325) * feat(app): group added projects in settings (anomalyco#60221) * refactor(client): share the persistent pty protocol (anomalyco#60323) * fix(core): check redirects, PowerShell method calls, and dynamic directory changes (anomalyco#60376) * test(app): mock persistent pty routes (anomalyco#60327) * fix(console): use default OG image on /go (anomalyco#60397) Co-authored-by: adamdotdevin <adamdotdevin@users.noreply.github.com> * fix(core): run dynamic provider npm fallback after external sdk hooks (anomalyco#60380) * fix(ai): accept typed tools in loop (anomalyco#60390) * feat(tui): mark the selected composer suggestion under new designs (anomalyco#60393) Co-authored-by: jlongster <17031+jlongster@users.noreply.github.com> * feat(ai): type OpenRouter options on the Responses route (anomalyco#60044) * fix(ai): don't send filename with file_url on Responses (anomalyco#60382) * fix(app): tidy the Extensions submenus (anomalyco#59468) * chore(release): require notes for stable releases and remove old changelog tools (anomalyco#60429) * fix(core): show Code Mode programs the same failure text as direct calls (anomalyco#60109) * fix(app): ⌘W closes the side panel when nothing in it is left to close (anomalyco#59002) * fix(app): keep the mobile drawer open while a session loads (anomalyco#59427) * fix(codemode): preserve null in text encoding (anomalyco#57639) * fix(app): keep the @ menu's highlight and Enter on current results (anomalyco#59434) * refactor(ai): one tool() for both clients (anomalyco#60407) * fix(ai): keep polling queued generations through unknown statuses (anomalyco#59988) * fix(codemode): copy extension results without trusting their own methods (anomalyco#58031) * fix(desktop): prevent main bundle require collision (anomalyco#60430) * fix(app): stabilize side-panel click targets across reactive updates (anomalyco#60436) * fix(core): preserve plugin-supplied model variants (anomalyco#60097) * fix(app): give focus back when ⌘W closes the side panel's last New tab (anomalyco#60435) * fix(app): feather the sidebar list's scroll edges (anomalyco#59467) * feat(app): page the timeline from the composer (anomalyco#59426) * fix(cli): allow blob frames and objects in web UI CSP (anomalyco#60453) * fix(ai): keep tools with a throwing execute runnable (anomalyco#60431) * fix(app): derive model reasoning capability from variants and compatibility (anomalyco#60457) * fix(desktop): sign zip data descriptors in debug log export (anomalyco#60456) * fix(app): match own server loopback port before treating http servers as local (anomalyco#60460) * feat(desktop): spellcheck toggle and system-language dictionaries (anomalyco#59129) Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com> * fix(app): insert consecutive dropped file mentions in composer (anomalyco#60463) * fix(app): render inline content for built-in skills (anomalyco#60464) * fix(desktop): retain renderer permissions across shared-session windows (anomalyco#60458) * fix(client): evict missing sessions on 404 instead of retrying or toasting (anomalyco#60459) * fix(app): keep project avatars on busy session rows (anomalyco#60384) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> * fix(app): keep sidebar search muted on hover and show its shortcut (anomalyco#60046) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> * fix(desktop): ship the Icon Composer app icon on macOS (anomalyco#60020) Co-authored-by: iamdavidhill <iamdavidhill@users.noreply.github.com> * fix(app): tidy the pairing dialog's layout (anomalyco#59659) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> * fix(ai): match the Promise client to the Effect API (anomalyco#60465) * chore(tui): use OpenTUI 0.8.0 (anomalyco#60385) * feat(app): fit the session header at narrow timeline widths (anomalyco#59512) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> * fix(app): match the Context tab's close button to other side panel tabs (anomalyco#59511) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> * fix(app): remove context tab indicator tooltip (anomalyco#59505) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> * fix(app): show the review icon on the files changed tab (anomalyco#58358) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> * fix(app): size the agent menu to its options (anomalyco#60026) Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com> * fix(app,gui-plugins): port renderer UUID generation to HTTP-safe uuid() (anomalyco#60462) * fix(app): fade home project list edges on scroll (anomalyco#52358) * feat(app): copy version from about settings (anomalyco#52356) * fix(app): tighten tab preview spacing and wrap paths between folders (anomalyco#60467) * fix(app): never hide every server from Home (anomalyco#60454) Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com> * feat(ai): keep partial output on errors (anomalyco#60477) * fix(cli): report usage for steps that end after run goes idle (anomalyco#60475) * fix(core): keep inner tool progress from replacing execute's call rows (anomalyco#60480) * fix(app): align queue row action buttons (anomalyco#60485) * fix(app): stop the ResizeObserver loop error while timeline rows grow (anomalyco#60481) * fix(session-ui): compare timeline rows without Effect structural hashing (anomalyco#60489) * refactor(ai): move speech, transcription, video and realtime to experimental (anomalyco#60484) * fix(app): stop scrolling toward an end that page zoom cannot reach (anomalyco#60488) * fix(app): make the session details popover match menus (anomalyco#60494) * fix(desktop): restore maximized windows on their saved display (anomalyco#60482) * feat(app): split new session project and worktree triggers (anomalyco#60486) * fix(core): apply webfetch permissions to Code Mode fetch (anomalyco#59954) * fix(ui): remove select dropdown animation (anomalyco#60499) * fix(desktop): describe macOS folder access in the app bundle (anomalyco#60503) * fix(app): keep models with unknown release dates visible (anomalyco#60496) * fix(app): keep settings nav scroll inside the list (anomalyco#60504) * feat(core): add Claude Agent provider (anomalyco#58782) * fix(session-ui): compute highlight styles once per subtree in Chromium (anomalyco#60513) * docs(go): raise DeepSeek V4.1 Flash limits to $100/$200/$105 (anomalyco#60515) Co-authored-by: MrMushrooooom <19261047+MrMushrooooom@users.noreply.github.com> * docs(go): revert DeepSeek V4.1 Flash limits to $80/$160/$85 (anomalyco#60532) Co-authored-by: MrMushrooooom <19261047+MrMushrooooom@users.noreply.github.com> * fix(ui): watch the toast portal instead of the whole page (anomalyco#60537) * fix(session-ui): relabel code copy buttons only when the language changes (anomalyco#60538) * fix(app): stop :has() rules restyling every ancestor on DOM insertion (anomalyco#60521) * fix(session-ui): send and render only the markdown blocks a delta changed (anomalyco#60562) * fix(session-ui): allocate less per streamed markdown delta (anomalyco#60549) * release: v2.2.3 (anomalyco#60564) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(tui): span vertical session tab pulse across full tab width (anomalyco#60338) * test(tui): drop PHP highlighting test (anomalyco#60408) * fix(app): simplify the session location popover (anomalyco#60492) * fix(session-ui): stop re-checking the timeline projection on every streamed delta (anomalyco#60577) * fix(session-ui): copy user messages from the actions menu (anomalyco#60576) * fix(core): drop Claude Code's /login hint from login errors (anomalyco#60592) * feat(app): guide Claude Agent setup before connecting (anomalyco#60603) * feat(app): add base branch picker shortcut (anomalyco#60579) * feat(core): add experimental artifacts with pluggable providers (anomalyco#60351) * feat(core): rename persistent terminals (anomalyco#60329) * fix(tui): add right padding to edit and patch diffs (anomalyco#60614) Co-authored-by: Tarquinen <78942835+Tarquinen@users.noreply.github.com> * fix(app): list commands above sessions in command palette search (anomalyco#60616) Co-authored-by: jlongster <17031+jlongster@users.noreply.github.com> * fix(tui): keep plugin-set prompt cursor styles (anomalyco#60599) Co-authored-by: Tarquinen <Tarquinen@users.noreply.github.com> * chore(core): use @opencode/pty 0.4.0 (anomalyco#60387) * feat(core,server): enable schedules by default and run loop in opencode serve (anomalyco#60478) * docs(go): raise DeepSeek V4.1 Flash limits to $100/$200/$105 (anomalyco#60625) Co-authored-by: MrMushrooooom <19261047+MrMushrooooom@users.noreply.github.com> * refactor(ai): one ai object for Promise and Effect (anomalyco#60516) * chore(release): leave AI package changes out of the changelog (anomalyco#60631) * Update CHANGELOG.md to remove breaking changes Removed breaking changes section regarding `@opencode/ai` and updated the changelog with new features and fixes. * Update CHANGELOG with new features and breaking changes Added breaking changes section and updated features. * refactor(core): store background job markers in a SQLite job table (anomalyco#60491) * feat(app): move the desktop terminal to persistent PTYs (anomalyco#60346) * fix(cli): keep loopback requests off environment proxies (anomalyco#60441) Co-authored-by: r266-tech <r266-tech@users.noreply.github.com> Co-authored-by: pj.reid <pj.reid@zetier.com> * fix(core): allow local config to override Console-supplied providers Reverts the lock from anomalyco#60315 that ignored user, project and remote overrides on providers the Console supplies. It also blocked harmless overrides such as model limits, names and variants (anomalyco#60597) and internal gateways. * fix(app): keep terminal tab clicks and renames on the right tab (anomalyco#60645) * chore: update nix node_modules hashes (anomalyco#60187) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(core): keep schedules off unless experimental.schedules is enabled Restores the opt-in default from before anomalyco#60478. The schedule loop still runs in every server process and keeps its leader election; it only fires schedules in locations that enable the flag. * feat(ui): add flat input surfaces and compact sizing (anomalyco#60598) * fix(app): add top inset to recently closed menu search (anomalyco#60652) * fix(core): cap compaction's prompt size at the context window (anomalyco#60660) * release: v2.2.4 (anomalyco#60664) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(ai): stop demoting unsigned and interrupted reasoning to text (anomalyco#60498) * feat(core): accept a percentage for compaction.buffer (anomalyco#60647) * fix(app,tui): hide MCP integrations from the connect menu (anomalyco#60675) * fix(core): keep MCP quiet while a location shuts down (anomalyco#60668) * feat(app): send quotes directly from the comment popup (anomalyco#60640) * fix(core): run calls to a bash tool as shell (anomalyco#60687) * fix(stats): scroll dense charts to latest data in Safari (anomalyco#57459) * fix(core): show the closest lines when edit misses oldString (anomalyco#60689) * fix(ci): fail desktop publish when an R2 upload fails (anomalyco#60696) * fix(core): say which folder is missing when read misses (anomalyco#60688) * fix(ai): recognize LiteLLM context overflow errors (anomalyco#60692) * fix(core): frame MCP stdio messages without the SDK read buffer (anomalyco#60694) * feat(app): add worktree picker shortcuts and toggle shell mode (anomalyco#60713) * chore: refresh docs and provider ordering (anomalyco#60731) * release: v2.2.5 * refactor(tui): drop the full-screen TUI and keep only what cli imports Forge's interface is the desktop app, and the opencode binary only hosts its server. packages/tui keeps the modules cli still imports (config, mini, form, plugin discovery, editor, model preference, spinner, logo) as an internal library and loses its root entry. The default command now prints a pointer to the desktop app, `opencode mini` and `opencode run`. Removes the TUI dev harness, update preflight, the dev:vite scripts, and the tests for deleted files. * chore(desktop): rebrand to Forge and port the release setup Names, app IDs (io.github.divyanshusingh2903.forge), the forge:// scheme, window and app titles, desktop icons, the macOS Icon Composer art, favicons, the terracotta accent, and the FORGE wordmark (home, splash, About, connect). Publishing points at the Forge GitHub releases. Adds forge.json, the forge-release workflow (builds the server for each target, then the app), the version and update-manifest scripts, NOTICE and a README that lists what is not ported yet. Removes the OpenCode-shaped lodger, pin board and brick breaker on the home screen, and the legacy OpenCode Linux launcher. * feat(desktop): draw Linux window controls on the tab bar Linux hides the native title bar and uses Electron's title bar overlay, as Windows does, so minimize, maximize and close sit on the same bar as the tabs. The tab bar, top inset, header drag areas and splash follow the Windows overlay sizing, and the overlay refreshes with zoom and theme changes. * feat(app): add persistent projects sidebar with pinned and collapsible sessions A resizable, collapsible Projects sidebar in the shell for the horizontal-tabs layout on every route. Each project lists its sessions (remembered open or closed, eight at a time), sessions can be pinned into a Pinned section that shows each session's project icon, and only the session on screen is highlighted. Home drops its own Projects column while the sidebar is showing. Adds the pin icons, English strings, and a per-project session index on the Home sessions controller. * feat(app): give the projects sidebar its own open setting The top-tabs Projects sidebar had shared the side-tabs sidebar's open state, so hiding one hid the other after a layout switch. It now saves projectRailOpen separately, and the sidebar toggle acts on whichever sidebar the layout has. * chore(app): use the Forge name in app strings Replaces OpenCode with Forge where the text names the app: settings descriptions, the About page and its website link, project and workspace hints, error and pairing messages, and the voice assistant label, in every locale. OpenCode's own services (Go, Console, Free), the opencode command and file names, and the trademark attribution keep their names. * feat(app): float the hidden Projects sidebar on hover Resting on the window's start edge or the show button peeks the hidden rail over the content, reusing the vertical-tabs sidebar peek. Its header button docks it, and leaving it hides it again. * feat(core): add permission modes and Plan mode Sessions carry a Forge permission mode (Manual, Accept edits, Auto) in their metadata, applied by a permission hook that only ever turns asks into allows. Plan mode replaces upstream's Plan agent with Forge's v1 workflow: the plan agent may edit only plan files under .opencode/plans, follows the v1 plan prompt and format, and presents the plan with present_plan. Accepting switches to Build in the chosen mode; revising carries the user's feedback. Build can suggest Plan with plan_enter, and a prompt that asks for a plan is nudged to it. * feat(app): add mode picker, plan dock and plan review tab The composer's agent picker becomes one mode list (Manual, Accept edits, Auto, Plan) that Mod+. cycles, and the auto-approve setting picks a new session's starting mode. A Plan tab in the side region shows the plan file live with a section outline, opens when a plan is presented, and lets the user quote parts of it. The plan dock answers plan forms with numbered shortcuts; quotes and their comments join a revision. Plan files stay out of Review changes, and the session header shows while a plan is being written or reviewed. * feat(app): add built-in MCPs with their own header button Ship GitHub, GitLab, Linear, Notion, Sentry and Jira as built-in remote MCPs with brand icons, a dedicated MCP button next to the context icon in the session header, and an MCPs settings tab. GitHub signs in with an access token sent as a Bearer header or a pre-registered OAuth app, since it has no dynamic client registration. MCP on/off choices are saved and apply in every project, open session and after restarts. * feat(app): show hover cards for issue and PR links Add a link preview endpoint on v2 core and server that resolves GitHub, GitLab, Linear and Jira issues, PRs, merge requests, releases and projects through the gh and glab CLIs or the connected MCPs, and show a hover card with state, author and activity for those links in session messages. * feat(app): sign in to the GitHub MCP with the GitHub CLI Add a GitHub CLI sign-in method to the GitHub MCP that reuses the token from `gh auth token`, or runs `gh auth login --web` first and shows its one-time code in the sign-in dialog. The new tab is the dialog's default; the access token and OAuth app options remain. * feat(app): add launcher buttons to the session header Show Review, Browser and Terminal as icon buttons in the session header through a new `header` flag on side region launchers, rename the plan button to Review Plan, and show context usage against the model's window, e.g. 20K / 200K. * feat(app): add session options menu to sidebar sessions Give sidebar sessions a three-dots menu with Rename, Export and Delete, shared with the session title menu. While either menu is open, R, E and D choose an item directly, and each item shows its letter. Rename edits the sidebar title in place. * fix(core): use the GitHub MCP token for GitHub link previews Pass the GitHub MCP's saved access token to `gh api` as GH_TOKEN, so previews work where gh has no login of its own, such as under a separate XDG config directory. Report a CLI that is not signed in as unavailable rather than forbidden, so the preview retries after sign-in. * fix(core): show GitLab issue previews through the GitLab MCP Recognise GitLab's /-/work_items/N issue links, and resolve GitLab issues and merge requests through the connected GitLab MCP's get_work_item and get_merge_request tools, falling back to glab. Map work items' uppercase states, camelCase dates and label widgets onto the preview card. * feat(app): show hover cards for Notion links * feat(app): add git staging, commit and remote actions to the review panel - Add a core GitActions service and /api/git routes for state, stage, unstage, discard, commit, generated commit messages, fetch, push, pull and stashes, ported from v1 - Watch the index, refs, config and working tree and send git.changed so the panel follows changes made outside Forge - Show changes as a compact tree with staging checkboxes on files and folders in every mode, scoped to the listed files outside Uncommitted - Port v1's remote and stash dialogs and git translations - Count unpushed commits on branches without an upstream and offer Push and set upstream - Add the outline split button variant from v1 * feat(app): lead the session header with context usage * fix(desktop): show Forge name and icon in notifications * feat(app): expand every timeline action by default Default the timeline to the Everything preset. A Compact preset saved before this default is read once as unchosen, since v2 saved its defaults with any other setting change; a later Compact choice stays. * feat(app): show the Forge mark while a session loads Keep the startup splash's mark over the timeline until history loads and a cold timeline reveals its rows, then fade it out. * feat(core): add local auto memory Port Forge's v1 local memory onto v2. Each repository gets a memory folder under the data directory, keyed by the Git common directory so worktrees share it. Its guidance and bounded MEMORY.md index load as the forge/memory Instruction Source, so a saved note reaches the session as an instructions update. The forge.memory plugin approves the folder for every agent and keeps subagents read-only. `memory: false` turns it off and keeps existing notes. * feat(app): break down context usage by part Add a session.usage endpoint that describes the next model request: the system prompt, tools, MCP and Code Mode tools, skills, instruction files, memory and messages, each estimated from its text and scaled to the provider's latest count, plus the input window and the auto-compact threshold. The Context tab shows it as a colour-coded bar and legend with the auto-compact buffer and free space, above OpenCode's existing stats. The System Prompt section now shows the assembled prompt part by part instead of the last instruction update. * fix(app): keep the thinking level picker visible * feat(app): show question dock shortcuts on its buttons * docs: update maintaining, agents and contributing guides for v2 * feat(session-ui): show web search results as cards and direct tool responses * feat(core): let tools describe their Code Mode call rows * feat(app): show browser actions and screenshots readably in the timeline * fix(app): retire the app-side auto-approver The client answered every permission request itself while the auto-approve setting was on, whatever mode the session was in, and hid the approval cards. Modes already settle requests on the server, so the setting now only picks the mode new sessions start in, and Mod+Shift+A switches the current session between Auto and Manual. * fix(desktop): update ad-hoc macOS builds from the release page Squirrel.Mac rejects updates for builds without a Developer ID signature, so those builds now report a download and open the latest release instead. The update check sends a Forge user agent, and What's New no longer reads OpenCode's changelog. * fix(core): ask before shell commands and file edits by default Upstream agents allow every action, so Manual mode had nothing to ask about. The asks follow each agent's base allow-all rule, so agent rules and config still override them. * feat(app): ask before browser actions outside Auto mode Each browser call now checks permission when it runs, with its operation as the resource. Attaching the pane allows reads and asks for everything that acts, and drops the allow-all grant earlier versions left behind. Approval cards and timeline rows name the element, URL or tab a call works on. * feat(app): number the permission dock's choices * fix(app): show the Forge mark while a session opens * fix(desktop): stamp release builds with the full Forge version The update manifests carry v<opencode>-v<forge>, but the app took only the OpenCode half, so every install would read the latest release as a different version. v1 stamped the full version the same way. * ci: run checks on GitHub-hosted runners for main, including macOS * ci: run e2e tests on macOS * fix: repair unit tests that broke in CI * style: add the blank lines the anti-slop lint asks for * ci: add macOS e2e summary job and drop the TUI simulation build check * test(app): align e2e specs with Forge's timeline, project rail and mocks * test(sdk): run the lazy-provisioning shell test in Auto mode * test(app): expect the thinking picker to stay visible * test(core): count the GitActions repository probe in the boot git-spawn budget * test(core): expect Forge's plan tools in the Location tool list * test(core): keep Forge's memory guidance out of the recorded runner prompt * test(core): compare git-actions file contents with normalized line endings * test(server): create the schedule test session in Auto mode * test(app): point the branding specs at Forge's title, site link and changelog * test(app): update composer specs for /agent and the always-visible thinking picker * test(app): open MCPs from Forge's header button and settings tab * test(app): drop MCP from the mobile details drawer specs * test(app): move MCP expectations from the details menu to the header popover * test(app): fit the session header spec to Forge's menu keys and breakpoints * test(app): find review changes by Forge's staging rows and mirror the diff in git state * test(app): expect Forge's context usage text and header width * test(app): fit settings search specs to the MCPs page and the narrower empty state * test(app): move the settings MCP specs to the MCPs page and clear the rail's resize handle * test(app): widen the nested-session header test for Forge's header * ci: install the pinned ripgrep for the Linux and macOS unit tests * test(app): drop the focus-into-list steps the Skills list can't take * test(core): keep Forge's memory guidance out of the session runner prompts * chore: clear the anti-slop lint findings in Forge's changed files * ci: lint v2-port's changes against the upstream release it was ported from * chore(protocol): regenerate the OpenAPI document * fix(core): keep the plan file path slash-separated on Windows * ci: install the pinned ripgrep on Windows too * test(app): retry Escape until the MCP popover closes in the RTL spec * test(core): expect the plan file's native absolute path in the reminder * test(app): give the large review list time to render on slow runners * test(app): give the all-locales dictionary check time on slow Windows runners * test(server): give the artifacts API test time on slow Windows runners * ci: give macOS a current bash and fewer e2e workers * test(cli): wait longer for a managed service on slow runners * test(cli): give the upgrade help test room on slow runners * test(app): press and expect the platform modifier in e2e specs * test(app): use the literal Control binding where the app has no mod shortcut * test(app): press the literal Control bindings for tab cycling and new terminal * test(server): give the VCS 503 test room to boot the location on slow runners * test(app): use macOS shortcuts for document edges, file browser and zoom in e2e * test(app): scroll regions with Home and End on macOS in touch-scroll e2e * test: give SDK bundling room on macOS and skip the missing Ctrl+Home check there * test(server): give slow location and OAuth fallback tests room on macOS --------- Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: vimtor <36263538+vimtor@users.noreply.github.com> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: Sayuri Nekomiya <contactme@awsl.rip> Co-authored-by: adamdotdevin <adamdotdevin@users.noreply.github.com> Co-authored-by: jlongster <17031+jlongster@users.noreply.github.com> Co-authored-by: xjh <87483213+xiajiahang@users.noreply.github.com> Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: iamdavidhill <iamdavidhill@users.noreply.github.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: frank <48937772+joe-ieta@users.noreply.github.com> Co-authored-by: DS <78942835+Tarquinen@users.noreply.github.com> Co-authored-by: MrMushrooooom <19261047+MrMushrooooom@users.noreply.github.com> Co-authored-by: Tarquinen <Tarquinen@users.noreply.github.com> Co-authored-by: r266-tech <r266-tech@users.noreply.github.com> Co-authored-by: pj.reid <pj.reid@zetier.com> Co-authored-by: Dax Raad <d@ironbay.co>
Switching models mid-session or resuming after an interrupted stream used to demote unsigned reasoning into normal assistant
text. In local session logs, that demotion causedreasoning_extractionblocks when switching back to Claude or retrying cut streams, HTTP 400s on Cohere v2 whentextlanded next totool_calls, and reasoning leaks into flattened compaction summaries.Chat Completions (
openai-chat.ts) already forwards unsigned reasoning inreasoning_contentacross models, and open-weight/messagesproviders withrequireSignature: falsealready forward{ type: "thinking", signature: "" }. The bug was the fallback totype: "text"in the four places where a signature or provider metadata was required or a turn had errored:toLLMMessages (packages/core): sameModel && !error && !interrupted -> keep reasoning + providerMetadata !sameModel -> keep unsigned { type: "reasoning", text } - error || reasoningInterrupted -> lower reasoning to { type: "text", text } + error || reasoningInterrupted -> drop reasoning anthropic-messages (packages/ai): !requireThinkingSignature -> send { type: "thinking", thinking, signature: "" } - requireThinkingSignature -> demote unsigned reasoning to text + requireThinkingSignature -> drop unsigned reasoning bedrock-converse (packages/ai): - missing signature -> demote unsigned reasoning to text + missing signature -> drop unsigned reasoning mistral-chat (packages/ai): - missing mistral.thinking metadata -> append reasoning to assistant text + missing mistral.thinking metadata -> drop unsigned reasoning (and compute prefix: true from emitted messages) gemini & google-interactions (packages/ai): - model-ID regex gate on gemini.ts only + inject "skip_thought_signature_validator" on unsigned tool calls across both protocolsI also added
requireSignature: falseonproviders/minimax.ts/messages(plusqwen*andminimax-*inanthropic-messages.tsfor gateway routes) and removedrequireSignature: falsefromproviders/meta.ts/messages, since live trials showedMiniMax-M3recalls unsignedsignature: ""thinking (and returns empty text if thinking is dropped in a tool loop) while Meta Muse usesredacted_thinkingdataand ignoressignature: "".Out of scope:
step.tsstill marks a finished model stream asSTEP_INTERRUPTEDwhen local tool execution is cancelled or declined, which strips valid stream metadata on the next turn.reasoningTextdrop unsigned reasoning rather than replaying it until model-level capability metadata distinguishes reasoning vs non-reasoning Converse targets (nova-pro400s onreasoningContent).Verification
bun run check(35/35 workspace packages passed)bun testandbun run typecheck:tscinpackages/ai(1990 passed)bun run script/test.ts test/session-runner-message.test.ts test/session-runner.test.ts test/session-compaction.test.tsandbun run typecheck:tscinpackages/core(306 passed)generateContent/interactionsmodels, 15 Mistral models, and 7/messagesendpointsVisual
Added by the opencode bot (AI-generated).
Related: #38620, #55096
Public API: adds optional
requireSignatureto model/providercompatibilityin @opencode/schema and OpenAPI (backwards compatible; unset now defaults to false for model IDs outside the signed families)