Skip to content

Release v1.0.0 — OpenCode V2, on desktop and mobile - #1028

Merged
shantur merged 261 commits into
mainfrom
dev
Oct 24, 2026
Merged

shantur merged 261 commits into
mainfrom
dev

Conversation

@pascalandr

@pascalandr pascalandr commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

CodeNomad 1.0.0 — OpenCode V2, on desktop and mobile

The best desktop and mobile client for OpenCode V2.

0.20.0 brought CodeNomad to OpenCode V2. 1.0.0 turns more of its native power into a complete workspace: coordinated agents, deeper history access and visual automation, with speed, efficiency and everyday stability at its core.

Highlights

  • Desktop and mobile: Pair another device through OpenTunnel-powered Remote Control, with an interface adapted to phones and tablets.
  • Very fast and efficient: Responsive streaming and navigation, controlled resource use and persistent workspace state support demanding, long-running agent workflows.
  • Missions: Combine OpenCode V2's native sessions, subagents and human questions into coordinated work toward an objective.
  • Beyond the public API: Native plugins and RPC unlock full-history queries, timeline navigation and selective cleanup.
  • Visual automation: Let agents inspect and operate CodeNomad and desktop browser previews to check what they build.
  • An extensible workspace: Files/Git, panel extensions, online help, customizable shortcuts and signed Tauri updates complete the experience.

Built for demanding workflows

  • A responsive workspace: Streaming, search and navigation render the content you need, while project restoration prioritizes your active conversation.
  • Controlled resource use: Bounded history queries, targeted rendering and lazy Files/Git refreshes support large conversations and busy repositories without loading everything into the interface.
  • Built for long sessions: Stable reading anchors and on-demand output expansion let you inspect earlier work while agents continue running.
  • Continuity across your work: Saved projects, drafts and window state carry your workspace across sessions; native request recovery keeps questions and permissions connected to their conversations.

Built around OpenCode V2

  • The shared OpenCode service keeps conversations available to terminal and other clients. New sessions respect native default agents, including plugin-provided defaults; collapsed families show subagent activity.
  • Attach skills through @; inspect Code Mode scripts, nested tool calls and web-search sources. Search providers support global defaults and project overrides.
  • Answer native questions and permissions above the composer, with source labels, preserved drafts and readable answer/decision receipts.
  • Manage native provider accounts and optionally rotate eligible subscription accounts when a quota is exhausted. Usage follows the selected account; Stats adds service-wide activity, token, recorded cost and per-model views.

Missions and native multi-agent coordination

  • An objective becomes a plan: Coordinators delegate to specialists through real OpenCode sessions, native subagents or independent conversations, using your chosen agents, models and reasoning settings.
  • Five approaches: Flexible, Debugging, Exploration, Product delivery and Compare two models. The new comparison scenario gives two models the same task, then produces a blind, scored verdict. Dependencies and results remain visible as plans evolve.
  • Human decisions stay human: Native questions and permissions bring you into the work; milestone briefings, summaries and reports explain progress without requiring you to follow every conversation.
  • One-time or daily work: Prepare and explicitly start a mission, use pause/stop/recovery controls, and save reusable briefs. Execution, background work and plugin infrastructure stay native to OpenCode—not a replacement engine.

From your desktop to your phone

  • Remote Control uses OpenTunnel, as does OpenCode's remote pairing: access your running workspace from a mobile browser or another desktop without opening an inbound port or configuring a VPN. Devices are revocable; enabled Remote Control resumes at launch.
  • The composer follows the on-screen keyboard; questions and permission actions stay reachable. Tablet panels remember portrait/landscape layouts, with compact headers and touch-friendly menus.
  • Mobile uses the responsive web interface, not a native iOS/Android app. Direct/LAN access remains a separate option.

History and visual automation

  • Native plugins run inside OpenCode and expose authenticated RPC over stored history, extending the public API with full-session/workspace search, timeline navigation and technical-content cleanup. Results identify their conversation and worktree and open the matching exchange.
  • Agents can inspect, click, type, select, scroll, drag, use keyboard shortcuts and wait for results in CodeNomad and desktop browser previews—verifying rendered work, not only editing source.
  • Each conversation retains its browser page and navigation history. Preview tabs share sign-ins within the desktop profile, separately from the trusted application.

Files, Git and everyday work

  • Workspace, Changes and Commits share one Files panel: edit text/Markdown, inspect images and diffs, stage/unstage, review history and initialize Git. Scoped filesystem updates refresh visible folders; large inventories stay responsive and refreshed diffs preserve selection and scroll.
  • Temporary projects, tab renaming, a compact launcher and Back/Forward conversation navigation simplify everyday work. Shortcuts jump between your own messages, and an alternate send (Alt+Enter by default) queues or steers a follow-up against your usual choice. An optional whitespace filter focuses reviews on meaningful changes.
  • Native worktrees, session-family navigation, device attachments and /btw build on 0.20.0. After sending, use the conversation for keyboard reading; typing resumes the composer.

Extensions, help and customization

  • Install panel extensions from the online catalogue without rebuilding CodeNomad. MCP Assets collects tool-returned images and attachments with explicit session-asset consent. Native OpenCode plugins retain Global/Project activation and package check/update actions.
  • Online help covers setup and workflows in ten languages, from Help and the command palette.
  • Customizable shortcuts, notification routing and unread badges join compact Settings, localized menus and more consistent light/dark surfaces. Message content presets choose how much of each response to show. A new hexagon CN logo marks the app, and macOS windows use native window controls with a single menu bar. SideCars provide additional workspace panels.

Desktop updates and setup

  • Signed Tauri updates support Windows, macOS Intel/Apple Silicon and Linux AppImage. Stable/Preview feeds are separate from data profiles, preserving your tabs and drafts when changing feeds. Settings → General lists the data profiles on this computer, switches to one or creates and deletes them.
  • OpenCode setup adds clearer progress, interrupted-install recovery and explicit restart controls. Older installations on PATH no longer leave setup without a path forward.
  • Preferences restore the correct project context; incomplete request recovery preserves known questions and permissions and reports its cause.

Upgrading

  • OpenCode 2.1.2 or newer is required; 2.1.4 is recommended. This release adopts the OpenCode 2.1 message, event and plugin contracts, and 2.1.2 fixes Code Mode's JavaScript execution order. With 2.1.4, input requests from MCP servers appear in the request dock. CLI updates and shared-service restarts remain separate; Git enables the full repository/worktree experience.
  • Downloads: Windows Tauri .exe or Electron .zip; Linux Tauri AppImage or Electron .deb; Tauri or Electron macOS builds, separate for Intel and Apple Silicon. The Electron .deb replaces the former Tauri .deb—choose AppImage to stay on Tauri.
  • Stable 0.20.0/0.20.1 users install from the release download or package manager; recent signed previews already have the updater. Update remote CodeNomad servers too. Multi-window state, restoration and named profiles remain supported.
  • The macOS builds are not yet notarized by Apple: macOS can block their first launch, see Troubleshooting → macOS quarantine. The Windows installer is not code-signed, so SmartScreen can ask for confirmation.

Thanks for contributions

Thanks to creator @shantur, @pascalandr, @heunghingwan, and everyone who contributed ideas, reports and testing.

Changes since 0.20.0: v0.20.0...v1.0.0

…#856)

Removes the four benchmark JSON exports, two screenshots, evaluation
Markdown and AGENTS note introduced by #854: 31,419 text lines deleted.
Runtime code and runnable tests are retained. Measurement summaries stay
in #854's discussion. Validation: deletion-only diff, git diff --check,
and no remaining references to the removed paths.
## Summary

Cleanup following an inventory of 146 merged PRs from August 29 through
October 6, 2026, with targeted inspection of oversized diffs, generated
files, historical reports and current references.

- Remove the palette gallery, 21 PR screenshots, ten historical
reports/plans, the historical tail of the compatibility register, and
the unused February SDK tarball/metadata under `temp/`.
- Stop tracking five generated Tauri schemas; ignore their output
directory and root `temp/`. Keep capability/permission sources, build
logic and the vendored native fixes intact.
- Remove exactly 16 unused Plugins translation keys in each of ten
locales. Preserve the dynamically selected Global/Project labels and all
active labels.
- Repair links, index descriptions and the palette-test comment.
Preserve current runtime qualification/ownership contracts, palette
attribution and all executable regression tests.

58 files changed: **7 insertions, 11,723 deletions** (net **-11,716
lines**), including 22 binary removals. No new repository files or
committed audit artifacts.

## Validation

- UI typecheck: passed.
- Production UI build through Tauri `dev:prep`: passed.
- Palette quality: 40/40 passed.
- Real-component browser Plugins regressions: 8/8 passed.
- Clean Windows Tauri build with all five checked-in schemas absent:
188/188 tests passed, including actual generated ACL authority checks.
- Warm build, delete generated schemas again, incremental rebuild:
passed; generated JSON exists and remains ignored.
- Exact baseline comparison: only the intended 16 translation entries
removed per locale; retained compatibility body unchanged.
- Retained tracked text: no references to deleted reports or removed
translation keys. Synthetic Git-history fixture paths remain synthetic
and do not read the deleted gallery.
- Permission sources and runtime/build code unchanged; no added files;
staged whitespace check passed.

Validation was local on Windows, not a claim of completed Linux/macOS
CI. Existing locale `settings.ts` files remain approximately 677-691
lines; this cleanup removes entries without unrelated restructuring.
## Summary

Remove the hardcoded `base_version: 0.20.0` from the development release
workflow. The reusable workflow already defaults to the package version
at the exact dev SHA being built, currently `0.20.1`.

One-line deletion; the date/SHA suffix, development npm channel and
remote UI policy are unchanged. Future stable version bumps no longer
require updating this workflow.

## Validation

- Parsed both workflows with the existing YAML dependency and checked
the default/fallback contract and retained source SHA/channel inputs.
- Confirmed the source package version is `0.20.1`.
- `git diff --check` passes.

Existing prereleases are not renamed. No CI waiting or monitoring.
Resolve each @./ and @. mention directly in the shared prompt preparation helper. Previously every occurrence became a temporary root marker but only the first marker was restored, so later references could reach the model as ___ROOT___. Removing the sentinels also preserves literal marker text written by the user.

Keep parent-path matching, attached-path handling and verbatim pasted content unchanged. Add regression checks for repeated references, pasted display metadata, message history, shell submission and slash-command arguments. The three new tests fail on the unchanged helper and pass with this fix; all 21 targeted tests and UI typechecking pass.
I removed the temporary root markers from prompt preparation. Each `@./`
or `@.` reference now resolves directly, so repeated references no
longer send `___ROOT___` to the model.

