Visitar URL original
feat(core): use freeform tool input on models that support it by rekram1-node · Pull Request #61703 · anomalyco/opencode · GitHub
Skip to content

feat(core): use freeform tool input on models that support it - #61703

Merged
rekram1-node merged 14 commits into
devfrom
freeform-tools
Nov 10, 2026
Merged

rekram1-node merged 14 commits into
devfrom
freeform-tools

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Issue for this PR

No linked issue.

Type of change

  • New feature

What does this PR do?

Core previously discarded the freeform formats listed on each models.dev API. Tools like patch and execute had to ask for a JSON string even when the model could write the text directly.

Tools can now declare freeform alongside their usual input. patch declares its Lark grammar and execute declares plain text. Core checks the selected model's formats before each request:

patch declares Lark What the model writes What the executor gets
API lists lark Raw patch text { patchText }
Other formats, empty or missing list JSON object { patchText }

Support comes only from the model's models.dev entry or capabilities.freeform in config, never from the provider package. Pointing a catalog provider at another base URL keeps the listed formats; set capabilities.freeform: [] if that proxy rejects them. A custom provider gets JSON tools unless its config lists formats.

Inside Code Mode, a freeform tool drops the object wrapper, the way Codex calls apply_patch. Core marks the tool freeform when it hands it to Code Mode, and Code Mode handles the rest. Both calls deliver { id: "OPS-42" } to validation, hooks and the handler:

tools.ticket("OPS-42")         // catalog shows tools.ticket(id: string)
tools.ticket({ id: "OPS-42" }) // still works

Other tools keep their object signatures. patch itself stays a direct tool for now (codemode: false).

How did you verify your code works?

The Code Mode suite passed on both engines (1,636 tests each), including a test that a freeform tool takes both call forms and a plain tool keeps its object signature. Focused Core tests passed for config, models.dev parsing, request formats, execute and patch.

A live eval on gpt-6.1-sol and gpt-6-luna (96 trials) compared execute as a JSON tool and as freeform text. Every first call was plain JavaScript with no code fence or JSON wrapping, and every first program ran.

bun run check and Core and Code Mode's official TypeScript checks passed.

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72" fill="none" color="currentColor" role="img" aria-label="Raw patch text and JSON both reach the same patch tool">
  <style>@media(prefers-reduced-motion:no-preference){.raw,.json{animation:flow 3s infinite}.json{animation-delay:1.5s}@keyframes flow{0%,45%,100%{opacity:1}55%,90%{opacity:.35}}}</style>
  <g stroke="currentColor"><path class="raw" d="M71 19h15l14 17h24"/><path class="json" d="M71 53h15l14-17"/><rect x="124" y="20" width="108" height="32" rx="5"/></g>
  <g fill="currentColor" font-family="monospace" font-size="12"><text x="7" y="23">raw text</text><text x="7" y="57">JSON</text><text x="135" y="40">{patchText}</text></g>
</svg>

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Public API: adds optional freeform (text/regex/lark) to plugin Tool.Info, Model.Capabilities.freeform (OpenAPI/client, new Tool.FreeformFormat schema), model config capabilities.freeform, and freeform?: boolean to @opencode/codemode tools (backwards compatible)

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change works as described. I ran OpenCode from source on the base and with this PR against a local fake OpenAI-compatible endpoint, with the patch tool offered on a gpt-* model:

  • With capabilities.freeform: ["lark"], patch goes out as a custom tool with the Lark grammar, and a raw patch call from the model reaches the executor as { patchText } and applies the patch.
  • Without the setting, and on the base, patch stays a JSON function tool and nothing else changes.

The Core tests touched here pass (113), as do the AI freeform tests (14), and tsc passes for core, schema, plugin and ai.

Public API changes. All are additions and backwards compatible:

  • Plugin API: Tool.Info gains an optional freeform (text | regex | lark), for both Effect and Promise plugins.
  • Schema, OpenAPI and client: Model.Capabilities gains an optional freeform: string[].
  • Config: providers.<id>.models.<id>.capabilities.freeform is a new optional key.
  • LLM.Freeform in @opencode/ai now re-exports Tool.Freeform from schema, with the same shape.

One concern that doesn't block a merge (inline): the freeform formats models.dev lists for a model's original API stay on the model when config points it at a different package or proxy.

