Add oxlint-tsgolint and configure typescript/no-unnecessary-type-assertion
to flag redundant `!` and `as Foo` casts. Type-aware mode is left off by
default to keep `npm run lint` fast; the rule sits configured for when we
turn type-aware on intentionally. Auto-fix removed ~283 redundant casts;
two manual touch-ups: a real tsgolint false positive in split-container.tsx
and a stale ChildProcess import after a double-cast collapsed.
Subagent task_notification system messages arrive without
parent_tool_use_id but with tool_use_id pointing at the parent's Task
call, so they slip past the sidechain router and render as top-level
"Task Notification" rows in the parent's timeline. Skip them when the
referenced tool_use is a Task call; parent-level background bash
notifications still flow through.
The Fastfile called wait_for_build_processing_to_be_complete, which
isn't a built-in fastlane action and isn't published as a plugin
anywhere on rubygems. The submit_review lane has therefore failed
on every release that reached it (v0.1.65-beta.1, v0.1.66, v0.1.67).
Replace it with a direct Spaceship::ConnectAPI poll: fetch the build
matching the latest TestFlight build number, wait until its
processing_state is VALID, then hand off to deliver. 30-minute cap
with a clear failure message on INVALID/FAILED or timeout. Spaceship
ships with fastlane so no plugin or extra gem is required.
Lands in the next release; v0.1.67 needs a manual review submission
from App Store Connect.
The async settings gate added in f0d96f8e called
shouldStartDaemon() inside Promise.resolve(...).then(...) with no
catch. If loadDesktopSettings() rejects (corrupted JSON, FS error),
the daemon never starts, no error renders on the splash, and the
retry button repeats the same silent path.
Add a recordError method to DaemonStartService and an onGateError
callback to startDaemonIfGateAllows / startHostRuntimeBootstrap.
_layout.tsx wires it through so a gate rejection populates
lastError, the splash shows the failure, and retry has signal.
Test #1 was asserting agent-reconnecting-toast on a workspace with no
agents — but commit 1daa1314 moved the reconnect indicator from the
workspace banner into the per-agent panel toast. Create an idle agent
and navigate to it so the agent panel mounts before dropping the
daemon gate.
Test #2 regex still pinned the old "Connecting to localhost" copy;
commit c534c857 tightened it to just "Connecting" with the hostname in
the description. Match the new title plus the existing offline copies.
f0d96f8e added packages/desktop/src/daemon/daemon-manager.test.ts,
which calls vi.mock("@getpaseo/server", ...). Vite's module
resolution still needs to find the package's main entry, so
desktop CI must build the server (and its dependency chain) before
running the tests. Mirrors the prebuild steps used by typecheck,
server-tests, and playwright.
7c85341a moved first-agent branch auto-naming out of the worktree
service (sync slugify) into the workflow layer (async LLM call).
The MCP test stubs createPaseoWorktree, bypassing the workflow,
so the placeholder mnemonic branch is never renamed at this layer.
Replace the obsolete sync-slugify outcome assertion with a
placeholder-presence check; the auto-name logic itself is covered
in worktree-branch-name-generator.test.ts.
Plumbs persistSession through the structured response pipeline so
metadata, branch name, commit message, and PR text generators no
longer leave provider-side artifacts behind:
- Codex: passes ephemeral: true on thread/start (no rollout file)
- OpenCode: deletes the session via session.delete on close
- Claude: forwards persistSession: false to the SDK query
Adds a real-credentials e2e test per provider that asserts the
generated title is applied AND no persistence artifact is produced
(rollout for Codex, project JSONL for Claude, leftover session for
OpenCode).
Server: drop the synchronous branch-name generator dep, use the
worktree slug as the initial branch, and move first-agent branch
auto-naming to an async post-creation step backed by a structured
LLM call. Guards against renaming when the placeholder branch has
already been changed.
App: make workspace and worktree archive optimistic, rolling back
the local hide if the daemon call fails, and suppress incoming
workspace upserts while an archive is pending.
Linux EAS runners don't ship Ruby/Bundler, so `bundle install`
fails with "bundle: command not found". macOS workflow runners
have Ruby 3.2 + Fastlane preinstalled, so switching this job to
runs_on: macos-medium gets us there without a manual install step.
The eas/checkout step already cd's into packages/app (where eas.json
lives), so the explicit `working_directory: ./packages/app` on the
Fastlane steps resolved to /home/expo/workingdir/build/packages/app/packages/app
and failed every release with ENOENT.
The workspace gate (loading/unreachable/missing) used surfaceWorkspace,
which clashed with the panels' surface0. Switch the gate shell to
surface0 so it matches the agent panel.
Also remove three hot-path useUnistyles() calls from _layout.tsx that
were re-rendering the whole app on every theme/breakpoint/insets change:
RootLayout and AppContainer move to StyleSheet.create, and the desktop
window-controls effect lifts into a small leaf component.
The workspace reconnect banner ignored safe-area insets on Android,
overlapping the system status bar. The agent panel already shows a
"Reconnecting..." toast on disconnect, so consolidate on that and drop
the banner entirely. Add persistent toast support (durationMs: null)
and dismiss the toast when the connection returns to online instead of
auto-dismissing after 2.2s.
The 'Connecting…' placeholder was removed from welcome-screen.tsx in
02adadbb (Always show welcome copy on welcome screen) but the e2e
helper still asserted it visible, breaking startup-loading.spec.ts.
f58cfe9 (Open script service URLs in-app or external) added
serviceUrlBehavior to AppSettings and a new useDropdownMenuClose
export from dropdown-menu. The unit tests were not updated:
- use-settings.test.ts: expected settings objects missed the new
serviceUrlBehavior field.
- workspace-scripts-button.test.tsx: dropdown-menu mock missed
useDropdownMenuClose, breaking HostLinkRow render.
fa20f313 wired AsyncStorage persistence into
activateNavigationWorkspaceSelection. The mock here returned undefined
from setItem, so the .catch() in persistLastWorkspaceRouteSelection
threw. Make get/set/removeItem return resolved promises like real
AsyncStorage.
Adds a Service URLs setting (ask / in-Paseo / external browser) that
controls where URLs from running scripts open on desktop. First click
prompts with a "don't ask again" checkbox; in-Paseo opens a workspace
browser tab. Closing a browser tab now clears its session partition.
Drop the "Connecting…" placeholder shown while reconnecting to saved
hosts. The screen now keeps the welcome copy and add-host actions
visible until the auto-redirect to an online host fires.
Spawn the shared OpenCode server in its own POSIX process group and terminate provider child process trees with graceful timeout escalation before forcing SIGKILL. Reuse the same helper for Codex app-server cleanup so process-tree behavior is covered consistently across providers.
Add cross-platform unit coverage for macOS/Linux process groups, Windows taskkill cleanup, and timeout escalation. Add the process-tree test to the Windows-critical CI allowlist; Linux full server tests already include it.
Refs #227
Update CHANGELOG.md in place per beta policy and add a compat note on the
`sub_agent.actions` field flagging it as drop-on-cleanup once clients
<= 0.1.65-beta.3 are out of the field.
With capability negotiation in place, the consumer-side tolerance pattern is unnecessary. Drop the dead readDeclaredClientCapabilities helper, inline the parsing in ClientCapabilities.fromHello, delete a tautological assertion in the relay reconnect test, and document the compatibility rules in architecture.md.
- Refresh icon: use single-arrow RotateCw instead of two-arrow RefreshCw
- URL bar selects all text on focus
- Cmd+R now reloads the active browser pane (tracked via IPC) when
the URL bar has focus, instead of reloading the Electron window
- Forward Paseo shortcut keys (b, e, w, t, k, /, \, comma, digits,
enter, arrows) from the embedded webview to the main window so
sidebar/tab/etc. shortcuts still fire when focus is inside the
loaded page
Previously, dictation was cancelled the moment the user switched away
from the active agent tab, losing any in-progress recording. Drop the
auto-stop-on-blur behavior so users can start dictating, switch tabs,
and come back to finalize.
* feat: add browser pane element picker
Port the browser pane from PR #198 and route picked elements through workspace attachments so the context follows every agent composer in a workspace.
Co-authored-by: Jason Kneen <jason.kneen@bouncingfish.com>
* docs: document electron platform overlays
* fix: harden browser pane navigation
* fix: polish browser tab interactions
* fix: route browser pane focus shortcuts
---------
Co-authored-by: Jason Kneen <jason.kneen@bouncingfish.com>
Claude, Codex, OpenCode, and ACP each reimplemented the same `run()`
orchestration: pre-start event buffering, turn-id filtering, terminal
event settling, timeline collection, and final-text reduction. Hoist
the loop into `provider-runner.ts` with a small adapter (startTurn,
subscribe, getSessionId) and pluggable final-text reducer.
Net -252 lines across the four providers. Also fixes a latent bug
where a `turn_failed` emitted before startTurn returned was buffered
and silently swallowed instead of rejecting the run.
Centralize pending-run, waiter, and finalized-turn bookkeeping in a
new ForegroundRunState module so streamAgent, replaceAgentRun,
cancelAgentRun, reload/close, and stream dispatch share one
coordinator instead of hand-rolling the lifecycle in five places.
Move workspace descriptor map building, fetch_workspaces filtering /
sorting / cursor paging, and archiving overlay state out of the Session
god class into a dedicated WorkspaceDirectory module.
Session keeps thin delegation methods so existing tests and callers see
the same surface; cursor errors are still mapped to SessionRequestError
at the session boundary.
The MCP get_agent_activity tool sliced the raw timeline before passing
to the curator, so `limit: 5` could land mid-message and return a
trailing fragment of streamed assistant text. Now goes through the
existing projection pipeline so the limit counts whole projected
entries — assistant deltas merged, tool calls collapsed by callId,
reasoning chunks merged.
Same bug fixed in the wait_for_agent timeout's "Recent activity"
preview, which used the same raw slice.
Consolidates the merge logic: activity-curator no longer ships its own
collapseTimeline, it delegates to projectTimelineRows. One source of
truth for "what is a complete message".
Drops the meaningless `provider` parameter that was threaded through
the projection module — merge logic never branched on it. The wire
schema keeps `provider` per entry; session stamps it once at the emit
boundary. Adds `reasoning_merge` to the projection kind enum (additive
wire change).
Three sibling modules (session-stream-lifecycle, session-timeline-seq-gate,
and the bootstrap-tail policy from session-timeline-bootstrap-policy) only
served the stream reducer. Inline them as private helpers in
session-stream-reducers.ts; keep deriveInitialTimelineRequest exported for
the hook caller. Removes shallow seams without changing behavior.
Move slot map, listener fanout, binary input encoding, and stream-frame
decoding out of daemon-client.ts into TerminalStreamRouter so the
4.5k-line client no longer owns that bookkeeping. Public DaemonClient
API and on-the-wire behavior unchanged.
Two breakages surfaced when CI started running desktop tests:
- Root vitest.config.ts replaced default exclude with `["**/.claude/**"]`,
losing `**/node_modules/**`. Desktop has no local vitest config, so
vitest scanned into `node_modules/zod/src/v4/classic/tests/*.test.ts`
and failed on missing peers (`@web-std/file`, `recheck`). Spread
`configDefaults.exclude` so node_modules stays excluded.
- window-manager dark titlebar color changed from #18181c to #181B1A in
963c7926 (white-flash fix) but the test was not updated. Update the
expected color to match the new intended behavior.
Skills installed via Paseo desktop never updated after first run, so
users were stuck on already-fixed skill content. On every launch, if
the paseo base skill is present in `~/.agents/skills/`, sync each
bundled skill (incl. `references/`) into agents, claude (via symlink/
junction), and codex — overwriting only files whose content differs.
Files on disk that are not in the bundle are left alone; deprecation
goes through tombstones (e.g. `paseo-orchestrate` redirects to
`paseo-epic`).
Also refreshes the bundled skill set: drops `paseo-chat`, adds
`paseo-advisor` and `paseo-epic`, and turns `paseo-orchestrate` into
a tombstone redirecting to `paseo-epic`.
Adds a desktop test job to CI on Ubuntu and Windows so the
junction/symlink path is exercised on both platforms.
Replace base64-in-JSON file transfers with binary WebSocket frames to
unblock the JS thread when assistant markdown messages contain images.
Receive cost (JSON.parse over multi-MB base64) and persist cost (base64
re-encode to disk) both removed.
- Split daemon-client `exploreFileSystem` into `listDirectory` + `readFile`;
`readFile` returns `{ bytes, mime, size, modifiedAt }`. Encoding stays
private to the seam.
- New `packages/server/src/shared/binary-frames/` module hosts terminal
frames (moved from `terminal-stream-protocol.ts`) and new file-transfer
opcodes (`FileBegin` / `FileChunk` / `FileEnd`) with request-id correlation.
- Add optional `acceptBinary` to `FileExplorerRequestSchema`. New daemons
emit binary frames when set; legacy JSON path remains for old clients.
Compat fallback lives only inside `readFile`.
- New `persistAttachmentFromBytes` writes bytes directly: native via
`expo-file-system` `File.write(Uint8Array)`, web via Blob -> IndexedDB,
desktop via new `write_attachment_bytes` IPC. `persistAttachmentFromBase64`
removed.
Map OpenCode `task` tool events into the existing sub_agent timeline shape,
enabling live activity tracking for OpenCode subagent sessions.
- Add `childSessionId` (optional) to sub_agent schema
- Make `actions` optional with default [] for backward compatibility
- Parse task tool input/output into sub_agent detail (tool-call-detail-parser)
- Track child session linkage and buffer/flush child tool parts
- Render sub_agent actions and session IDs in the app component
- Add glob tool detail mapping
- Include backward-compat schema test
Co-authored-by: Ryan Swift <ryan@mlh.io>
Squash commits keep the original commit author, but `gh pr view N --json author` returns the PR opener — using that field silently mis-credits work to the maintainer who landed the PR (and the skip-@boudra rule then drops the credit). Document the `--json commits | unique logins` query as the source of truth for attribution.
Replace top-level `attachments` and `nameContext` on `create_paseo_worktree_request` with `firstAgentContext: { prompt?, attachments? }`, the single signal driving auto-rename. Drop the `worktreeSlug || …` short-circuits so a random placeholder slug no longer blocks renaming, drop the attachment-as-PR fallback in favor of explicit `githubPrNumber`, and reshape the MCP `create_worktree` tool around a `target` discriminated union (`branch-off` / `checkout-branch` / `checkout-pr`) with concise field descriptions so callers stop misreading `prompt` as a name field.
The gutter was showing the message-circle icon on every reviewable line on
native and on compact form factors, displacing the line number whenever
review draft mode was active. Restore the default by computing showAction
purely from interaction state (hover/press), comment presence, and editor
visibility — drop the showPersistentAction flag and the isNative bias.
Tap on the gutter directly opens the inline review editor on every
platform. On native, long-pressing a line of code (350ms) also opens the
editor via a new LongPressableLine wrapper, preserving web text selection.
Moves terminal RPC, binary streaming, slot allocation, and exit-subscription
bookkeeping out of the 9.7K-line Session class and into a dedicated controller
with a small facade (dispatch, handleBinaryFrame, killTerminalForClose,
killTerminalsUnderPath, dispose, getMetrics). Session loses 7 fields and 22
methods (-651 LOC) and the cluster's invariants now live next to their state.
Replace the duplicated 3-stage Zod pipeline (pass1 -> pass2 envelope
union -> pass2 discriminated union) in each provider mapper with a plain
resolveToolKind(name) function over readonly name sets. Opencode's
toolKind was unused; Claude/Codex used it only as a switch discriminator.
Five set_agent_*_response and update_agent_response payloads were
pixel-identical. Share one named schema so the concept is explicit and
new agent-action responses don't copy-paste the same four fields.
Wire format and inferred types are unchanged.
Only /api/health remains public for liveness probes. /api/status
exposes hostname, version, and server ID which shouldn't be
accessible without authentication.