Relied on Claude echoing AUTONOMOUS_WAKE_<token> verbatim, which is model-compliance, not the wake mechanism. The structural turn_started + turn_completed pair on the post-foreground subscribe stream is uniquely emitted by startAutonomousTurn() and proves the autonomous live turn fired.
* 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).
Pressable's click handler calls stopPropagation, so the bubble-phase
preventDefault on the wrapping <a> never fired and the Electron view
navigated to the href in addition to the existing helper opening it
in the OS browser. Move preventDefault to onClickCapture (and
onAuxClickCapture for middle-click) so it always runs first. The
existing Pressable.onPress helper remains the only path that actually
opens links.
Replaces the hardcoded Copy/Paste/Select All menu with one driven by
ContextMenuParams: link items (Open in Browser, Copy Link Address),
image items (Copy Image, Save Image As), spellcheck suggestions, and
proper Cut/Copy/Paste in editable inputs. Both the main window and
the browser webview menus go through the shared helper.
MarkdownLink (web branch) now wraps its Pressable+Text in a real
<a href> with `display: contents` so Electron exposes linkURL on
right-click. Native branch is untouched.
create_agent previously threw on cross-provider inheritance unless an explicit
mode was passed. In practice, sub-agents launched without a mode should run
unattended — keying permissions per sub-agent rarely makes sense. When the
caller is in an unattended mode (Claude bypassPermissions, Codex/OpenCode
full-access, Copilot autopilot) and no mode is requested, default the child
to the target provider's own unattended mode. Non-unattended callers still
require an explicit cross-provider mode, and explicit modes always win.
When a Codex-backed agent is archived from Paseo, call Codex's
thread/archive API so the session is also marked archived in the
Codex app. This keeps both UIs in sync.
- Add optional archiveNativeSession() to AgentClient interface
- Implement for Codex provider via thread/archive JSON-RPC call
- Call best-effort in both archiveAgent() and archiveSnapshot() paths
- Failures are logged as warnings and do not block the archive flow
Closes#822
- Make `docs/` the explicit source of truth: "the docs"/"check the docs" mean this directory, not the web.
- List all 16 docs in CLAUDE.md (was 8); group by purpose so unistyles, glossary, data-model, providers, etc. are discoverable.
- Delete the old design.md (designing-features prose) and rename design-system.md → design.md so design tokens own the name.
@gorhom/portal renders children at the host's location in the tree, so
sheets portaled to the root host lost QueryClient context (crash:
"No QueryClient set" when opening provider diagnostic sheet). Reorder
RootProviders and document the invariant: PortalProvider stays innermost.
Single workflow now: update lockfile + hash, build, then commit the fix on
main only if the build passed. Eliminates the structural first-failure where
Nix Build always raced the autofixer and lost.
submit_for_review failed with "appStoreVersions ... is not in valid
state - missing required attribute 'whatsNew'". Apple requires a
non-empty "What's New" string on every new version. With skip_metadata,
deliver never sets it, so we populate it ourselves via spaceship for
every existing localization on the edit version.
submit_review reached the deliver step but failed with "could not find
an editable version for 'IOS'". With skip_metadata + skip_screenshots,
deliver does not auto-create the "Prepare for Submission" entry on App
Store Connect, so submitting fails when the marketing version has not
been created yet.
Use Spaceship's ensure_version! to create or reuse the editable App
Store version for the build's marketing version, and pass app_version
to deliver so it targets the right entry.
latest_testflight_build_number returns the build number (e.g. "2"), not
the marketing version. Previous fix used the version: kwarg which maps
to filter[preReleaseVersion.version] — that's the marketing version
("0.1.70") and never matched the integer build number, so the query
returned no builds and the lane spun on "processing state: unknown"
until timeout.
Apple's API names are confusing: filter[version] is the build number,
filter[preReleaseVersion.version] is the marketing version. The
build_number: kwarg in spaceship maps to filter[version], which is
what we want.
The submit_review lane was crashing with "unknown keyword: :filter"
because Spaceship::ConnectAPI::Build.all doesn't accept a filter hash;
it takes specific kwargs (app_id, version, build_number, etc.) and
constructs the JSON:API filter internally.
Also drop includes: "preReleaseVersion" — it's part of ESSENTIAL_INCLUDES
by default.
Add resubmit-ios-review.yml workflow so we can re-run only the
fastlane submit_review lane (no rebuild) against an existing
TestFlight build via workflow_dispatch.
`release:prepare` runs `npm install` which churns optional-dep `dev`
markers in package-lock.json. That dirties the tree before the
`npm version` step in `version:all:*`, and the pre-commit format hook
rejects a lockfile-only commit because oxfmt internally skips
package-lock.json. Document the run-format/lint/typecheck-first
guardrail in docs/release.md.
isAvailable() honored runtimeSettings.command.mode === "replace" but
resolveClaudeBinary() only looked for `claude` on PATH. Users with a
custom claude wrapper and no `claude` itself on PATH passed the
availability check and then failed at session creation with "Claude
binary not found".
resolveClaudeBinary now mirrors isAvailable: if the runtime command is
in replace mode, look up the override binary first.
- spawn.launch-regression: insert `--` between `node -e <body>` and the
user JSON args. Without it Node treats `--config` as a CLI option and
exits with code 9 ("bad option"). The `--` stops Node's flag parsing
so userArgs land in process.argv intact.
- claude-agent.integration + claude-sdk-behavior: inject
spawnClaudeCodeProcess that routes the SDK's launch through our
spawnProcess (shell: false). On Windows the SDK's default spawn fails
with EINVAL when pathToClaudeCodeExecutable resolves to a `.cmd` shim
(CVE-2024-27980). This mirrors what claude-agent.ts does in production.
- 3 cleanup sites (event-stream, integration, sdk-behavior): swallow
EBUSY/ENOTEMPTY/EPERM around rmSync. The maxRetries window doesn't
reliably win the Windows cwd-lock race after Claude exits; the OS will
reap the tmpdir.
Add three end-to-end tests in spawn.launch-regression.test.ts that exercise
findExecutable -> spawnProcess for each Windows-supported extension. Each
test creates a uniquely-named binary in a tempdir, prepends it to PATH,
verifies findExecutable returns the fixture path, then launches via
spawnProcess and asserts an exact JSON-arg roundtrip plus stdout/exit code.
The previous "does not detect a PowerShell shim from PATH" test is removed:
its expectation was satisfied via PATH-prepend (not isolation), so global
claude.CMD on CI made it return non-null. The supported-extension contract
matches which's hardcoded fallback (.EXE;.CMD;.BAT;.COM); .ps1 is not
supported by spawnProcess and not surfaced by which without explicit PATHEXT.
Also add maxRetries: 5 / retryDelay: 100 to rmSync cleanups in three Claude
integration tests to absorb the brief Windows cwd lock after Claude exits.
Installing claude-code globally on the Windows server-tests runner
exposed 12 pre-existing Windows-specific test failures that were
silently skipped before (rmdir EBUSY during cleanup, .CMD spawn
EINVAL via the SDK, plus the launch-regression test that asserts
claude is *not* on PATH).
The Linux e2e tests (model-catalog, live-preferences, models) are
the only ones that needed the binary to flip isAvailable() true;
none of those run on Windows. Skip the install on Windows so those
Windows compatibility gaps stay surfaced separately and don't block
the SDK upgrade.
Two regressions from the previous CI install:
- Installing opencode-ai globally in server-ci/server-tests caused
opencode-agent.test.ts (gated on \`hasOpenCode\`) to start running
for the first time and surface real but unrelated test breakage.
Trim the install to claude-code, which is all that's needed for the
Claude e2e tests that motivated the change.
- claude-agent.integration.test.ts's supportedModels test calls the
SDK's \`query()\` directly (bypassing ClaudeAgentClient) and didn't
pass \`pathToClaudeCodeExecutable\`. The 0.2.71 SDK had a bundled
cli.js fallback; 0.2.133 doesn't, so on Ubuntu CI it tries the
per-platform binary path (\`...claude-agent-sdk-linux-x64-musl/claude\`)
which isn't installed via the \`claude-code\` global. Resolve the
binary explicitly in the test and pass it as the SDK option.
server-tests in ci.yml and Server CI both need claude on PATH for the
e2e tests that probe model catalogs and live preferences. cli-tests
already installs the trio (claude-code, codex, opencode-ai); server
tests now match.
The session previously called resolveClaudeBinary() directly in
buildOptions(), with no seam for tests. CI ran without claude on PATH,
so every test that exercised buildOptions hit the production throw.
Apply the ports-and-adapters pattern at the client boundary (the same
place tests already configure queryFactory):
- ClaudeAgentClient takes optional resolveBinary, defaults to
resolveClaudeBinary which throws "Claude binary not found..." when
the executable is missing — preserving production fail-fast.
- Client passes resolveBinary through to ClaudeAgentSession options.
- buildOptions calls this.resolveBinary() instead of importing the
free function directly.
- Tests construct the public client with a stub resolver. No
__claudeAgentInternals export, no reaching into the session.
User-facing fail-fast still lives where it always did — AgentManager
gates start/resume/draft flows on client.isAvailable(). The throw in
resolveClaudeBinary is defense-in-depth that mirrors Codex.
resolveClaudeBinary threw eagerly inside buildOptions, breaking unit tests
that mock the SDK at the module level (env, redesign, interrupt-restart-
regression suites). The user-facing fail-fast already lives in isAvailable();
in buildOptions, fall back to omitting pathToClaudeCodeExecutable so the SDK
surfaces its own error if the binary truly is missing.
Bumps @anthropic-ai/claude-agent-sdk from ^0.2.71 to ^0.2.133. The new SDK
hardens handleControlRequest against post-close transport writes (v0.2.94+),
which fixes the unhandledRejection that crashed the daemon when the SDK fired
a fire-and-forget control_request after we'd torn down the transport.
The SDK now ships per-platform Claude Code binaries via optionalDependencies
(~210 MB each). Paseo requires user-installed `claude` on PATH instead — same
posture as Codex/OpenCode:
- after-pack now prunes @anthropic-ai/claude-agent-sdk-* from the Electron
bundle, matching the existing per-platform pruning for onnxruntime, sharp,
and node-pty.
- claude-agent.ts gains resolveClaudeBinary() (mirrors resolveCodexBinary):
throws with install instructions when `claude` is missing, instead of
silently falling back to the bundled binary.
- isAvailable() now checks isCommandAvailable("claude") instead of always
returning true.
Adds a pre-start preferences step (onboards users with no
orchestration-preferences.json), a capture-intent step that runs
even under --autopilot, an immutable Requirements block in the plan
that includes acceptance criteria, and a final lightweight check
that audits every requirement before deliver. Drops --no-grill.
* Run server tests on Linux+Windows matrix
Replace the hand-curated Windows server test allow-list with a full-suite matrix run.\nBoth Ubuntu and Windows now run the same server test command with shared setup and secrets.
* Add isPlatform helper and gate Windows-hostile server tests
- Add a shared server test isPlatform helper.
- Migrate existing Windows-gated spawn, worktree, executable, and worktree-core tests to the helper.
- Gate Windows-hostile symlink, macOS path-normalization, and POSIX shell setup tests with skipIf.
* Replace POSIX shell calls in test fixtures with Node primitives
- Replaced test fixture mkdir/echo shell setup with fs mkdirSync/writeFileSync calls in touched server tests.
- Replaced git shell strings with execFileSync/spawnSync argv calls across checkout, worktree, MCP, script, and workspace git fixtures.
- Gated the directory suggestion symlink escape tests on Windows because those fixtures require POSIX symlink behavior.
* Fix Windows server-test failures and split POSIX-only suites into sibling files
- Fix Windows path/cwd assertions in terminal, session/workspace, git-service, checkout-git, MCP, logger, spawn, and registry bootstrap tests.\n- Keep terminal tests runnable on Windows by canonicalizing temp cwd fixtures and ensuring a Windows shell fallback.\n- Gate POSIX-only shell, signal, Unix socket, and git-worktree reuse fixtures that need dedicated Windows coverage later.
* Move POSIX-only test blocks into sibling .posix.test.ts files
- terminal.test.ts: moved PTY/bash interaction blocks into terminal.posix.test.ts.
- worktree.test.ts: moved git-worktree and teardown shell blocks into worktree.posix.test.ts.
- worktree-bootstrap.test.ts: moved setup shell and terminal-backed service blocks into worktree-bootstrap.posix.test.ts.
- worktree-core.test.ts: moved the POSIX-only worktree-core suite into worktree-core.posix.test.ts and removed the empty original.
- provider-availability.test.ts and file-explorer/service.test.ts: moved POSIX PATH/symlink blocks into sibling suites.
* Fix Windows 8.3 short-name, EBUSY cleanup, and node-pty shell-path failures in server tests
- Normalize temp home directories in directory suggestion assertions to avoid Windows short-name mismatches.
- Use Windows-valid terminal shells/cwds in terminal fixtures and wait for terminal-manager PTYs before cleanup.
- Replace hardcoded POSIX paths in workspace-git/MCP/loop fixtures with platform-resolved paths or command files.
- Gate the two explicitly Linux-only workspace-git watcher tests.
* Replace hardcoded POSIX paths with portable Node path constructions in server tests
- checkout-git.test.ts and worktree-session.test.ts: compare Windows temp paths with realpathSync.native to avoid 8.3 short-name mismatches.
- directory-suggestions.test.ts: canonicalize result and expected paths with realpathSync.native.
- session.workspaces.test.ts: replace literal /tmp and /Users fixtures with path.resolve/path.join constructions.
- workspace-git-service.primitive.test.ts, loop-service.test.ts, and mcp-server.test.ts: use canonical repo/temp paths and shell-safe relative verify commands.
* Fix loop-service verify-check shell and workspace-git-service path-separator on Windows
- Run the loop-service verify script through the current Node executable with a relative script path.
- Normalize the workspace-git-service expected repo cwd to forward slashes for the listWorktrees assertion.
* Fix loop-service worker PTY spawn on Windows
- Canonicalize the loop-service temporary root and workspace with realpathSync.native before worker agents use the path as cwd.
* Gate loop-service real-worker-PTY test on Windows
- Skip the real worker PTY loop test on Windows after ConPTY path resolution still fails with node-pty error 267.\n- Keep the test running on POSIX so the loop behavior remains covered.
* Stub getMetricsSnapshot in test mocks to suppress async-leak uncaught exceptions on Windows
- Add a no-op AgentManager metrics snapshot to the WebSocket notification test server stub.
* Fix terminal-manager Windows-path test to avoid PTY spawn into nonexistent dir
- Use an existing temporary cwd for the createTerminal absolute-path validation assertion so node-pty does not spawn into a missing Windows directory.
CLI provider e2e test (15-provider.test.ts) pinned the claude catalog
size; PR #799 added claude-sonnet-4-6[1m] but did not update this list.
Sibling fix to 50405c3b.