const fallback = base ?? Capabilities.default()
return {
tools: config.tools ?? fallback.tools,
freeform: (config.freeform ?? base?.freeform)?.slice(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

freeform is a property of the models.dev API that route picked, but it stays on the model when config sends the model somewhere else. That happens with config.package or a provider package (the inherit path in config/plugin/provider.ts clears model.package but keeps the capabilities), and with a proxy baseURL. For example, an openai model moved to @opencode/ai/providers/openai-compatible behind LiteLLM keeps lark. Every request then sends patch as a custom tool, and the README says unsupported endpoints return a provider error with no fallback to JSON. It may be worth dropping the inherited freeform when the package changes, unless the config sets freeform itself.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My earlier point is addressed: catalog freeform formats are no longer inherited when config sends a provider or model to a different package, baseURL or modelID, and formats set explicitly in capabilities.freeform still apply. I checked it with the CLI against a fake OpenAI-compatible endpoint. With a custom baseURL plus explicit freeform: ["lark"], patch still goes out as a custom tool and applies. The Core tests pass (114), as does core tsc. The new commits don't change the public API, and I have no new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new commits look fine. Unknown models.dev format names are now filtered out instead of being passed through, and freeform is narrowed to text | regex | lark. I re-ran the CLI against a fake OpenAI-compatible endpoint: with freeform: ["lark"], patch still goes out as a custom tool and applies. The Core tests pass (114), and tsc passes for core and schema.

Public API: this adds the Tool.FreeformFormat schema (OpenAPI and client), and freeform in Model.Capabilities and model config capabilities becomes ("text" | "regex" | "lark")[] instead of string[]. Nothing released uses the key yet, so it is still backwards compatible. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push only removes the README paragraph about Core's freeform selection from packages/ai/README.md, which puts it back to the text on dev. No code changed, and the plugin and models docs pages still cover the behaviour. The public API is the same as in my last review. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Code Mode support works as described. The new Core test passes: freeform tools list tools.x(input: string), and both string and object calls reach the executor as the object input. The full packages/codemode suite passes (1500), and tsc passes for codemode and core.

Public API: @opencode/codemode's Tool.make options and Tool type gain an optional freeformField, and the "expects at most one…" error text now says "argument" instead of "input object". Both are backwards compatible. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push looks sound. Code Mode now finds the string field from the input schema itself, and only for a single required string field; other shapes keep their object signature. The codemode tests pass (1504, including the new eligible and ineligible cases), as do Core's tool-execute/tool-patch tests, and tsc passes for codemode and core.

Public API: in @opencode/codemode, the Tool.make option freeformField?: string becomes stringInput?: boolean, and the Tool field becomes stringInputField. freeformField was added earlier in this PR and never released, so this is backwards compatible. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flat Code Mode option works as described. The codemode suite passes (1505), as do Core's tool-execute/tool-patch/model-request tests (62), and tsc passes for codemode, core, plugin and schema. I also probed the single-object case by hand. A positional call (scan({recursive:true})), a named call (scan({options:{…}})) and a two-argument call each produced the expected input object, and a raw JSON-Schema tool rendered and bound correctly.

Public API: plugin Tool.Options gains an optional flat?: boolean, only on Code Mode tools. In @opencode/codemode, stringInput becomes flat?: boolean and the Tool field stringInputField becomes parameters; both were added earlier in this PR and never released. Named-object calls keep working, so this is backwards compatible. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push looks sound: the plugin-level options.flat is gone, and Core turns positional input on only for freeform tools with one required string field. Core's tool-execute/tool-patch tests pass (57), and tsc passes for core, schema and plugin.

Public API: compared with my last review, the plugin Tool.Options.flat addition is withdrawn (never released). What remains is optional freeform on plugin Tool.Info, freeform in Model.Capabilities and model config capabilities (new Tool.FreeformFormat), and flat on @opencode/codemode tools. All are backwards compatible. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push is a rebase onto newer dev; the PR's own changes are unchanged apart from merging next to dev's new ...generation spread in model-request.ts. After the rebase, the focused Core tests (130) and the Code Mode suite (1505) pass, and core tsc passes. The public API is the same as in my last review. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push rebases onto newer dev and adapts the Code Mode binding to dev's durable tools: normal, durable-start and resume calls now all bind arguments through the shared decode. The new suspend/resume test covers both named and positional input. The Code Mode suite (1641) and the focused Core tests (148) pass, and tsc passes for codemode and core. The public API is the same as in my last review. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The simplified version works: against a fake OpenAI-compatible endpoint with freeform: ["text","lark"], execute goes out as a text custom tool, and the model's raw code runs and returns its result. The Code Mode suite (1636) and the focused Core tests (148, plus 249 in session-runner) pass, and tsc passes for codemode and core.

My earlier thread stays open. The route-override handling was removed in favour of a documented capabilities.freeform: [] opt-out. With execute now declaring text, a proxy in front of a catalog model that lists text would also receive execute as a custom tool.

Public API changes since my last review:

  • @opencode/codemode no longer has the flat option (never released).
  • In Code Mode, every tool whose input is exactly one required string field now shows tools.x(field: string) and accepts a bare string. This covers plugin and MCP tools too, not only freeform ones, and changes the catalog text the model sees. Object calls still work.
  • The built-in execute tool declares the text freeform format.

All of these are backwards compatible.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push looks sound. The positional string call form is now limited to tools that declare freeform, so other plugin and MCP tools keep their object signatures in the Code Mode catalog, as on dev. The Code Mode suite (1636) and the focused Core tests (285) pass, and tsc passes for codemode and core.

Public API: @opencode/codemode's Tool.make options and Tool type gain an optional freeform?: boolean, and the catalog no longer changes for non-freeform tools. The rest is as in my last review: optional freeform on plugin Tool.Info, Model.Capabilities.freeform and model config capabilities.freeform, and execute declaring text. All of it is backwards compatible. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push only changes how models.dev freeform formats are decoded: they are now read as strings, and names this version doesn't recognise are still dropped. The models.dev parsing and request-format tests pass (31), and core tsc passes. The public API is the same as in my last review. No new concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push is a small schema refactor: the freeform schema is renamed Tool.Freeform and an unused type alias is dropped. Nothing else used the old name, and it doesn't appear in the OpenAPI spec or the client. The Core models and request-format tests (31) and the AI freeform tests (14) pass, and tsc passes for core, schema and ai. The public API is the same as in my last review. No new concerns.

@rekram1-node
rekram1-node enabled auto-merge (squash) November 10, 2026 21:06
@rekram1-node
rekram1-node enabled auto-merge (squash) November 10, 2026 21:12
@rekram1-node
rekram1-node merged commit 53f2d63 into dev Nov 10, 2026
18 of 19 checks passed
@rekram1-node
rekram1-node deleted the freeform-tools branch November 10, 2026 21:15
SirWellington added a commit to WellingtonHQ/opencode that referenced this pull request Nov 13, 2026
* chore(site): remove the unused arcade sound library from the media manifest (anomalyco#62611)

* fix(site): bring the latest site fixes from the new-website workshop (anomalyco#62613)

Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>

* feat(data): switch the mono font to OpenTUI Mono (anomalyco#62623)

* feat(site): add the shared product and logo menus to every nav (anomalyco#62654)

Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>

* fix(data): keep native light-dark() so the shared nav menus get their colours (anomalyco#62660)

* feat(core): wake sessions on Console event watches (anomalyco#62662)

* fix(site): match the product chevron to the docs chevron (anomalyco#62665)

Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>

* fix(site): stop the page dimming when a product card opens (anomalyco#62622)

* perf(core): fetch Console instructions and skills concurrently (anomalyco#62672)

* docs: antivirus HTTPS scanning and W-9 tax form (anomalyco#62661)

* fix(site): stop server functions failing on the dev server behind a proxy (anomalyco#62677)

* fix(site): pin the Like letters? bar so Safari's toolbar turns blue with it (anomalyco#62674)

* fix(site): tidy the docs bars and search on phones and desktop (anomalyco#62669)

* fix(site): use one dark link blue across the sites (anomalyco#62676)

* feat(data): per-page Open Graph cards for model and lab pages (anomalyco#62692)

* fix(core): clearer shell exit and timeout status, pass unmatched zsh globs through (anomalyco#62141)

Co-authored-by: Aiden Cline <aidenpcline@gmail.com>

* docs(legal): cover integrations with third-party services (anomalyco#62700)

* fix(site): follow up anomalyco#62700 with dev's commit in the legal history and new legal bundle budgets (anomalyco#62701)

* fix(core): keep Console policies when its instructions or skills fail (anomalyco#62702)

* fix(core): don't fail MCP tool calls whose results break their output schema (anomalyco#61254)

* fix(site): tidy the phone nav, docs menus and icons (anomalyco#62707)

* chore(perf): bench with strobe 0.20.0 from github.com/anomalyco/strobe (anomalyco#62705)

* feat(core): watch workspace MCP servers' events through the Console plugin (anomalyco#62734)

* chore(perf): bench with strobe 0.21.0 (accuracy audit) (anomalyco#62747)

* feat(site): keep the page and current section stuck on phone docs (anomalyco#62751)

* fix(site): return Safari's toolbar to the page colour when the letters go back (anomalyco#62754)

* fix(site): make the phone sticky rows full width so their tap fill reaches both edges (anomalyco#62759)

* fix(core): keep Code Mode catalog updates small and stable (anomalyco#62176)

Co-authored-by: exe.dev user <exedev@anton.exe.xyz>

* fix(core): make build metadata a required service so flags can't silently fall back to release defaults (anomalyco#62686)

* chore(perf): bench with strobe 0.22.0; tabs' warm switches end on the frame after the answer (anomalyco#62764)

* fix(core): keep a v1 provider's baseURL when api is also set (anomalyco#62570)

* fix(desktop): show an error when opening a path in the default app fails (anomalyco#62572)

* Add Frantic Ferret Free (anomalyco#62770)

Co-authored-by: Daniel <daniel@anoma.ly>

* fix(app): slide the side panel shut when its last tab closes (anomalyco#62758)

* feat(core): frame Console channel follow-up wakes as maybe unrelated, and let watch take minutes (anomalyco#62774)

* Remove Frantic Ferret Free from docs (anomalyco#62780)

* fix(desktop): restore the review shortcut (anomalyco#62567)

* docs(legal): cover hosted agent sessions and their retention (anomalyco#62789)

* fix(site): point the legal history's hosted agents entry at dev's commit (anomalyco#62790)

* fix(core): tail reads from the end of a file and suggest close file names (anomalyco#62147)

Co-authored-by: Aiden Cline <aidenpcline@gmail.com>

* docs: strobe moved to github.com/anomalyco/strobe (anomalyco#62706)

* feat(core): say which credential changed in credential.updated (anomalyco#62762)

* chore(perf): bench with strobe 0.23.0 (anomalyco#62799)

* chore: let bun resolve strobe's Windows on Arm package (anomalyco#62804)

* feat(core): tag shell, tool and model-stream spans with session, agent and command (anomalyco#62806)

* fix(server): stop waiting for plugins in request handlers (anomalyco#62796)

* fix(core): refresh the Copilot API endpoint with the model list (anomalyco#62171)

Co-authored-by: exe.dev user <exedev@anton.exe.xyz>

* feat(ai): support predicate criteria in decisions (anomalyco#62819)

* fix(core): stop retrying ChatGPT workspace usage limits (anomalyco#62823)

* feat(acp): support _session/steering (anomalyco#54394)

Co-authored-by: Dan Smolsky <dannysmo@gmail.com>

* docs: document cache.ttl and per-model cache.warming (anomalyco#62829)

* fix(core): stop applying well-known remote config after logout (anomalyco#62760)

Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com>

* fix(core): refresh MCP OAuth tokens under the shared credential lock (anomalyco#62827)

* refactor(core): run GitHub Copilot on native ai protocols (anomalyco#62034)

Co-authored-by: exe.dev user <exedev@anton.exe.xyz>

* fix(app): drop the dead plugin-boot retry on catalog syncs (anomalyco#62830)

* docs(go): add LongCat 2.5 Free to Free and Go (anomalyco#62878)

Co-authored-by: Jack <jack@anoma.ly>

* refactor(tui): remove the import cycle in plugin (anomalyco#62884)

* refactor(core): remove the import cycle in v1 migration (anomalyco#62885)

* refactor(codemode): remove the import cycle in interpreter (anomalyco#62886)

* docs(console): lower Claude Sonnet 5.5 cached read price to $0.10 (anomalyco#62887)

* feat(app): mark sessions read or unread from their menus (anomalyco#62889)

* fix(session-ui): show the picked question answer as a raised card (anomalyco#62896)

* chore(perf): bench with strobe 0.24.0 (anomalyco#62895)

* feat(core): return usage from session generate (anomalyco#62835)

* fix(tui): use keyboard-navigable buttons in update and confirm dialogs (anomalyco#62906)

* fix(ui): remove focus form borderless inline input while editing (anomalyco#62919)

* fix(ui): keep selects, menus, tooltips and toasts reachable from a dialog (anomalyco#62913)

* fix(ui): list types before import in packed exports and export select.css (anomalyco#62914)

* chore(infra): drop stripe and aws from deploy, move share to its own worker (anomalyco#62908)

* fix(site): align Copy as Markdown with the end of the docs page footer (anomalyco#62933)

* chore(infra): drop SiteDomain and retain zen buckets and trust dns before untracking (anomalyco#62938)

* docs(console): add Muse Ember 1.0 (anomalyco#62935)

Co-authored-by: Daniel <daniel@anoma.ly>

* fix(core): hold Console watch wakes after 5 in 10 minutes or 20 since the prompt (anomalyco#62924)

* fix(tui): use the base text color for the dialog search cursor (anomalyco#62939)

* chore(infra): untrack zen buckets and trust dns now that they are retained (anomalyco#62940)

* fix(tui): drop usage and the model detail line from the model picker (anomalyco#62927)

* fix(tui): remove dark gap between execute detail and its footer (anomalyco#62928)

* fix(tui): add a blank row under the OAuth dialog title (anomalyco#62929)

* fix(tui): give the shell output dialog new-design footer actions (anomalyco#62932)

* docs(console): add Claude Fable 5.2 (anomalyco#62949)

Co-authored-by: Daniel <daniel@anoma.ly>

* fix(core): time warming from the model's cache.ttl by default (anomalyco#62831)

Co-authored-by: Ryan Skidmore <4580448+ryanskidmore@users.noreply.github.com>

* chore(perf): bench with strobe 0.26.0 (anomalyco#62931)

* feat(app): close side panel tabs to the right and make ⌘W/⌘⇧T side-panel aware (anomalyco#62922)

* ci: read deploy and release secrets from aws secrets manager (anomalyco#62954)

* fix(perf): type navigation.bench.ts's setup key on its target (anomalyco#62952)

* fix(core): stop warming a session after a ping misses the cache (anomalyco#62926)

Co-authored-by: Ryan Skidmore <4580448+ryanskidmore@users.noreply.github.com>

* fix(core): count the warming interval from when the request was sent (anomalyco#62941)

* fix(site): keep open menus under the pinned header (anomalyco#62968)

* docs: match Gemini Flash intro pricing on Console models (anomalyco#62937)

Co-authored-by: Frank <frank@anoma.ly>

* docs(console): deprecate Crouton Alpha Free (anomalyco#62975)

Co-authored-by: Daniel <daniel@anoma.ly>

* fix(site): fetch the docs' status bars from a 79-byte endpoint when the footer comes near (anomalyco#62962)

* fix(site): stop idle polling and scroll layout thrashing (anomalyco#62966)

* fix(files): cache the site's media for a year (anomalyco#62980)

* fix(site): flatten masked logo SVGs so they render sharp (anomalyco#62946)

* refactor(site): keep dev-only code out of production pages and group small shared chunks (anomalyco#62964)

* fix(site): load the nav menus, console API and Hex pop-up on intent (anomalyco#62965)

* fix(site): serve AV1/HEVC card videos and load card media after the page (anomalyco#62971)

* refactor(core): give each warmed session one owned loop (anomalyco#62972)

* chore(perf): bench with strobe 0.27.0 (anomalyco#62985)

* refactor(site): parameterize src/docs for multiple docs sets (anomalyco#62950)

* feat(site): serve V1 docs in the new design at /v1/docs (anomalyco#62951)

* feat(site): translate the V1 docs' chrome and missing pages (anomalyco#62956)

* feat(app): keep assistant copy visible on dev builds (anomalyco#62992)

* chore(core): update MCP SDK to 2.4.0 (anomalyco#62982)

* refactor(site): prune component CSS variants and split shell from route stylesheets (anomalyco#62990)

* fix(stats): map crouton alpha to muse ember (anomalyco#62994)

* chore(infra): point docs.opencode.ai at the site worker (anomalyco#62999)

* docs(console): add MiniMax M3.1 Flash Free (anomalyco#63005)

Co-authored-by: MrMushrooooom <19261047+MrMushrooooom@users.noreply.github.com>

* fix(core): count session generate requests in the session's cost (anomalyco#63006)

* feat(ai): add embedding models with the OpenAI embeddings protocol (anomalyco#62832)

* refactor(ai): remove the ImageResponse.image shortcut (anomalyco#63011)

* fix(core): keep MCP OAuth credentials when a refresh fails temporarily (anomalyco#63012)

* feat(ai): add Google embedContent embeddings (anomalyco#62843)

* feat(core): add GitHub Copilot Auto and a model resolve hook (anomalyco#62997)

* refactor(plugin): edit the model in place in experimental.model.resolve (anomalyco#63015)

* feat(ai): add Vertex predict embeddings (anomalyco#62844)

* feat(ai): add Google and Vertex embedding entry points (anomalyco#63017)

* refactor(core): share OAuth credential refresh between provider logins and MCP (anomalyco#63022)

* fix(core): keep background compaction from stopping a running Claude Agent session (anomalyco#63025)

* fix(core): keep catalog capabilities when a v1 model override sets only some (anomalyco#62573)

* fix(site): build the Worker as one file so its chunks can't import each other (anomalyco#63031)

* feat(site): serve the reading pages as static HTML (anomalyco#63032)

* fix(core): send gatewayApiKey to Cloudflare AI Gateway (anomalyco#62574)

* fix(core): put Console event watches behind the console_watches flag (anomalyco#63036)

* fix(core): stop splitting an emoji when read truncates a long line (anomalyco#62575)

* fix(core): list Console's watch tool and say pull requests wake with their checks (anomalyco#63039)

* fix(core): stop interrupting Claude Agent tool calls when instructions or tools change (anomalyco#63033)

* ignore: wait for plugins again where managed policy applies  (anomalyco#63040)

* refactor(site): share the static pages' behaviour with their components (anomalyco#63041)

* chore(perf): bench with strobe 0.28.0 (anomalyco#63043)

* release: v2.2.6 (anomalyco#62450)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(site): serve the home and product pages as static HTML (anomalyco#63060)

* refactor(site): make every page static and stop shipping the app's JavaScript (anomalyco#63067)

* feat(app): make session IDs easier to find and copy (anomalyco#63083)

* release: v2.2.7 (anomalyco#63085)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(app): show tab shortcut numbers while holding the mod key (anomalyco#63089)

* fix(site): answer other methods on pages with a 405 (anomalyco#63102)

* fix(site): send every unhandled method a 405 listing what the address takes (anomalyco#63104)

* fix(app): tighten gap between titlebar search and first tab to 10px (anomalyco#63108)

* chore(release): mint short-lived cloudflare token for r2 uploads (anomalyco#63115)

* docs(console): list Muse Ember 1.0's over-200K price (anomalyco#63116)

* feat(app): queue a follow-up first with Alt+Enter (anomalyco#63109)

* feat(app): steer the next queued prompt with Mod+Enter (anomalyco#63111)

* fix(site): load the changelog's collapsed lists when opened (anomalyco#63122)

* chore(web): move the V1 docs into services/site and remove packages/web (anomalyco#63117)

* fix(release): publish platform packages before the cli package (anomalyco#63124)

* feat(app): link Console MCPs and skills in Extensions settings (anomalyco#63125)

* fix(site): serve 1344-wide hero videos to 1x screens and phones (anomalyco#63135)

* refactor(site): keep Solid off the static pages until an overlay opens (anomalyco#63140)

* refactor(site): drop code only the hydrated production pages used (anomalyco#63147)

* fix(core): retry failed streams cleanly and keep finished work on interrupt (anomalyco#60674)

* fix(core): send Copilot requests to the login's own endpoint until models load (anomalyco#63146)

* feat(core): background shell commands when their timeout expires (anomalyco#63024)

* docs(console): add Gemini 4 Carbon to Zen (anomalyco#63153)

Co-authored-by: Daniel <daniel@anoma.ly>

* refactor(core): remove the import cycle between provider and aisdk-native (anomalyco#62883)

Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>

* refactor(ai): remove the import cycles between media modules and their clients (anomalyco#62882)

Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>

* refactor(site): share the docs' and static pages' shell code (anomalyco#63161)

* fix(site): keep the Like letters? bar's look when the announcement banner is off (anomalyco#63157)

* docs(console): document paying for Go with credit (anomalyco#63162)

* fix(site): give the not-found page one stylesheet of what it renders (anomalyco#63169)

* feat(app): add an experimental customizable sessions sidebar (anomalyco#63143)

* feat(core): speak opencode-pty protocol v8 (anomalyco#62976)

* fix(app): slide the file tree sidebar open and shut again (anomalyco#62773)

* fix(core): keep a shell command's output when the user interrupts its call (anomalyco#62977)

* fix(app): name the main checkout by its branch instead of Local (anomalyco#63187)

* feat(core): run shell commands in the process daemon across handoffs (anomalyco#62978)

* feat(plugin): let promise tools stop cooperatively through their signal (anomalyco#63127)

* feat(core): use freeform tool input on models that support it (anomalyco#61703)

* fix(core): accept decision-type models from LM Studio (anomalyco#56214)

* fix(tui): draw paste chip text dark in dark mode and light in light mode (anomalyco#62930)

* fix(tui): open the right activity tab and make tabs clickable (anomalyco#60168)

* feat(app): add Recent sessions sidebar experiment and iOS-style recency (anomalyco#63177)

* feat(tui): default transcript verbosity to low (anomalyco#63207)

* fix(app): show Recent sidebar rows exactly like side tabs (anomalyco#63211)

* fix(desktop): stop freezing the main process during the login shell env probe (anomalyco#63208)

* v0.8.4 (anomalyco#63055)

* fix(core): accept null context length from LM Studio models (anomalyco#63198)

* fix(core): report a refused Console auto-watch (anomalyco#63217)

* fix(cli): update npm installs through their own prefix (anomalyco#63152)

* feat(codemode): rank tool search with name-weighted BM25 (anomalyco#62984)

* feat(core): run LM Studio decision models (anomalyco#63200)

* fix(core): describe the execute program as plain JavaScript for every tool shape (anomalyco#63226)

* chore(ci): keep the heavy test tier off pull requests

pull requests now run the affected unit tests on linux only. the windows unit
tests, the compiled builds and the four playwright suites still run on every
push to dev and beta, on nightly runs, and as a release.yml gate before a
stable release publishes.

* chore(skills): describe the test tiers as they now run

* chore(ci): gate a stable release on the linux browser suites

the windows playwright legs still fail on dev pushes often enough that a
release gate over them would stall shipping. unit tests and builds still cover
both OSes, and dev pushes and nightly runs still cover the windows browser
legs.

* feat(tui): open sessions from session IDs in messages (anomalyco#63224)

* chore: move the api worker into the legacy service

* fix(tui): only re-highlight blocks whose session link changed (anomalyco#63247)

* chore: move the data site off sst

the stats site, its og worker and the sync workflow deploy from services/data with cf as
opencode-data-<stage>, opencode-data-og-<stage> and opencode-data-sync-<stage>. the beta
web app and the whole sst setup (sst.config.ts, infra/, sst-env.d.ts, the sst dependency)
are gone, so nothing deploys through sst anymore (security finding anomalyco#920).

* chore: drop the sst-env.d.ts files sst regenerated

* fix(core): refresh MCP OAuth tokens before they expire (anomalyco#63028)

* fix(site): keep the bare host as a custom domain

* fix(ai): keep exhausted request rate limits retryable (anomalyco#59816)

* fix(codemode): return compact JSON results (anomalyco#63258)

* feat(core): support MCP events in the MCP client (anomalyco#63254)

Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>

* ci(deploy): mint short-lived cloudflare tokens from the token issuer

* feat(core): wake a session on MCP events with a watch tool (anomalyco#63269)

Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>

* fix(core): send saved keys during local model discovery (anomalyco#63266)

* refactor(data): strict dev/production stages and data naming

- services/data/stage.ts: stage(mode) throws on anything but dev or production, used by all three configs, build.ts and deploy.ts
- packages/stats -> packages/data (@opencode/data-app, -core, -server)
- StatsSyncWorkflow -> DataSyncWorkflow, instance ids data-hourly-/data-history-, Data* resource keys, data sync log lines
- worker, route, workflow and cron names unchanged

* feat(core): tell agents when they are in a fork (anomalyco#63235)

Co-authored-by: OpenCode Agent <opencode-agent[bot]@users.noreply.github.com>

* fix(deploy): run Cloudflare deploys from verified artifacts behind pinned targets

Repository code outside .github/ needs no review, but every deploy job held a
Cloudflare token strong enough to write any Worker on the account (the data
sync Workflow PUT has no per-Worker permission). A dev merge could therefore
overwrite production Workers.

Split each service into a build job with no environment, secrets, or OIDC token
that uploads a SHA256SUMS artifact, and a deploy job in dev|production that
checks out only .github/release/, verifies the artifact against the build job's
digest, installs the cf/wrangler CLI pinned here, and runs only deploy-*.sh.

The deploy scripts treat the artifact as data and refuse it unless its resolved
config equals the pinned target in .github/release/targets/ exactly: Worker
names, routes, custom domains, crons, Workflow names, and bindings. For cf
services the pinned config is what cf reads. That is what makes an account-wide
token safe, and it keeps production out of reach of a dev merge.

Adds the cf 1.0.0-beta.12 and wrangler 4.110.0 lockfiles the deploy jobs
install with npm ci --ignore-scripts.

* fix(core): fetch Console instructions and skills only when the config says the workspace has them (anomalyco#63259)

* fix(ci): treat require-digests=false as off in verify-artifacts

The download action always sets OPENCODE_REQUIRE_DIGESTS (false by default),
so testing for a non-empty value required digests for release artifacts that
legitimately have none and broke sign-macos, sign-windows and
publish-attestations. Compare the value to true instead.

* fix(site): show download and install menus again after closing them (anomalyco#63311)

* feat(app): add the New timeline experiment (anomalyco#63295)

* fix(app): snap vertical tab preview to its tab (anomalyco#63074)

* feat(app): limit Recent sidebar to 7 days and 30 rows, align its hover preview (anomalyco#63313)

* fix(tui): give plugins a live theme view (anomalyco#63306)

* fix(app): accept a pasted redirect URL for provider sign-in (anomalyco#63315)

Co-authored-by: Aiden Cline <aidenpcline@gmail.com>

* fix(session): measure tps from the first streamed token (anomalyco#63321)

* chore(protocol): refresh generated openapi for the first-token event (anomalyco#63324)

* fix(app): show only the branch in the new session row (anomalyco#63182)

* refactor(codemode): remove the legacy interpreter; the VM is the only engine (anomalyco#62642)

* fix(core): keep the truncated preview when tool output can't be saved (anomalyco#63334)

* fix(app): show the agent switcher by default (anomalyco#63337)

* feat(process-daemon): pipe mode on Windows (anomalyco#63196)

* fix(update): verify access identity on admin routes (anomalyco#63340)

* fix(app): remeasure the desktop drag area when direction changes (anomalyco#63347)

* chore: remove beta what's new and visual svgs (anomalyco#63325)

* feat(core): report subagent results to the parent every time a child goes idle (anomalyco#63209)

* chore(process-daemon): release 0.5.1 (anomalyco#63366)

* refactor(process-daemon): simplify Windows pipe mode (anomalyco#63346)

* chore(process-daemon): allow 60 minutes for Windows release builds (anomalyco#63378)

* feat(core): always run subagents in the background (anomalyco#63382)

* refactor(core): remove the unused foreground path from SubagentJob (anomalyco#63223)

* fix(core): defer restart wakes until parent attempts are counted (anomalyco#63367)

* chore(process-daemon): release 0.5.2 (anomalyco#63381)

* feat(ai): add Google Decisions (anomalyco#63369)

* fix(acp): stream the parent's reaction to background subagent reports (anomalyco#63395)

* chore(core): bump gitlab-ai-provider to 6.24.0 (anomalyco#63342)

* ci: deploy dev workers in anomaly-dev (anomalyco#63379)

* read dev data sync from the dev lake

* fix(core): show the start of tool output that exceeds the limit (anomalyco#63272)

* feat(site): simplify the docs header menu and move the V1 notice into the header (anomalyco#63399)

* chore(site): update OpenCode link cards (anomalyco#63401)

* refactor(core): remove leftover foreground subagent and reattach code (anomalyco#63392)

* refactor(app): remove leftover foreground subagent UI (anomalyco#63391)

* fix(tui): show the subagent card instead of launch instructions in mini scrollback (anomalyco#63389)

* chore(site): remove the /ide page (anomalyco#63386)

Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>

* ci: run macos build jobs on blacksmith (anomalyco#63397)

* fix(storybook): provide settings context to stories

* fix(app): avoid transforms on scrolling composer suggestions

* fix(app): keep mobile tab menu open when closing tabs

* fix(desktop): update build-windows.bat to post-merge workflow

The merge adopted the upstream desktop workflow, which folded prebuild into build and removed package:win. Drop the stale prebuild step and package the Windows NSIS installer directly with electron-builder.

* chore: ignore Astro and Cloudflare build artifacts

Ignore the generated .astro content modules, .wrangler miniflare state, and the generated services/www/public/cli.json schema so local builds of the site packages no longer show up as untracked files.

* fix(core): keep ephemeral server credentials from wiping shared auth sessions

A stdio/ACP auxiliary server mints its own random password and shares the data directory (and database) with the canonical server. On startup, AuthSession.configure treated the verifier mismatch as a credential change: it deleted the whole auth_session table, wiping the canonical server's remembered logins, and adopted a random verifier. Thread an ephemeral flag from the CLI (set when the password was minted, not from env/config) down to configure, which now skips the reconcile for such a process. The canonical server's behavior, including invalidation on a real credential change, is unchanged.

* fix(desktop): update build-mac.sh to the post-merge build workflow

The script called bun run prebuild and bun run package:mac, neither of which exists in packages/desktop/package.json, so it aborted before producing a build. Mirror build-windows.bat: run bun run build, then bunx electron-builder --mac --publish never --config electron-builder.config.ts.

* feat(app): add sortable schedule list with persisted preference

The schedules panel gains a sort selector with three modes: name (alphabetical, the default), lastRun (most recent first, never-run tasks last), and status (enabled first, name order within each group). The choice is stored with the app's local preference storage (home.schedules.sort) and restored when the panel reopens.

A neutral review pass pinned the never-run ordering (second undefined fixture plus a numeric-aware name pair) and replaced the -Infinity comparator sentinel with -1, since lastRunAtMs is a NonNegativeInt, so the comparator always returns a finite number.

* feat(session-ui): show full input in expanded generic tool calls

---------

Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com>
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
Co-authored-by: Kit Langton <kit.langton@gmail.com>
Co-authored-by: vprdev <mail@alanrice.me>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: exe.dev user <exedev@anton.exe.xyz>
Co-authored-by: James Long <longster@gmail.com>
Co-authored-by: Dan Smolsky <78942835+Tarquinen@users.noreply.github.com>
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Daniel <daniel@anoma.ly>
Co-authored-by: smagnuso <smagnuso@gmail.com>
Co-authored-by: Dan Smolsky <dannysmo@gmail.com>
Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com>
Co-authored-by: Jack <jack@anoma.ly>
Co-authored-by: Stefan Avram <98915060+Slickstef11@users.noreply.github.com>
Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com>
Co-authored-by: Ludvig Rask <74653458+ludvigrask@users.noreply.github.com>
Co-authored-by: Dax <mail@thdxr.com>
Co-authored-by: Ryan Skidmore <4580448+ryanskidmore@users.noreply.github.com>
Co-authored-by: Frank <frank@anoma.ly>
Co-authored-by: Jay <air@live.ca>
Co-authored-by: MrMushrooooom <19261047+MrMushrooooom@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Liyang Zhu <tomzhu1024@outlook.com>
Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com>
Co-authored-by: Simon Klee <hello@simonklee.dk>
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Kyle Bush <157062883+Kyle-j-bush@users.noreply.github.com>
Co-authored-by: Sebastian <hasta84@gmail.com>
Co-authored-by: OpenCode Agent <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com>
Co-authored-by: Swapnil Devesh <sid.swapnildevesh@gmail.com>
Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com>
Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant