* Extract client SDK package
* Polish SDK client identity defaults
* Build client before dependent CI jobs
* Restore daemon client server export
* Extract protocol and client SDK packages
* Fix provider override schema validation
* Fix app test daemon client imports
* Simplify workspace build targets
* Fix CLI test server build bootstrap
* Run SDK package tests in CI
* Fix rebase package split drift
* Restore lockfile registry metadata
* Update SDK config test for prompt default
* Move terminal stream router test to client package
* Fix rebase drift for protocol imports
* Fix SDK agent capability fixture
* Restore legacy server client exports
* Fix server export compatibility test
* Advertise custom mode icon client capability
* Remove server daemon-client exports
* Format rebased mode control import
* Fix rebase drift for protocol imports
Files added by upstream PRs (#893, #1147, #1154) referenced the pre-split
shared/ paths that this branch moves into @getpaseo/protocol. Redirect
those imports to the protocol package so typecheck stays green after the
rebase.
Surface all enabled providers in the model selector with inline loading
and error states instead of silently hiding non-ready ones. Tap any row
to drill in; loading and error states show in the drill-down body, with
a Retry button on error. Providers with no models render a synthetic
"Default" row so every provider's drill-down has symmetric structure.
Redesign the per-provider settings sheet: search lives in the header,
discovered and custom models share one scrolling list, and the footer
hosts action buttons. Diagnostic and "Add model" each open as their own
sub-sheet, so neither expands inline or shifts the surrounding layout.
Snapshot refresh now keeps cached data visible: setQueryData replaces
the cache atomically and sibling-scope caches are invalidated (not
removed), so refreshing a provider no longer blanks its row. Search
inputs across the app are uncontrolled — onChangeText drives parent
state, and resetKey remounts the field when an explicit reset is needed
— to avoid RN's controlled-input flicker.
Adds a dev-only mock-slow provider whose discovery never resolves, so
the 30s snapshot timeout produces a real error and lets loading and
timeout-error UI be exercised end to end.
Move optimistic user message reconciliation into the stream reducers and keep draft-created agents in a handoff lifecycle until authoritative history lands.
* Add Pi extension-launch plumbing for in-process tree primitives
* Add Rewind controls for agent sessions
* Preserve SDK checkpoint env var so Claude file rewind works
The SDK injects CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true when file checkpointing is enabled. The earlier parent-env stripping from c4782ec71c was too broad and removed that child flag along with real parent-session markers.
Keep stripping parent markers, but preserve the checkpoint flag so the SDK rewind path can run directly. With that boundary fixed, the JSONL file-rewind fallback is no longer needed.
* Update OpenCode history replay tests for rewind state
* Show Rewind actions in an adaptive sheet
* Fix rewind semantics across providers
* Refactor Pi IDs through extension capture
* Restore user message to composer after rewind
* Fix Claude rewind tool result mapping
* Fix Claude rewind session switch roundtrip
* Fix rewind and optimistic message regressions
* Tighten rewind test discipline
* Fix OpenCode optimistic message reconciliation
* Reconcile optimistic user messages by marker
* Reorder user message actions
* Unify provider user message contract
* Add Claude rewind flow Playwright contract
* Add Codex rewind flow Playwright contract
* Add OpenCode rewind flow Playwright contract
* Add Pi rewind flow Playwright contract
* Remove real-provider rewind Playwright contracts
* Gate real-provider rewind specs from CI Playwright
Home screen now leads with action tiles (Add a project, Import session,
Setup providers, Pair device) instead of a single button, giving new
users a clear path in even before they've added a project.
Import session is the onboarding hook: the sheet now works without a
workspace context and lists recent sessions from every provider's native
history (Claude, Codex, OpenCode, Pi, ACP). Picking one imports the
agent and lands the user on its workspace.
To make that work end-to-end, the daemon upserts the imported agent's
cwd as a workspace so the sidebar surfaces it even when the user never
explicitly opened the folder.
Also adds a Home entry to cmd+K and a refresh button to the import
sheet header for explicit re-fetch.
Adds Kiro CLI as an opt-in ACP provider catalog entry, plus a generic ACP extension-notification sink so unknown notifications no longer fail JSON-RPC dispatch.
* Open chat file links in a side pane on cmd-click
* Add hover tooltips to file links showing cmd-click hint
* Collapse file-link open path to a single disposition seam
Replace the parallel "open in side" callback that was threaded
alongside the normal open callback at every layer between the chat
click handler and the workspace router. One callback now carries a
`OpenFileDisposition` ("main" | "side") so the modifier-key decision
lives at the leaf and the routing decision lives at the workspace
screen, with no branching in between.
* Give assistant file links a home
* fix(app): require long-press for sidebar dnd on mobile
* fix(app): right-edge open gesture for explorer sidebar
* fix(app): left-edge open gesture for workspace sidebar
* fix(app): allow gesture children to scroll on web
* fix(app): lock viewport zoom on compact web
* fix(app): don't submit on enter in compact web composer
---------
Co-authored-by: jon <nikuscs@gmail.com>
* Add project rename so duplicates get distinguishable names
Two checkouts of the same remote collapse into a single project record
(same projectId), which left users unable to tell duplicates apart in
the UI. customName lives alongside the derived displayName as an
override that reconciliation never touches, so renames persist across
git-remote changes. The rename UI lives in project settings.
Closes#987 (rename half).
* Fix COMPAT(projectCustomName) version to v0.1.76
* Update useProjects shape test for new projectCustomName key
* Use sendCorrelatedSessionRequest for renameProject
Match the pattern used by checkoutPrMerge and other newer RPCs
instead of hand-rolling the select callback.
* Use project.rename.{request,response} dotted RPC names
Match the convention from docs/rpc-namespacing.md (added in 75a6f8277).
Schemas, type exports, daemon client, session handler, and tests all
move to the namespaced names. No back-compat shim needed since the RPC
hasn't shipped.
The GitHub picker showed "No results found." for any running agent after
the first message was sent. useAgentInputDraft owned a local cwd state
that clear("sent") wiped to "", so the composer read cwd: "" even though
the agent's real cwd was correct on the wire.
Drop cwd ownership from the hook. Composer takes cwd as a prop sourced
by the parent — agentState.cwd for running agents, composerState.workingDir
for drafts, sourceDirectory for setup flows. The hook now persists only
{text, attachments}. Legacy persisted drafts with a cwd field still
hydrate; tagged COMPAT(draft-cwd) for removal after 2026-11-09.
Also moves attachment pills back inside the composer's bordered surface
via a new attachmentSlot prop on MessageInput, and shrinks pill height
from 48 to 32 — the subagents-track tuck previously had attachments
floating between it and the input.
Adds one Playwright spec asserting the draft-create-agent path submits
the workspace cwd.
- Bucket finished subagents as `done` instead of `attention`; drop
"needs attention" segment from the section header — subagents are
transient and don't carry an unread state worth surfacing.
- Gate the subagents selector on the existing pending-archive query so
optimistic archives survive authoritative agent snapshots that wipe
the local `archivedAt`. Last subagent archived now cleanly hides the
whole track via the existing empty-rows guard.
* feat(checkout): merge PR action with real-GitHub e2e test
Adds a `checkoutPrMerge` server RPC backed by `gh pr merge` (squash, merge,
or rebase) and surfaces three new actions in the git-actions menu —
"Squash and merge", "Create a merge commit", "Rebase and merge" — with
squash taking the primary slot when a PR is open and mergeable. Also
renames the existing local merge action to "Merge locally" to disambiguate
it from the new GitHub-side merge.
Adds an end-to-end test that creates a real temporary GitHub repo, opens a
PR via the daemon, polls until mergeable, calls `checkoutPrMerge`, and
verifies the merge landed on `main` — no mocks. The test pre-flights the
`delete_repo` scope so it skips cleanly on tokens that can't tear down,
and fails loud if cleanup ever errors so we don't leak repos.
PR status gains an optional `mergeable` field (`MERGEABLE | CONFLICTING |
UNKNOWN`) populated from `gh pr view --json mergeable`. The schema is
backward compatible — old payloads without the field still parse and
default to `UNKNOWN` via `z.catch`.
* Update PR status snapshot expectations
* refactor: collect git/checkout/PR feature into packages/app/src/git/
Move 21 git/checkout/PR files out of components/, hooks/, stores/, screens/,
utils/ flat-peer slop into a single packages/app/src/git/ module with
shrunk filenames (git-diff-pane.tsx -> diff-pane.tsx, use-git-actions.ts ->
use-actions.ts, etc).
Dedupe: drop the duplicate useGitActionHandlers from diff-pane.tsx — the
shared useGitActions hook is the single source. Drop the
buildGitActionsForPane wrapper. Collapse the merge-PR triple fan-out
(mergePrSquash/Merge/RebaseStatus) into one record keyed by
CheckoutPrMergeMethod. Streamline server-side single-use helpers
(dispatchStash/PullRequestMessage adapters, GitHubPullRequestMergeError
wrapper, legacy worktree test helpers).
Schema-additive only: messages.ts exports CheckoutPrMergeMethod and
PullRequestMergeable types; daemon-client.ts uses the typed alias.
Backward-compatible.
Net delta: 43 files, +219/-1212.
* fix(app): use GitHub icon for all merge-PR menu actions
* test: namespace temp GitHub repos under `paseotmp-` and centralize naming
Old prefixes (`paseo-checkout-ship-`, `paseo-e2e-`, `paseo-checkout-pr-merge-`)
collided with the `paseo-*` namespace of real repos, making bulk cleanup
unsafe. Switch every test that creates real GitHub repos to a single
unmistakable `paseotmp-` prefix, owned by one shared helper per test
boundary so no caller can name a repo outside the namespace.
- New `packages/server/src/server/test-utils/temp-github-repo.ts` exports
`TEMP_GITHUB_REPO_PREFIX` + `createTempGithubRepoName(category)`; both
server e2e tests (checkout-ship, checkout-pr-merge) consume it.
- `packages/app/e2e/helpers/github-fixtures.ts` locks the prefix internally
and now requires callers to pass a `category` instead of an arbitrary
`prefix`. Updated pr-pane and composer-attachments specs accordingly.
* Surface paseo subagents in a collapsible section above the composer
Subagents no longer auto-open as tabs. Each parent agent's pane shows
a compact tab attached to the top of the composer, listing its direct
children with name and status; clicking a row opens or focuses the
subagent's tab. The section is provider-neutral so Claude Code Task
and Codex subagent sources can feed the same UI later. Reconciliation
now separates pruning (`activeAgentIds`) from auto-opening
(`autoOpenAgentIds`) so manually opened subagent tabs survive.
* Tighten subagents composer code from review
Collapse duplicate helper in workspace reconciliation, swap a hand-rolled
shallow comparator for fast-deep-equal (matching repo convention), drop
an unused React default import and a single-use formatCount helper, and
extract a shared PaneOverrides type in the agent-panel test.
* Fix oxlint failures in subagents code
Switch type aliases to interfaces, hoist inline JSX styles/handlers in
SubagentsSection to satisfy react-perf rules, and lift the test
makeAgent helpers onto AGENT_DEFAULTS so their cyclomatic complexity
drops below the configured cap.
* Add missing unistyles mock exports after rebase
After rebasing onto main, SubagentsSection now imports both
useUnistyles and (transitively, via WorkspaceTabIcon) withUnistyles.
Two test mocks needed updating to expose them.
Test drift fix: the implementation contract is correct; the local
react-native-unistyles mocks were stale.
* Tuck subagents tab under the composer
Subagents tab now grows visually from beneath the composer's rounded
top edge: the surface overlaps the composer by the corner radius so
the rounded curve nests into the tab. Composer's top padding is
removed so the tab sits flush against the input box with no
click-dead gutter. The tab is full-width in both states, the header
label drops the medium weight to follow design-system §3 (content
text inside a surface is normal), and hover covers the entire
visible tab when collapsed but stays scoped to the header row when
expanded.
* Decouple subagent close-tab from archive
Closing a subagent's tab no longer archives the agent — the tab is
removed from the per-client layout, the agent stays in the parent's
track until explicitly archived. Track rows gain an archive button (X)
to make the lifecycle gesture explicit. AgentManager.archiveAgent now
cascades to children carrying the paseo.parent-agent-id label so
subagent fleets don't outlive their orchestrator.
Trade-offs documented in docs/agent-lifecycle.md, including the known
limitation that handoff agents launched via the same MCP path get
cascade-archived alongside true subagents until a richer relation
model lands.
* Stabilize subagent archive button hover
Apply the sidebar workspace row pattern to fix two issues with the
new archive button: the row no longer drops hover when the pointer
moves onto the button, and the button slot reserves layout space
when invisible so adding/removing it doesn't shift the row.
The wrapper View tracks hover via onPointerEnter/onPointerLeave
(workspace rows do the same), and the slot stays mounted with
opacity 0 + pointerEvents="none" when the button is hidden. Also
swap the X icon for the Archive icon to match the gesture's intent.
* Match design language for subagent archive button
Hover state now changes the icon color from foregroundMuted to
foreground, matching the project-row trailing-action pattern in
sidebar-workspace-list.tsx. Drops the surface3 background hover
that didn't match the design language.
Adds a tooltip ("Archive subagent") on the trigger using the
canonical Tooltip + TooltipTrigger asChild pattern. Disabled when
the slot is hidden so it never appears unbidden.
* Slot subagents into one module + harden cascade
Reshape the subagents-in-composer feature so it lives behind one
module and integrates at a few explicit sites instead of being
smeared across generic files.
- Extract `PARENT_AGENT_ID_LABEL` to `packages/server/src/shared/agent-labels.ts`. Sweeps 16 literal sites across server, client, CLI, and tests.
- `packages/app/src/subagents/` is now a deep module: `index.ts` is the only React entry (`SubagentsSection`, `useArchiveSubagent`, `useSubagentsForParent`, `selectSubagentsForParent`, `resolveCloseAgentTabPolicy`, `shouldAutoOpenAgentTab`); `policies.ts` is a second designed entry for non-RN data consumers. Internal files renamed (`section.tsx`, `select.ts`, `close-tab-policy.ts`, `auto-open-tab-policy.ts`, `use-archive-subagent.ts`).
- Move the 22-line subagent archive flow out of `agent-panel.tsx` into `useArchiveSubagent`. Agent-panel becomes a one-line consumer.
- Move the close-tab subagent branch out of `workspace-screen.tsx` into `resolveCloseAgentTabPolicy` (discriminated union).
- Move the auto-open exclusion rule out of `workspace-agent-visibility.ts` into `shouldAutoOpenAgentTab`.
- Theme chevrons in `section.tsx` via `withUnistyles`; drop the forbidden `useUnistyles()` call from the hot path.
Server-side cascade hardening:
- Single write path: extract `markRecordArchived` and route both `archiveAgent` and the off-memory cascade branch through it. Off-memory cascaded children now notify subscribers (previously silent).
- Cascade no longer swallows child failures with `try/catch warn`. Failures propagate.
- Cascade test extended from 1 weak assertion to 5 contract tests: full archive shape (archivedAt, normalized lastStatus, requiresAttention=false), running child runtime stop, off-memory branch, subscriber notification for in-memory and off-memory children, partial-failure surfacing.
Net diff vs the start of this PR: -1037 / +444 lines. The feature now slots into the repo at four explicit sites: `agent-panel.tsx` (3 imports + a hook + the section), `workspace-screen.tsx` (one policy import), `workspace-agent-visibility.ts` (one predicate import), and `agent-manager.ts` (the cascade plus the shared label constant).
Directly opening a workspace URL created a different React Navigation state shape than entering from /. Expo Router parsed the URL as a workspace layout route with a nested index child whose route.path and params pointed at the original workspace. Later sidebar navigation used router.dismissTo for another workspace. React Navigation's POP_TO reused the existing workspace route by name and updated the parent params, but preserved the nested child state. The result was a hybrid state: parent workspace params for B, child index path for A. Expo Router serialized the focused child path, so the address bar stayed on A and sidebar clicks looked like no-ops.
The fix is to stop using workspace/[workspaceId]/_layout.tsx as the actual screen. That layout did not render a Slot, so it only added an extra navigation level for Expo Router to preserve. Move the workspace UI, bootstrap boundary, open-intent consumption, and retained WorkspaceDeck into the leaf index route, making /h/:serverId/workspace/:workspaceId one real rendered route.
Keep workspace switches on navigateToWorkspace/router.dismissTo. This is not another replace workaround: replace previously created duplicate mounted workspace shells. The route-shape fix removes the stale nested child state that made dismissTo fail after cold deep links.
Also route archive/new-agent and notification flows through the centralized workspace navigation path so they do not bypass the same route contract.
Regression coverage: cold-load workspace A, click workspace B in the sidebar, and assert the URL changes to B. Verified with workspace-navigation-regression.spec.ts, settings-navigation.spec.ts, npm run typecheck, and npm run lint.
Centralize workspace navigation behind a small route-derived API so the workspace shell reads active selection from the current Expo Router route instead of a separately synchronized store.
Remove the stale layout-driven active-workspace write that regressed web after 02e5b564.
Replace 21 raw page.locator/getByTestId/getByText calls in the spec body
with named DSL operations in helpers/settings.ts. Each test body now reads
as prose: open settings, navigate to section, expect content.
New helpers: openCompactSettings, expectCompactSettingsList,
expectSettingsSidebarVisible/Hidden/Sections, goBackInSettings,
expectSettingsBackButton, clickSettingsBackToWorkspace,
verifyLegacyHostSettingsRedirect, openCompactSettingsHost,
expectHostSettingsUrl, expectAddHostMethodOptions, fillDirectHostUri,
expectDirectHostFormValues, expectDirectHostSslEnabled,
expectDirectHostUriValue/Hidden, expectDiagnosticsContent,
expectAboutContent, expectGeneralContent.
Export requireServerId from sidebar.ts so helpers encapsulate serverId
logic — spec has no direct env reads.
Also fix pre-existing typecheck error in use-keyboard-shortcuts.ts:
cast action.route to the expo-router Href type at call sites.
Moves all preference-resolution logic into a pure `resolveAgentForm(state, action)` reducer
with a discriminated-union action type. The hook becomes a thin React container: it holds state
via `useReducer`, dispatches actions, and owns the hydration-ref timing logic.
Removes the `__private__` export and the vi.mock-heavy live test in favour of direct pure-function
unit tests (53 tests, zero vi.mock of internal modules).