Pin the server and UI client to the installed beta runtime and reject mismatched CLI versions before shared-service startup. Delegate proven shared-service shutdown to Service.stop while retaining CodeNomad lease, peer, registration, endpoint, and process-identity checks.
Adopt native Forms and the client/solid data reducer for live messages, tools, permissions, and input forms. Propagate internal stream generations into authoritative reconnect reconciliation and replace directory-wide session scans with native project cursor pagination.
Fail worktree deletion when session evacuation fails and deduplicate canonical workspace folders instead of exposing non-isolated duplicate instances. Update migration notes and CI coverage for the reviewed contract.
Validated with server/UI/Electron typechecks, 244 UI partition tests, 122 Electron native tests, 248 server tests plus 3 platform skips (the sole Windows cleanup race passed in isolation), UI/server/Electron builds, Tauri cargo check --locked, and git diff --check.
Reduce the V2 test matrix to CodeNomad-owned behavior instead of repeating generated OpenCode request shapes, mock choreography, equivalent endpoint permutations, and source-level helper wiring.
Retain representative happy paths and the boundaries that protect workspace ownership, traversal and credentials, PID and WSL lifecycle proof, shutdown timeouts, permission isolation, restore handoff, deletion authority, streaming reconciliation, and persisted client state.
This removes 2,818 net test and CI lines. The full server suite passes with 249 tests and 3 platform skips; the PR UI matrices pass with 204 standard and 53 browser-conditioned tests, both server and UI typechecks pass, and git diff checks are clean.
Delete the obsolete pre-MVP roadmap and task references now that Git preserves that development history, while retaining current V2 operational and architecture guidance.
Remove behavior-neutral server and UI plumbing that has no remaining caller: unused workspace options and cached endpoint state, redundant auto-accept metadata, an unused PTY get adapter, and a one-use message-loading helper. Keep lifecycle proof, restore gates, timeline projection, and targeted invalidation because they still enforce runtime behavior.
Validated with server and UI typechecks, the full server suite (308 passed, 3 skipped), focused UI matrices, browser-conditioned UI tests, production server/UI build, diff checks, caller searches, and an independent regression review.
Allow the read-only OpenCode /api/plugin endpoint through the workspace proxy so the status panel can distinguish active plugins from configured but unloaded packages.
Keep the route inside the explicit proxy allowlist and cover it with the instance proxy suite. The focused 25-test proxy suite, server typecheck, and diff check pass.
The V2 migration first disabled and then removed the OpenCode update action because the native runtime no longer exposes V1's global upgrade endpoint. The remaining version card also queried the legacy opencode-ai package instead of the V2 beta channel.
Restore the settings action using the official @opencode-ai/cli beta installation flow for npm, pnpm, bun, and yarn. Restrict automatic updates to the managed opencode2 command, preserve custom binaries, compare monotonically numbered beta builds numerically, and verify the configured binary after installation before reporting success.
Validated against the live beta registry, 11 focused update tests, 307 server tests with 3 skipped, 544 UI tests, server and UI typechecks, the production UI build, and diff checks.
The instance bridge duplicated every OpenCode event payload into a synthetic properties field because server-side auto-accept still consumed the V1-style envelope. This expanded the SSE contract and left UI deletion handling with legacy fallbacks.
Move auto-accept and session deletion to native event.data, publish OpenCode events unchanged, type the shared stream as OpenCodeEvent, and guard PTY consumers before narrowing their event union. Internal CodeNomad worktree events remain separate.
Validated with server and UI typechecks, 51 targeted server tests, 21 targeted UI tests, 304 passing server tests with 3 platform skips, 535 UI tests, and git diff checks.
The Tauri macOS ARM prebuild combines DOM and Node timer declarations, so injected and global setTimeout implementations can return either a numeric browser handle or a Node Timeout. The previous inferred alias accepted only the Node variant and failed server compilation on that matrix leg.
Represent the manager timer handle as the actual portable union and avoid Array.prototype.at in a server test because the server TypeScript target predates ES2022.
Validated with the complete server build, 304 passing server tests with 3 platform skips, 11 targeted workspace tests, and git diff checks.
Stop deleting OPENCODE_DB from legacy CodeNomad environment preferences during settings layout migration. Environment variables now migrate uniformly without a database-specific exception.
The OpenCode V2 service destination remains unchanged and continues to use ~/.local/share/opencode2/opencode.db through the launch-time service environment override.
Validated with the settings migration test, root typecheck, the complete server suite, and git diff --check.
Attempt configured WSL host-to-service translation before treating POSIX paths as already service-native. This preserves real Windows behavior while allowing Linux CI to exercise Windows workspace ownership with temporary git worktrees.
Remove the environment-dependent assertion that a bare opencode2 command resolves to a packaged executable. Deterministic shim coverage remains in buildServiceLaunchSpec and the Windows PATH/PATHEXT tests.
Validated with full typecheck, 27 spawn tests, 9 workspace manager tests, and the complete server suite with 304 passing tests and 3 skips.
Preserve the last authoritative non-empty tab snapshot through native shutdown, make shutdown capture terminal, and restore the exact saved active tab. Seed scroll snapshots before session selection and wait for usable Virtua measurements so project, tab, session, and anchor position survive A/B restarts.
Render native runtime forms inline only when their active-session tool target is resolved, retain the floating fallback for unresolved requests, share form schema visibility and URL validation with provider auth, and remove the unused session form-list proxy route and obsolete V1 request wrapper.
Return the native V2 session.active map without a legacy data envelope, avoid full-history refreshes for deltas already applied locally, and bound timeline projection refreshes during streaming. Coverage includes UI restoration, form targeting, proxy contracts, native streaming, and CI test registration.
Validated with root typecheck, 529 UI tests, 305 passing server tests with 3 skips, 74 Tauri tests, 122 Electron native tests, production A/B builds, a no-seed native handoff, and runtime verification of the active-session proxy shape.
Update the experimental OpenCode client, protocol, and schema dependencies to the latest reviewed next release while removing the exact CLI version gate from shared-service discovery. This lets CodeNomad use current opencode2 releases instead of timing out solely because the runtime advanced.
Refresh migration, contributor, architecture, and skill guidance to require release-note, documentation, declaration, and proxy-parity review on each upgrade. The isolated opencode2 database behavior is documented consistently.
Validated with server and UI typechecks, 36 passing targeted service/workspace tests, a release Tauri build, and a packaged desktop smoke. The smoke opened D:\CodeNomad, reused three existing sessions, and received three distinct exact prompt responses from opencode2 next-17444.
Force the V2 service to use the opencode2 database under the current user's data directory so parallel V1 launches cannot redirect it through shared environment settings.
Keep the bare opencode and opencode2 commands out of custom binary history while preserving real executable paths, and document the parallel desktop smoke procedure. Existing history entries are intentionally left untouched for manual cleanup.
The server manager test and typecheck passed, and the Tauri package built successfully before the final UI-only simplification. No additional compilation was run per request.
Merge upstream dev into the OpenCode V2 foundation while preserving native V2 session, provider, proxy, PTY, and explicit database behavior.
Resolve desktop workspace-open and provider visibility conflicts across Electron and Tauri, bound local API requests, refresh standalone dependency locks, and keep grouped integration preferences keyed by native provider IDs.
Validated with desktop typechecks, 267 UI tests, 308 server tests, 122 Electron tests, 74 Tauri tests, packaged builds, and an exact OpenCode 0.0.0-next-17353 Tauri smoke covering prompt, reload, agent fallback, and PTY lifecycle.
Restore reliable V2 catalogs, agent identifiers, cursor pagination, prompts, variants, attachments, forms, native event reconciliation, Yolo persistence, and location-aware PTY controls. Prevent completed control records from appearing as streaming placeholders and keep live output refreshing during sustained token streams.
Harden the shared service and workspace proxy with explicit OPENCODE_DB isolation, launch-signature validation, traversal protection, WSL namespace translation, ownership-scoped routes, bounded SSE backpressure, reconnect recovery, and safe process-proof transfer and shutdown semantics.
Align provider, VCS, LSP, update, documentation, and CI behavior with the experimental next-17353 contract. Validation includes server/UI/Electron typechecks, 252 workflow UI tests, focused server suites, production UI/server builds, 68 Tauri tests, and a real V2 workspace/session/prompt smoke.
Load sessions from each logical root and worktree using the native V2 directory filter, preserve locally known sessions when a combined response may be incomplete, and resolve locationless events through their session IDs. This restores worktree sessions, prompt activity, and deletion updates without reintroducing the removed transport.
Require the bundled OpenCode prerelease for shared-service discovery and surface structured prompt errors. Move Electron and Tauri restore state and election files under the V2 namespace, copying V1 state once without mutating or deleting the legacy files.
Cover multi-directory loading, locationless event routing, version-constrained discovery, and cross-host state isolation. Validated UI, server, Electron, and Tauri tests and built the Tauri release executable and NSIS installer.
Treat the persisted V1 default command 'opencode' as 'opencode2' so restored and newly selected workspaces use the required V2 service instead of waiting for the obsolete binary to time out.
Keep the folder selection view mounted while startup restore is active and feed it the existing loading state, preventing the renderer from appearing blank while saved workspaces are recreated.
Cover the legacy binary setting and restore loading gate with regression tests. Server/UI typechecks, targeted restore tests, the UI production build, and a real lifecycle smoke from an 'opencode' configuration pass.
## Summary
- add per-provider model visibility controls to Settings > Providers and
the floating provider dialog
- persist exact hidden model IDs as CodeNomad presentation preferences
- keep large plugin catalogs manageable with search and Show all / Hide
all actions
## Behavior
- all current and newly reported models remain visible by default
- unchecked models are hidden only from model picker lists
- active/default models remain usable even when hidden
- favorites remain persisted and return when their models are shown
again
- the same management UI is available from Settings and the floating
model-picker dialog
This intentionally mirrors OpenChamber/OpenCode Desktop client-side
hiding. It does not rewrite OpenCode configuration or change model
execution/default semantics.
## Validation
- npm run typecheck
- node --import tsx --test packages/ui/src/lib/model-visibility.test.ts
- npm run build --workspace @codenomad/ui
- Gatekeeper reviews: PASS
Closes#636
Remove the duplicate Tauri native event transport and use the server EventSource stream consistently across web, Electron, and Tauri clients. Align the generated client and required opencode2 CLI on next-17353.
Restrict workspace proxying to the required V2 API surface, enforce session and directory ownership, validate local file URIs, strip routing headers, and tighten provider, Yolo, worktree, and session event state handling.
Protect the shared service lifecycle with private registration state, process-identity leases, safe ownership transfer, bounded shutdown, Windows executable resolution, and WSL namespace-aware PID verification. Unknown wrappers now fail closed rather than leaving an unowned service.
Validated with server and UI typechecks, 433 UI tests, focused server lifecycle and proxy suites, 67 Rust tests, server and UI builds, and a real next-17353 service lifecycle smoke test.
Replace the V1 SDK, custom plugin, and per-workspace runtimes with the pinned OpenCode V2 client and one shared service. Model workspaces through native locations and route sessions, messages, events, files, VCS, permissions, questions, providers, commands, and MCP directly through V2 APIs.
Remove the legacy plugin and background-process layers, use native Shell and PTY support, preserve server-side Git mutations where V2 lacks parity, and update the UI, packaging, CI, architecture documentation, and translations for the new model.
Add focused coverage for shared-service ownership, Windows and WSL startup, proxy boundaries, worktree event routing, native event normalization, provider authentication, voice instructions, and location-scoped requests. Server and UI typechecks pass; remaining security review items and the real opencode2 smoke test are documented in MIGRATION_V2.md and will be completed before the draft PR is marked ready.
## Summary
- validate the authenticated OpenCode process configuration before
publishing a workspace as ready
- stop and remove instances that report invalid configuration
- show localized configuration diagnostics, affected paths, validation
issues, and typo suggestions in the existing launch-error dialog
## Existing coverage
PR #195 already handles binary spawn and early-exit failures. PR #582
surfaces session-list loading failures. This change closes the remaining
case where OpenCode reports healthy while configuration-dependent
endpoints fail.
## Validation
- `bun test ./packages/server/src/workspaces/manager.test.ts`
- `node --import tsx --test packages/ui/src/lib/launch-errors.test.ts`
- server and UI typechecks
- UI build
- manual reproduction with OpenCode 1.18.5
- independent Gatekeeper reviews: PASS
Closes#508
## Summary
- retry an ephemeral port when an automatic listener receives Windows
`EACCES`
- keep explicitly configured ports strict
- keep non-Windows `EACCES` behavior unchanged
## Context
CodeNomad already treats 9898 and 9899 as preferred rather than
mandatory ports. The fallback previously handled only `EADDRINUSE`, so a
Windows port reservation could terminate Tauri startup before the
operating system was asked for an available port.
The specific WinNAT attribution in the issue remains unverified, but the
observable startup failure is fixed regardless of which Windows
reservation produced `EACCES`.
## Validation
- focused listener retry test passes
- server typecheck passes
- server suite: 256 passed, 4 skipped, 1 pre-existing Windows-only
`git-worktrees.test.ts` fixture failure
- autonomous gatekeeper review will be published as a PR comment after
opening
Fixes#627
## Summary
PR #561 introduced `createInstanceClient` so server modules would stop
hand-assembling the OpenCode loopback URL and use the generated SDK
client. Three call sites adopted it; the background-process completion
prompt was the remaining holdout — it still hand-built
`http://127.0.0.1:{port}/session/{id}/prompt_async` with manual
auth/content-type wiring. This PR routes it through the factory +
`client.session.promptAsync`, and consolidates the loopback host
constant.
## Why
- **Consistency** — the last hand-built OpenCode-instance loopback URL
(explicitly flagged in #561 as a future adoption target) now uses the
same version-correct SDK path as the permission replier, yolo metadata,
and the opencode updater.
- **Correctness** — the old call sent no directory at all;
`notify.directory` was stored but never used (dead data). The factory
now scopes the prompt to the session's own directory, which for a
worktree/subdirectory session is the correct project context (a normal
session coincides with the workspace root, so no change there).
## What changed
**Migration** — `background-processes/manager.ts`:
`sendCompletionPrompt` → `createInstanceClient(...)` +
`client.session.promptAsync({sessionID, parts:[{type:"text", text,
synthetic:true}]}, {throwOnError:true})`. Synthetic `<system-message>`
text + `synthetic:true` preserved. Side effect: the call now carries the
factory's 10s loopback timeout (the old fetch had none; the failure is
swallowed + warn-logged, so finalization is unaffected).
**Factory** — `workspaces/instance-client.ts`: new optional `directory`
override in `InstanceClientOptions` (defaults to workspace root); adopts
shared `LOOPBACK_HOST`. Fetch wrapper unchanged from `dev`.
**Consolidation** — new `workspaces/loopback.ts` exporting
`LOOPBACK_HOST = "127.0.0.1"`, adopted by `instance-client.ts`,
`instance-events.ts`, and the workspace manager's health + TCP probes
(`manager.ts`). (Other `127.0.0.1` uses — remote-access proxy, sidecars
— are different concerns, left alone.)
**Tests**
- `workspaces/instance-client.test.ts` (8 cases): null-when-no-port,
loopback host/port targeting, auth header attach/omit, directory scoping
present/absent, explicit directory override, timeout aborts stuck call.
- `background-processes/manager.test.ts` (2 cases, first test for this
manager): drives the real lifecycle (spawn + exit) against a mocked
transport; asserts the `prompt_async` POST
URL/body/authorization/`x-opencode-directory` (= session dir, distinct
from workspace root), and that a failed prompt is swallowed +
warn-logged without aborting finalization.
## Behavior parity
| Aspect | Before | After |
|---|---|---|
| Route / body | `/session/{id}/prompt_async`,
`{parts:[{type,text,synthetic}]}` | **same** (via SDK) |
| Auth header | manual | via factory |
| Directory scoping | none | session's `notify.directory` |
| Loopback timeout | none | 10s (factory default) |
| Error handling | throw on non-2xx → caught + warn-logged |
`throwOnError` → caught + warn-logged (**same**) |
## Testing
10 new tests pass. The background-process integration test is stable
12/12 under `--test-concurrency=4` and is mutation-killed if the
`directory` override or `throwOnError` is removed. Full server suite:
255 pass / 1 fail — the single failure is pre-existing
(`git-clone.test.ts` Chinese-locale git message, confirmed failing
identically on `dev`). Server typecheck clean.
## Risk / rollback
Additive refactor; the factory is production-proven by #561's consumers.
Rollback = revert the single commit.
## Notes
- Per the file-length guideline:
`packages/server/src/background-processes/manager.ts` is ~684 lines
(above the 500 warn threshold, under the 800 limit); this PR shrank it
by ~16 lines net.
## Summary
- prevent Linux workspace startup from failing when process identity
discovery exceeds its one-second command deadline
- capture the launched process and its existing process-group members
without spawning helper commands for every `/proc` entry
- preserve the leader-exit cleanup guarantees introduced by #602
- read immutable Linux process start ticks correctly and tolerate
multiline task names
## Root cause
Workspace startup captures an immutable process identity before
accepting the OpenCode runtime. The Linux implementation scanned every
process and launched several `cat`, `cut`, `sed`, `basename`, and
`dirname` helpers per entry. On the affected Mint host, that synchronous
shell command exceeded its one-second timeout. Identity capture then
failed closed and stopped the newly launched OpenCode process; cleanup
retried the same expensive probes and produced the repeated `spawnSync
sh ETIMEDOUT` errors.
The identity parser also used `$20`, which POSIX shells interpret as
`$2` followed by `0`, rather than the twentieth positional field. This
did not cause the startup timeout but weakened immutable process
matching and is corrected here.
## Safety
- launch discovery still retains every already-started member of the
observed process group
- guarded cleanup remains identity- and launch-token-based; no
unverified PID fallback is introduced
- WSL, macOS, and Windows paths retain their existing platform-specific
probes
## Validation
- server TypeScript typecheck
- focused process identity and runtime suite: 23 passed, 1 Linux-only
test skipped on Windows
- real WSL `/proc` probe: correct start identity, 2 group members, 4 ms
- broader workspace suite: 79 passed, 3 platform skips
- `git diff --check`
The broader workspace run still has the unrelated existing Windows
fixture failure in `git-worktrees.test.ts` (`undefined` instead of
`main`).
Closes#624
## Summary
- count only fuzzy-qualified paths toward the 8000 workspace search
candidate cap
- scope the bounded cache to the current query and entry type
- stop recursive directory-link cycles without hiding legitimate alias
paths
- expand the @file picker to its parent width and horizontally scroll
long paths
## Validation
- 6 focused filesystem search/cache tests pass
- server typecheck passes
- workspace UI/Electron typecheck passes
- UI production build passes
- gatekeeper round 3: zero findings
- full server suite: 241 pass, 3 skip; the unrelated existing Windows
git-worktrees fixture fails to parse its mocked branch output
Closes#503
## Summary
- Populate the remote login username input with the configured default
value instead of displaying it only as a placeholder.
- Disable mobile capitalization, autocorrection, and spellchecking for
username and password inputs.
- Add a route-level regression test for the rendered username value and
mobile credential attributes.
## Validation
- node --import tsx --test
packages/server/src/server/routes/auth.test.ts
- npm run typecheck --workspace @neuralnomads/codenomad
- node packages/server/scripts/copy-auth-pages.mjs
Closes#616
## Summary
- reorganize Settings into focused General, Chat, Notifications, Speech,
Remote Access, OpenCode, Providers, SideCars, Config files, Advanced,
and Info sections
- add independent expanded, collapsed, and hidden controls for Thinking,
Tool output, Diagnostics, Tool inputs, and Token usage
- embed the shared Provider manager directly in Settings while retaining
the model-selector dialog entry point
- move environment, session cleanup, sub-agent idle markers, and Tauri
transport controls into Advanced
## Reliability
- scope provider loading and authentication operations to the current
ready instance
- clean up OAuth callbacks and popups when the instance or view changes
- preserve failed disconnect state and prevent conflicting provider
operations
- improve focus management and live status/error announcements
## Validation
- npm run typecheck
- npm run build:ui
- 144 runnable UI tests from the settings review cycle
- git diff --check
- independent settings and accessibility gatekeepers: SHIP
Closes#609
## Summary
- Persist server-owned Yolo state through OpenCode session metadata so
it survives workspace and CodeNomad restarts.
- Restore expanded and collapsed parent/subsession state from the
desktop client snapshot.
- Serialize CodeNomad metadata mutations so Yolo and worktree metadata
cannot overwrite each other.
## Yolo persistence
- Stores state under metadata.codenomad.yolo with enabled and
rootSessionId fields.
- Accepts a persisted marker only when it is owned by that session,
preventing copied fork metadata from enabling an unrelated family.
- Hydrates the project session tree before processing permission events
and queues events while hydration is pending.
- Keeps the server authoritative for family inheritance, permission
replies, toggles, and cleanup.
- Uses get/merge/update writes that preserve worktreeSlug and
third-party metadata.
- Serializes writes by session across CodeNomad instances and routes
worktree slug updates through the same server-owned path.
- Reconciles late ancestry and repeated root migrations without
re-enabling a family after a concurrent disable.
## Expansion restoration
- Captures bounded, deduplicated expanded session IDs per workspace in
the local desktop snapshot.
- Restores explicit expanded and collapsed state while preserving IDs
that are temporarily unavailable during startup reconciliation.
- Gives live user changes, deletions, and loaded-session authority
precedence over preserved state.
- Cleans expansion state when sessions or instances are removed.
- Keeps the active child visible when loading legacy snapshots that
predate expansion persistence.
## Validation
- 39 focused server permission and metadata tests pass.
- 66 focused client snapshot and restoration tests pass.
- Server and UI TypeScript typechecks pass.
- git diff --check passes.
## Limits
- Project hydration follows the existing 10,000-session list ceiling.
- Snapshot expansion persistence is capped at 256 session IDs.
Closes#607
Depends on anomalyco/opencode#23068.
## Summary
- keep the Status tab visible while provider usage refreshes in the
background every minute
- simplify Yolo controls, and rename the quota subsection to Usage
- render the provider name prominently and each quota as one discreet
text row plus a progress bar
- show Included for subscription-model cost and remove the supplemental
Codex credit-balance row
- shorten the session-header Yolo badge while preserving its accessible
label
## Provider safety
- remove the Claude Pro/Max OAuth quota adapter because it's not usable
anymore
## Refresh behavior
- replace the suspense-aware provider resource with contained
signal-based polling
- preserve the last successful result during background refresh failures
- ignore stale requests after the active session or provider changes
## Validation
- 8 targeted server usage tests
- server typecheck
- UI typecheck
- production UI build
- git diff checks
- focused regression review of polling, failures, and session changes
Closes#605
## Summary
- add an OpenCode update card to the OpenCode settings section
- show the installed version and a versioned update button only when npm
advertises a newer release
- run the upgrade through the generated OpenCode SDK against a ready
instance using the configured binary
- keep running instances uninterrupted; newly started instances use the
installed version
- localize loading, unavailable-instance, success, and failure states
across all supported locales
## Server behavior
- resolve the selected binary with the existing binary probe
- check opencode-ai/latest with a 10-second timeout and a five-minute
cache
- require a ready workspace using that exact binary before enabling the
upgrade
- invoke client.global.upgrade({ target }) rather than assembling an
OpenCode API request manually
- return stable error codes without exposing server details
## Validation
- pm run typecheck in packages/server
- pm run typecheck in packages/ui
- ode --import tsx --test src/opencode-update/service.test.ts
- pm run build in packages/ui
- pm run build in packages/server
- git diff --check
<img width="1034" height="688" alt="image"
src="https://github.com/user-attachments/assets/a71a01c4-d3e2-46f6-a333-a408c058255c"
/>
<img width="1018" height="360" alt="image"
src="https://github.com/user-attachments/assets/983a87a9-6103-48d7-8bcf-e10c5ce6a105"
/>
## Summary
- Add provider quota usage to an expanded-by-default subsection in the
session Status panel.
- Resolve the active provider and model automatically from the current
session.
- Display localized quota windows, reset times, values, and color-coded
progress bars inside the same bordered rounded container used by
neighboring subsections.
## Server implementation
- Add GET /api/usage/:providerId with optional modelId filtering.
- Add a 60-second in-memory cache and deduplicate concurrent provider
requests.
- Read credentials only on the server and never return secrets through
the API.
- Support Claude, Codex, GitHub Copilot, Google/Gemini/Antigravity,
Kimi, NanoGPT, OpenRouter, z.ai, Zhipu, MiniMax global/CN, Cursor,
Ollama Cloud, Wafer, and OpenCode Go.
- Use existing OpenCode and Antigravity access tokens for Google calls;
optional environment-provided OAuth client values are required only for
token refresh.
## UI behavior
- Keep Provider usage open by default while allowing it to collapse like
the other Status subsections.
- Show loading, unsupported, not configured, unavailable, and no-session
states.
- Localize all user-visible strings across the nine existing locales.
## Attribution
- Include the MIT notice for the OpenChamber usage-provider
implementation used as the reference.
## Validation
- Server typecheck
- UI typecheck
- 7 targeted usage tests
- Production UI build
- Server build and npm package-content verification
- Tauri release build without bundling
- git diff checks
<img width="484" height="444" alt="image"
src="https://github.com/user-attachments/assets/69afbaf8-4c9a-4cb3-b316-e5788e416141"
/>
## Summary
- Restore the primary desktop client's window bounds, zoom, workspace
tabs, active sessions, drafts, bounded attachments, scroll positions,
and panel layout across launches.
- Keep additional Electron and Tauri processes independent: they still
start normally but do not read or write the shared native snapshot.
- Add translated settings to disable startup restoration or clear the
saved state.
## Implementation
- Persist a versioned, bounded snapshot atomically in Electron and
Tauri, preserving unknown future envelopes until an explicit clear.
- Elect one primary process with stale-owner recovery and process
identity checks, then protect renderer access with per-document
capability tokens.
- Flush renderer state before close, quit, reload, and app-owned
navigation with bounded failure handling and fresh token rotation.
- Reconcile partial or timed-out workspace hydration without losing
unsent prompt state or resurrecting explicitly cleared drafts,
attachments, sessions, or tabs.
- Correlate restore-created workspaces so cancellation and delayed SSE
events cannot produce ghost tabs.
- Harden workspace launch cancellation and cross-platform process
cleanup so failed starts cannot orphan detached POSIX, WSL, or Windows
children; incomplete shutdown exits nonzero.
The restored shell state remains local to the desktop client. Durable
semantic session properties continue to use OpenCode session metadata
separately.
## Validation
pm run typecheck --workspace @neuralnomads/codenomad
- Electron native suite: 43 passed
- Tauri suite: 54 passed
- Focused UI restore, reconciliation, attachment, authority, and race
suites passed
- Focused server workspace runtime, manager, process identity, route,
and shutdown suites passed
- pm run build
- pm run build:tauri
- git diff --check
- 16-pass adversarial review loop completed with final No findings
- extensivly optimized, tested and used day by day
## Platform notes
- Native menu reloads and app-owned navigation have awaited durability
guarantees. Browser-engine crashes or forced process termination remain
inherently best effort.
- Process cleanup uses identity-guarded platform adapters and fails
conservatively when target ownership cannot be proven.
## Summary
Voice input (speech-to-text / STT) and voice output (text-to-speech /
TTS) currently share a single API key, base URL, and provider
configuration. Users cannot use different providers for each direction —
for example, Groq for transcription (STT) and OpenAI for synthesis
(TTS).
This PR adds a `separateProviders` toggle to the speech settings. When
**off** (default), behavior is unchanged — existing configs work as-is.
When **on**, STT and TTS each get their own `apiKey`, `baseUrl`, and
`model` fields, allowing independent OpenAI-compatible endpoints per
direction.
## Changes
### Server
- **`api-types.ts`**: Added `separateProviders`, `sttConfigured`,
`ttsConfigured`, `sttBaseUrl`, `ttsBaseUrl` to
`SpeechCapabilitiesResponse`
- **`speech/service.ts`**: Extended Zod schema with `separateProviders`
+ nested `stt`/`tts` sub-objects. Split `createProvider()` into
direction-aware `createSttProvider()`/`createTtsProvider()` with
`resolveSttSettings()`/`resolveTtsSettings()` resolvers that fall back
to shared values when per-direction fields are absent
- **`settings/public-config.ts`**: Extended `sanitizeServerOwner` to
strip per-direction `stt.apiKey`/`tts.apiKey` and set
`stt.hasApiKey`/`tts.hasApiKey` booleans (same pattern as the shared
`apiKey`)
- **`OpenAICompatibleSpeechProvider`**: Completely unchanged — it
receives the same flat `NormalizedSpeechSettings` it always has
### UI
- **`stores/preferences.tsx`**: Extended `SpeechSettings` type with
`separateProviders`, `stt`, `tts` sub-objects. Updated
`normalizeSpeechSettings()` and `updateSpeechSettings()` to handle
per-direction patches
- **`components/settings/speech-settings-card.tsx`**: Added toggle at
the top of the card. When enabled, shows two sections (Input/STT and
Output/TTS) with their own API Key, Base URL, and Model fields. Playback
Mode and TTS Format remain shared. Also includes "Test input" button
alongside existing "Test playback"
- **`lib/audio-utils.ts`**: Extracted shared `blobToBase64`,
`createMediaRecorder`, `stopTracks` utilities (used by both
`usePromptVoiceInput` and `useTranscriptionTest`)
- **`lib/hooks/use-transcription-test.ts`**: New hook for testing STT
transcription from the settings card
- **`components/prompt-input/usePromptVoiceInput.ts`**: Uses
`sttConfigured` instead of combined `configured`
- **`stores/conversation-speech.ts`**, **`lib/hooks/use-speech.ts`**,
**`speech-settings-card.tsx`**: TTS consumers use `ttsConfigured`
instead of combined `configured`
- **`styles/components/settings-screen.css`**: Added
`settings-card-section-header`/`settings-card-section-title` styles
- **i18n**: Added new keys per locale across all 9 locales (en, es, ja,
zh-Hans, fr, de, he, ne, ru)
## Backward Compatibility
- `separateProviders` defaults to `false` — existing configs work
unchanged
- The `configured` field remains in capabilities response as
`(sttConfigured || ttsConfigured)` so any consumer not yet updated still
works
- `NormalizedSpeechSettings` (the provider-facing flat interface) is
unchanged
## Testing
- 13 unit tests covering direction-aware resolution (shared mode,
separate mode, partial config, fallback chains, model overrides) and
config sanitization
- All tests pass: `npx tsx --test
packages/server/src/speech/service.test.ts
packages/server/src/settings/public-config.test.ts`
- Server typecheck: clean
- UI typecheck: clean
- UI build: succeeds (no duplicate i18n keys)
detect realpath when open workspace
try avoid path issue in windows
- d:\xxx
- D:\xxx
- symbol link
now they are same path when try detect exists workspace
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
Yolo (permission auto-accept) currently lives entirely in the UI. Each
browser keeps its own toggle in `localStorage` and auto-replies to
permission requests over a 4-hop path (`OpenCode → server SSE → UI →
server proxy → OpenCode`). The server — which already sits on the event
stream that carries every `permission.asked` — does none of the work.
This PR makes the **server authoritative**: it owns the toggle state,
resolves family-root inheritance, and performs the auto-reply in-process
via loopback using the same `"once"` semantics the UI used to send. The
UI becomes a pure view: it toggles via REST and mirrors state from a
`yolo.stateChanged` SSE event.
## Why
- **Correctness**: the server already consumes the instance SSE stream
(`InstanceEventBridge`); auto-accepting there is the natural choke point
instead of bouncing to the UI and back.
- **Multi-client**: previously each browser had independent
`localStorage` state and never synced. Toggles now broadcast to all
connected clients in real time.
- **Headless**: Yolo keeps auto-accepting even when no UI is connected
(useful for long autonomous runs).
- **Latency**: drops from 4 hops to a single in-process loopback call.
## What changed
**Server (new, authoritative)**
- `permissions/auto-accept-store.ts` — in-memory state keyed by family
root. Faithful port of `resolvePermissionAutoAcceptFamilyRoot`:
fork/`revert` sessions root at themselves; enabling any member enables
the whole family.
- `permissions/auto-accept-manager.ts` — subscribes to `instance.event`,
builds the session tree from `session.created/updated/deleted`
(`properties.info`), intercepts `permission.v2.asked` /
`permission.asked`, dedupes in-flight replies, emits `yolo.stateChanged`
/ `yolo.autoAccepted`, clears per-instance state on
`workspace.stopped/error`.
- `permissions/opencode-replier.ts` — default replier calling OpenCode
directly (`getInstancePort` + auth header), mirroring
`background-processes/manager.ts`.
- `server/routes/yolo.ts` — `GET/POST
/workspaces/:id/yolo/sessions/:sid[/toggle]`, following existing route
conventions.
- `api-types.ts` / `events/bus.ts` — `YoloStateResponse` + the two new
event types registered in `onEvent` so they flow over `/api/events`.
**UI (pure view)**
- `permission-auto-accept.ts` — removed `localStorage`, persistence, and
drain logic; now a runtime (non-persisted) projection.
`resolvePermissionAutoAcceptFamilyRoot` is **retained as a display aid**
so the badge still lights up for child/sub-sessions of an enabled family
(preserving the inheritance UX).
- `instances.ts` — toggle calls REST (optimistic, reconciled on success,
reverted on failure); subscribes to `yolo.stateChanged`;
`ensureYoloStateSynced` backfills the active session's state on first
connect (deduped per session, reset on SSE reconnect so it re-syncs
after a server restart).
- `session-events.ts` — removed the three
`drainAutoAcceptPermissionsForInstance` hooks (the server drains now).
- `api-client.ts` — `getYoloState` / `toggleYolo`.
## Behavior parity
| Aspect | Before | After |
|---|---|---|
| Reply semantics | `"once"` | `"once"` (unchanged) |
| Inheritance | whole family (root + non-fork descendants) | **same** |
| Fork isolation | `revert` session is its own root | **same** |
| Persistence | UI `localStorage` | none (intentional, see Notes) |
| Multi-client sync | ❌ independent per browser | ✅ real-time via SSE |
| Works with UI closed | ❌ | ✅ |
| Auto-reply path | 4 hops | 1 in-process hop |
## Testing
32 unit tests added (`node:test`), all passing. Server + UI typecheck
clean.
- **Store (16)**: inheritance (parent/child/sibling), fork isolation,
cyclic parent chains, late parent discovery, `revert` re-rooting,
per-instance independence, tree maintenance.
- **Manager (13)**: real `properties.info` event shapes, v2 vs legacy
reply, in-flight dedup + retry-after-resolve, `yolo.stateChanged`
emission, `session.deleted` keeps toggle, `workspace.stopped` clears
state, `stop()` unsubscribes.
- **UI (3)**: retained `resolvePermissionAutoAcceptFamilyRoot`
display-projection tests.
## Notes for reviewers
- **No persistence is intentional** for this milestone — server restart
resets all Yolo state (matches the agreed scope). The UI mirror
self-heals via SSE reconnect + `ensureYoloStateSynced`. Persistence can
be layered on later (e.g. into `~/.config/codenomad/config.json`)
without touching the manager.
- **Family-root resolution lives in two places on purpose**: the server
resolves it to decide whether to auto-reply; the UI resolves the same
pure function to render the badge instantly (synchronous memo). Both are
faithful ports; no network round-trip is added for display.
- **`properties.info` nesting**: OpenCode wraps session records under
`properties.info` for `session.*` events (permission events are flat).
The manager handles both and the tests use the real nested shape to
guard against regressions.
- `getYoloState` / `yolo.autoAccepted` are wired but `yolo.autoAccepted`
is not yet consumed by the UI — it's available on the wire for future
observability (e.g. an audit log / toast).
## Risk / rollback
The change is additive on the server and the UI gracefully degrades to
the SSE mirror. If the server lacks the new routes (mixed-version), the
UI's optimistic toggle still flips locally and `toggleYolo` failures are
logged + reverted, so no hard breakage.
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- Adds editable workspace display names for recent folders.
- Shows the saved workspace name in the recent folder list and instance
tab, including the launching workspace state.
- Preserves workspace names when recent folders are relaunched and keeps
basename fallback for unnamed folders.
Fixes#510
## Validation
- npm run typecheck --workspace @codenomad/ui
- npm run typecheck --workspace @neuralnomads/codenomad
## Summary
This fixes#518 by moving CodeNomad worktree execution from
directory-header routing to OpenCode experimental workspace routing.
OpenCode changed existing-session routing so session routes can prefer
the session's stored directory over `x-opencode-directory`. That made
the old CodeNomad worktree proxy model unreliable for sessions that
should execute in a worktree. This PR switches CodeNomad to resolve an
OpenCode workspace ID for each CodeNomad worktree and pass that
workspace ID on worktree-scoped OpenCode calls.
## What changed
- Start OpenCode with `OPENCODE_EXPERIMENTAL_WORKSPACES=true`.
- Change the OpenCode server base URL from
`/workspaces/:id/worktrees/root/instance` to `/workspaces/:id/instance`.
- Add a root OpenCode client helper in
`packages/ui/src/stores/opencode-client.ts`.
- Add OpenCode workspace sync/cache helpers in
`packages/ui/src/stores/opencode-workspaces.ts`.
- Sync OpenCode workspaces after CodeNomad worktree hydration and after
worktree create/delete flows.
- Map CodeNomad worktree slugs/directories to OpenCode `workspace.id`
values discovered by `experimental.workspace.syncList` and
`experimental.workspace.list`.
- Replace all OpenCode worktree clients with the root client plus
explicit `workspace` payloads where the active session/worktree requires
it.
- Route session, permission/question, file browser reads, SDK git
status, and prompt/action calls through root OpenCode client + workspace
ID.
- Keep CodeNomad local git worktree server APIs intact; those still need
filesystem directories for local git operations.
## Review fix
- Fixed right-panel file saves so they no longer write to the root
workspace after reading from a selected worktree.
- The existing CodeNomad file-content API now accepts an optional
`worktree` query parameter.
- Right-panel saves pass `worktreeSlugForViewer()`, and the server
resolves that slug to the same worktree directory used by local git
worktree APIs before writing.
- Root saves continue to use the original root workspace path.
## Removed old routing
- Removed `/workspaces/:id/worktrees/:slug/instance` OpenCode proxy
routes.
- Removed directory override proxy support using `/__dir/<encoded>`.
- Removed proxy injection of `x-opencode-directory`.
- Removed the remaining background-process completion prompt
`x-opencode-directory` header.
- Removed `getOrCreateWorktreeClient`,
`getOrCreateWorktreeClientWithDirectoryOverride`, and worktree proxy
path helpers from the UI worktree store.
- Simplified `sdkManager.createClient` because clients are no longer
keyed by worktree slug.
## Why this fixes#518
Worktree sessions can now stay visible under the root project session
listing while worktree execution is selected through OpenCode's
workspace routing model. CodeNomad no longer depends on
`x-opencode-directory` to override existing session directories, so
sessions should not disappear from the root-directory list or execute in
the wrong directory because of stale session directory fallback
behavior.
Fixes#518
## Validation
- `npm run typecheck --workspace @codenomad/ui`
- `npm run typecheck --workspace @neuralnomads/codenomad`
- `git diff --check`
## Notes
Some touched files are already oversized and were not refactored as part
of this migration: `packages/server/src/server/http-server.ts`,
`packages/ui/src/stores/instances.ts`,
`packages/ui/src/stores/session-api.ts`,
`packages/ui/src/stores/session-state.ts`,
`packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx`,
`packages/ui/src/stores/session-events.ts`, and
`packages/server/src/background-processes/manager.ts`.
## Summary
- Upgrade `@opencode-ai/sdk` to `1.15.13` and adapt UI types for the
current session/diff API surface.
- Add a session metadata store helper that safely updates OpenCode
session metadata via read-merge-full-replace while preserving
non-CodeNomad metadata.
- Migrate CodeNomad worktree assignments from
`.codenomad/worktreeMap.json` into `metadata.codenomad.worktreeSlug`,
with legacy fallback, stale-entry pruning, and server-side deletion of
the legacy map once empty.
- Add `.codenomad/background_processes/` to CodeNomad-managed
`.git/info/exclude` entries.
## Details
- New sessions and worktree reassignment now persist explicit session
worktree assignments in OpenCode session metadata under:
`metadata.codenomad.worktreeSlug`
- Worktree resolution now prefers session metadata, then falls back to
legacy `parentSessionWorktreeSlug`, then `root`.
- Legacy migration runs after session/map loading; after the full
session list is known, missing legacy session IDs are treated as stale
and pruned.
- The UI no longer uses `defaultWorktreeSlug`; the fallback is `root`.
- `writeWorktreeMap` deletes `.codenomad/worktreeMap.json` server-side
when no legacy parent-session mappings remain.
## Validation
- `npm run typecheck --workspace @codenomad/ui`
- `npm run typecheck --workspace @neuralnomads/codenomad`
## Summary
Fixes#468 and #470. The quick-start examples crashed on first run
without a password, and the browser self-signed certificate warning was
not documented anywhere a new user would see it.
## Changes
- Add `--password` to all npx quick-start examples (main README + server
README)
- Document the three ways to configure auth: `--password`, env var,
`auth.json`
- Show `auth.json` schema so users understand the expected format
- Add browser warning note to self-signed certificates section with
step-by-step instructions for Chrome/Brave and Firefox
- Mention `--https=false --http=true` as an alternative for local-only
use
## Validation
- Reviewed rendered markdown structure
- Verified auth.json schema matches AuthFile interface in auth-store.ts
## Summary
- Add an allowlisted server API for editing global config files,
starting with OpenCode global config.
- Add a Config Files settings section using the existing Monaco editor
with save, reload, dirty-state handling, and responsive layout.
- Improve compact settings navigation with a full-width section
selector, settings icon, and close action in the compact toolbar.
## Validation
- npm run typecheck in packages/server
- npm run typecheck in packages/ui
- npm run build in packages/ui
## Notes
- Existing Vite warnings about virtua JSX import source and large chunks
remain unrelated.
## Summary
- Expands Linux `--launch` browser discovery for Brave and Vivaldi
package variants (`brave-browser-stable`, `brave`, `vivaldi-stable`).
- Adds snap path candidates for Brave and Vivaldi plus Flatpak launch
commands.
- Adds `xdg-open` as the final fallback when direct browser app-mode
launch candidates are unavailable.
## Validation
- Attempted `npm run typecheck --workspace @neuralnomads/codenomad`, but
it failed before checking this change because the worktree is missing
the `node` type definitions (`TS2688: Cannot find type definition file
for 'node'`).
Fixes#469
--
Yours,
[CodeNomadBot](https://github.com/NeuralNomadsAI/CodeNomad)
---------
Co-authored-by: Shantur Rathore <i@shantur.com>
## Summary
- Rename the OpenCode config template into a versioned npm-packable
CodeNomad plugin package.
- Build and package the plugin through the server bundle, with
Electron/Tauri carrying it via existing server resources.
- Replace OPENCODE_CONFIG_DIR injection with JSONC-aware
OPENCODE_CONFIG_CONTENT merging that appends the CodeNomad plugin while
preserving user config.
## Validation
- npm run build --workspace @codenomad/codenomad-opencode-plugin
- npm run prepare-plugin --workspace @neuralnomads/codenomad
- npm run typecheck --workspace @neuralnomads/codenomad
- npm run typecheck --workspace @neuralnomads/codenomad-electron-app
- node --import tsx --test \"src/opencode-plugin.test.ts\"
\"src/workspaces/__tests__/spawn.test.ts\"
## Notes
- Production plugin loading uses an explicit npm file alias for the
packaged tarball.
- Dev loading still references the TypeScript plugin entry directly.
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- Reuses the directory browser for web file attachment selection and
removes the separate filesystem browser dialog.
- Reads web-selected files through the filesystem browser API so
selection respects unrestricted and configured workspace roots.
- Keeps Electron/Tauri picker selection on the FileList ingestion path
so picked files behave like dropped files with byte-backed attachments.
## Validation
- npm run typecheck --workspace @codenomad/ui
- npm run typecheck --workspace @neuralnomads/codenomad
- npm run typecheck --workspace @neuralnomads/codenomad-electron-app
- cargo check
- git diff --check
## Notes
- Follow-up to #417; this branch currently includes the commits from
that PR plus the attachment picker parity fix.
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- Add ephemeral per-session web preview mode for opening arbitrary
HTTP(S) URLs inside the session workspace without persisting preview
state across reloads.
- Reuse the SideCar iframe shell by extracting a shared BrowserFrame
with navigation, refresh, path entry, viewport presets, and no-injection
element comment targeting.
- Add authenticated preview proxy routes for HTTP and WebSocket traffic,
sharing lower-level proxy forwarding with SideCars to avoid duplicating
proxy code.
- Localize all new preview and viewport strings across English, Spanish,
French, Hebrew, Japanese, Russian, and Simplified Chinese.
## User-facing behavior
- Users can open a web preview from the session toolbar and toggle back
and forth between chat and preview mode.
- Preview mode replaces only the message stream/timeline area; the
prompt input and attachments stay available below the preview.
- Comment mode lets users select elements in the same-origin proxied
iframe and append structured page/element references to the prompt
draft.
- The generated prompt quotes the page/element reference while leaving
the user's actual comment as normal prompt text.
- Viewport selection supports responsive, desktop, tablet
portrait/landscape, and mobile portrait/landscape canvases, with fixed
sizes isolated inside the iframe scroll container.
## Implementation notes
- Preview sessions are in-memory and keyed by short-lived tokens exposed
through `/previews/:token`.
- Preview proxy responses strip frame-blocking headers and rewrite
same-origin redirect locations back under the preview route.
- SideCarView now composes the shared BrowserFrame, preserving existing
SideCar behavior while gaining shared viewport controls.
- Parent-side iframe DOM access powers hover highlighting and element
metadata extraction, avoiding HTML/script injection for the first
implementation.
## Validation
- `npm run typecheck --workspace @codenomad/ui`
- `npm run typecheck --workspace @neuralnomads/codenomad`
## Notes
- Existing unrelated local changes to `package.json` and
`package-lock.json` were intentionally left out of this PR.
- The initial proxy does not yet rewrite all HTML/CSS asset references;
pages with root-relative assets may need follow-up URL rewriting.