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
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
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.