Commit Graph

2419 Commits

Author SHA1 Message Date
Mohamed Boudra
c2ad4a02ef refactor: unify worktree creation workflow (#636)
Route app, MCP, and agent worktree creation through one workflow boundary. Move branch auto-naming into workspace creation and keep agent metadata title-only.
2026-04-30 21:37:17 +08:00
Mohamed Boudra
93c6c3e5f8 fix: keep archived worktrees optimistically hidden (#640) 2026-04-30 21:36:41 +08:00
Mohamed Boudra
92afb826d9 Add inline review comments to git diff pane (#530)
* Add inline git diff review comments

Adds a review draft store keyed per diff target, inline gutter + thread
rendering in the diff pane, and a generated review attachment that flows
through the composer and agent providers.

* Strip generated review attachments before saving to draft store

Drafts persist only UserComposerAttachment (no review kind), so filter the
auto-generated review attachment out when seeding the draft or restoring
after a failed auto-submit.

* Fix inline review typecheck and test harness drift

* Unslop inline review feature

- Fix stale-closure bug in composer queueMessage: clear drafts for the
  actually-queued attachments, not the latest props
- Drop dead useEffect mirroring initialBody in inline review editor
  (parent already remounts via key)
- Restore typed AgentAttachment signature on renderPromptAttachmentAsText;
  delete schema redispatch helper and drop null-checks across 5 providers
- Inline single-use review prompt formatters and replace marker switch
  with const map
- Drop normalizeComment field-copy redundancy after type guard
- Remove __reviewDraftStoreTestUtils export; test migration via
  rehydrate() public path
- Refactor findTarget to return the target line directly (no non-null
  assertion at call site)
- Drop redundant isGeneratedReviewAttachment helper; inline kind check

* Update review pill to withUnistyles after rebase

Main's unistyles refactor stripped useUnistyles in favor of withUnistyles +
StyleSheet. Convert ReviewAttachmentPill to use ThemedCircleDot + ICON_SIZE so
the icon picks up the foreground-muted mapping like the other pills.

* Fix lint and typecheck after rebase onto main

Resolve drift after main's withUnistyles refactor:
- Composer complexity: dedup voice?.isVoiceSwitching to drop to 20
- Move per-comment Pressable handlers in git-diff-inline-review into
  CommentRow subcomponent so callbacks are stable
- Hoist style/onPress factories to module level (iconButton,
  iconButtonDestructive, ghostButton)
- Memoize style arrays in git-diff-pane review wrappers
- Replace inline {} placeholders with useMemo styles
- review-draft-store: extract applyCommentUpdates to avoid map-spread,
  include input.key in useMemo deps
- Test fixtures: hoist EMPTY_COMMENTS, add buildReviewActions helper

* Drop GitDiffPane default param to keep complexity at 20

After the rebase, main's `enabled = true` default param combined with the
inline review additions pushed cyclomatic complexity to 21. Drop the
default and resolve `enabled !== false` at the call site (comparison
doesn't count toward complexity).

* Reshape inline review behind @/review module

Drop the `generated: true` attachment flag and consolidate inline-review
logic behind a deep `@/review` module so composer and git-diff-pane stop
hand-composing review primitives. Workspace-scoped review snapshots are
now derived at submit/queue time rather than persisted in the draft
store. Composer review references drop from 51 to 4; git-diff-pane drops
~108 lines and now consumes a controller hook + slot components.

* Fix optimistic review attachments
2026-04-30 21:35:58 +08:00
Mohamed Boudra
4bcf98a9e3 feat(cli): add paseo import --provider <name> <id> to resume existing sessions (#632)
Surfaces the daemon's existing resume-from-persistence capability through a
new top-level CLI command so users can pull existing Claude Code, Codex, or
OpenCode sessions into Paseo without losing history. Adds a new additive
`import_agent_request` WebSocket message; no existing schemas change.

Refs #611, #237, #492; partially addresses #268.
2026-04-30 21:33:44 +08:00
Mohamed Boudra
5bb177d33b fix(app): split local-daemon bootstrap into default vs configured override
EXPO_PUBLIC_LOCAL_DAEMON is a developer directive that the app must show
the configured host even when disconnected, so the user can see and
diagnose connection failures. The previous bootstrap conflated this with
the default "auto-add localhost:6767 if a daemon answers" behavior, and
the silent inner catch hid every failure mode.

- Split bootstrapLocalhost into bootstrapDefaultLocalhost (probe-then-
  upsert, unchanged) and bootstrapConfiguredOverride (unconditional
  upsert with placeholder serverId local:<endpoint>).
- Reconcile the placeholder to the daemon's real serverId in place when
  the probe receives server-info; rekey controllers and listeners.
- Replace the silent catch with console.warn carrying endpoint and error.
- On every override boot, purge any host owning the override connection
  under a non-matching serverId so a fresh dev daemon (rotated keypair)
  no longer leaves the registry pointing at a defunct host.
- Prune cross-endpoint placeholders when EXPO_PUBLIC_LOCAL_DAEMON changes.

Refs #637.
2026-04-30 18:12:03 +07:00
Mohamed Boudra
4cada20e38 fix(server): make Cursor CLI and other ACP custom providers reliable (#628)
* refactor(server): validate ACP mode/model selection and subscribe to live state

Match Zed's selection validity model: resolve the requested mode/model
against availableModes/configOptions before issuing the ACP RPC, log a
warning instead of throwing on unknown values, and adopt the
currentValue echoed back in setSessionConfigOption responses. Subscribe
to current_mode_update / config_option_update so AgentManager re-emits
state when the agent changes selection unprompted.

Also drops the Copilot Autopilot auto-approve shim to mirror Zed's pure
pass-through requestPermission path.

* refactor(server): emit ACP session config drift via namespaced events

Replace the parallel subscribeToSessionState callback channel with three
payload-bearing variants on the existing AgentStreamEvent union
(mode_changed, model_changed, thinking_option_changed). Manager updates
its record from the event payload and emits state — no callbacks back
into the session. Variants suppressed before websocket dispatch so the
wire schema is unchanged.
2026-04-30 18:43:27 +08:00
Mohamed Boudra
f9e6a1c5ea fix(opencode): surface invalid mode/model errors instead of hanging
The OpenCode adapter sent the prompt before its SSE event subscription
was active. Since /event does not replay, terminal session.error/idle
events for invalid mode or unsupported model could land before the
subscription, leaving the agent stuck in running forever.

Await subscription readiness before promptAsync, and surface synchronous
or rejected prompt errors as turn_failed. Also force-drop sockets in
daemon shutdown so test harnesses don't hang on lingering WS upgrades.

Adds a real-opencode e2e regression test for the invalid-mode case and a
unit test for the synchronous-throw path.
2026-04-30 16:57:00 +07:00
Mohamed Boudra
dec47d72d9 docs: lowercase internal docs + migrate website docs to public-docs/ (#634)
* docs: rename to lowercase + drop leftover plans

Rename docs in docs/ to lowercase kebab-case for consistency and update
all references in CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md,
packages/server/CLAUDE.md, and inter-doc links.

Drop two leftover design plan docs:
- docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md
- docs/plan-approval-normalization.md

* docs: drop stale uppercase entries from case-insensitive rename

* feat(website): power /docs from public-docs/ markdown tree

Move website docs out of TSX route components and into a root-level
public-docs/ directory of plain markdown files with frontmatter
(title, description, nav, order).

- Add packages/website/src/docs.ts loader using import.meta.glob with
  ?raw to compile the markdown into the bundle at build time.
- Replace the 9 hand-written docs/*.tsx routes with a single $.tsx
  catch-all that renders any slug via react-markdown.
- Drive the docs sidebar nav from frontmatter order/nav.
- Auto-discover docs routes in vite.config.ts so the sitemap stays in
  sync without manual edits.

* fix(website): bind dev server to 0.0.0.0 so port collisions trigger fallback

`host: "127.0.0.1"` (or unset) lets macOS coexist with another process
holding an IPv6 dual-stack `*:8082` socket, so Vite never sees
EADDRINUSE and silently binds alongside it. Forcing IPv4 wildcard
makes the conflict real, and Vite's default `strictPort: false`
falls through to the next free port.

* fix(website): restore docs page styling after markdown migration

Add a .docs-prose class that mirrors the styling the original
docs/*.tsx components hand-rolled (h1/h2/h3 sizes, paragraph/list
spacing, link colors, code blocks, callout-style blockquotes).

ReactMarkdown was emitting unstyled HTML because the previous
wrapper class only had inline-code rules — headings and code
blocks fell back to user-agent defaults.
2026-04-30 17:27:15 +08:00
Mohamed Boudra
5d9dc0c4d5 fix(claude): preserve session across query restarts
Fixes the Claude query restart path that could drop conversation context after thinking effort changes or rewind-driven restarts.

Addresses #439.

Related to #156 and #200.
2026-04-30 15:37:31 +07:00
Mohamed Boudra
0b78379b77 fix: percent escaping for git --format on Windows (#629)
* test(server): add real-spawn round-trip for % in argv

RED test: pass --format=%(refname)... through spawnProcess to a child
node script that echoes argv[2]. Asserts the child receives the original
string verbatim. Wired into the Windows CI matrix to expose the
escapeWindowsCmdValue %-doubling bug under cmd.exe.

* test(server): use bare 'node' command so cmd.exe path is exercised

process.execPath has both an extension and a path separator on Windows,
so shouldUseWindowsShell returns false and the broken %-escape pipeline
is never invoked. Use the bare command name 'node' instead.

* fix(server): stop doubling % in cmd.exe arg escaping on Windows

cmd.exe only collapses `%%` → `%` inside batch files; on the command
line / via `cmd /c "..."` `%%` stays literal. Doubling `%` in
escapeWindowsCmdValue meant args like `--format=%(refname)` reached git
as `--format=%%(refname)`, which git interprets as the escape sequence
for a literal `%`, so format atoms appeared verbatim and the branch
picker showed `%(refname)%09%(committerdate:unix)` instead of branch
names.

Update unit tests that pinned the broken doubling behavior.
2026-04-30 14:22:19 +08:00
Mohamed Boudra
6a61066e2c fix(desktop): warn about Rosetta installs on Apple Silicon (#621)
* fix(desktop): warn about Rosetta installs on Apple Silicon

* docs: note workspace build sync before type fixes
2026-04-30 13:13:30 +08:00
Mohamed Boudra
ae43b6c4cd fix(desktop): skip packaged-app smoke when build arch differs from host
Cross-arch packaging (e.g. arm64 build on an x64 Windows runner)
can't smoke the unpacked binary because the host can't spawn it.
Skip the smoke instead of crashing with spawn UNKNOWN.
2026-04-30 12:09:13 +07:00
Mohamed Boudra
f286a5beea feat(app): add pull-and-push git action (#627)
Combines `git pull` followed by `git push` behind a single action in the
git actions split-button. Push is skipped if pull fails, and errors are
surfaced through the same toast pathway as the existing pull and push
actions.
2026-04-30 12:56:09 +08:00
Mohamed Boudra
461387636a chore(release): cut 0.1.65-beta.2 2026-04-30 11:48:38 +07:00
Mohamed Boudra
9c3c0f9140 feat(desktop): build Windows arm64 alongside x64
Multi-arch Windows build in one electron-builder pass, with the website
self-healing across the rename. Asset names go from
`Paseo-Setup-${version}.exe` to `Paseo-Setup-${version}-{x64,arm64}.exe`.

The website now reads the actual installer filename off the latest
GitHub release, so legacy single-arch releases keep rendering a single
"Download" pill while dual-arch releases promote to "Intel / x64" +
"ARM64" automatically. No homepage code change needed when ARM ships.
2026-04-30 11:39:56 +07:00
Mohamed Boudra
a7e1753834 Merge branch 'main' of github.com:getpaseo/paseo 2026-04-30 10:38:26 +07:00
Mohamed Boudra
0aa7c768ba Fix visible changes pane diff subscription 2026-04-30 10:38:07 +07:00
Mohamed Boudra
bdb8f6f535 fix(app): restore tool detail selection affordance 2026-04-30 10:34:44 +07:00
Yurui Zhou
c1cfc7c743 fix(server): preserve fatal log on crash and guard socket.send against close races (#613)
The daemon was dying silently with no log trail. Two compounding causes:

1. uncaughtException/unhandledRejection handlers in index.ts called
   process.exit(1) immediately after logger.fatal, but pino is configured
   with async streams (sync: false console + rotating-file-stream), so the
   fatal entry never flushed. main().catch already worked around this with
   a 200ms setTimeout — extract exitAfterPinoFlush() and apply it to the
   process-level handlers too.

2. socket.send() in the relay transport adapter and ws.send() in
   sendToClient/sendBinaryToClient were unprotected. The ws library throws
   synchronously on send-after-close, and the readyState === 1 check has a
   race window. With an active foreground turn streaming agent_stream
   output, a peer-side disconnect could turn into an uncaughtException —
   which (per #1) then exited without logging.

Wrap the three send sites in try/catch and warn-log on failure; let
onclose/onerror drive cleanup. The readyState fast-path stays as an
optimization.

Refs getpaseo/paseo#612

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 11:16:48 +08:00
Mohamed Boudra
76c5ec56b5 fix(release): exclude beta versions from mobile release workflow 2026-04-29 21:37:22 +07:00
Mohamed Boudra
e0cb0302b9 chore(release): cut 0.1.65-beta.1 2026-04-29 19:54:45 +07:00
Mohamed Boudra
47223b375f feat(app): show loading and error states for assistant images
Render an activity indicator while image dimensions resolve and an
"Image unavailable" fallback if loading fails, replacing the previous
behavior where the surface stayed blank. Reserves a fixed minimum
height in both states so message layout doesn't shift.
2026-04-29 19:47:01 +07:00
Mohamed Boudra
98f2736233 fix(app): keep diff gutter row aligned when line number is blank
The recently added numberOfLines={1} on the gutter <Text> wraps the
content in display:-webkit-box; overflow:hidden on web, where a plain
ASCII space collapses to zero height. That shifted every line number up
by one row, leaving "1" next to the hunk marker. Use a non-breaking
space so the cell keeps its line height.
2026-04-28 21:58:22 +07:00
Mohamed Boudra
f19007cd83 Add diagnostic logging to daemon startup 2026-04-28 21:49:35 +07:00
Mohamed Boudra
2f5f974a0e fix(desktop): match VS Code Linux sandbox handling, scope --no-sandbox to AppImage
The Linux wrapper renamed the executable to Paseo.bin and exec'd it with
--no-sandbox, which surfaced as Paseo.bin in the dock and process list on
Ubuntu. Drop the wrapper and the chrome-sandbox deletion entirely. .deb/.rpm
now ship chrome-sandbox SUID 4755 (electron-builder 26 default postinst,
matches VS Code) and run Paseo directly with the Chromium sandbox on.
AppImage cannot carry SUID, so --no-sandbox is now injected at runtime only
when process.env.APPIMAGE is set.

Fixes #602
Re #381
2026-04-28 21:19:49 +07:00
Mohamed Boudra
88141ab456 fix(server): make /compact slash-command test mock yield session.idle
The empty event-stream mock added in #597 races with the summarize-success
path: consumeEventStream sees the empty stream end immediately and emits a
"stream ended before terminal event" turn_failed which gets buffered before
session.run has assigned its local turnId. The buffered event replays after
startTurn returns, marks settled = true, and rejects completion — but
session.run skips await completion because settled is true, leaving the
rejection unhandled and tripping vitest.

Yield session.idle so the stream resolves the turn the same way real
OpenCode SSE does.
2026-04-28 20:46:40 +07:00
Mohamed Boudra
17b1d8f732 fix(server): keep setup/teardown/terminal action paths failing loudly on bad paseo.json
Follow-up to the projection fix. The mechanism/policy split landed projection
on a graceful-degrade path, but I also made the setup, teardown, and terminal
wrappers silently absorb parse failures — which broke the existing contract
that `runWorktreeSetupInBackground` surfaces "Failed to parse paseo.json" to
the user when they invoke setup against a workspace with a malformed config.

Restore the throw, but with the path included and the underlying parse error
attached as `cause` (Pino expands it; UI surfaces the path + detail). Extract
`paseoConfigParseError` so spawnWorkspaceScript and the three wrappers throw
the same shape.

Also update opencode-agent.full-access.test.ts mode-order assertion that the
reorder commit forgot to update.
2026-04-28 20:38:11 +07:00
Mohamed Boudra
283390e6d5 chore(release): cut 0.1.64 2026-04-28 20:09:12 +07:00
Mohamed Boudra
0dfd909646 fix(opencode): reorder default modes to Build -> Plan -> Full Access 2026-04-28 19:53:27 +07:00
Mohamed Boudra
00219cfd04 fix(server): isolate paseo.json parse failures so they don't block fetch_workspaces
A malformed paseo.json (e.g. git-conflict markers) in any registered
workspace caused readPaseoConfig to throw, which propagated through
getScriptConfigs -> buildWorkspaceScriptPayloads ->
Session.describeWorkspaceRecord and failed the entire
fetch_workspaces_request with code=fetch_workspaces_failed. The client
showed no workspaces at all.

Reshape: separate loading (mechanism) from policy (per-caller).

- readPaseoConfig now returns a discriminated result
  ({ ok: true; config } | { ok: false; configPath; error }). Pure load,
  no logging, no opinions.
- Workspace projection owns its own policy via readPaseoConfigForProjection
  in script-status-projection.ts: warn via pino with configPath +
  workspaceDirectory + err, treat workspace as having no scripts, keep the
  list alive.
- spawnWorkspaceScript throws with cause so the existing
  handleStartWorkspaceScriptRequest catch logs it once with full context.
- The setup/teardown/terminal wrappers retain their pre-existing silent
  degradation on missing/broken config.
- getScriptConfigs takes a parsed PaseoConfig | null instead of repoRoot
  so the projection can pass the result through without double-reading.

Closes https://github.com/getpaseo/paseo/issues/598
2026-04-28 19:48:24 +07:00
Thanh Minh
fbf10ba4c6 fix(server): support OpenCode full-access approvals (#595)
* fix(server): support OpenCode full-access approvals

* fix(opencode): unconditionally auto-approve tool permissions in full-access mode

OpenCode permissions that resolve to allow do not surface prompts in the first place, so checking runtime permission rules before replying made Paseo full-access misleading. Permissions that default to ask, like external_directory and doom_loop, would still surface even in full-access.

Make full-access match OpenCode's own --dangerously-skip-permissions behavior by replying "once" to every tool permission prompt, while still letting reply failures fall back to surfacing the permission to the user.

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-04-28 20:47:12 +08:00
Yurui Zhou
2a4945ad0b fix: keep @ file mention responsive on very large projects (#600)
Typing @ in the chat input to reference a file froze the app on huge
workspaces. Two compounding causes:

1. The autocomplete hook fed fileFilterQuery straight into the React
   Query key, so every keystroke fired a new directory_suggestions
   request and a fresh BFS scan on the daemon. Added a 180 ms debounce
   that mirrors the existing pattern in new-workspace-screen so rapid
   typing coalesces into one request.

2. searchWorkspaceEntries only ignored node_modules, so dist, build,
   target, out, coverage, vendor and __pycache__ each consumed the
   5,000-entry scan budget before any real source file was reached.
   Extended WORKSPACE_IGNORED_DIRECTORY_NAMES with those names and
   added a regression test that proves a deep source file is still
   reachable when those directories are full of bait entries.

Refs #599

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:43:38 +08:00
Thanh Minh
3ba4d09294 fix(opencode): support executable slash commands (#597) 2026-04-28 20:25:04 +08:00
Mohamed Boudra
013e53881b fix(app): prevent infinite loop in @-mention scan when @ is at index 0
`findActiveFileMention` walked back through `@` candidates with
`lastIndexOf("@", atIndex - 1)`, but per spec a negative `position`
clamps to 0 — so once `atIndex` hit 0 with an invalid query (e.g. a
trailing space), the loop stepped from 0 back to 0 forever, freezing
the JS thread on every keystroke and forcing a quit.

Refs #596, #425
2026-04-28 19:16:04 +07:00
Mohamed Boudra
599fcb34c5 chore(release): cut 0.1.63 2026-04-28 17:37:29 +07:00
Mohamed Boudra
e41fee0366 fix(desktop): close two pre-ship rollout edge cases
Bucket divisor now produces [0, 1) so the single UUID hashing to
0xFFFFFFFF is no longer permanently excluded by the strict-less-than
admit condition.

Mirrors the post-stamp manifest validation step from desktop-rollout.yml
into desktop-release.yml's finalize-rollout job so a stamp-rollout.mjs
regression can't silently ship a broken manifest.

Adds boundary tests: bucket=0 stays blocked at exact release time,
max-bucket admits at and past rollout end, unparseable releaseDate
admits, bucketFromStagingUserId is strictly < 1 at the upper bound.
2026-04-28 16:58:54 +07:00
Mohamed Boudra
25eb26db0f feat(desktop): add time-based staged rollout for stable updates
Linear ramp from 0% at publish to 100% at releaseDate + rolloutHours
(default 24h, configurable per release). Beta channel and rolloutHours=0
short-circuit to admit everyone. Per-machine bucket derives from a
persistent UUID at <userData>/.updaterId and feeds electron-updater's
isUserWithinRollout hook.

Adds desktop-rollout.yml workflow for in-place rolloutHours edits on
already-published releases (hotfix to 0 or extend the ramp), serialized
against finalize-rollout in desktop-release.yml via a shared concurrency
group keyed on the tag. Replaces the hand-rolled mac manifest merge
with a js-yaml round-trip that preserves unknown fields.
2026-04-28 16:48:18 +07:00
Xh74d82hl
814c0660ab fix(loop): pass provider/model to daemon in loop/run (#594)
Fixes #593
2026-04-28 14:54:19 +08:00
Mohamed Boudra
01728e7c22 fix(server): apply provider model overrides to agent default resolution
Built-in provider clients bypassed mergeModels in listModels(), so
configured profileModels/additionalModels were honored by the catalog
but ignored when AgentManager picked a default for new agents.

Wrap the inner client whenever overrides exist (not only when the
provider id differs) and route listModels through mergeModels.

Fixes #579
2026-04-28 13:41:04 +07:00
Mohamed Boudra
7ccf97eb2f fix(server): detect Pi availability via SDK model registry (#586)
The hardcoded OPENAI/ANTHROPIC/OPENROUTER env-var check missed
DEEPSEEK_API_KEY and ~14 other providers the pi-ai SDK supports,
hiding the Pi provider entirely when only those keys were set.
Delegate to ModelRegistry.getAvailable() so every supported provider
is recognized automatically. Diagnostic now lists configured providers
dynamically instead of three hardcoded keys.
2026-04-28 13:31:24 +07:00
Mohamed Boudra
f4fded1472 fix(app): size file preview gutter to fit 3-digit line numbers (#592)
The gutter width formula was calibrated for the diff pane's 12px font,
so 3-digit numbers wrapped to two lines in the file preview's 14px
gutter. Parameterize on font size and add numberOfLines={1} as a guard.
2026-04-28 13:28:02 +07:00
Mohamed Boudra
ea72c72998 test(server): codify subfolder of archived git repo stays as directory workspace
When a user archives a git repo and later opens a subfolder of it that is
not its own git project, the resulting workspace is a directory — not a
revived git workspace pointing at the archived parent. To get git features
back the user unarchives the parent (S6/S11).
2026-04-28 13:22:33 +07:00
Mohamed Boudra
e13634380c chore(release): cut 0.1.63-beta.5 2026-04-28 12:38:33 +07:00
Mohamed Boudra
ccf248c332 fix(app): contain triple-click selection to one message bubble
Triple-clicking a message extended the document Range across the
sibling composer and into the textarea, copying both on Cmd+C. Mark
the messages region non-selectable on web and re-enable selection on
each bubble's outer container so triple-click stays scoped.
2026-04-28 12:13:31 +07:00
Mohamed Boudra
05d279cc65 fix(server): surface real errors and child-process output in diagnostics
Provider diagnostics were stringifying Error objects through JSON.stringify,
which yields {} and hides the cause. Spawned children logged stderr to the
daemon log but never attached it to thrown errors, so the diagnostic showed
"Server: Unavailable ({})" with no clue why.

Generalize toDiagnosticErrorMessage to extract message, code, signal, stderr,
stdout, and recursive cause from any Error (including execFile-style errors),
and never return {} or empty. Buffer stderr/stdout in OpenCode startServer
and include them in startup-failure rejections. Stop swallowing errors in
resolveBinaryVersion. Drop the duplicate stringifyUnknownError helpers in
opencode-agent and pi-direct-agent and route every callsite through the
single canonical helper.
2026-04-28 11:58:19 +07:00
Mohamed Boudra
221b76bf52 fix(server): keep archived workspaces out of cwd resolution (#564)
Filter archivedAt records out of the prefix-fallback resolver and stop
canonicalizing into archived parents — opening a child of an archived
git ancestor now creates a fresh workspace at the input path instead
of resurfacing the archived parent.
2026-04-28 11:32:35 +07:00
Mohamed Boudra
b090b4d237 fix(server): align tests with SSH alias resolver and Windows rm flake
- session.test.ts: drop stale 4th workspaceGitService arg from
  createPullRequest assertion (signature changed in ee5a23d9).
- workspace-git-service.test.ts: bump local flushPromises microtask
  count to cover the extra await resolveGitHubRemoteForTarget step
  added by the GitHub remote resolver.
- provider-windows-launch.test.ts: retry rmSync in afterEach to
  ride out Windows file locks from the spawned claude.cmd.
2026-04-27 20:43:01 +07:00
Myriad-Dreamin
c78c30ef54 Support opening workspace branches on GitHub (#583)
* Add GitHub branch open target to workspace button (#96)

* refactor: simplify workspace open targets to flat shape

Collapse the editor/url discriminated union into a single OpenTarget
{ id, label, icon, onOpen } so the button renders targets uniformly
and the mutation is shape-agnostic. Drop the heavily-mocked button
test in favor of the URL builder tests, restore the decodeURIComponent
guard for valid-syntax-but-invalid-UTF-8 pathnames, and note the SSH
host alias limitation.

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-04-27 21:31:11 +08:00
hissin
34626e3b8f fix(server): skip home directory in workspace prefix matching (#563)
Skip the home directory in workspace prefix matching so child paths under the home workspace get their own workspace records.

Closes #562
2026-04-27 20:28:46 +08:00
Jakub Skałecki
ee5a23d962 Support GitHub SSH host aliases for PR actions
Resolves the case where a remote like `git@github-work:owner/repo.git`
points at github.com via `~/.ssh/config` but Paseo treats it as
non-GitHub.

Adds a centralized GitHub remote resolver that parses standard remotes
directly and resolves SSH aliases via `ssh -G` (with hostname
validation to keep the alias out of ssh's option parser). The resolver
is shared by workspace GitHub feature detection, PR status, PR
creation, and workspace git metadata.
2026-04-27 20:28:10 +08:00