The round-four review's v6 pass found three factual gaps and the v5 pass two nits. Correct them before merge: - The in-process note claimed a koffi signature mistake is 'contained to the worker thread' — worker_threads share the process, so a native access violation takes down the whole Node process with no PowerShell fallback. State the real blast radius and record the deferred pkg-VFS worker-spawn arm in Consequences (both languages, pairing re-recorded). - The 2026-07-27 picker note claimed unconditional 'per-monitor-v2 DPI'; PMv2-less hosts (Server 2016 / Win10 1607) cascade to per-monitor or system-aware. Say 'the best thread DPI awareness the host accepts' (both languages, pairing re-recorded). - built-worker.e2e.ts was not in any keyless gate (vitest.e2e config is not part of the default unit run and builtBinSmokeGate's explicit list missed it), so lib/worker.cjs load regressions passed keyless CI. Add it to builtBinSmokeGate alongside the workflow-workerthread sibling. - Remove the dead trailing 'void bindings' in win32-dialog-logic.spec.ts and give native-picker.spec.ts the sibling module header it lacked.
4.5 KiB
Agent Note: Native workspace directory picker
Status: implemented
English | 中文
Problem
The desktop GUI asks users to type an absolute path when they add an existing workspace. This is slower and more error-prone than choosing a directory with the operating system's native picker. The GUI is delivered through the local Web carrier, so opening a native dialog also creates a privileged boundary that ordinary remote requests must not cross.
Decision
Add a single-folder host.pickDirectory RPC and expose it through WorkspacesService. The workspace menu presents the flat Add workspace... action (two actions when this was decided — Open local folder... beside a create-by-name entry the one-route Note later removed). Selecting a folder reuses the existing workspace.create({ path }) flow, selects the returned workspace, and starts a blank session.
The workspace manager must upsert the returned workspace before the selection callback runs. A newly adopted directory therefore renders its basename immediately. Reopening an already registered path preserves its existing workspace title.
Interaction contract
- The picker accepts one directory on macOS, Windows, and Linux.
- Cancelling the system dialog is silent and returns
null. - A duplicate path selects the existing workspace.
- A different canonical path adopts a separate Workspace even when its derived title matches another Workspace (identity decision).
- Other picker failures show a compact retryable error.
- The create-by-name flow this decision left untouched is gone; picking a directory is now the whole of adding a workspace (one-route Note).
Host boundary
The native dialog RPC is accepted only from a loopback socket with same-origin browser metadata. The RPC does not use the default 30-second request timeout because a system dialog may remain open indefinitely; caller and connection aborts still propagate to the platform process.
Platform adapters open the dialog without a shell — spawned native tools on POSIX, an in-process COM conversation on Windows:
- macOS:
osascriptand the system folder chooser. - Windows: the in-process koffi
IFileOpenDialogworker with the best thread DPI awareness the host accepts (per-monitor-v2 when available; PMv2-less hosts cascade to per-monitor or system-aware) (in-process dialog note); the PowerShell chain (pwshin STA mode, then Windows PowerShell 5.1, both DPI-corrected) remains the fallback (picker fix). - Linux:
zenity, withkdialogas a fallback when Zenity is unavailable.
Alternatives considered
- A custom directory browser duplicates operating-system behavior and permissions, and belongs to the Web implementation rather than this desktop-only change.
- Reusing the manual path field keeps the current error-prone interaction.
- Adding authentication infrastructure for one local native dialog would expand the change beyond its threat model; loopback and same-origin checks are sufficient for this carrier.
Consequences
The current GUI opens one local folder through a native picker on macOS, Windows, and Linux. Cancelling changes no state, failures remain retryable, duplicate paths are idempotent, and distinct same-basename paths coexist as separate Workspaces. The selected workspace and its displayed name refresh before a new blank session starts. This picker is now the only route to a workspace (one-route Note): the operator picks an existing directory, or creates one inside the chooser.
The added host, runtime, component, and GUI tests cover the native boundary, request trust checks, cancellation and failure handling, existing-path reuse, same-basename adoption, and the immediate visible-name update. The privileged RPC remains specific to the local desktop carrier; a remote Web directory browser is outside this decision.
Risks
- Linux desktop environments may provide neither supported picker. The GUI reports that limitation instead of falling back to a typed path.
- Browser metadata varies outside the supported local carrier. The endpoint intentionally rejects requests that cannot prove the required local same-origin context.