I added regression tests for repeated references, literal marker text,
pasted content and display metadata, shell input and command arguments.
All three new tests fail before the fix; all 21 targeted tests and UI
typechecking pass afterward.

I also built the Tauri resources and passed the packaged-resource
checks. I did not replace the installed app or restart the shared
daemon.

Fixes #863
…2.0.24 (#865)

## Summary

- Qualify and pin the generated client/plugin at OpenCode **2.0.24**,
recommended independently of the unchanged **2.0.7** technical minimum.
- **On every runtime version**, automatic Form/permission recovery uses
only the authenticated loaded-only broker. Missing capacity stops
recovery non-authoritatively; it never falls back to ordinary lists
across historical/inactive worktrees.
- Retain known requests and editor drafts through unavailable/partial
recovery and rehydration, and show a localized recovery-incomplete
status even when no request is known. Only explicit settlement
reconciliation may use ordinary lists at the attempted request's
authorized location. Keep mutation fences and settlement tombstones.
- **Remove the private reader's runtime and Effect version allowlist.**
An untested/custom/prerelease/missing version label no longer disables
recovery. Registration checks the real native RPC capability; each read
validates graph, existing-only lease, queue, codec and identity
contracts. These checks fail safely without mass-loading fallback.
Tested versions remain evidence, not grounds for blocking other
versions.

## Contract audit

The authenticated isolated 2.0.24 daemon exposes 117 native OpenAPI
paths vs 116 on 2.0.22. The new `/api/vcs/init` is not exposed by
CodeNomad. Of 167 structural schema changes, most add declared
`LocationNotFoundError` failures to location-sensitive routes; other
changes add optional `ServerInfo.capabilities.persistentPty`,
WorktreeError's literal `_tag` and the VCS-init error. Published
declaration changes: 11/30 client files (+service-probe), 3/60 plugin,
8/36 protocol and 3/102 schema files (three retired IDE/question
declarations removed). No removed declaration is imported by CodeNomad;
no consumed dependency establishes a higher minimum.

The upstream source audit leaves LocationServiceMap, Form, Permission
and RPC contracts unchanged; the Location-services wrapper changes only
its TypeScript error parameter. Effect remains rc.112. The published
native implementation chunks are hash-checked against separate receipts
before each isolated fixture run.

Native SDK service contender/probe/PTY handoff internals change, but
CodeNomad retains its selected-CLI lifecycle adapter and bounded
authenticated registration fallback rather than using
`Service.ensure/stop`. **No shared daemon restart, storage mutation or
OpenCode PR is part of this change.**

The source audit also covers compaction-agent model/budget selection,
ChatGPT's temporary remote-model synchronization removal, local-provider
discovery/cache sharing, text-capable default selection, bounded MCP
connection/catalog retries and OAuth callback timing, Windows
instruction ancestry, skill frontmatter, subagent text boundaries and
PTY runtime storage/availability. These remain native-owned; no
CodeNomad adapter change is justified by the consumed contracts.
`dev-docs/OPENCODE_V2_COMPATIBILITY.md` records the consequences and
limits: synthetic account tests are not real OAuth/provider
certification, compaction isolation does not exhaust alternate
summarizer configurations, and no desktop terminal-survival test across
service restart is claimed.

## Native loaded-only evidence

### Follow-up: unjustified version lock removed

At the user's explicit direction, remove both
`PENDING_SNAPSHOT_NATIVE_VERSIONS` and the Effect-version equality
check, as well as all version checks in registration and reader
execution. The reader no longer accepts or consults an executing-version
argument. Regression coverage registers across 2.0.7–2.0.24,
newer/custom/prerelease/missing labels and a context whose `app` getter
throws if accessed. Actual malformed/missing contracts, unsafe data,
eviction/disposal races and timeouts remain non-authoritative. The rule
against blocking untested versions is recorded in `AGENTS.md`.

The source comparison across published tags 2.0.7–2.0.24 finds an
unchanged LocationServiceMap and existing-only lookup, RPC contracts and
queue-list interfaces. Changes in older releases affect Form
cancellation feedback/state and permission rejection feedback, not queue
listing. All these tags use Effect rc.112. This is source evidence, not
a claim that all eighteen releases have passed native fixtures.

After gate removal, **29/29 reader/broker/admission tests pass**, as do
server typecheck and pruning build. Fresh published native fixtures on
**2.0.23 and 2.0.24 pass**, recovering permissions/global Forms with 100
cold candidates (three actual Git worktrees), four native boots
before/after and zero cold constructions. The fresh bounded
**full-server run on the follow-up passes: 888 passed, 0 failed, 6
skipped (894 tests, 76 suites)**. Receipts: `no-gate-server.log`,
`no-gate-native-23/receipt.json`, `no-gate-native-24/receipt.json`,
`matrix-source-inventory.json`, `server-all-no-gate-final.log`.

The follow-up also records the distinction between pinned build
dependencies and an independently updated external service in
`AGENTS.md`; no README changes. Independent read-only gatekeeper review
of the **full diff**
`1abeba78043fc6ec1fac4eb57eb6c82a541cf66e..e159068`:
**APPROVE, 0 findings**, 59 independent targeted tests passed plus an
isolated reactive check. The review is published separately on this PR;
it is an agent review, not human approval or deployment acceptance. At
the pre-merge CI inspection, runtime contracts (minimum/latest),
OpenCode compatibility (Linux/Windows/macOS) and native pruning
(Linux/macOS) passed; general tests, native pruning on Windows and Tauri
tests were still in progress. Earlier cancelled/skipped runs are not
passes. The user explicitly requested an admin merge after zero
gatekeeper findings; no all-green CI claim is made.

The committed Bun fixture now uses the same managed `.ts` plugin
discovery shape as production, not configured-package directory
resolution. It uses an authenticated unmodified published HTTP router
in-process, with an in-memory database, private discovery paths, no
listener/service discovery and no external provider.

| Published runtime | Preloaded Locations | Locations after recovery |
Native boot logs before/after | Cold candidates | Cold constructed |
|---|---:|---:|---:|---:|---:|
| 2.0.22 | 4 | 4 | 4 / 4 | 100 (three actual Git worktrees) | 0 |
| 2.0.24 | 4 | 4 | 4 / 4 | 100 (three actual Git worktrees) | 0 |

Both runs recover three permissions, six Forms including three global
Forms. They exercise absent/present backend presence, two backends
retaining registration, expiry, return and final disposal, coexistence
with the existing Promise pruning RPC, authenticated 401 rejection,
schema limits and non-authoritative failures. The boot-log assertion
also detects briefly constructed Locations even if they later disappear
from the map.

Short-fixture RSS: 2.0.24 460,599,296 → 461,824,000 bytes; 2.0.22
463,929,344 → 461,701,120 bytes. These are receipts, **not a memory
profile or comparable deployment soak**, and do not attribute the
reported 10 GiB to this fallback.

## Validation

Environment: Windows, Node **25.2.1**, Bun **1.3.14**. All native checks
use owned processes, synthetic providers/configuration/data and explicit
isolated CLI paths; never the shared daemon or user database.

- `bun scripts/test-pending-snapshot-native.mjs <qualified-packages>
<fresh-output>`: passes separately on published 2.0.22 and 2.0.24.
- 2.0.24 native automation, pruning parent runner,
Location/worktree/proxy/event relay, environment, fork, aside, degraded
Git, prompt skills, provider usage/accounts, permission receipts and
blank-session cleanup: **12 suites pass**.
- Minimum 2.0.7 with the same 2.0.24 client/plugin dependencies: **nine
isolated suites pass** (automation, pruning,
Location/worktree/proxy/relay, environment, fork, aside, degraded Git,
prompt skills and blank-session cleanup). Operation-specific
usage/account/receipt checks do not establish a new global minimum.
- `test-compaction-isolation-native.mjs <2.0.24-cli> --context-mib 2`:
passes with native compaction and outline enabled; the second session
completes before releasing the first session's provider gate.
- Focused UI pending recovery, compaction, Forms and permission
lifecycle: **37/37 pass**. Coverage includes 100 cold worktrees on
unsupported capability, reconnection, request retention, per-kind
mutation fences, settlement reconciliation, late supported/unsupported
responses and compaction admission.
- Final browser dock/selection/mobile regressions: **44/44 pass**. The
incomplete status appears with no known request at 393 px, has no
horizontal overflow, preserves a typed answer on repeated unavailable
recovery and disappears only after full recovery. Real-shell provenance,
recursive interruptions, Android keyboards and per-conversation drafts
remain covered.
- Server/UI typechecks and shared server/UI/plugin builds: pass.
- Final full server unit run after dependency reinstall, `node --import
tsx --test --test-concurrency=2 'packages/server/src/**/*.test.ts'`:
**888 pass, 0 fail, 6 skipped** (894 tests, 76 suites). An earlier
unbounded parallel run exceeded the existing 30-second pending-broker
deadline; the bounded rerun passes without changing that deadline.
- Broad UI unit tests: **1189 pass, 2 fail**. Both failures are
reproduced from unmodified base `1abeba78` sources in an extracted tree:
`classic-dark-surfaces.test.ts` expects a removed accent-hover override;
`tool-content.test.ts` expects copy text where the renderer returns
none. These paths use OpenCode types only, no runtime client import.
This suite is **not green**; unrelated UI corrections are intentionally
not included.
- `git diff --check`: pass. Lockfile retains unrelated package
versions/integrities; npm peer metadata recalculation remains.
- Lockfile reproducibility: `npm ci --ignore-scripts --no-audit
--no-fund` passes after browser fixtures release the Windows esbuild
executable. An earlier attempt during those fixtures encountered
`EPERM`; a simultaneously launched server rerun was invalidated by
dependency removal and is not counted as product evidence.

Detailed local receipts/logs are retained under
`C:\Users\Admin\AppData\Local\Temp\opencode\q224`:
`pending-native-final/receipt.json`, `pending-native-22/receipt.json`,
`schema-audit.log`, `*-declarations.diff`,
`server-all-bounded-final.log`, `ui-pending-final.log`,
`ui-all-final.log`, `ui-unrelated-baseline.log`,
`browser-interruption-final.log`, native-suite logs and
`compaction-2mib-final.log`.

## Deployment acceptance and limits

Not deployed to the active user service. After authorized deployment,
compare passive-recovery Location boot/eviction counts and process
RSS/private memory over a comparable observation window, separating
explicit user actions. Accumulated pre-fix Locations are not
automatically evicted. Only an explicitly confirmed shared-service
restart may release them. The separate Usage-flicker work is not a
substitute for this fix.

Unknown requests cannot be exhaustively recovered on a daemon without
the capability; known requests and live events remain usable. Existing
bootstrap warming, bounded coverage, compaction admission and ownership
constraints remain documented in
`dev-docs/OPENCODE_V2_COMPATIBILITY.md`.

File-size signal: `packages/ui/src/stores/instances.ts` remains
approximately 2,300 lines; no threshold-only refactor is included.
#862)

## What changes

I add independently distributed right-panel UI extensions and an
official online catalogue in Customize right panel. Users browse/search
available addons, choose one and confirm installation directly from its
exact GitHub Release, without a manual ZIP download, rebuilding
CodeNomad or restarting OpenCode. Manual ZIP installation remains
available.

I publish the curated index, contribution rules and Session info demo at
https://github.com/NeuralNomadsAI/CodeNomad-Extensions. That repository
can host several official addons; the index can also reference
author-owned repositories. Separate means independent of the main
application repository, not a mandatory repository per addon.

I show the manifest and exact archive SHA-256 before trust confirmation.
Installation starts disabled; users enable it for all projects in the
server profile or for the exact opened folder. Replacement requires the
previous digest and revokes every activation grant. Removal is explicit.
Packages and grants stay outside project/OpenCode storage.

I keep author code out of the main renderer. API 1 exposes only session
ID, locale and appearance through a per-mount MessageChannel. It does
not expose filesystem, transcript, images, credentials, native commands
or the OpenCode proxy. Frames are sandboxed without same-origin access
and receive a restrictive CSP; obsolete responses and ports are fenced
on project/session changes, disconnects, revocation and replacement.

I document the package, permissions, API compatibility, scope,
release/checksum and maintainer rules in `dev-docs/PANEL_EXTENSIONS.md`,
with a standalone starter in `examples/panel-extension/`. GitHub's
source ZIP is not an installable package. There is no arbitrary URL
installer or automatic update.

I load catalogue metadata only during discovery. Inspection and
installation each revalidate a fresh index, exact ZIP digest and full
manifest; unsupported APIs/permissions cannot install. Downloads are
bounded, deadline-limited, credential-free HTTPS requests to GitHub's
exact release hosts, with constrained redirects. A withdrawn or changed
selection cannot become a different trusted package. Offline catalogue
failures leave installed addons and ZIP installation available.

## Scope and limits

I keep #801's assets gallery for a separate follow-up. It needs a
bounded, authorized image/history capability rather than access to
internal stores. This PR only establishes external distribution and a
minimal context API.

Windows Tauri may inject native bridge objects into subframes: object
presence is not a grant. My isolated WebView2 fixture proves parent
command execution while direct/raw child calls do not reach it, and
verifies context delivery plus DOM/network restrictions. A trusted
hostile extension can still stall its renderer or attempt
self-navigation; this is not process isolation or a universal offline
guarantee. The installer warns users to trust the author.

I have not run a native Electron/macOS/Linux qualification or changed
the shared daemon. The runtime implementation is shared between desktop
hosts; native GUI validation here is Tauri only.

## Validation

- UI and server typechecks.
- 14 focused server/registry/manifest tests.
- Real right-panel browser tests for ZIP consent, activation scopes,
replacement, revocation, context changes, stale reads and frame
isolation.
- Five catalogue/server checks and seven right-panel browser scenarios
cover metadata-only browse/search, online consent, withdrawal,
incompatibility, offline ZIP fallback, hashes/manifests, redirect
restrictions and size budgets.
- Live public GitHub index and Release download matched SHA-256
`cf80226a40dd077a6a8cc2a4b65bbb46bf33619d62007982bd7dcea34ae4545f`;
installation and activation used a disposable isolated store.
- `node scripts/test-panel-extension-native.mjs` on Windows, using the
application's locked Tauri dependencies and an isolated browser profile:
parent native call allowed; child native calls, parent DOM and network
APIs blocked; session context delivered.
- UI build, Windows Tauri release build (`--no-bundle`) and
packaged-resource smoke passed. Full browser suite results are recorded
in the follow-up validation comment.

Existing oversized files touched only for integration:
`packages/server/src/api-types.ts` (~735 lines),
`packages/server/src/index.ts` (~713),
`packages/server/src/server/http-server.ts` (~2,438).
I restore right-panel customization to one compact Extensions action and move catalogue, ZIP installation, activation and trust/removal confirmations into a separate non-modal utility window. Lists stay single-line, metadata is available on hover and keyboard focus, toolbar actions remain reachable at narrow translated widths, and disposal fences late results.\n\nI remove speculative per-folder activation from the UI, client contract and backend. Each installed addon has one general activation control per CodeNomad profile. Existing packages and general grants survive; legacy folder-only consent is never broadened, and obsolete grants are removed only during an explicit successful mutation. Trust, digest-bound replacement/removal and owned-instance executable-panel reads remain enforced.\n\nI publish independent gatekeeper iterations until zero findings on the exact final head. Both typechecks, nine server tests and thirteen real-component extension/Status tests pass, including migration and French/Hebrew/Japanese/Spanish narrow layouts. Windows Tauri/WebView2 command denial passes. I use the requested admin override with the documented CI caveat: four unrelated browser failures also occurred in the final pre-correction run. The installed desktop and shared OpenCode daemon are unchanged.
## Requested change
Restore the normal question/permission icon for requests from unrelated
conversations. Show the complete shared red status badge, including its
localized text, separately at the trailing end of the collapsed bar.
Clicking the badge expands the selected request.

## Layout
Retain leading disclosure chevrons and source titles. Crowded mobile
headers wrap trailing controls instead of squeezing away the
conversation name. Reuse existing badge geometry, colors and
translations.

## Validation
- Initial dock/mobile run: 36 passes, one cold fixture navigation
timeout.
- All six final targeted checks pass, including the previously timed-out
recovery case; fixture initial navigation now has the same 60-second
budget as the real-shell fixture. Assertions preserved.
- Final UI TypeScript, production UI build and diff checks pass.
- Desktop/mobile compact question and permission captures inspected.
- Independent gatekeeper reviewed final diff/captures: ZERO FINDINGS.
Review will be published before the requested admin merge without
waiting for CI.

Installation is separate; no running application or native daemon was
restarted.
I put Extensions below a separator in the customization popup, collapsed
by default. I combine installed addons and the catalogue in one compact
searchable list and remove the separate modes and floating window.

I make the existing addon checkbox the only profile-wide activation
control. Disabled installed addons stay listed; reset never grants
consent. Explicit trust, digest-bound replacement/removal, offline ZIP
installation and sandbox protections remain intact.

I pass the UI typecheck, 9 server tests and 14 real-component
extension/Status browser tests, including narrow translated/RTL layouts
and real ZIP gestures. I inspect the interactive preview validated by
the user. Windows Tauri release qualification and the final independent
gatekeeper review are in progress; I do not replace the installed app or
stop the shared daemon.
Project the existing bell/history unread count onto browser tab favicons and the native dock or taskbar in both Electron and Tauri. Reading, deleting or clearing notifications updates the badge without querying sessions or adding polling; the original favicon returns when no unread items remain.

Aggregate per-window counts in the native hosts, accept bounded count-only calls from owned local and remote primary renderers, and keep preferences windows and preview guests outside the capability. Navigation and closure remove only that window's contribution, with document-generation and main-frame checks fencing stale updates. Windows overlays are generated by the host; other platforms use available native count APIs.

Add real Chromium regressions for notification lifecycle, icon pixels and restoration, unavailable native support and late image completion, alongside focused Electron bridge/security and Tauri state/ACL tests. UI/Electron typechecks, 15 focused Electron Node tests, four Tauri tests and Windows Tauri resource prebuild/smoke checks pass; no installed app or shared daemon was restarted.
Serialize the shared renderer-to-host badge calls so asynchronous Tauri dispatch cannot publish an older unread count after a newer clear. Final cleanup stays in the same FIFO, while rejected native calls do not prevent subsequent updates.

Add a Chromium regression with deliberately held native promises to check send order through a rejected call, rapid add/clear, and disposal. All six browser badge tests and the UI typecheck pass; Tauri resource prebuild and packaged smoke checks also pass with the ordered adapter.
Make the history search window and command palette movable from their existing top bars with native mouse and touch pointers. Share placement, pointer cleanup and viewport bounds in DismissibleWindow while leaving inputs, selectors and close buttons interactive and retaining non-modal dismissal and session authority.

Portal the movable inline search without losing its initial transcript anchor. Align utility stacking with opening order, revalidate placement as content grows, and bound height to the visual viewport. Native overflow and a sticky existing toolbar keep the last result, pagination and drag/close controls reachable in landscape and at pinch zoom; preserve the palette's 60vh limit and horizontal edge access when zoomed.

Extend the real-component browser regressions with drag, touch, RTL, viewport shrink, content growth, asymmetric drawers, overlap, zoom/pan and pagination. All 62 targeted browser scenarios, UI typecheck and production build pass. Five independent Gatekeeper passes corrected eight findings and the final review reports zero actionable findings on bb13b96.

Merge with the requested administrator override. Native CI jobs pass; the full browser suite has 489 passes, three pre-existing permission-fallback-diff failures and two skips. Read-only base/head reproduction confirms those unchanged tests wait for Copy patch in an intentionally collapsed external request without expanding it. Do not change permission semantics or hide these unrelated failures.
Observe primary WebView2 renderer/browser exits and WebKitGTK web-process termination without polling, new dependencies or guest capabilities. Factory enrollment happens before native build, so first loads do not depend on registration timing. Physical-window identities fence retired page, failure and close callbacks; failure revokes count admission until a new page starts.

Native subscriptions capture only a weak host rooted outside managed application state, avoiding controller/handler/AppHandle ownership cycles. Windows ignores frame, GPU, utility and unresponsive-process events. Update the existing narrow remote-capability regression to include the badge-only command.

Document the macOS limitation: pinned Tauri does not forward Wry's Darwin builder termination hook, so a surviving-window crash may retain its count until reload/navigation/close. Do not replace the runtime-owned WKWebView delegate or invent process monitoring. All 195 Windows Rust tests pass serially; Linux API review is not presented as compile/runtime qualification.
## Summary

Mirror the existing bell/history unread notifications on the browser tab
favicon and native desktop dock/taskbar, as clarified in #868. This
includes all existing notification kinds, including update notices;
session idle/attention status does not introduce a separate counter.

- Use a small red dot on the tab favicon and restore the original icon
when the unread count reaches zero.
- Forward count-only updates through the shared native adapter for both
Electron and Tauri. Native hosts aggregate live primary-window
contributions so clearing one window does not clear another's unread
notifications.
- Keep native badge authorization restricted to owned local/remote
application renderers, not preferences windows or preview guests, and
remove contributions on reload/navigation and window closure. Electron
handles renderer crashes; Tauri uses native termination signals on
Windows/Linux, with physical-window fences and weak native handler
ownership.
- Reuse history add/read/read-all/delete/clear updates without scanning
sessions or polling. History remains per-window and in-memory, as
before.

Browser tabs are covered, not installed-PWA launcher icons. Native
dock/taskbar availability depends on the operating system and desktop
environment.

**Tauri/macOS limitation:** the pinned public Tauri API does not forward
Wry's Darwin web-content termination hook. A surviving-window renderer
crash may retain its last count until reload/navigation/close. This does
not replace or swizzle the runtime's WKWebView delegate or add process
polling; normal macOS badge updates remain supported. See
`packages/tauri-app/NOTIFICATION_BADGES.md` for the API evidence.

## Validation

- Six real Chromium regressions cover unread lifecycle, generated badge
pixels, icon restoration, the 50-item history cap, Electron/Tauri
adapter dispatch, unavailable native support, disposal before image load
and FIFO native updates through failures/cleanup.
- UI and Electron typechecks passed.
- Fifteen focused Electron Node tests and all 195 Windows Tauri Rust
tests passed without launching either application. The Rust suite runs
serially (`--test-threads=1`); an initial parallel run had three
native-service fixture failures that pass serially.
- Windows Tauri resource prebuild/smoke checks passed. No native
visual/runtime qualification or Linux/macOS validation is claimed.
- The Linux termination hook was reviewed against the installed API, but
was not compiled or runtime-tested on this Windows host.
- Broad Electron Node suite: 208 passed; two failures outside the badge
tests (missing local Electron installation and a Windows
process-termination assertion). The broad suite is not claimed green.

Fixes #868
Do not mount a permission-receipt request for the internal no-session draft sentinel. Reject malformed session IDs at the receipt API boundary with a 400 instead of forwarding them to the native client where they raise a 500.

Cover the draft-to-session transition and reconnect in the browser fixture, and exercise malformed IDs alongside valid pagination and ownership checks on the server. Targeted browser/server tests and both TypeScript typechecks pass.
Closes #876.

The no-session draft sentinel is only a local draft identity; it must
never enter the permission-receipt API. Skip the read while that draft
is selected and reject invalid session IDs as HTTP 400 at the API
boundary rather than forwarding them to OpenCode as HTTP 500. Valid
session pagination and ownership checks remain unchanged.

Validation: 12/12 server permission-receipt tests; 9/9 targeted browser
tests; UI and server TypeScript typechecks.
Treat idle historical session directories as optional recovery hints while keeping roots, registered worktrees, active sessions and known request placements required. Exclude only paths rejected by ownership, retaining real loaded-only RPC negotiation and non-authoritative failure behavior. Replace the recurring interruption banner with the existing once-per-episode notification.\n\nIndependent gatekeeper reviewed the complete change and the updated dev merge tree with zero findings; the verdict is published as agent review comments. Targeted broker, UI and browser suites, typechecks and server build passed; other CI jobs were still running at admin merge.
## Behavior
- Selected skills appear once in the existing composer attachment bar
alongside files. Removing a skill preserves the draft and other
attachments.
- Sent and historical user messages display their attached skill names,
including messages containing only a skill. Labels survive native
history reloads.
- Long skill names wrap within the message width instead of being
clipped on narrow desktop, touch and RTL transcripts.

## Implementation
The shared message content filter now admits normalized skill parts into
the existing badge renderer. The redundant composer skill row and its
styles are removed.

## Validation
- The browser fixture exercises native message loading, normalization,
the store and the full transcript rather than an isolated badge. It
covers mixed attachments, deduplication, removal, sending, skill-only
history and reloads.
- The history-label regression fails on the original content filter.
- All three narrow long-name regressions fail before the wrapping fix
and pass afterward, measuring message, badge and text bounds.
- Final head `b2a31f339c6879f1fd76d32c55db9375669547cf`: focused
browser/skill checks: 16 passed. Session-action regressions: 53 passed.
UI typecheck and build passed; narrow rendered captures inspected.

Existing large files touched:
`packages/ui/src/components/message-block.tsx` (~1,889 lines),
`packages/ui/src/components/prompt-input.tsx` (~1,078 lines).
…#874)

Add the shared, explicit API 2 session.assets.read capability required by the independently distributed MCP Assets addon. Keep API 1 context-only, preserve one general activation checkbox, and require consent tied to the exact installed package digest.

Use the bundled native history integration for bounded metadata pages and exact embedded tool-result bytes, with session/Location ownership and final consent revalidation. Fence native/backend disconnects, generation changes and stale responses; refresh after failed tool results and direct pruning. Admit bounded source dimensions before host bitmap decoding and preserve thumbnail aspect ratio. Include the new native modules in the packed plugin as well as the shipped bundle.

Two autonomous gatekeeper rounds ended with zero P0–P3 findings on 9da8513; the initial three P2s have regression coverage. Local validation passed 16 browser, 14 server/image, seven package/presence and three bundled-installation checks, both shared typechecks, and the outside-checkout native fixture with official OpenCode 2.0.24. CI native plugin tests passed on Windows/Linux/macOS, as did runtime-contract and compatibility jobs.

Admin merge is explicitly requested by the maintainer. Global CI is not green: its three permission-fallback-diff Copy patch timeouts also occurred in the parent PR #872 (run 37632060353), and Windows reported two unchanged cross-host election-test failures. Preserve these failures as follow-up diagnostics rather than claiming full CI success or modifying unrelated paths in this PR. No protection settings are changed.
An offline, symbolicated dump identifies the captured Windows F6 failure as a WebView2 browser-process null dereference in MultiContentsView reached through FocusNextPane. Prevent only that native browser action on every created local, preferences, remote and preview webview.

Use the per-key WebView2 accelerator policy to preserve DOM delivery and configured UI shortcuts. Hosts without that interface swallow only F6 as a safe fallback; other accelerators, capabilities and platforms stay unchanged. Handlers capture no application/controller state and do no locking, IPC or message pumping.

Add a fresh-profile, static-loopback native fixture and ownership-fenced PowerShell runner for F6, Shift+F6, Ctrl+F6 and an untouched F8 control. Native decorated/frameless checks and 196 serial Rust host tests pass. Document the captured stack and the limitation that the minimal baseline does not reproduce the full installed-app crash; no fixed installed-app retest is claimed.
Address both gatekeeper findings: sanitize inherited WebView2 overrides before creating threads, force the fresh owned user-data folder and controlled browser arguments, and verify the environment's actual user-data directory. Every runner launch injects conflicting temporary overrides and checks that their folder stays unused.

Require heartbeat advancement during the final two post-input intervals, after F8 confirms the last input arrived. Add a negative native check that deliberately stops only those heartbeats and verifies the fixture rejects it even though all key-policy/delivery checks pass and ProcessFailed is absent. Positive framed/frameless and negative checks pass on WebView2 154.0.4258.62.
Closes #875.

## Captured cause
A trusted, foreground-checked F6 reproduced the black interface on
installed Tauri 0.20.1-dev-20261007-c995044b / WebView2 154.0.4258.62.
The native host remained responsive, but the WebView2 process tree
exited. An offline Crashpad dump with matching Microsoft symbols
identifies a browser-process null dereference in
`MultiContentsView::GetActiveContentsContainerView`, reached through
`chrome::FocusNextPane` and native pane-focus rotation. This is not a
hard refresh or the earlier Tao input deadlock. The dump remains local
because it can contain secrets.

## Mitigation
- Disable F6 browser actions only, through
`ICoreWebView2AcceleratorKeyPressedEventArgs2`, on each created local,
preferences, remote and preview webview.
- Preserve DOM delivery, including configured F6 shortcuts; retain all
unrelated browser accelerators. Older runtimes without the per-key
interface swallow only F6. No version gate or global accelerator
disabling.
- No callback locks, IPC, message pumping, app/controller capture or
additional preview capabilities. Electron and other platforms remain
unchanged.

## Validation
- 196/196 Windows Rust host tests, serial mode.
- Real isolated Tauri/WebView2 154.0.4258.62 tests in frameless and
decorated windows: F6, Shift+F6 and Ctrl+F6 browser policy disabled
while DOM delivery remains; F8 policy/delivery untouched; continuing
JavaScript heartbeat; no ProcessFailed.
- Positive native runs now verify the effective isolated profile and
final post-input heartbeat advancement under deliberately conflicting
inherited overrides; both framed/frameless runs pass. The negative
stopped-heartbeat check correctly rejects otherwise successful keyboard
checks.
- Frameless post-input capture visually inspected. No installed
application or shared OpenCode daemon replaced or restarted.
- Windows release build passes (`cargo build --release --bin
codenomad-tauri`); packaged-resource smoke checks pass. Existing
dead-code warnings remain.

The minimal baseline does not reproduce the captured installed-app
crash. Native fixtures validate the prevention mechanism and keyboard
preservation, not a full application A/B reproduction. No fixed
installed-app, Electron, Linux or macOS runtime qualification is
claimed. See `dev-docs/TAURI_WINDOWS_F6_CRASH.md`.
Remove the committed diagnostic/validation report and its AGENTS.md entry at the user's request. Keep the F6 mitigation and all runnable regression checks unchanged; investigation notes remain local rather than creating unsolicited development documents.

Verified this change touches documentation only, passes diff checks and leaves no references to the removed file.
Remove superseded MVP/UI/tool-rendering specifications, duplicate implementation and index summaries, completed attachment planning, incident readouts and redundant CI/pruning/native-backport reports at the user's explicit request. Trim the worktree validation diary while retaining placement, ownership and native-instruction contracts.

Preserve public extension contracts, attribution/licenses, private RPC safety, runtime capability policy and vendor patch provenance/removal criteria. Redirect native-backport instructions to the existing vendor README and remove dangling deployment links and a stale architecture dependency snapshot.

Validation: Markdown-only diff, whitespace checks and repository-wide checks for removed-file references. No runtime source, tests, build dependencies, installed application or shared OpenCode service is changed.
Remove unsolicited/obsolete development documentation at the user's
request, without changing the F6 fix or any runtime/test behavior.

- Delete 15 documents: the F6 diagnostic report, superseded
MVP/UI/tool-rendering specs and redundant summaries/index/implementation
docs, completed attachment planning, incident readouts and duplicated
pruning/CI/native-backport reports.
- Remove worktree validation chronology while retaining placement,
ownership and instruction contracts.
- Keep public extension APIs, licences/attribution, current private-RPC
safety and capability contracts, cache/interaction rules and existing
vendor provenance/backports. Redirect remaining references to retained
vendor documentation.

Validation: Markdown-only changes; `git diff --check` passes;
repository-wide removed-file reference search is empty. No new
document/report, source change, build, graphical test, desktop restart
or daemon mutation.
## Scope
Implement the two repository-wide Ponytail audit lists, deduplicating
overlapping recommendations. Based on current `dev@aa2776e`, on
`refactor/ponytail-cleanup`; no direct dev changes. Initial delta:
**3,548 net lines removed**, two direct dependencies removed (and their
unused transitive lockfile entries).

## Changes and preserved behavior
- Delete superseded MVP/UI/tool-rendering/summary docs; compact the
index and canonical architecture references and remove
per-review/temp-path bookkeeping from runtime-transition history.
Preserve security/ownership rules, technical minimum rationale,
compatibility provenance and reproduction commands.
- Remove confirmed-unused UI surfaces, Electron JSON storage IPC,
obsolete release fetch/shell runners and dead
bridge/storage/command/keyboard utilities. Keep existing native
environment discovery and shared-service lifecycle untouched.
- Replace the empty first-party plugin loading layer with direct
built-in right-panel construction. External sandboxed extensions,
consent, assets authority, activation, ordering and visibility remain
intact.
- Retire the unused incremental-part message mutation engine and
exclusively owned pending-part buffers/budgets/types. The active native
reducer still publishes bounded authoritative snapshots. Preserve
optimistic send identity, pending sends, permissions, usage,
pruning/revert, memory accounting and scroll restoration. Rewrite old
tests using snapshots rather than removing relevant behavioral
assertions.
- Share speech streaming/base64 helpers, retaining playback lifecycle
and cancellation in each caller; deterministic transport regression
covers ordering, start-once, completion and failures.
- Replace three Material icons with existing Lucide icons; remove
`@suid/icons-material` and unused `ts-node` without upgrading other
dependencies.
- Drop the redundant Favorites implementation-shape test; behavioral
store/browser regressions stay. Repair two inherited tests to current
palette and lazy copy contracts, preserving image/privacy assertions.

## Validation
Server/UI/Electron typechecks, UI production build, targeted
core-panel/Favorites/audio/image/palette checks pass. Full local suites
and focused browser/native Electron regressions are in progress. The
broad server run found one existing deadline-sensitive pending-broker
test; its entire 20-test file passes in isolation. Independent
gatekeeper review is required on this exact committed head, findings
will be corrected and re-reviewed to zero before the explicitly
authorized admin merge. No user credentials/shared daemon/database were
used for testing.

## Size notes
Touched existing large files:
`packages/ui/src/stores/message-v2/instance-store.ts` ~1,450 lines,
`packages/ui/src/stores/message-v2/instance-store.test.ts` ~516 lines,
`packages/ui/src/stores/conversation-speech.ts` ~432 lines. No size-only
refactor; source/test coverage reduction is tied to proven-unused paths.
Native application is not replaced/restarted by these isolated checks.
Reproduce valid recovery being reported incomplete on a large live workspace: an eight-directory loaded-only broker read returned complete HTTP 200 coverage after 19.4 seconds, while the frontend's ten-second timer abandoned the same request path. The broker already budgets thirty seconds for Git/directory ownership and its bounded native snapshot.

Share the existing broker budget through the API contract and allow that budget plus a five-second transport margin only for loaded-only snapshot reads. Ordinary pending-list and liveness reads keep their ten-second deadlines. Retain cancellation, generation/ownership fences, queues, compaction admission and no-fallback/no-replay behavior.

Add a controlled-clock store regression for a valid nineteen-second response and a stalled thirty-five-second read, preserving known permissions/Forms and refusing ordinary-list fallback. No new development report or installed-app/daemon restart.
## Root cause
The installed 0.20.1-dev-20261008-027f5094 includes #877, but slow valid
broker reads can still be classified as incomplete. The server gives
directory/Git authorization and the native loaded-only snapshot a
30-second budget; the frontend aborts the broker at 10 seconds.

On the live Windows Tauri workspace (253 inventoried worktrees, none
missing), a bounded read of eight existing worktrees returned HTTP 200 /
complete coverage after **19.4 seconds**. Repeating the same batch with
the current frontend deadline aborted at **10.0 seconds**. A later warm
read with a 35-second cap returned complete coverage in 4.1 seconds.
These are variable-latency observations and a reproduction of the
premature-abort mechanism, not a latency guarantee or attribution of
every historical notification.

## Change
Share the existing server budget through the API contract; only
loaded-only broker reads get that budget plus a 5-second transport
margin. Ordinary queue/status reads keep 10 seconds. No warning
suppression, added retry, cold-worktree loading, prompt replay, weaker
ownership or queue clearing.

## Validation
- 41/41 UI store tests: delayed valid recovery, bounded real failure,
preserved permissions/Forms, compaction, cancellation, tombstones,
global Forms and no ordinary-list fallback.
- UI/server typechecks, UI production build and diff checks pass.
- Server broker suite: 19/20 pass in two full runs; the unchanged
real-Git 64-directory deadline-sensitive case reaches the same existing
30-second budget under load. That case passes alone (22.1 seconds). No
deadline increase or test weakening to conceal it; the server deadline
value is unchanged.
- Independent gatekeeper rerun: 41/41 UI and 20/20 broker tests pass
(including the 64-directory case at 26.9 seconds), plus both typechecks.
Published review: zero actionable findings. Prior under-load deadline
sensitivity remains disclosed.
- No installed application or shared daemon restart, no new development
documentation.

Existing large files touched: packages/ui/src/stores/instances.ts
(~2,335 lines), packages/server/src/api-types.ts (~738 lines). No
unrelated refactor.
## Summary

- Give each conversation its own browser preview, keyed by project
folder and session ID, rather than reusing the project's URL across
conversations.
- Preserve the actual native guest when returning to chat, selecting
another session, showing Info or hiding the project. Page state and
native Back history survive these transitions.
- Keep guest DOM order stable. Only explicitly opened browser shells
remain mounted; inactive transcripts and composers still unmount and the
existing transcript-memory budget remains independent.
- Both the conversation toolbar toggle and the preview's X return to
chat while keeping the browser, document state and native history
intact. The X is not a destructive browser close.
- Adopt an old project-wide saved URL once for the first restoring
conversation.
- Persist a bounded URL history and cursor per session. After restart,
browsers are restored only on explicit opening: load the current URL,
never replay intermediate visits or eagerly create saved guests.
- Add Back/Forward traversal using owned native CDP history IDs for live
entries. When an entry no longer exists natively, load only that
selected saved URL. Persist neither native pageState, POST bodies nor
form/DOM/JavaScript state; existing cookies/site storage remain native.

## Why

The preview was conditionally mounted inside the active
session/transcript view. Going back to chat or changing sessions
destroyed its webview; reopening recreated only the last URL, losing the
native history. The preview store was also keyed by project folder
despite the native guest being session-bound.

This fixes the lifetime and ownership at their existing shared UI
boundaries. Electron unregisters hidden guests from automation without
destroying them; Tauri hides and reuses its existing child. Native
controllers, authentication and browser storage partitions are reused.

## Validation

- UI, Electron and server typechecks passed.
- Full-shell browser/history suite: **9/9 passed**. Real Electron guests
and the production controller cover guest/form retention,
X/chat/session/project/Info transitions, renderer restart, lazy restore,
no intermediate replay, Back/Forward and hidden navigation. Also covers
interrupted native/iframe traversal, inactive transcript/composer
disposal, project/session separation, Tauri hide/reuse and one-time
legacy migration.
- Tauri native suite: **197/197 passed** (serial rerun after an
unrelated Windows service-fixture failure under parallel load).
- Focused Electron browser controller/preload: **23/23 passed**,
including hidden owned-history access without automation visibility and
foreign-owner/unsafe-entry refusal.
- Existing BrowserFrame native/renderer regressions: **19/19 passed**.
- Focused existing header/session/preview-close regressions: **3/3
passed**.
- Preview persistence/native adapter unit cases and the
browser-conditioned transcript-budget regression passed.
- `git diff --check` passed.

The broad local Electron suite hits unrelated, unchanged Windows
process-identity/service-start fixtures; focused browser/native checks
pass. The broad header run exceeded the local command timeout; relevant
cases were rerun separately. Tauri browser interaction is exercised
through the real Solid shell with mocked native IPC and Rust controller
regressions, not a packaged WebView2 smoke.

History is capped at 32 URLs per session within the existing 4 KB
aggregate layout-value budget; trim history before evicting saved
previews. Application/project closure ends the live document: restored
history preserves URLs/cursor, not unsent forms or scroll.

## Autonomous gatekeeper loop

Independent pass 1 found hidden Tauri navigation loss and explicit Go
superseding a pending Back corrupting an entry. Pass 2 found hidden
Electron navigation loss before restart and stale iframe Back callbacks
after Go/Refresh. All four findings were fixed with regression tests;
the final independent pass reports **zero actionable findings**. The
final review will be published separately on the exact pushed head
before the requested admin merge.

Prior-head CI also exposed three failing clipboard-diff tests. An
independent diagnostic reproduced them unchanged on exact `dev` baseline
`027f5094`: the fixture omitted the session context required since #848.
The fixture-only correction seeds/passes its session and all three cases
pass. Production permission/approval behavior is untouched.

## Size notes

Existing large files touched:
`packages/ui/src/components/instance/instance-shell2.tsx` (~1334 lines),
`packages/ui/src/components/browser-frame.tsx` (~1005),
`packages/ui/src/components/session/session-view.tsx` (~723),
`packages/tauri-app/src-tauri/src/main.rs` (~2712), and
`packages/tauri-app/src-tauri/src/browser_controller.rs` (~1674).
…ory (#1107)

## Summary

- The notification history's **Clear all** and **Settings** text buttons
become icon buttons (red trash can, gear), like the panel's close
button.
- Their labels stay as tooltips (`title`) and accessible names
(`aria-label`), with the existing translations. **Mark all as read**
keeps its text.
- Shared `window-icon-button` geometry; the existing toast-history
action classes keep hover, danger and disabled styling.

## Validation

- Screenshots of the real `ToastHistoryPanel` with two notifications,
dark and light themes, including the hover state.
- `npm run typecheck --workspace @codenomad/ui`.
## Summary

The app icon looked smaller than its neighbours in the Windows taskbar:
the square source SVG kept an empty ~4 % margin on each side, so the 24
px icon drew the CN in 22 x 12 px on a transparent background.

- `images/CodeNomad-Icon.svg` is cropped to a square centred on the
mark, touching the left and right edges (height stays about half: the CN
is twice as wide as tall).
- Master PNG, in-app copy, Electron/Tauri `.ico`/`.icns`/`.png` and
Linux sizes regenerated: 24 px now 24 x 14 px, 32 px now 32 x 18 px.
- In-app logos use the same PNG, so their heights shrink by the same
ratio to keep the approved on-screen sizes (5.55rem, 4.44rem narrow,
7.4rem loading); `dev-docs/UI_CONVENTIONS.md` updated.

## Validation

- Regenerated `icon.png` loses no opaque pixel of the master; the mark
spans columns 0-1023.
- Measured bounding boxes in the 16/24/32/48 px `.ico` entries before
and after.
- `npm run typecheck --workspace @codenomad/ui`.
…permission (#1109)

## Summary

System notifications on Windows: everything about them in one PR.

1. **Windows data profiles never showed system notifications**
(`fix(windows): show system notifications from every data profile`).
Tauri runs each data profile, such as **dev**, under its own
AppUserModelID (`…client.scope.s<hash>`), and its notifications carry
that ID; Windows shows a desktop app's notifications only under an ID it
knows, and the installer's Start menu shortcut carries the stable ID
alone. Windows recorded every notification (100 on the affected machine)
and displayed none. Every launch now registers the running ID under
`HKCU\Software\Classes\AppUserModelId\<identifier>` (`DisplayName`
CodeNomad, `IconUri` the bundled web app icon
`resources\server\public\pwa-192x192.png`, which the packaged-resource
smoke check requires; `windows_notification_identity.rs`). Electron
always uses the stable ID.
2. **The permission was lost at every restart on Windows.** The
notification plugin's page script reports *denied* there until the page
requests permission, though Windows never asks, so Settings asked again
after each restart. The check now asks the native plugin command, which
answers granted on desktop: granted once, kept.
3. **Notifications were held back whenever the window was visible**
(`fix(notifications): hold system notifications back only while
focused`). With **Notify when the app is focused** off (the default), an
open window stays visible behind other applications or on another
screen, so nothing was sent unless CodeNomad was minimized. Delivery is
now skipped only while the window is visible and focused
(`document.hasFocus()`). The setting's subtitle and the Settings help
say *focused* in all ten languages; `dev-docs/SETTINGS.md` records the
rule.

Out of scope by decision: several CodeNomad windows open on the same
project may each send the event's notification (as they already do when
the setting is on), and focus inside a browser preview counts as leaving
the page.

## Validation

- Affected machine: no profile notification showed before; after
registering its ID as above, a notification sent under it showed in the
notification centre under CodeNomad.
- `cargo test --bins -- windows_notification_identity` (writes the key,
reads back both values).
- `tests/browser/system-notification-focus.test.ts`: real routed
notification from a visible page; nothing while focused, one once focus
is lost, one while focused with the setting on; red on the previous
visibility check.
- `npm run typecheck --workspace @codenomad/ui`, `npm run test:help`.
heunghingwan and others added 2 commits October 23, 2026 11:48
…bound (#1106)

## Problem

Long model reasoning still appears to freeze roughly 30 seconds in, even
after #1088 kept the store streaming across shared-stream reconnects.
The visible thinking text stops growing while the panel keeps visibly
refreshing — the live duration keeps ticking, the transcript keeps
following, and the reasoning scroller stays pinned to its bottom. To a
user watching a long think, it is indistinguishable from the earlier
store freeze, which is why the previous fix looked like it had not
worked.

## Root cause

The record display projection caps every reasoning part's text at
`REASONING_RENDER_CHARACTER_LIMIT` (10 000 characters —
`extractReasoningTextForRender` inside `buildRecordDisplayData`,
introduced by #648's memory bounding). The cap applies while the part
streams, not only once it settles:

- A reasoning stream at typical model speed crosses 10 000 characters
around 30 seconds in — the same moment the connection-recheck freeze
used to surface, so the two bugs produced nearly identical symptoms.
- Past the bound, every delta still re-projects the message (the row
re-renders, the header duration ticks, the transcript keeps following —
"refreshing, just no content"), but the displayed text is byte-for-byte
the same 10 000-character prefix, so `ReasoningStreamOutput`'s `<pre>`
never changes again until the part completes.
- The store keeps the whole text the whole time and copy actions read it
whole; only the display copy is silently capped, with no reveal.

## Changes

- `stores/message-v2/record-display-cache.ts`: a reasoning part without
`time.completed` (still streaming) now projects its whole `text`. That
is a reference to the store's string, not a copy — per delta it is
cheaper than the previous sliced 10 000-character copy. Once the part
completes, the projection returns to the bounded 10 000-character
prefix, preserving #648's retained-copy bound for settled transcripts.
- `dev-docs/TEXT_DISPLAY_BOUNDS.md` describes the split: completed
reasoning keeps the bounded prefix; a still-streaming part renders
whole.

## Behavior compatibility

- Completed reasoning renders exactly as before (bounded prefix,
whole-text copy).
- Timeline markers, hover previews and search slice their own text from
the raw parts (`message-timeline.tsx` still bounds previews at 4096
characters) and are unchanged.
- A reasoning part that never settles (interrupted mid-think) keeps its
whole text by reference — it renders live and retains no extra copy.
- No help-page change: the bound was never documented in the user help
and no labels, controls or defaults changed.

## Validation

- Red→green unit regression in `instance-store.test.ts`: the streaming
projection keeps the whole text past the bound; the completed projection
keeps the bounded prefix. Pre-fix it fails at the first assertion (10
000 vs 10 020 characters).
- New browser regression `tests/browser/reasoning-stream.test.ts` (real
`SessionView`, native SSE dispatcher): three ~6.9K-character reasoning
deltas keep rendering whole past the bound, and
`session.reasoning.ended` returns the card to the 10 000-character
prefix. Pre-fix it fails exactly like the live bug: `AssertionError:
delta 2 renders whole (got 10000)`.
- The `session.tsx` fixture gains `startReasoning` / `reasoningDelta` /
`endReasoning` emitters plus an opt-in `revealThinking` preferences hook
(thinking rows are hidden by default).
- `tsc --noEmit` clean; `instance-store` 30/30; `empty-content` +
`message-part-grouping` + `render-text-window` 18/18.
- Shared-fixture `message-scroll` suite: 127/128 **identically on this
branch and on the clean base commit**; the single failure is the first
test's page-load navigation timeout, a pre-existing local Vite
cold-start issue (the same environment phenomenon class #1088
documented), not a regression.

---------

Co-authored-by: Pascal André <pascalandr@gmail.com>
)

## Summary

- New logo: a hexagon framing a C and an n, in equal bands (ring, gap,
strokes, counters share one width), readable down to 16 px.
- **App icon**: white ring and letters over black gaps, transparent
canvas, hexagon spanning top to bottom; one fixed icon for light and
dark taskbars, docks and tabs.
- **In the UI** (`components/brand-logo.tsx`): black hexagon with white
gaps on light palettes, white version on dark ones, picked in CSS from
`data-theme` (system appearance when unset), so it also works outside
ThemeProvider (title bar). Loading screen (always dark) uses the white
one. Sizes: 4rem beside a text-3xl title, 3.2rem narrow column, 5.3rem
loading screen, 16px title bar.
- **Icons rendered from the vector**: `scripts/generate-icons.mjs` draws
every `.ico` entry (16-256 px), `.icns` slot, Linux size and the 1024 px
master straight from `images/CodeNomad-Icon.svg` with Chromium, instead
of shrinking one large PNG; it writes the containers itself, so Electron
drops `png2icons`/`pngjs`. One command replaces BUILD.md's four manual
steps.

## Validation

- New `tests/browser/brand-logo.test.ts`: real title bar + home page;
which hexagon each logo shows on light, dark and system (both ways)
palettes, with heights; red without the logo stylesheet.
- UI + Electron typechecks, Electron tests (283), `macos-titlebar` and
`notification-icon-badge` browser tests, `npm run test:help`.
- Icon checks: ICO entries 16-256 (DIB, PNG at 256), ICNS slots
icp4-ic14; UI captures on porcelain, linen, mist and ember palettes.
## Summary

At 16 px the title bar's hexagon was too small for its C and n to read.
Compared in the Windows and macOS title bars on light and dark palettes
at 16, 20 and 24 px, 20 px reads clearly, stays close to the menu text
and leaves margins in macOS's 28 px bar (24 px nearly filled it).

## Validation

- `brand-logo` browser test expects the 20 px title bar logo;
`macos-titlebar` still finds it centred and clear of the traffic lights.
- `dev-docs/UI_CONVENTIONS.md` records the size.
…1117)

A follow-up sent while the agent works (follow-up behaviour "queue") shows
the "Queued" badge until the engine delivers it. After some turns the
badge never cleared: the message was answered, the engine went idle, and
only closing and reopening the session removed it (#1112). The stale
pending entry also kept the message pinned below the running turn.

The session view lists queued prompts from the session reducer's inbox
through a memo. CodeNomad replaces that reducer while the session stays
open: an authoritative resync after an event-queue overflow, a rotation
failure or a reconnect, and a pruning invalidation. The replacement was a
plain property write, so the memo kept reading the disposed reducer,
which never sees `session.inbox.delivered`. On 0.20.1 nothing rebound it
until the view remounted; on dev the reducer retirement at settlement
happened to rebind it at the end of the turn, but the badge stayed for
the rest of the turn, and indefinitely when retirement waits (another
prompt still parked, an unsettled tool or shell).

Every replacement now goes through replaceTranscriptEntry, which assigns
the new reducer, disposes the previous one and bumps a per-transcript
entryRevision signal that getOpenCodeSessionInbox tracks. Only that
session's inbox observers re-run; a transcript destroyed and recreated is
still rebound by the instance revision. Several queued follow-ups keep
their badges until each is delivered; steer-mode actions on a delivered
prompt disappear the same way.

Validation: opencode-inbox-rebinding.test.ts queues two follow-ups,
replaces the reducer by pruning invalidation and by an event-queue
overflow, then delivers them one by one with no terminal event: each
leaves the memo at once. Both cases fail without the fix. A browser test
in session-rendering.test.ts runs the real SessionView and SSE dispatcher:
queued badge, a pruning event, delivery, badge gone while the turn still
runs; it fails without the fix with the badge still visible. The session
fixture's native inbox now lists parked prompts as an array and stubs the
permission and form lists, as a real daemon does, so an authoritative
resync completes. The new unit file is listed in the PR build's UI unit
step, which passes locally (64 files, 562 tests), as do the UI own-tsconfig
and root typechecks and the six browser suites using the session fixture
(206 tests).
…1118)

A session whose history holds image tool results (for example `read` on
PNG files) never opened over a slow link (#1113): the 200-message page
was 16.8 MB, 15.7 MB of it base64 in five tool results. It took 20 s at
6.5 Mbit/s, and a reverse proxy cut the transfer, so the UI showed
"Failed to fetch". Every open, reload and history window paid for every
image, shown or not.

Transcript pages now carry large tool images by reference. The UI's
native message list reads (createInstanceFetch) and history windows send
the `x-codenomad-media: deferred` opt-in. For those reads only, the
server replaces each tool image whose data URI exceeds 32 KiB with a
`/api/workspaces/<id>/session-media/<session>/<message>/<part>/<index>/
<sha256>` path, in the instance proxy (forwardRuntimeRequest gains an
optional whole-JSON transform for successful JSON responses) and in the
history window route. toolImageSource resolves that path on the API
base, so the existing lazy `<img>` loads only the images that are shown,
as binary rather than base64.

The new session-media route checks that the workspace owns the session,
reads the native message, and refuses a session that moved during the
read and messages at or after an undo boundary. It serves the bytes only
when the image still matches the digest, as an immutable, nosniff,
sandboxed image; anything else falls back to "Unable to display this
image".

What stays whole:
- Single-message reads (`session.message.get`), so pruning revisions
  keep hashing native content.
- Live events, user attachments, small images, non-image files and
  error responses.
- Requests without the opt-in.
The opt-in header is never forwarded to OpenCode. On the reported
session, the page drops from 16.8 MB to about 1.1 MB.

Validation:
- session-media.test.ts: only large renderable tool images change, and
  only their URI.
- routes/session-media.test.ts: exact bytes and headers; invalid
  references, digest and position mismatches, the undo boundary,
  ownership and a move during the read.
- proxy.test.ts: the transform applies to successful JSON only and
  releases once.
- instance-proxy.test.ts: an opted-in list read is deferred, the opt-in
  is not forwarded, and plain list or single-message reads stay whole.
- session-navigation.test.ts: history windows.
- sdk-manager.test.ts: only list reads opt in, keeping other headers.
- tool-content.test.ts: path resolution, forged paths rejected; it is
  now listed in the PR build.
- tool-images.test.ts: a deferred image decodes from the route in the
  real ToolCall renderer, and a missing one shows the fallback.

The UI own-tsconfig, server and root typechecks pass, as do:
- the server suite (2,467 passed; one index.test.ts process test timed
  out under load and passes alone, as on dev);
- the UI unit step of the PR build (564 tests);
- seven browser suites (278 tests).
…1121)

The manifest host's analytics showed one address fetching version.json
about every six seconds for a whole day. The server reads the manifest
once per start, so that is a server restarting in a loop, but every
request carried the same bare "CodeNomad-CLI" User-Agent: nothing told
which CodeNomad version was involved.

The UI manifest read and the UI package download now send
"CodeNomad-CLI/<server version>". Nothing else changes: the manifest is
still read once per start, and a failed read still falls back to the
cached or bundled UI.

Validation: a new remote-ui test serves the manifest from a local HTTP
host and asserts the exact User-Agent it receives; it fails with the
bare User-Agent. Server own-tsconfig and root typechecks pass.
…tup row (#1122)

## Summary

- **Updates** is its own card again (About → Updates → Diagnostics →
connectivity), subtitle restored in 10 languages: latest version,
install/download + Refresh, then the **update feed** with its help and
save error last. Rows resuming after actions open with their separator.
Help speaks of the Updates card again (10 locales).
- **Data profile** moves from About to General's startup card (Restore
previous state), above Other saved profiles; still only for an explicit
non-default profile (`current-profile-settings-row.tsx`, shared by
inline settings and Preferences). Label key moved in 10 languages;
desktop help and `DESKTOP_DATA_PROFILES.md` point to the new place.
- The Clear saved startup state button keeps space above the rows'
separator.

## Validation

- `server-info`: card order, feed out of About, after the actions, with
its separator; no data profile in Info.
- `other-profiles`: running profile named above other saved profiles,
clear of the button, Electron and Tauri Preferences; default profile
never named.
- `desktop-updates`, `npm run test:help`, UI typecheck.
…stion (#1123)

Choosing a desktop data profile needed `CODENOMAD_PROFILE` or, on a first
packaged launch with several restorable profiles, a native question that
blocked startup. Settings only named a non-default profile and offered to
delete all the other profiles at once.

Settings > General > Restore previous state now ends with one folded line,
"Data profile: <name>". Unfolded, it lists every profile on the computer,
the open one first and the others by last use, each with its size, creation
date and last use. A radio chooses the profile the next launch opens (any
profile of this configuration; choosing the open one again cancels), "New
profile" creates one by name, "Restart in <name>" restarts into the choice,
and each other profile has its own delete button with a confirmation. A
launch pinned by `CODENOMAD_PROFILE`, its alias or a development run shows
the profiles without letting the user choose; deletion still works.

Without a remembered choice, a packaged launch no longer asks: it opens the
most recently used of the default, `dev` and `dev-v2` profiles (ties go in
that order) and remembers it, as it already did with zero or one candidate.
A launch that finds the selection lock held opens the detected profile
without remembering it instead of waiting.

Implementation: the existing host listing gains the open profile, creation
(folder birth time, links not followed) and last-use (newest client-state
write) dates, the remembered next-launch profile and whether the launch is
pinned. Two host operations are added on both hosts, granted to local and
Preferences windows only and serialized with list and delete: choosing the
next launch's profile (validated with the `CODENOMAD_PROFILE` grammar,
written under the selection lock, overwriting this configuration's entry,
creating a new profile's scope folder inside the lock and removing it again
if the write fails) and restarting through the graceful developer-mode
relaunch. Electron's throwaway question storage, relaunch and private
hand-offs are removed; it still sweeps leftover question folders and drops
the two hand-off variables an older build's relaunch can carry. Tauri keeps
only the invalid-profile error dialog. `CODENOMAD_DESKTOP_PROFILE` and the
server's `desktopProfile` meta field are removed, since nothing displays them.

Only a profile whose name leads back to its own folder can be chosen, so a
folder the deprecated alias made under another profile's name (such as
default-<hash>) is listed under its stored name but not offered. The
listing reports the next launch's profile only when the launch would open
it (the launch's own remembered-profile check), and Restart in <name> lists
again first and restarts only while that still names the shown profile.
Choosing with the keyboard keeps focus on the chosen radio.

Validation: UI browser tests for the manager (listing, choose and restart,
new profile and name validation, single deletion, macOS note, pinned
launches, plain browser), Electron native tests (listing dates, current,
next launch, pinned, set-next-launch outcomes, relaunch wiring, preload,
automatic choice, lock and sweep) and Tauri tests (the same cases, grants
and command wiring). Developer doc and help (10 languages) updated.
…get (#1124)

The authentication hook decided whether a request needs a session from the
raw request target, while Fastify's router matches a decoded path. A
percent-encoded or absolute-form target therefore reached protected routes
without a session: "/%61pi/workspaces" routed as "/api/workspaces", and
"/%73idecars/<id>/..." reached a SideCar, on any listener the server
exposes, including LAN listeners protected by the server password. Remote
Control was not affected: its gate already judged a decoded path.

Both checks now share `routedPath()` (server/routed-path.ts), which returns
exactly the path find-my-way matches: cut at the first "?" or ";" (Fastify's
default semicolon delimiter) and decoded with decodeURI, so reserved
characters such as %2F stay encoded and %25 is never decoded twice. Targets
the router would reinterpret (absolute-form, a leading "//", a fragment) or
reject (malformed percent-encoding) are refused with 400 before any access
decision. The Remote Control gate used a broader decodeURIComponent
variant; sharing the router-exact helper keeps the two classifications
identical to routing.

Validation: `routed-path.test.ts` checks the helper against the decoder of
the router Fastify itself loads and its refusals. `request-path-auth.test.ts`
drives the real HTTP application over raw sockets: encoded /api, /sidecars
and /workspaces targets, queries and semicolons get 401 without a session,
reinterpreted or malformed targets get 400, public routes stay public and a
session still reaches encoded paths. It fails on the previous hook
("/%61pi/test/echo" answered 200). The Remote Control surface tests and the
whole server suite pass, and the server typecheck is clean.
…1125)

When an iframe preview loads, the browser sends the CodeNomad origin's
cookies with the frame's document request: the parent page initiates that
navigation, so the SameSite=Lax session cookie and any SideCar service
cookie set at the app origin are attached. The HTTP preview proxy forwarded
that Cookie header unchanged to the preview target, which can be any
external site. Opening an untrusted page in a browser or mobile client's
preview therefore handed it a working CodeNomad session.

Preview HTTP requests now drop the Cookie header, matching preview
WebSocket upgrades, which already forwarded none. Target Set-Cookie headers
were and remain stripped, so previews keep their behavior: the path proxy
never carried cookies a previewed site could have set. All three entry
points share proxyPreviewTargetRequest: the /previews/<token> path proxy,
the per-token preview.localhost capability host and the root-relative
asset fallback resolved from a preview Referer. Desktop native guests load
the target directly and are unaffected.

Validation: a new sidecar-proxy regression sends the CodeNomad session and
a SideCar service cookie through each entry point and asserts the preview
upstream receives no Cookie header. It fails on the previous proxy (both
cookies reached the upstream through all three). The server proxy, preview,
SideCar and Remote Control suites pass (192 tests) and the server typecheck
is clean.
## Problem

OpenCode 2.1.4
([#58184](anomalyco/opencode#58184)) delivers
MCP elicitation requests live, as global-session Forms (`sessionID:
"global"`) whose `form.created` event carries the server's location.

CodeNomad's live event path (`stores/instances.ts`) queued
`event.data.form`, which has no `location`. `formRequestOptions()` reads
`form.location`, so the dock's reply and cancellation went out **without
`x-opencode-directory`**. For a Form created in a worktree or
subdirectory, OpenCode answered `FormNotFoundError` until a
pending-request rescan replaced the entry with its located copy.

Found by the independent review of the 2.1.4 qualification (#1126),
which reproduced the headerless `FormNotFoundError` on an isolated 2.1.4
daemon.

## Fix

Recovery already attaches the snapshot location to global Forms. The
live path now does the same with the event location before queuing.
Session-owned Forms are unchanged, since their replies need no location
header.

`dev-docs/NATIVE_INTERRUPTION_UX.md` states that live and recovered
global Forms keep their location.

## Tests

- New regression in `instances-pending-requests.test.ts`: a real SSE
dispatch of two global Forms from a worktree, then a reply and a
cancellation. Both must send `x-opencode-directory` for that worktree.
It **fails without the fix**, because the queued Form has no location.
- With the fix:
  - pending-request, compaction and Form store tests: 55/55;
  - full UI unit suite: 1700/1700;
  - interruption dock and mobile interruption browser tests: 43/43;
  - UI typecheck.
## Summary

- Qualify OpenCode **2.1.4** (latest stable, 23 October) with the
existing locked **2.1.0 client/plugin** dependencies, and **recommend**
it.
- Keep the technical **minimum at 2.1.2**: no consumed contract changed,
so the minimum has no new reason to move.
- 2.1.2 and 2.1.3 daemons stay ready and get an optional upgrade offer.
2.1.4 is the `tested` version. Newer and custom labels keep
authenticated contract recognition.
- The reviewed-contract fast path lists the 2.1.2 minimum and the 2.1.4
target.
- The recommended version is updated in the help (all ten locales), both
READMEs, `CONTRIBUTING.md`, `dev-docs/OPENCODE_COMPATIBILITY.md` and the
architecture skill.

## Contract review (v2.1.3 → v2.1.4, 72 commits)

- **Authenticated OpenAPI:** 121 → **124 paths**, 146 → **149
operations**. No operation changed. The changes:
  - added `POST /api/experimental/client-mcp/token`;
  - added `GET /api/experimental/client-mcp` (WebSocket);
  - added `POST /api/debug/heap-dump`;
- `Config.InfoEncoded` gains the opt-in
`experimental.background_compaction`.

The session event (`V2EventEncoded`) and message schemas are unchanged.
None of the new routes is on the instance proxy allowlist, and the proxy
relays WebSockets only for SideCars and previews.
- **Published declarations:**
  - the client adds `mcp.client.token` and `debug.heapDump`;
- the schema and plugin changes are type refactors and unused-export
removals, among them `SessionID.descending`, `SessionEvent.Source` and
`PromptInput.FileAttachment.create`; `Session.Message.ID.fromEvent` now
takes any string instead of an event ID. CodeNomad uses none of these,
and every host module its native plugin bundles import
(`@opencode/plugin`, `/effect`, `/promise/adapter`, `/rpc`,
`@opencode/schema/location`, `/session`) still exists.
- **MCP elicitation forms**
([#58184](anomalyco/opencode#58184)): an MCP
server asking for input during a tool call now creates a global-session
Form whose `form.created` event carries the server's location.
CodeNomad's live path requires that location for global Forms and shows
them as a **Project request**. Review found that the live path dropped
the location from the queued Form, so its reply lacked
`x-opencode-directory` and failed for a worktree or subdirectory until a
pending-request rescan. **#1127** fixes that; recovery already kept it.

A native probe (minimal stdio MCP server, a real Code Mode `execute`
call, CodeNomad-style reply) gives:

| Runtime | Form listed for the location | `form.created` carries the
location | Reply completes the tool |
  | --- | --- | --- | --- |
  | 2.1.3 | yes | **no** | yes |
  | 2.1.4 | yes | **yes** | yes (`action: accept`, `channel: stable`) |

On 2.1.3 such a request only appeared after a pending-request rescan; on
2.1.4 it appears live. The probe sends the location header itself, so it
checks the runtime contract; #1127 covers CodeNomad's live reply path.
- **Other changes** are covered by the native suites below:
- compaction (oldest-first shortening, ignored buffers, overflow
detection);
  - the agent's model for sessions without one;
  - V2 agent files;
  - V1 global-session import attribution;
  - MCP lifecycle fixes.
- **Live check:** this machine's shared daemon runs 2.1.4, and the
CodeNomad desktop session works through it.

## Isolated Windows qualification

Base: dev `9ac792fff`; Node **24.20.0**; exact official CLI **2.1.4**;
unchanged locked client/plugin **2.1.0**.

- **Build and checks:** production server/UI/native plugin builds,
server and root typechecks, native bundle guard.
- **Native runtime (9/9):**
  - Code Mode job ordering, plugin runtime, automation;
  - complete pruning, history, ownership, worktree and event relay;
  - loaded-only pending snapshot;
- Missions contract, family control, native and Compare/Wayfinder
trajectory.
- **Sessions (15/15):** environment, fork, aside, interrupt, lifecycle,
blank cleanup, prompt skills, provider usage/accounts/selection, service
usage, web search, plugin packages, output limit, Promise-tool failure.
- **Recurring (5/5):** all **11 recurring Mission journeys**, subagent
depth, **2 MiB** compaction, permission receipts.
- **Migrations (10/10):**
- history from **2.0.3, beta-19271, 2.0.32, 2.1.0, 2.1.1, 2.1.2 and
2.1.3** to 2.1.4;
  - Git degraded/init;
  - temporary workspaces.
- **Installation (3/3), with the 2.1.4 recommendation:**
  - fresh install of the 2.1.2 minimum;
- native **2.0.15 → 2.1.4** upgrade, after which the already-running
daemon keeps its PID and 2.0.15 version;
  - explicit OpenCode **1.18.10 → 2.1.4** replacement.
- **After the recommendation change:**
- admission, transport, update, installation and instance proxy units
**116 passed, 1 POSIX-only skipped**;
  - full server **2,471 passed, 8 skipped**, no failure or cancellation;
  - UI units **1,699/1,699**;
  - focused browser **126/126**;
  - native pruning with the real rendered UI;
  - the exact CI invocation of the Code Mode regression (without tsx);
  - ten-locale help checks (**9/9**).

This is Windows automation evidence. In CI, `latest` resolves to 2.1.4:
the compatibility job runs the native suites against the minimum and
latest on Windows, and the migration and Git-degraded certification runs
them on Ubuntu, Windows and macOS. WSL, packaged desktop,
installers/updater end-to-end and real mobile devices keep their
separate release gates.
…ote Control (#1128)

Phones lost their only entry to the session web preview when the
conversation actions were reorganized: on mobile, the Full screen action
took the preview's place instead of joining it. Previews still work there
through direct (LAN) access, which serves them through the capability
iframe proxy. Remote Control, on the other hand, serves no preview content
(`/previews/*` is host-only), so its clients, desktop browsers included,
were offered a preview that could only fail to load.

The conversation header now offers the preview on every platform, inline
and in its overflow menu, with Full screen added on mobile. `/api/meta`
reports `remoteControl: true` for requests that arrived through the Remote
Control ingress, `lib/server-meta.ts` exposes it as `remoteControlClient()`,
and the shell hides the preview entry for those clients. The shell asks for
the server metadata on mount; the read is cached and shared with the other
callers, and the entry stays visible until a Remote Control answer arrives.

The Remote Control help page says, in all ten locales, that conversation
actions hide Open web preview there; dev-docs/UI_SHELL.md records the
convention.

Validation: a header-windows browser test drives the real conversation
header on a Pixel 5 profile and on desktop Chrome, with and without the
Remote Control metadata, and checks the inline buttons and the overflow
menu. It fails on the previous shell (no preview entry on the phone, and a
preview entry kept through Remote Control). meta.test.ts and the Remote
Control surface test cover the flag locally and through the real ingress
(the surface fixture's metadata gains the `host` field /api/meta needs).
The UI unit tests run by CI, the server routes, proxy and Remote Control
suites, the help checks and both typechecks pass.
@pascalandr
pascalandr marked this pull request as ready for review October 24, 2026 13:37
@pascalandr
pascalandr requested a review from a team October 24, 2026 13:37
@shantur
shantur merged commit 8370937 into main Oct 24, 2026
38 checks passed
pascalandr added a commit that referenced this pull request Oct 24, 2026
A stable release is meant to open with the description of its dev to main
PR, followed by GitHub's generated change list. The v1.0.0 release (and,
before it, v0.20.1) got only the generated list. The workflow found the PR
through commits/<sha>/pulls. For the v1.0.0 merge commit, which exists only
on main while the default branch is dev, that endpoint returned no PR, still
minutes after the merge, so the notes fell back to --generate-notes alone.

The Create GitHub release step now lists the closed PRs based on main and
picks the merged one whose merge_commit_sha is the release commit, through
.github/scripts/release-pr-notes.cjs. That does not depend on the default
branch. A release commit without such a PR still gets the generated notes
only, and the existing-release path is unchanged.

Validation: the script's tests cover the matching rules, an unchanged
Unicode body through the command and the workflow wiring; they run with the
other .github/scripts tests in PR Build Validation. Run against the real
data, the workflow's exact command returns #1028's description for the
v1.0.0 merge commit (the old lookup returns nothing), #843's for v0.20.1,
and an empty body for a commit without a release PR. The v1.0.0 release
notes were repaired with the same lookup.
pascalandr added a commit that referenced this pull request Oct 24, 2026
)

## Problem

A stable release should open with the description of its `dev → main`
PR, followed by GitHub's generated change list. **v1.0.0** (and before
it **v0.20.1**, whose notes were completed by hand) got only the
generated list.

The `Create GitHub release` step found the PR through
`commits/<sha>/pulls`. For the v1.0.0 merge commit, which exists only on
`main` while the **default branch** is `dev`, that endpoint returned no
PR, still minutes after the merge, so the notes fell back to
`--generate-notes` alone.

## Fix

The step now lists the closed PRs based on `main` and picks the merged
one whose `merge_commit_sha` is the release commit, through
`.github/scripts/release-pr-notes.cjs`. This does not depend on the
default branch.

A release commit without such a PR still gets the generated notes only,
and the existing-release path is unchanged.

## Validation

- `release-pr-notes.test.cjs` covers the matching rules (merged, base
`main`, exact merge commit), a Unicode body passed through the command
unchanged, and the workflow wiring. It runs with the other
`.github/scripts` tests in PR Build Validation (3/3 locally).
- The workflow's exact command, run against real data:

  | Commit | Old lookup | New lookup |
  | --- | --- | --- |
| v1.0.0 merge (`8370937`) | empty | #1028's description (8,759 chars) |
| v0.20.1 merge (`a8545a3`) | #843 (now on `dev`) | #843's description |
  | `dev` HEAD (no release PR) | — | empty → generated notes only |

- The **v1.0.0 release notes were repaired** with the same lookup:
#1028's description followed by the generated "What's Changed" list.

This branch was successfully deployed

1 active deployment
github-pages — 036a008c Deployed Oct 24, 2026 by pascalandr via deploy #209
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants