## 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>
## Problem
PR #565 (commit e29c3a08) added `scope=project` to session list requests
so worktree/project sessions remain visible. With OpenCode 1.17.x, the
`/session` endpoint can ignore the `directory` filter when
`scope=project` is present and return sessions from unrelated folders
that share the same project/global bucket.
This can make sessions appear in the wrong project tab. Deleting one of
those sessions still deletes it from the shared OpenCode database, so the
UI must avoid showing unrelated sessions in the wrong tab.
## Reproduction
```bash
PORT=4096
curl -s -u "codenomad:$PASSWORD" \
"http://127.0.0.1:$PORT/session?directory=/home/user/project-a&scope=project" \
| jq 'length'
# Can include sessions from other folders too.
curl -s -u "codenomad:$PASSWORD" \
"http://127.0.0.1:$PORT/session?directory=/home/user/project-a" \
| jq 'length'
# Filters to the requested folder, but may miss project/worktree sessions.
Related: anomalyco/opencode#33113
Fix
Keep scope: "project" so OpenCode still returns project/worktree
sessions, then filter the returned sessions client-side to the current
root directory plus known CodeNomad worktree directories.
This preserves the worktree-session visibility behavior from #565 while
avoiding unrelated cross-directory leakage when OpenCode ignores
directory alongside scope=project.
Validation
Focused session-pagination tests pass.
UI typecheck passes.
UI build passes.
Tauri release build passes.
Manual exe tested for validation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Problem
Mobile users experience SSE disconnections (wifi drops, Cloudflare idle
timeouts) but get no visual feedback. The per-instance status dots
remain green even when the SSE transport is down, because instance
status updates travel over the broken SSE pipe.
## Solution
This PR implements **immediate connection status feedback** by
propagating SSE transport state to all instance status indicators:
### Changes
1. **server-events.ts**: Add `onDisconnect()` handler
- Mirrors existing `onOpen()` pattern
- Fire `disconnectHandlers` in `scheduleReconnect()` when connection
drops
2. **sse-manager.ts**: Register SSE lifecycle handlers
- `onDisconnect`: set ALL instances to `connecting` (amber dot)
- `onOpen`: clear `connecting` status (green dot restored by subsequent
events)
- Log transitions for debug visibility
3. **AGENTS.md**: Add PR Review Principles
- Check regressions first, look for better implementations
- Be the PR gatekeeper, ruthless code quality
- Test before responding, UI/server version parity
## Verification
- ✅ TypeScript compilation clean (pre-existing SDK errors unrelated)
- ✅ Vite build successful
- ✅ Mobile testing: SSE disconnect → immediate amber dots
- ✅ Reconnect → green dots restored
## Edge Cases Handled
- **Transient drop**: amber → green on reconnect (no false disconnect
modal)
- **Instance dies during outage**: amber → green → `disconnected` event
→ red
- **0 instances**: Map empty, loop is no-op
- **Rapid reconnect cycles**: idempotent (setting `connecting` on
`connecting` is no-op)
## Related
- Complements PR #519 (pong retry with timeout) - merged upstream
- Addresses mobile UX gap: workspace-level SSE state now visible to all
users (not just debug overlay)
---------
Co-authored-by: Shantur Rathore <i@shantur.com>
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- update @opencode-ai/sdk to 1.17.8
- load session lists through client.session.list with scope=project
- use a single high-limit project request because this endpoint does not
support cursor pagination
## Validation
- npm run typecheck --workspace packages/ui
## Notes
- Existing untracked .opencode/package-lock.json was left untouched.
## Summary
- Refine narrow session header controls, including overflow menu
placement and centered command/context controls.
- Autosize the narrow prompt input while preserving desktop/hybrid
behavior and avoiding mobile keyboard refocus flicker.
- Improve tab bar behavior at small widths by preventing action button
shrink and disabling drag-reorder on touch-only devices.
- Includes earlier branch changes for message scroll control collapse
and square-corner styling documentation.
## Validation
- npm run typecheck --workspace @codenomad/ui
## Summary
- add a central tool-call registry for renderer lookup and configurable
expansion defaults
- add Appearance presets for transcript detail: Minimal, Balanced,
Detailed, and Everything
- allow per-item customization for thinking, known tool calls, and Other
tools with responsive settings layout
- persist toolCallExpansionDefaults while keeping legacy expansion
preferences synchronized for command compatibility
## Validation
- npm run typecheck --workspace @codenomad/ui
## Summary
This branch implements a compact-mode redesign for the message
stream, focused on making dense conversations easier to scan while
keeping important actions available.
### Message Stream Redesign
- Compacts user, assistant, reasoning, step, compaction, and tool-call
surfaces into tighter header/body layouts.
- Moves message time next to the speaker / agent-model identity instead
of using a separate right-side timing cluster.
- Restores the pre-branch user message background so user prompts remain
visually distinct.
- Aligns right-edge action positioning across text messages, reasoning
blocks, tool calls, compaction rows, and usage rows.
### Action Model
- Consolidates action controls across message parts.
- Keeps only copy and speak visible inline on wide layouts.
- Moves selection, delete, delete-up-to, fork, revert, tool input
visibility, and task-session navigation into overflow menus.
- On narrow layouts, hides inline actions and exposes all available
actions through overflow.
- Replaces native selection checkboxes in message surfaces with
overflow-menu selection actions using lucide square/check-square icons.
### Tool Calls and Bash Output
- Merges tool-call header chrome into the tool call itself instead of
wrapping it with an extra external header.
- Moves the tool status icon next to the tool title instead of pinning
it to the far right.
- Refines input/output spacing for expanded tool calls.
- Keeps streaming bash output pinned and improves final ANSI output
rendering/restoration.
### Thinking / Reasoning Blocks
- Compacts thinking blocks into a header-first treatment.
- Uses extracted reasoning titles or duration fallback labels for
compact scanning.
- Keeps reasoning copy/speak available while moving
secondary/destructive actions to overflow.
### Styling and i18n
- Updates message and tool-call CSS for compact spacing, right-edge
alignment, action button consistency, and surface cleanup.
- Adds localized strings for new reasoning labels/actions across
supported message locales.
## Validation
- npm run typecheck --workspace @codenomad/ui
- git diff --check
## Summary
- Keep Hold mode latched until the user manually reaches the true
bottom.
- Restrict Hold targets to user-readable Assistant answer text.
- Exclude tool/status/output and reflection/reasoning blocks from Hold
target eligibility.
## Validation
- node --test packages/ui/src/components/virtual-follow-behavior.test.ts
- npm run typecheck --workspace @codenomad/ui
- npm run build --workspace @codenomad/ui
## Summary
- Route `session.created` SSE events through the existing session update
handler.
- Prepend newly-created root sessions to the session list pagination ids
so they appear in the sidebar immediately.
Fixes#560
## Validation
- `npm run typecheck --workspace packages/ui`
## Summary
- notify the existing virtual follow-list path after streaming bash ANSI
output updates
- restore the inner bash output scroll immediately after direct pre
updates
- apply the same coalesced notification pattern to final ANSI output
## Validation
- npm run typecheck --workspace @codenomad/ui
## Summary
- wire Winget submission into the stable release pipeline instead of
relying on a separate `release.published` workflow
- keep the existing release asset resolution and Komac-backed submission
flow
- document the new trigger model and manual fallback path
## Validation
- `git diff --check origin/dev...HEAD`
- `node --check scripts/winget/resolve-release-asset.cjs`
- `node scripts/winget/resolve-release-asset.cjs --help`
- live release metadata and asset-resolution dry-run against stable
`v0.17.0`
## Notes
- this fixes the case where a release created by GitHub Actions with the
default `GITHUB_TOKEN` does not fan out into a second workflow run
- assumes the existing Winget repo secret/variables remain configured
- refs #462
## Problem
Streaming responses send many `message.part.delta` events per second.
Each event triggers a Solid store mutation + re-render, causing:
1. **Mobile freezes** — KaTeX/highlight.js re-renders on every delta
block the main thread
2. **Text duplication** — When `message.part.updated` replaces a part
with the server's complete state, previously enqueued deltas would flush
AFTER the replacement, concatenating stale text onto the fresh part
## Solution
### 1. Delta throttling (50ms buffer)
Accumulate deltas in a buffer and flush every 50ms instead of updating
on every event. This batches Solid mutations and cuts main-thread
blocking during streaming.
### 2. Stale delta cleanup
When a full `message.part.updated` arrives, clear any pending deltas for
that part before applying the update. The update already contains the
complete server state, so accumulated deltas are stale.
## Changes
- `packages/ui/src/stores/session-events.ts`: Added `enqueueDelta`,
`flushDeltas`, `clearPendingDeltasForPart`; updated
`handleMessagePartDelta` and `handleMessageUpdate`
## Verification
- Build passes: `npm run build:ui`
- Tested on mobile — eliminates visible freezes during long streaming
responses
- No duplicate text observed at end of assistant messages after part
updates
---------
## Summary
Adds a CONTRIBUTING.md file to help new contributors get started.
## Contents
- Quick start setup
- Finding issues to work on (label guide)
- Branch naming conventions
- Development workflow (fork, branch, commit, PR)
- Project structure overview with key file paths
- Styling conventions and file size guidelines
- i18n workflow for adding translations
- Code principles (KISS, DRY, single responsibility)
This gives new contributors everything they need in one place to start
contributing effectively.
---------
## Summary
- make permission Deny submit immediately while preserving optional
feedback
- replace the gated deny-confirmation UI with a compact one-line
feedback field
- remove visible feedback label/hint text and update placeholders across
all locales
## Validation
- npm run typecheck --workspace @codenomad/ui
## Notes
- Left unrelated untracked .opencode/package-lock.json out of the
commit.
## Summary
- switch the Tauri desktop runtime from the browser `EventSource` path
to a native Rust desktop event transport while leaving browser and
Electron unchanged
- restore SSE heartbeat parity by parsing named `event:` frames and
replying to `codenomad.client.ping` with an authenticated
`/api/client-connections/pong`
- add a Tauri-only settings toggle that lets the current device fall
back to the browser `EventSource` transport without leaking that choice
through shared config
- remove the temporary benchmark harness from the shipped code now that
the transport behavior has been validated
## Benchmark
The temporary in-app benchmark harness used during validation has been
removed from the final code, but the measured results are retained here
for review context.
Real Tauri/WebView2 benchmark on Windows using the dedicated session:
- workspace: `D:\CodeNomad`
- session: `ses_21feb15b3ffeLz3uRModK4KKnG`
Short command:
- `node -e "for (let i = 1; i <= 400; i += 1) console.log('line ' + i)"`
Results:
- browser `EventSource` forced in Tauri:
- timed out after `131479.7ms`
- `sawWorking=false`
- `reachedIdle=false`
- `batchesReceived=84`
- `eventsReceived=84`
- `maxBatchSize=1`
- Rust-native transport:
- completed in `1437.4ms`
- `sawWorking=true`
- `reachedIdle=true`
- `batchesReceived=4`
- `eventsReceived=45`
- `maxBatchSize=27`
Long heartbeat / stale-timeout validation:
- command: `powershell -NoProfile -Command Start-Sleep -Seconds 70`
- Rust-native transport:
- completed in `71689.5ms`
- `sawWorking=true`
- `reachedIdle=true`
- `batchesReceived=13`
- `eventsReceived=72`
- `maxBatchSize=25`
Confirmed separately afterward: the native transport also behaves better
on Linux.
## Validation
- `cargo test named_ping_event_is_routed_to_ping_channel`
- `cargo test session_cookie_is_attached_to_requests`
- `cargo test --no-run`
- `npx tsc --noEmit --pretty -p packages/ui/tsconfig.json`
- `npx tsc --noEmit --pretty -p packages/server/tsconfig.json`
- manual Tauri/WebView2 benchmark on Windows
- manual confirmation on Linux after the benchmark phase
## Notes
- this remains a Tauri-only transport; browser and Electron stay on the
browser `EventSource` path
- the Tauri fallback toggle is now genuinely device-local and restarts
the local event stream immediately when changed
- the long run validates heartbeat / stale-timeout robustness, not
headline perf
## Summary
This PR removes tracked NomadWorks/OpenCode/task-tracking artifacts that
were accidentally committed to the repository and now interfere with
local NomadWorks usage for other contributors.
NomadWorks generates local workflow state such as `tasks/`, SCR
registries, and codemaps as needed. Keeping those generated artifacts
tracked in the shared repository creates stale state, false active
tasks, and confusing/broken NomadWorks behavior for other users who
initialize or run the workflow locally.
## Problematic commits / artifacts
The cleanup addresses artifacts introduced by the following
Shantur-authored commits:
- `e708c565ef`
- `docs(wake-lock): record wake-lock change workflow`
- Introduced NomadWorks wake-lock SCR/task artifacts:
- `docs/scrs/SCR-2026-04-21-001-wake-lock-system-sleep-only.md`
- `tasks/current.md`
-
`tasks/discussions/DISCUSSION-001-wake-lock-behavior-change-for-macos-sleep-vs-screen-lock.md`
- `tasks/todo/055-wake-lock-investigation.md`
- `tasks/todo/056-wake-lock-behavior-change.md`
- `tasks/todo/057-implement-system-sleep-only-wake-lock.md`
- `a337c19b63`
- `Init nomadworks`
- Introduced tracked NomadWorks/OpenCode initialization artifacts:
- `.nomadworks/**`
- root `codemap.yml`
- `docs/scrs/current.md`
- `docs/scrs/done.md`
- `tasks/done.md`
- `.opencode/opencode.jsonc`
- `.opencode/package-lock.json`
- The removed `.opencode/opencode.jsonc` only contained the OpenCode
schema plus a commented NomadWorks plugin entry:
- `"$schema": "https://opencode.ai/config.json"`
- `// "@neuralnomads/nomadworks@0.1.0-rc.10"`
- The removed `.opencode/package-lock.json` was an orphan lockfile with
no tracked `.opencode/package.json`. It pinned old OpenCode
plugin/runtime packages, notably:
- `@opencode-ai/plugin@1.14.24`
- `@opencode-ai/sdk@1.14.24`
- Those pinned OpenCode versions are stale for the current
CodeNomad/OpenCode integration, so keeping this lockfile is misleading
and can imply an unsupported local plugin setup.
- `b06b8104a5`
- `Add task specifications for Phase 5 advanced input features`
- Introduced tracked task-planning artifacts including:
- `tasks/todo/015-keyboard-shortcuts.md`
- `tasks/todo/020-command-palette.md`
- `tasks/todo/021-file-attachments.md`
- `tasks/todo/022-long-paste-handling.md`
- `tasks/todo/023-symbol-attachments.md`
- `tasks/todo/024-agent-attachments.md`
- `tasks/todo/025-image-clipboard-support.md`
- `0a3ac6cbf2`
- `docs: add theme overhaul task series`
- Introduced tracked task-planning artifacts `tasks/todo/041-*` through
`tasks/todo/045-*`.
- `cb56eed4f9`
- `docs: add tailwind refactor follow-up tasks`
- Introduced tracked task-planning artifacts `tasks/todo/046-*` through
`tasks/todo/048-*`.
- `7267baf23d`
- `docs: queue remaining style cleanup tasks`
- Introduced tracked task-planning artifacts `tasks/todo/049-*` through
`tasks/todo/054-*`.
Some older mixed product commits also added `tasks/done/*` or
`PROGRESS.md` while introducing real product code. Those commits should
not be fully reverted because that would remove product functionality,
so this PR removes the stale tracking files directly in a final cleanup
commit.
## Changes
- Reverts the previous partial NomadWorks cleanup so the full original
faulty state can be reverted cleanly.
- Fully reverts the original `Init nomadworks` commit.
- Fully reverts the wake-lock NomadWorks workflow artifact commit.
- Removes remaining stale historical progress/task-tracking files that
are not used by CodeNomad runtime, builds, or GitHub workflows.
- Removes stale `.opencode` root config/lock artifacts introduced by the
NomadWorks initialization because the config only references a commented
NomadWorks plugin and the lockfile pins unsupported old OpenCode
packages without a matching package manifest.
## Validation
- `git status --short` is clean.
- `git diff --check upstream/dev..HEAD` passes with no output.
- Final diff only removes tracked NomadWorks/OpenCode/task/progress
artifacts; no runtime source files are changed.
## Problem
When the client receives a ping from the server, it responds with a pong
via HTTP POST. On unstable networks (mobile, WiFi with poor signal,
network switches), this POST can fail in multiple ways:
- **Hung requests**: `fetch()` never rejects, blocking retries
indefinitely
- **Network disconnection**: `Failed to fetch`
- **Brief disconnections**: transient network errors
Previously, a single missed pong would cause the server to close the SSE
connection after 45s, leaving message responses stuck in queue until the
next message triggered a reconnection.
## Solution
Three improvements to make the pong POST resilient:
### 1. Request timeout (10s)
Each pong POST is now bounded with a 10s `AbortSignal` timeout. Hung
requests fail fast instead of blocking indefinitely, allowing retries to
start before the server's stale connection sweep.
### 2. Selective retry with `isRetryableError()`
Only retries transient failures where recovery is possible:
- `AbortError` / `TimeoutError` (hung or timed-out requests)
- `Failed to fetch` (network disconnected)
- `NetworkError` (browser network errors)
Non-retryable errors like `404 Client connection not found` (permanently
closed connection) fail immediately instead of wasting retry attempts.
### 3. Exponential backoff (3 attempts, 100ms → 2000ms)
Handles burst failures gracefully without hammering the server.
## Changes
- **`packages/ui/src/lib/retry-utils.ts`** (new): Reusable retry utility
with `timeoutMs` and `shouldRetry` predicate support
- **`packages/ui/src/lib/server-events.ts`**: Updated pong handler to
use bounded timeout + selective retry
## Verification
- Build passes: `npm run build:ui` ✅
- Manually verified in production logs: retries now visible as `Pong
failed after retries` instead of single immediate failure
- SSE monitor log at `~/.codenomad/logs/sse-monitor.log` shows `PONG_OK`
/ `PONG_FAIL` / `STALE` events for ongoing monitoring
## Summary
- store per-session message load failures so automatic session effects
stop retrying after server errors
- show the server-provided error in the message stream with a reload
action that forces a fresh request
- add localized labels for the load-error title and reload button
## Validation
- npm run typecheck --workspace @codenomad/ui
- npm run build --workspace @codenomad/ui
## Summary
- rescope the UI portion of #385 to the remaining main-session selector
issue from #384
- keep main-session primary selectors limited to selectable primary
agents
- preserve child/subagent behavior by keeping the current hidden agent
available for steering
- add focused tests for selector eligibility behavior
This intentionally excludes the OMO/config-directory merge and plugin
retry scope from #385, since that part was determined to belong outside
CodeNomad. Credit to @jollyxenon for the original #385 investigation and
UI direction.
## Validation
- ode --test packages/ui/src/types/session.test.ts ✅
- pm run typecheck --workspace @codenomad/ui ⚠️ fails on existing
SDK/type drift outside this diff
Fixes#384
Based on #385
## Summary
- Collapse intact placeholder-backed long pasted text blocks by default
in user message history for readability.
- Keep pasted content fully visible to the model; this is a display-only
history treatment.
- Persist pasted-text collapse metadata across optimistic message
replacement and app reloads.
- Fall back to normal plain-text history rendering when the
placeholder-backed pasted structure has been edited or is no longer
intact before send.
Fixes#266
## Behavior
When a user submits a prompt containing an intact long pasted block from
the existing pasted-text placeholder flow, the full pasted text is still
sent to the model unchanged, but the chat history renders that pasted
section as a collapsed expandable block.
This applies to the existing placeholder-backed long-paste flow only. If
the pasted placeholder structure is broken before submission, the
message renders in history as normal plain text with no collapse
metadata.
## Verification
- `npx tsx --test
"packages/ui/src/components/prompt-input/submitPrompt.test.ts"
"packages/ui/src/lib/prompt-display-metadata.test.ts"
"packages/ui/src/stores/message-prompt-display.test.ts"`
- `npm run typecheck --workspace @codenomad/ui`
- `npm run build --workspace @codenomad/ui`
- `npm run build --workspace @codenomad/tauri-app`
## Local desktop verification
- Built executable:
`packages/tauri-app/target/release/codenomad-tauri.exe`
- Built installer:
`packages/tauri-app/target/release/bundle/nsis/CodeNomad_0.15.0_x64-setup.exe`
- Launched the built Tauri executable locally and confirmed startup
succeeded.
<img width="487" height="129" alt="image"
src="https://github.com/user-attachments/assets/6c3d670c-b049-4fa7-bc8b-c69d0fb3ca25"
/>
Fixes#299
## Summary
- show the active session title in the instance header, just after the
menu switch.
- keep the title visible whenever the left session drawer is not pinned,
using a quiet two-line header treatment without active-item
highlighting.
- when the unpinned drawer is open as a floating overlay, keep the title
in the same left header slot so the drawer covers it instead of pushing
toolbar controls around.
- disable the feature in mobile view
## Validation
- `git diff --check`
- `npm run typecheck --workspace @codenomad/ui`
- `npm run build --workspace @codenomad/ui`
- visually validated in the rebuilt desktop app raw executable
Fixes#297
## Summary
- show total assistant response duration next to the existing message
timestamp using only explicit OpenCode message timing
- show reasoning duration on reasoning cards only when OpenCode provides
explicit part timing
## Why
This revision keeps the UI scoped to explicit server-side timing data
that OpenCode already provides, which matches the maintainer feedback on
this PR.
## Validation
- node --experimental-strip-types --test
"packages/ui/src/lib/message-timing.test.ts"
- npm run build --workspace @codenomad/ui
## Summary
- Stabilize virtualized message autoscroll by keeping DOM/Virtua
orchestration in `VirtualFollowList` and moving pure follow/hold
behavior into a colocated tested module.
- Restore per-session scroll positions using Virtua metrics and anchor
snapshots while preventing hidden/inactive panes from overwriting good
snapshots.
- Gate streaming magnetization on active streaming, hold state, and the
bottom-of-viewport item so long assistant replies can still rejoin near
their tail without using stale top-of-viewport position.
## Supersedes / fixes
- supersedes #375.
- supersedes #356.
- fixes#305.
## Validation
- `node --test
packages/ui/src/components/virtual-follow-behavior.test.ts`
- `npm run typecheck --workspace @codenomad/ui`
- `npm run build --workspace @codenomad/tauri-app`
- Many manual release executable testing.
Added comprehensive German (de) and Nepali (ne) translations to the
CodeNomad UI.
Key Changes:
New Localization Files: Created 17 new message part files for each
locale in packages/ui/src/lib/i18n/messages/de/ and
packages/ui/src/lib/i18n/messages/ne/. These cover all application
areas, including settings, commands, session management, and the loading
screen.
I18n Registration: Updated the core i18n configuration to include the
new locales, enabling dynamic loading of the translation bundles.
UI Integration: Added "Deutsch" and "नेपाली" to the language selection
picker on the home screen, allowing users to switch to these languages.
Translation Quality: Provided high-quality translations that maintain
the humorous and technical tone of the original English strings. The
Nepali version uses the Devanagari script and includes technical terms
in parentheses where helpful for clarity.
Verification:
Verified that the UI build process completes successfully with the new
files.
Confirmed that all translation keys from the English source are
accounted for in both new languages.
Conducted a code review to ensure compliance with project i18n patterns.
---------
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.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
- add a release-published workflow that prepares and submits Winget
manifest updates automatically
- poll the GitHub Release API for the stable Windows Tauri asset and
compute its SHA-256 before submission
- document the maintainer secret and repository variables needed for the
Winget automation flow
## Validation
- `node --check "scripts/winget/resolve-release-asset.cjs"`
- `node "scripts/winget/resolve-release-asset.cjs" --help`
- dry-run resolver against the published `v0.16.0` release asset
## Notes
- skips draft and prerelease GitHub releases
- uses the maintainer fork submission flow for `microsoft/winget-pkgs`
- live PR submission still depends on configuring `WINGET_GITHUB_TOKEN`
- Fixes#462
## 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
- Display only environment variable names in instance info panel (hide
values)
- Display only environment variable names in logs view (hide values)
- Change environment variable value inputs to password type in settings
editor
## Motivation
Environment variable values (which may contain sensitive data like API
keys, tokens, or secrets) were displayed in plain text across multiple
UI surfaces. This change:
1. **Reduces visual exposure** — instance info and logs view now show
only variable names
2. **Masks input fields** — the settings editor uses `type="password"`
for value inputs, preventing shoulder-surfing and accidental exposure in
screenshots or screen recordings
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## 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
- make YOLO/permission auto-accept resolve by session family root so
master and subagent sessions share the same state
- keep fork/revert sessions as their own family root
- keep existing local YOLO persistence behavior; no server-backed
persistence, migration, global setting, or confirmation dialog
- re-drain queued child permissions when session metadata arrives so
permissions that arrived before parent metadata can be auto-accepted
Solves #495
## Validation
- npm run typecheck --workspace @codenomad/ui
- npx tsx --test packages/ui/src/stores/permission-auto-accept.test.ts
- git diff --check
## Build Artifact
- Not generated in this update
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- Adds a two-step deny flow for permission requests so users can provide
optional feedback.
- Passes reject feedback through `sendPermissionResponse` to
`client.permission.reply` as `message`.
- Hardens the flow by ignoring permission shortcuts while text inputs
are focused and capping feedback length.
## Validation
- `npm run typecheck --workspace @codenomad/ui`
- `npm run build:ui`
- `npm run build:linux --workspace @neuralnomads/codenomad-electron-app`
## Showcase
<img width="1709" height="1118" alt="image"
src="https://github.com/user-attachments/assets/9f516100-7c3f-4c08-95b2-236b5168394e"
/>
Related to #496
---------
Co-authored-by: Shantur Rathore <i@shantur.com>
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- this fixes a bug where no plugin are displayed in the UI
- normalize OpenCode plugin metadata through a dedicated helper instead
of assuming every entry is a string
- support tuple-form plugin entries by displaying the tuple specifier
and preserving existing file:// normalization
- add focused regression coverage for string, tuple, and invalid plugin
entry shapes
## Validation
- npm run typecheck --workspace @codenomad/ui
- node --test packages/ui/src/lib/hooks/use-instance-metadata.test.ts
- npm run build --workspace @codenomad/ui
## Summary
- render Markdown horizontal rules instead of hiding them in message
content
- keep the existing Markdown parser behavior and style the generated hr
with the shared border token
Solves #491
## Validation
- npm run typecheck --workspace @codenomad/ui
- npm run build:linux --workspace @neuralnomads/codenomad-electron-app
## Test release artifact
Built locally for Linux; artifacts are ignored and not included in this
PR:
- packages/electron-app/release/CodeNomad-Electron-linux-x64-0.16.0.zip
-
packages/electron-app/release/CodeNomad-Electron-linux-x86_64-0.16.0.AppImage
Co-authored-by: Shantur Rathore <i@shantur.com>
Co-authored-by: Pascal André <pascalandr@gmail.com>
Reserve space for the absolute homepage controls on narrow screens so the language selector no longer overlaps the CodeNomad logo.
Apply the existing actions-column height constraint to the recent folder panel across viewport sizes so mobile lists scroll instead of expanding beyond the browse/actions section.
Validated with npm run typecheck --workspace @codenomad/ui.