With an API key, GPT-6 models on the openai provider now keep reasoning.effort
fixed and send the thinking level as configuration_update input items. Each
reply records its level, so earlier items are rebuilt unchanged on every request.
Controlled by compat.supportsMidConvoEffort; skipped for Sign in with ChatGPT
and thinking off.
closes#10853
- Use opencode.ai/inference and opencode.ai/inference/go base URLs; /zen/go has no Gemini route (404)
- Add google-generative-ai support to the OpenCode Go provider
- Use Anthropic Messages for OpenCode Go minimax-m2.7; openai-completions now returns ModelProtocolUnsupported
- Remove test for opencode-go deepseek-v4-flash, which is no longer listed
A host that wrote to a peer process which had just exited (an ack timer firing after the worker's exit, before the
channel's disconnect) got EPIPE as an unhandled 'error' event. IpcLink now passes a send callback and ends the link on
failure; host-manager control messages to a gone peer are dropped (its exit reports it).
- A child that says hello during a cluster reload is fenced at once and reconciled after the reload ends, to the
committed code version (it was marked in sync without voting); a running reconciliation also waits for the reload.
- A child retires a code version only after its parent did (spec section 12): the coordinator retires before it
prepares, and children learn already retired versions at hello and before a reconciliation's prepare.
- Forced retirement aborts the leftover roots of the old code version, relayed calls included, before it closes the
resources only that code used.
- A task's replacement starts after the old run has ended, at most taskStopDeadline later.
- A kept resource adopts the newest declaration's close at commit.
- Disposed objects and old code generations are released: Ref bindings are weak, finished disposals leave the dying
table, stopped tasks are dropped, and contexts link abort signals without AbortSignal.any (which kept linked signals
and their listeners reachable).
- Facets get FacetApi.signal, aborted when their run is replaced, removed, aborted or the host closes.
- Regression tests for each, including a garbage-collection test of old code generations.
- self.state(name, schema, initial, { version?, migrate? }) returns the cell; the state version and migration live on
the cell (ServeOptions.state is gone). self.derive(sources, fn) has no name and is recomputed at commit.
self.resource(name, { version, deps?, exclusive?, open, close? }) (rev is version). self.task(fn). self.on(name,
source, [select,] handler) replaces listen/listener. cell(), derivation(), resource(), listener() and the
declaration maps are gone; a name declared twice in one factory run is an error.
- A listener on state receives the current value at start and after every reload: handlers must reconcile.
- method(args?, result?): an omitted result is void, omitted arguments make a no-argument method called as
ref.name(ctx) and implemented as (ctx) => ...; the wire omits both.
- References are Ref<C> (was Handle<C>), objects ObjectImpl (record.ts is object.ts), ImplementationOf<C> (was
BehaviorOf<C>), ResourceDeclaration (was ResourceSpec); epochs are code versions (codeVersion in Ctx, RootOptions,
Shape, Host, frames and reload reports). Docs say object and reference.
- A remote object's variant is named variant (was kind): factories return { variant }, references expose ref.variant.
- Links announce a protocol version (PROTOCOL_VERSION = 2) in hello; peers of different versions close the link with
reason protocol-version <n> and report it through LinkOptions.onProtocolMismatch.
- ServeOptions.lifetime ("referenced" | "host") replaces eager: a host-lifetime object is local, has an empty input,
exists once per host and lives until the host closes; awaitEager is awaitHostObjects. A reload that makes a type
referenced releases the host's own reference.
The SDK always passed initial tools, so the constructor never restored the
loadout recorded in the transcript, and resumed sessions dropped tools activated
during the session. Sessions that use the default tools now restore the recorded
loadout, with --tools +name/-name entries applied to it.
fixes#10785
Anthropic rejects error tool results whose content is not all text, which
stalled sessions after a failed codemode script that had shown images.
Images in error tool results are now replaced with a text note.
fixes#10794
Invalid \u escapes such as \usepackage were treated as valid, so JSON
parsing failed and the partial-json fallback dropped the field and
everything after it. They are now kept as literal text like other
invalid escapes.
fixes#10868
Peers acknowledge processed frames (ack, announced with acks in hello). While a window of frames is unacknowledged, state frames wait at the sender, coalesced per subscription, instead of queuing in front of call results; slots that outgrow their bound become resets, or a downstream resubscription on relays. AcceptOptions gains maxCalls, answering calls beyond it with unavailable/too_many_calls. Link.send returns the encoded frame size.
forkDriver spread process.env into the child, so a child host that forks its own children (or a test run inside a p2 worker) passed its lock on, and children without a lock of their own failed with "locked".
Call args, results and state snapshots whose JSON exceeds 64K characters are deflated
(level 1): over IPC as z: [field, base64], over WebSockets as a binary message
(envelope plus deflated payload). A relaying host forwards the bytes as received; the
host that uses the payload inflates it. Browsers inflate with DecompressionStream in
order; the p2 TUI launcher turns packed messages back into text frames. Servers send
packed frames to clients but refuse them from clients.
On the 8b6ab5cc session: first view 523 KiB -> 78 KiB, all history 23.5 MB -> 4.9 MB.
Frame, RootRef, WireError and the reload types are derived from FrameSchema instead of
being declared twice. Error codes, contract tokens (Description is now a schema in
contract.ts) and ops are checked as typed; ops stay unknown until the delta decoder
validates them.
Network tests failed with self-signed certificate errors behind proxies that
inspect HTTPS (corporate proxies, sandboxes like Gondolin), because test.sh
starts tests with an empty environment.
A facet listed before another facet whose directory it imports from loaded a second copy of that module; register
all roots first so every facet of one load shares one copy, whatever the order.
- Give the listing cancel test 30 s: creating 10,000 files takes over 5 s
on Windows CI.
- Accept an x64 daemon on arm64 macOS: the darwin-x64 release daemon runs
through Rosetta in CI.
The coding agent no longer depends on chord, so the install lock has
neither chord nor esbuild, and the v1.2.0 Nix build failed in the
install check that loads esbuild.
A deep TMPDIR, such as ./test.sh's below macOS's per-user one, made the
lock socket path exceed 104 bytes and listen() failed with EINVAL. Fall
back to a private per-user directory under /tmp when the path does not fit.
7f022043f added packages/chord/node_modules, a symlink from a local worktree. The ignore pattern matched only
directories, so a node_modules symlink could be committed; it now matches files and links too.
A route that returned a dead link (a child that exited before its link was wrapped) left a closed connection in
the host, so every later call to that host name failed with unavailable until restart. Such a connection is not
added, closed children are skipped when routing by name, and no ping timer is started for it.
Sends copied text to the terminal with OSC 52 after the local clipboard
write, as over SSH, for remote clients without SSH variables such as
multiplexer attaches and remote IDE daemons.
closes#10826, closes#10835, closes#10114
A misplaced options line was ignored, so its timeout_ms never applied and
the script ran without a deadline. parseCodemodeSource now throws
CodemodeSourceError before the script runs.
fixes#10631
RemoteExecutionEnv.listFiles resolves the path and lists through the
daemon; a daemon without the listFiles feature yields not_supported.
LocalExecutionEnv is NodeExecutionEnv plus listFiles through a daemon on
the same machine, so local and SSH sessions list with the same code.
nodePlatform maps hello's Rust names to Node's (windows -> win32,
macos -> darwin, x86_64 -> x64, aarch64 -> arm64).
Lists a directory tree like fd --hidden --exclude .git without following
symbolic links, with the ignore crate of fd and ripgrep for git's ignore
rules. Records travel as payload (kind byte, relative / path, NUL); the
listing stops at maxEntries, maxBytes or timeoutMs with truncated, and a
cancel stops it between entries. hello reports features: ["listFiles"]
so clients can tell older daemons apart.
An environment may list the files, directories and symbolic links below a
directory: relative / paths, depth first in name order, without .git and
what git ignores in work trees, bounded with a truncated flag. Callers fall
back to their own walk when it is missing or fails with not_supported.
The env conformance suite checks it with the new listFiles option.
Retry known strict tool capacity rejections once with preferred tools using their original non-strict schemas. Preserve required constraints and payload hooks, and never retry a started stream.
closes#10409
A just-removed auth.json.lock directory can stay delete-pending on Windows
while another process holds a handle, making mkdir fail with EPERM. Retry
this briefly (capped separately from ELOCKED) so real permission errors
still fail fast.
fixes#10801
Print mode now writes a warning to stderr and exits 1 when the final
response stopped at the model's output token limit. The partial text
is still written to stdout.
fixes#10799
Pi Mobile (apps/mobile in pi-private) is replaced by p2 (apps/p2). The Android APK workflow and its pre-commit
check go; the hook runs p2's own check when apps/p2 exists (only in pi-private).
A tool call that was aborted or timed out while waiting for a shared OAuth
refresh was still retried by StreamableHttpTransport once the refresh
finished, so the server executed a call the caller had already given up on.
McpClient now passes a per-request signal to McpTransport.send(), aborted
once the request settles (cancelled, timed out, answered, or closed).
StreamableHttpTransport checks it before each dispatch. The shared refresh
itself is not aborted, so other requests waiting on it still succeed.
fixes#10598
The codemode row shows "Elapsed" while the script runs and "Took" once
it finishes, like bash rows. The timer logic moves from the bash renderer
to render-utils, and scripts send an initial update so the timer starts
before the first nested call.
closes#10708
Wheel events that no component handles now scroll the transcript instead
of reaching the focused overlay's handleInput as raw input. Overlays that
scroll with the wheel must implement handleMouse.
fixes#10769
MCP 2026-07-28 allows structuredContent to be any JSON value, including
arrays and null. callTool() rejected non-object values, failing the whole
tool call. CallToolResult.structuredContent is now unknown, and MCP tools
without an output schema declare structuredContent as unknown to codemode.
refs #10416
Servers with resources registered no tools until resources/list and
resources/templates/list finished, so a slow listing (a large drive)
kept their tools out of the first prompt. The lists only fed counts in
/mcp and pi mcp list; both now show that a server offers resources.
The resource tools still list and read resources on demand.
fixes#10526
Entries like !~/skills/pdf, +~/x and -~/x in the extensions, skills,
prompts, and themes settings were compared with a literal ~ and never
matched.
fixes#10765