Windows daemon paths like C:\Users\... don't start with "/", breaking
the explorer sidebar, checkout status, terminals, and file attachments
when connected to a Windows daemon. Consolidate three private
isAbsolutePath implementations into a shared utility and use it
everywhere, with correct file URI conversion for Windows and UNC paths.
`paseo logs` was only showing the last 40 collapsed timeline items
due to DEFAULT_MAX_ITEMS. Setting to 0 disables the cap so the full
timeline is shown by default. --tail still works for limiting output.
Windows npm shims (e.g. C:\nvm4w\nodejs\codex) fail with ENOENT when
spawned without shell. Use `shell: true` on win32 for all provider
launches instead of the overly complex shouldUseWindowsShell function.
OpenCode's message.part.delta events use field="text" for all parts
including reasoning, because "text" is the property name being updated.
Track part types from message.part.updated events and use them to
correctly classify deltas for known reasoning parts.
Also set PASEO_SUPERVISED=0 in vitest setup to prevent process.send()
conflicts with vitest's fork pool.
Spawning a missing/broken codex binary emits an async "error" event on
the child process. Without a listener, Node.js crashes the daemon with
no log entry. Add child.on("error") in CodexAppServerClient and global
uncaughtException/unhandledRejection handlers that log via pino before
exiting.
* style: add subtle teal tint to dark mode surfaces
Replace neutral gray surfaces with a teal-tinted palette across
the app and website, giving Paseo a warmer, more recognizable feel.
App uses a restrained tint (G-R ~3), website is slightly stronger
(G-R ~6) as a brand showcase.
* fix(opencode): handle message.part.delta events for assistant text streaming
OpenCode v2 streams assistant text via `message.part.delta` events (with
field "text" or "reasoning"), but the translator only handled
`message.part.updated`. This caused assistant messages to be silently
dropped during live streaming.
* feat: show agent short ID in tab context menu and tooltip (#161)
Add agent short ID to the "Copy agent id" menu item as trailing hint
text and to the tab tooltip next to the title. Add leading icons to
all workspace tab context menu items.
* add batch close rpc for workspace tabs
Use non-recursive directory watchers on Linux since recursive fs.watch
is not supported. Dynamically discover and watch subdirectories, refreshing
the watcher tree on changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use non-recursive directory watchers on Linux since recursive fs.watch
is not supported. Dynamically discover and watch subdirectories, refreshing
the watcher tree on changes. Also pin rewind command first in the slash
command list and remove stale .gitignore entry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WIP: fix archive tab reconciliation
* fix: converge archive into AgentManager for cross-session propagation
CLI archive left agent tabs visible in passive app clients because
Session.archiveAgentState only notified the archiving session's own
subscription. LoopService had the same gap via AgentManager.archiveAgent.
Converge on AgentManager.archiveAgent as the single canonical archive
path: persist updatedAt, call notifyAgentState before closeAgent so all
sessions receive the archived snapshot. Delete Session.archiveAgentState
and make handleArchiveAgentRequest a thin wrapper.
New CLI commands: ls, create, kill, capture (tmux capture-pane style with
line range slicing and ANSI stripping), and send-keys (with special token
interpretation). Server-side adds capture_terminal_request RPC and
list-all-terminals support (optional CWD). Includes e2e tests and skill docs.
Speech runtime was leaking 8+ individual resolvers into bootstrap and
blocking server startup for ~3s with synchronous Sherpa native model
loading. Refactor into a self-contained SpeechService that owns its
full lifecycle (init, download, monitor, cleanup) and defer start()
until after httpServer.listen() so native loading doesn't block
accepting connections. Server startup drops from ~3.2s to ~0.5s.
Also removes unused client-triggered speech model download/list path
(CLI commands, session handlers, message types) — the service manages
its own models.
The process.send check alone was insufficient because vitest also runs
workers with IPC channels. Now the supervisor sets PASEO_SUPERVISED=1
on the worker env, and bootstrap checks for it before sending.
The desktop app could time out connecting to the daemon on first launch
because the PID file advertised a listen address before the HTTP server
was actually listening. The supervisor now writes the PID lock with
listen: null and updates it only after the worker sends a paseo:ready
IPC message confirming the server is listening.
- Rename daemon-runner.ts to supervisor-entrypoint.ts
- PID lock acquired with listen: null, updated via paseo:ready IPC
- Worker sends paseo:ready after httpServer.listen() resolves
- Desktop polls until listen is non-null before returning to the app
- Remove PASEO_PID_LOCK_MODE and external lock mode
- Remove unnecessary env overrides (PASEO_HOME, PASEO_CORS_ORIGINS) from
desktop daemon spawn
- Reduce trace log bloat: inbound/outbound WebSocket messages now log
only message type and payload size instead of full payloads
- Supervisor restarts worker on SIGKILL (covers OOM)
Queries like "faro/main" were treated as literal paths relative to ~,
so the picker tried to list ~/faro/ (which doesn't exist) instead of
searching the home tree for paths containing "faro/main". Only treat
queries as paths when explicitly rooted with ~, ~/, ./, or /.
Checkout-diff watch targets were per-Session, so multiple clients (phone,
desktop, browser) watching the same workspace duplicated fs.watch sets and
snapshot computation. Move all checkout-diff subscription machinery into a
singleton CheckoutDiffManager that deduplicates watchers and snapshots
across sessions — same pattern as AgentManager.
Session now keeps only a Map<subscriptionId, unsubscribe> and thin message
handler wrappers. Shared utilities (resolveCheckoutGitDir, toCheckoutError,
READ_ONLY_GIT_ENV) extracted to checkout-git-utils.ts.
Adds @isaacs/ttlcache to avoid repeated gh CLI calls for the same
working directory. Concurrent lookups for the same cwd share a single
in-flight promise. Cache expires after 30s by default.
The file-based daemon-launch.log was added as temporary instrumentation
for debugging Windows startup. Desktop now logs lifecycle events through
electron-log; CLI and server supervisor drop the launch logging entirely.
Loop runs can now specify separate provider/model for worker and verifier
agents (--provider, --model, --verify-provider, --verify-model). The
--archive flag preserves agent conversation history after each iteration
instead of destroying them.
The desktop app uses the paseo:// custom protocol scheme, but the
origin was only passed via PASEO_CORS_ORIGINS when the desktop started
the daemon itself. If the daemon was started by the CLI, the origin
was missing and the Electron renderer's WebSocket connection was
rejected.
Also removes file:// and null from allowed origins — any page loaded
via file:// could connect to the daemon, which is a security gap.
* Add metrics collection and terminal performance tests
* feat: add loop, schedule, and chat commands with slash-namespaced RPC
Introduce three new server-side features with CLI command groups:
- `paseo loop` — iterative agent execution with verify-check/verify-prompt
- `paseo schedule` — recurring tasks on interval or cron cadence
- `paseo chat` — chat rooms for agent-to-agent coordination
All features use new slash-namespaced RPC methods (e.g. `loop/run`,
`schedule/create`, `chat/post`) instead of flat message types, backed
by file-based persistence and wired through the existing WebSocket
session dispatch.
* test: stabilize loop schedule chat rollout
- sherpa-runtime-env: use case-insensitive key lookup when modifying PATH
on plain env objects. On Windows, `{...process.env}` stores PATH as
`Path` but `applySherpaLoaderEnv` used hardcoded `"PATH"`, creating a
duplicate key that could shadow the real system PATH in child processes.
- runtime-toolchain: use `wmic` on Windows instead of Unix-only `ps -o`
to resolve the node executable path from a PID.
- claude-agent: pass `findExecutable("claude")` as
`pathToClaudeCodeExecutable` so the SDK uses the user's installed
binary when available. Update spawn hook to only replace bare
"node"/"bun" with process.execPath, preserving native binary paths.
- desktop/paseo.cmd: use ELECTRON_RUN_AS_NODE with node-entrypoint-runner
for the Windows CLI wrapper.
* Add metrics collection and terminal performance tests
* fix: handle Windows drive-letter paths across the codebase
Windows paths like C:\Users\foo\project were broken in multiple places:
- agent-storage slugified D:\MyProject as D:-MyProject (illegal colon)
- terminal-manager rejected all non-/ paths as relative
- bootstrap parser misparsed drive colons as TCP host:port
- daemon/client connection helpers misclassified Windows paths
- CLI cwd filtering used hardcoded / separators
- checkout-git worktree detection used hardcoded / in path checks
- worktree archive used split("/").pop() instead of path.basename()
All path helpers now normalize separators and handle Windows
drive letters with case-insensitive comparison where needed.
Codex delete operations were displayed as edits with green (added) lines
because the translation pipeline didn't distinguish deletes from edits.
Now detects kind="delete" and *** Delete File directives, producing proper
unified diffs with removed lines and +++ /dev/null headers.