fix(transcript): keep streaming live across shared-stream reconnects
## Problem
Long model reasoning streams stop updating the transcript roughly 30
seconds in: the visible thinking text freezes, and the display only
catches up when the thinking phase ends. Shorter streams never freeze.
A diagnostic reproduction against the real SDK/dispatcher pinned the
freeze exactly: after a mid-stream `server.connected`, ten live
reasoning deltas left the store text frozen at its pre-reconnect value
and **zero** `message.list` reads were even attempted — even
`session.reasoning.ended` and the execution terminal events could not
recover it.
## Root causes
1. **~30 s trigger** — after `CONNECTION_RECHECK_INTERVAL_MS`, any
request through the shared OpenCode service re-runs discovery; a failed
or changed probe (a busy daemon is slow to answer) invalidates the
healthy connection. The shared event stream tears down and resubscribes,
and every subscription starts with `server.connected`.
2. **Reset mid-stream** — the UI treated `server.connected` as a full
data reset (`destroyOpenCodeData`). The fresh reducer starts empty:
reasoning deltas no-op (`editReasoning` cannot find the message) and
nothing repopulates the projection until a later `session.step.started`
inserts the assistant row again.
3. **Starving recovery** — repopulation requires one `message.list` read
to return with an unchanged event revision; each arriving delta restarts
the read (the guard that never merges an obsolete page into newer
streaming messages). During continuous streaming that read cannot
complete, so any reconciliation starves until the stream pauses.
## Changes
- `stores/opencode-data.ts`: `server.connected` now runs
`reconnectOpenCodeData` instead of a bare destroy. A shared-stream
reconnect is transport recovery, not a data reset:
- The reset semantics are kept (generation fences, observer rebinding,
REST state untouched) — every existing reconnect test passes unchanged.
- Before the reset, each live session's last projected page is captured
and seeded into the fresh reducer through the existing `syncMessages`
snapshot mechanism, so post-reconnect deltas keep applying and the
display keeps streaming.
- One trailing reconciliation is scheduled per session (new non-dropping
`reconnectResync` mode): it fetches only after the stream stays quiet
for 1 s with a stable revision, then swaps in the authoritative page to
repair anything the disconnect window missed. It never drops events and
defers to dropping-mode recovery when an overflow collapse or pruning
takes over.
- `stores/opencode-data-reconnect.test.ts` (new, in the CI unit list):
three regressions — the revision-race read invariant (obsolete pages
never merge), dropping-mode admission (frozen until the stream pauses),
and reconnect continuity (deltas keep displaying; the quiet snapshot
republishes).
## Behavior compatibility
Suites covering reconnect semantics (projection reset, inbox observer
rebinding, compaction reconnect fencing, replay fences, generation
recovery, request authority) pass without modification.
Dropping-mode resync semantics (overflow, pruning) are unchanged and now
pinned by a regression test.
`opencode-data.ts` is 978 lines after this change (over the ~800
target); splitting the reconnect path into its own module is a
reasonable follow-up.
## Pre-existing failures (not caused by this PR)
Four tests fail identically on a clean checkout (verified by stashing
this change and re-running): the pinned `@opencode/client` 2.1.0 /
event-manifest contracts drifted and the fixtures were not updated:
- `opencode-transcript-replay.test.ts` × 2:
`session.compaction.started`/`ended` fixtures no longer decode (the
manifest now requires `data.recent`); both integration tests die in
fixture construction before any assertion runs.
- `opencode-data.test.ts` ("preserves SDK step, snapshot, reasoning…"):
expects the old `native` field on reasoning parts; the 2.1 reducer maps
it to `state`.
- `session-pruning-projection.test.ts`: expects a provider
`reasoningField` mapping that the current contract no longer returns.
These are fixture/expectation updates against the pinned SDK, unrelated
to the streaming machinery; CI may already be red on them. They should
be repaired in a separate PR so this fix stays reviewable.
## Validation
- Red→green: the new reconnect regression fails on the pre-fix tree at
the frozen-store assertion (`'before '` vs `'before after0 after1 '`)
and passes with the fix.
- UI unit baseline (CI file list + new file): 552 tests, 548 pass, the
same 4 pre-existing failures before and after.
- `tsc --noEmit`: the same 4 pre-existing errors before and after.
- Browser: mission-control 24/24, provider-usage 13/13,
mission-task-reader 18/18, message-scroll 128/128. `part-durations`
page-load timeouts reproduce identically on a clean tree (Vite
cold-start environment issue).
|
||
|---|---|---|
| .github | ||
| .opencode | ||
| dev-docs | ||
| docs | ||
| examples/panel-extension | ||
| images | ||
| packages | ||
| scripts | ||
| .gitignore | ||
| .node-version | ||
| AGENTS.md | ||
| BUILD.md | ||
| CODE_SIGNING.md | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| PRIVACY.md | ||
| README.md | ||
| THIRD_PARTY_NOTICES.md | ||
CodeNomad
The AI Coding Cockpit for OpenCode
CodeNomad is a desktop and browser workspace for OpenCode V2 — built for developers who work across projects, long conversations, and Git worktrees. It connects to OpenCode's shared background service, keeping your sessions accessible from CodeNomad and compatible terminal clients.
OpenCode gives you the engine. CodeNomad gives you the cockpit.
The 0.20 interface, rendered with demonstration data. Download the latest stable release.
User help · Read the English guide on GitHub — multilingual help for current CodeNomad and OpenCode V2.
Features
- 🚀 Projects and desktop windows — Keep multiple projects open, with per-window tabs, drafts, attachments, and view restoration.
- 🧠 Sessions and full-history navigation — Browse nested sessions, fork conversations, search beyond loaded messages, and jump through long histories with a timeline and previews.
- 🌳 Git worktrees — Create and select worktrees, move a session with its subsessions, and review Git changes without leaving the workspace.
- 💬 Rich conversations — Markdown, code, diffs, tool-result images, configurable reasoning/tool visibility, and
/btwfor a temporary side question. - 📎 Files and attachments — Pick, paste, or drop device files; use
@references for project files and browse your workspace from the file panel. - 🌐 Remote access — Connect to a CodeNomad server from a desktop client or browser, with password authentication and HTTPS support. Or turn on Remote Control to pair a phone or another computer from anywhere through an OpenTunnel relay, without opening ports: TLS ends on your machine.
- 🧩 Tools and previews — Monitor native background shells, manage MCP connections and Global/Project plugin activation, and open web tools as SideCars.
- 🔎 Agent-driven browser previews — Let your agent open and inspect a visible web preview in Electron or Windows Tauri.
- 🎙️ Voice input and speech — Dictate prompts and listen to responses.
- ⌨️ A workspace you can adapt — Command palette, notifications, customizable panels, independent light/dark palettes, and multiple interface languages.
Getting Started
🖥️ Desktop App
Available as both Electron and Tauri builds — choose based on your preference.
Download the archive or package for your platform from Releases.
| Platform | Electron | Tauri |
|---|---|---|
| macOS | ZIP, separate Intel and Apple Silicon builds | ZIP, separate Intel and Apple Silicon builds |
| Windows | ZIP (x64) | Installer (x64) |
| Linux | deb (x64) | AppImage (x64) |
The Linux builds are currently built and tested on Ubuntu 24.04. Compatibility with older distributions is not yet guaranteed. The Electron deb sets up Chromium's sandbox during installation and replaces a previously installed Tauri deb. The Tauri installer, macOS app and AppImage update themselves from within the app.
On Linux, from the download folder:
# Electron deb
sudo apt install ./CodeNomad-Electron-linux-x64-<version>.deb
# Tauri AppImage
chmod +x CodeNomad-Tauri-linux-x64-<version>.AppImage
./CodeNomad-Tauri-linux-x64-<version>.AppImage
Without FUSE (for example in a container), start the AppImage with --appimage-extract-and-run.
Desktop builds bundle Node.js and npm; you do not need to install Node.js separately. On first launch, select an existing compatible OpenCode executable or install OpenCode through the setup screen. You can manage the executable, updates, and service later in Preferences → OpenCode.
Open a project folder, connect your model provider, and start a session. Git must be available to the CodeNomad backend for repository and worktree features; see Requirements.
💻 CodeNomad Server
Run on your development machine and access it through a browser. Install Node.js/npm and OpenCode V2 on that host first; Node.js 24 LTS is the recommended runtime (see Requirements).
npx @neuralnomads/codenomad --password "your-password" --launch
The server binds to 127.0.0.1 by default. For remote access, add --host 0.0.0.0 and connect to the printed HTTPS address; see Server Documentation for binding and TLS configuration.
Authentication: Supply
--passwordorCODENOMAD_SERVER_PASSWORDon each start, or use the UI's persistent password setup.auth.jsonis managed by CodeNomad; see Authentication.
Self-signed certificate: On first launch with HTTPS enabled (the default), your browser will show a "Your connection is not private" warning. This is expected — the server generates a local self-signed certificate automatically. Click Advanced → Proceed to localhost to continue. For local-only use without the warning, run with
--https=false --http=true.
See Server Documentation for flags, TLS, auth, and remote access.
🧪 Dev Releases
Bleeding-edge builds from the dev branch:
npx @neuralnomads/codenomad-dev --password "your-password" --launch
OpenCode V2 and upgrading from 0.19.x
CodeNomad 1.0.0 requires OpenCode V2: 2.1.2 minimum and recommended, including the Code Mode JavaScript job-ordering fix. OpenCode V1 and the former V2 beta protocol are not supported. The current requirements are maintained in runtime-support.ts.
- One shared OpenCode service owns sessions and messages. Closing a CodeNomad tab, window, or backend leaves that service running. Desktop tabs, drafts, and layout are restored separately for each window.
- Use an existing CLI or install from CodeNomad. Executables on
PATHare preferred; the setup screen also supports a selected executable and a shared user npm installation. The former private CodeNomad OpenCode installation is no longer used. - Updating the CLI and restarting its service are separate actions. Use the explicit service controls in Preferences when you want the running daemon to use an updated executable.
- Remote users should upgrade their CodeNomad server too. The 0.20 UI requires CodeNomad server 0.20.0 or later.
For a manual CLI installation, follow the OpenCode V2 installation guide. The npm package is @opencode/cli and the command is opencode.
SideCars
SideCars let you open web tools running on the CodeNomad server host inside CodeNomad as tabs. Start the service separately, then add it in Preferences → SideCars.
A SideCar runs inside CodeNomad with the same access as the application, so only add services you trust, and avoid ones that display content you do not control.
Session web previews are also available from the conversation toolbar. Agent-driven native browser automation is supported in Electron and Windows Tauri; other Tauri platforms use an iframe preview. See Browser Automation for platform details.
Configuration
- Name: Display name used in CodeNomad
- Port: Numeric port of the service on the server host, at
127.0.0.1:<port> - Protocol: Select HTTP or HTTPS to match the service
- Base path: Derived from the name and displayed as
/sidecars/:id - Prefix mode:
- Preserve prefix forwards the full
/sidecars/:id/...path upstream - Strip prefix removes
/sidecars/:idbefore forwarding the request upstream
- Preserve prefix forwards the full
VSCode (OpenVSCode Server)
Run with Docker:
docker run -it --init -p 127.0.0.1:8000:3000 -v "${HOME}:${HOME}:cached" -e HOME=${HOME} gitpod/openvscode-server --server-base-path /sidecars/vscode
Add SideCar as:
- Name:
VSCode - Port:
8000 - Protocol:
HTTP - Base path:
/sidecars/vscode - Prefix mode:
Preserve prefix
Terminal (ttyd)
Run with:
ttyd --writable zsh
Add SideCar as:
- Name:
Terminal - Port:
7681 - Protocol:
HTTP - Base path:
/sidecars/terminal - Prefix mode:
Strip prefix
Requirements
- OpenCode V2 — 2.1.2 minimum and recommended for CodeNomad 1.0.0. Install/select it through desktop setup or provide a compatible CLI on the server host.
- Git — a prerequisite for full functionality (repository identity, Git operations and worktrees). Git must be executable in the CodeNomad backend's
PATH; it is not bundled with the desktop installers. - Node.js and npm — required for the standalone npm server and source builds, bundled with desktop releases. Use Node.js 24 LTS; development and release builds use the version in
.node-version(currently 24.20.0).
On Windows, install Git for Windows with the option to use Git from the command line and third-party software. A per-user installation is sufficient and does not require administrator rights; install into a user-writable directory and make Git available in the user PATH. On macOS or Linux, follow the platform installation instructions. Fully quit and restart CodeNomad after installation so its backend inherits the updated PATH. For a remote server, install Git on the server host for the account running CodeNomad and restart that server process, not just the browser. A Git installation available only inside WSL does not satisfy the Windows backend's requirement.
Without Git, CodeNomad tolerates a degraded, directory-only mode. You can open a folder and talk to your agent, including asking it to help install Git. The backend supplies the agent with Git availability and backend platform context before prompts and custom commands; there is no blocking Git installation screen. Git operations and worktree management require Git. Session access is restricted to the explicitly opened physical folder; open another checkout separately to access its conversations. The agent's shell and the CodeNomad backend can run on different hosts, so installation help must target the backend account and environment. Git availability is rechecked on subsequent sends and the agent context is removed once it is available.
Development
CodeNomad is a monorepo built with:
| Package | Description |
|---|---|
| packages/server | Core logic & CLI — workspaces, OpenCode proxy, API, auth, speech |
| packages/ui | SolidJS frontend — reactive, fast, beautiful |
| packages/electron-app | Desktop shell — process management, IPC, native dialogs |
| packages/tauri-app | Tauri desktop shell — native windows, integration, and packaging |
Quick Start
git clone --branch dev https://github.com/NeuralNomadsAI/CodeNomad.git
cd CodeNomad
npm ci --workspaces --include=optional
npm run dev
npm run dev starts the Electron development host. Use Node.js from .node-version, Git, and a compatible OpenCode V2 CLI. For Tauri, install the platform build prerequisites and run npm run dev:tauri. See CONTRIBUTING.md for the development and PR workflow.
Troubleshooting
macOS: "CodeNomad.app is damaged and can't be opened"
Gatekeeper flag due to missing notarization. Clear the quarantine attribute:
xattr -dr com.apple.quarantine /Applications/CodeNomad.app
On Intel Macs, also check System Settings → Privacy & Security on first launch.
Linux (Wayland + NVIDIA): Tauri App closes immediately
WebKitGTK DMA-BUF/GBM issue. Run with:
WEBKIT_DISABLE_DMABUF_RENDERER=1 ./CodeNomad-Tauri-linux-x64-<version>.AppImage
This applies to the Tauri WebKitGTK build. Electron uses Chromium and can be used as an alternative on the same machine.
Community
Built with ♥ by Neural Nomads · MIT License · Privacy policy · Code signing policy
