Adding or cloning a project now registers it and opens workspace setup
instead of creating a workspace implicitly. Keep Add Project independently
mounted from Search so closing one cannot control the other.
* Pin android-sdk to 21.0 and unify Java on 21 for mise
The `.tool-versions` `android-sdk latest` pin had resolved to the
ancient `1.0` cmdline-tools bundle, whose sdkmanager (3.6.0) predates
the `emulator` package and fails with `Failed to find package
emulator`. Pin `android-sdk 21.0`, point the hardcoded install paths
in `.mise.toml` at it, and add `cmdline-tools/21.0/bin` to PATH so
sdkmanager and avdmanager resolve.
Also unify the Java version on 21: `.tool-versions` already pinned 21,
but `.mise.toml` overrode it to 17. A local `npm run android` Gradle
build succeeds on 21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Document iOS simulator and Android emulator local dev setup
Add local-dev docs for running the app against a worktree daemon:
- development.md: prerequisites for the ios-simulator preview service
(Xcode, an iOS runtime, automatic CocoaPods install), the
xcode-select/simctl troubleshooting fix, and a "Running the iOS app
on a local simulator" section covering `npm run ios` and pointing
the app at a worktree daemon via EXPO_PUBLIC_LOCAL_DAEMON.
- android.md: a "Prerequisites (local dev)" section (mise java 21 +
android-sdk 21.0, sdkmanager components, AVD creation) and a
"Running on an emulator against a worktree daemon" section covering
REACT_NATIVE_PACKAGER_HOSTNAME and EXPO_PUBLIC_LOCAL_DAEMON=10.0.2.2
for reaching the host daemon.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Give full Intel x86_64 emulator setup commands in Android docs
Greptile flagged that the arm64-v8a -> x86_64 substitution for Intel
Macs was only a parenthetical. Provide a complete, copy-pasteable
x86_64 command block instead of asking readers to hand-edit the arm64
one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(server): create autonomous turns for spontaneous ACP session updates
When an ACP-based provider handles background-agent completion
notifications, the resulting spontaneous session/update messages have no
active foreground turn to attach to. Mirror the Claude provider's
autonomous-turn mechanism so these updates are recorded in Paseo's
timeline.
- Start an autonomous turn when a sessionUpdate arrives with no active
foreground turn
- Tag timeline events with the autonomous turn id
- Complete the autonomous turn after a timeout, or before a new
foreground turn starts
* Fix ACP autonomous event scoping
---------
Co-authored-by: zab <b13022010527@gmail.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(desktop): share persistent browser data across tabs
Keep browser sign-ins and site storage across tabs and app restarts, with a Settings action to clear the shared profile.
* fix(settings): prevent duplicate browser data clears
* fix(desktop): complete browser data cleanup
Clear legacy per-tab profiles after upgrades and localize the browser data settings in every supported language.
* fix(desktop): close browser profile cleanup races
Individual tool outcomes remain visible in the expanded list. The aggregate header no longer treats a handled tool result as a failure of the overall work.
* fix(chat): allow failed turns to be forked
Failed turns can end in Paseo-generated timeline items without provider
message IDs. Anchor forks to canonical timeline positions while retaining
legacy boundaries for compatibility.
Place the fork summary before the new prompt and delimit it as structured
chat history.
* fix(chat): tighten fork boundary handling
* perf(app): reduce composer draft writes
Persist draft checkpoints every 200 ms during sustained typing while keeping the latest in-memory draft current. A trailing write stores the newest checkpoint when typing stops.
* test(app): cover draft persistence checkpoints
Keep the throttle policy deterministic at its storage boundary, including latest-value coalescing and pending-write cancellation when drafts are cleared.
* fix(app): flush pending drafts on background
Flush the latest throttled checkpoint when the app leaves the foreground and report asynchronous persistence failures. Extend the storage-boundary tests across consecutive intervals and explicit lifecycle flushing.
Native scrollbar appearance changed the textarea's usable width, feeding alternating measurements back into the composer height mirror. Reserve the gutter so wrapping and height remain stable.
* feat(subagents): archive finished children and show native names
Keep native provider sessions intact while letting users clear completed children from the track. Persist dismissed provider child IDs so history replay does not restore them.
* fix(subagents): cover persistence and naming edge cases
* fix(subagents): preserve active descendants and native names
* fix(subagents): retain dismissed timelines and trim paths
* fix(subagents): retain history across hydration
* test(subagents): expect normalized native descriptions
* fix(subagents): separate hidden rows from retained history
* fix(subagents): preserve restored hidden tabs safely
* fix(subagents): integrate archive with run state
* test(codex): expect normalized child name
* fix(subagents): retain dismissed tabs on reload
* fix(subagents): hide finished native agents locally
Keep the header action presentation-only so it does not mutate agent lifecycle or expand the runtime protocol.
* fix(subagents): humanize native agent names
* fix(subagents): preserve names in Codex history
* fix(subagents): retain hidden rows across reloads
* fix(app): stabilize desktop sidebar controls
Keep the left-window-controls toggle mounted across sidebar state changes so its position and tooltip state remain stable. Preserve browser placement and show an explicit Explorer close control only where native right-side controls do not provide one.
* fix(app): suppress inactive sidebar chrome
* fix(server): render image paths with spaces
Raw spaces in local Markdown image destinations made the renderer treat provider images as text. Emit encoded file URIs for spaced paths so the client parses and resolves them as images.
* fix(server): encode all absolute image paths
Provider image paths without spaces could still contain URI-significant characters such as fragment or query markers. Route every absolute local path through the encoded file URI boundary.
* fix(server): encode network image paths
Windows network-share paths bypassed the absolute local path handling. Normalize UNC and extended UNC paths before encoding them as file URIs.
* fix(app): preserve network image paths
Host-based file URIs were decoded as relative paths. Restore the UNC prefix so provider images on network shares resolve through the file RPC correctly.
* fix(server): preserve double-slash POSIX image paths
Classify only backslash-prefixed paths as Windows network paths so POSIX double-leading-slash paths retain their filesystem semantics.
* fix(server): prevent hidden Codex subagents and stuck turns
Codex can announce native children only through its mirrored lifecycle stream, while rejected interrupts previously looked like successful local cancellation. Preserve those child announcements and keep manager state active until the provider acknowledges cancellation.
* fix(server): block actions after rejected cancellation
A provider can accept a turn before publishing the turn ID needed to interrupt it. Keep that interval non-cancelable, and prevent reload, replacement, or rewind from proceeding without an acknowledged cancellation.
* fix(server): surface rejected agent cancellations
Replacement prompts and Stop requests could appear accepted after the provider kept ownership of the active turn. Complete replacement cancellation before detaching the stream, and return cancellation failures through the client response.
* fix(server): handle turn completion during cancellation
* fix: finish cancellation lifecycle handling
* fix(server): close remaining cancellation races
* fix(server): settle autonomous cancellations
* fix: honor cancellation failures at call sites
* refactor(server): centralize agent run state
* fix(server): keep pending runs isolated from stale events
* fix: surface remaining cancellation failures
* fix(server): narrow cancellation error handling
* fix(app): preserve desktop layout in half-screen windows
Treat native window controls as physical corner obstructions instead of deriving clearance from compact layout state. Keep 751px windows in desktop mode while preserving usable content when persisted sidebars are wide.
* fix(app): retain sidebar width across window resizes
* fix(app): yield app sidebar to narrow settings
* test(app): wait for mobile sidebar motion to settle
* fix(app): yield navigation to narrow workspace explorer
* fix(app): mark sidebar sizing helpers as worklets
* fix(app): hide app navigation when space is constrained
* feat(app): refine tool call summaries and scrolling
Keep summary groups stable from the first tool call, preserve live activity feedback, and scroll expanded groups to the latest entry. Consolidate web scrollbar styling around the native browser mechanism.
* refactor(app): reuse tool call height constant
* fix(app): preserve tool call summary state
* fix(app): refresh web tool groups on expand
* fix(app): preserve grouped tool errors
* feat(ui): improve changelog and help navigation
Make release notes easier to scan and reach from the app. Also keep the homepage marquee within the layout viewport so it cannot create horizontal scrolling.
* fix(website): reject duplicate release anchors
* fix(pi): complete locally handled prompts
Slash commands the Pi CLI handles without starting a model turn previously
left the Paseo turn hanging until interrupt. Parse the optional
agentInvoked correlation from prompt acknowledgements, surface
command_output/notify output as timeline content, and complete the turn
locally when no agent turn will start. Older Pi binaries without the
correlation are detected via a getState compatibility probe.
* review: guard command output without an active turn, simplify delegates
- Drop late-arriving command_output when no turn is active instead of
emitting an orphaned timeline item with turnId undefined
- Remove redundant 'text' in event check (type is already narrowed)
- Inline recordNoTurnNotification delegate
* refactor(pi): consolidate local prompt state
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(workspaces): show provider subagent activity
Provider subagent lifecycle lives outside managed agent snapshots, so workspace aggregation dropped it after the parent turn finished. Count running provider subagents against the delegation root and emit workspace updates when their status changes.
* fix(workspaces): surface cross-workspace subagents
* Fix shortcut capture ignoring - = ; ' keys
KEY_MAP was missing Minus/Equal/Semicolon/Quote, so
keyboardEventToComboString returned null for those physical keys and
the capture UI silently dropped them. Add the four entries with their
shifted variants.
Also suppress the Electron View > Zoom accelerators (Cmd+-/=/0) while
capturing, so those combos reach the renderer instead of zooming the
window. Driven by the existing capturingShortcut flag via a new
paseo:menu:set-capturing-shortcut IPC.
* fix: address review feedback from PR #2047
Trim the capturing useEffect so it only fires when capture is active
(removes the redundant on-mount false and the double-fire on true->false),
and reset the main-process capturingShortcut flag on renderer reload via
browser-window-created + did-finish-load so a Cmd+R mid-capture can't
leave the zoom accelerators disabled.
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(server): patch OpenCode SDK unhandled rejection on stream abort
The SDK's SSE abort handler calls reader.cancel() without handling
its rejection. When Paseo aborts the event stream during normal
session close, that promise can reject and crash the daemon with an
unhandled rejection outside any Paseo-owned code path.
Patches both copies the SDK ships (dist/gen and dist/v2/gen, the
latter being what Paseo actually imports). Extends
postinstall-patches.mjs to support patch-package running from a
non-root cwd, since @opencode-ai/sdk lives in packages/server's own
node_modules rather than the hoisted root.
* fix(server): stop assuming OpenCode's default agent is "build"
OpenCode users can rename or delete any agent, including the built-in
"build"/"plan" defaults. Paseo was injecting a hardcoded "build"
agent in several places whenever no mode was explicitly requested:
- The protocol manifest declared defaultModeId: "build" for opencode,
which AgentManager.normalizeConfig applied to every session
(including internal metadata-generation sessions) before any
provider-level logic ran.
- OpenCode's own normalizeOpenCodeModeId defaulted an empty/"default"
modeId to "build" rather than omitting the agent field.
- Unattended child creates (e.g. subagents spawned by an unattended
parent) forced modeId: "build" to express unattendedness, even
though that's already carried by the auto_accept feature.
Now an unset mode stays unset end-to-end: the "agent" field is
omitted from OpenCode prompt/command calls entirely, letting OpenCode
fall back to its own configured default agent instead of Paseo
guessing one that may not exist.
* fix(server): validate agent create mode on the WebSocket session path
resolveAndValidateCreateAgentMode already rejects modes unknown to a
provider's discovered mode list, but it was only wired into the MCP
create path. App-created agents (create_agent_request) skipped it
entirely, so a client's remembered mode preference — which can go
stale when a user renames or deletes OpenCode agents — sailed through
to the provider and failed mid-turn with an opaque error instead of a
clear rejection at creation time.
resolveSessionCreateAgent now calls
providerSnapshotManager.resolveCreateConfig, matching the MCP path,
so an invalid mode now throws 'Invalid mode ... Available modes: ...'
immediately on create.
* chore: address review feedback on create-agent and postinstall script
- Document the cleanup-ordering constraint in resolveSessionCreateAgent:
mode validation runs after buildSessionConfig (cwd isn't known until
that completes), so a thrown validation error leaves any
worktree/workspace buildSessionConfig created for the caller to clean
up. session.ts already handles this for the worktree path; the
directory-only workspace path has a pre-existing gap, not introduced
by this validation.
- Log spawn errors from patch-package in postinstall-patches.mjs
(e.g. ENOENT if it's missing from PATH) instead of failing silently.
* fix(server): don't fabricate OpenCode modes when discovery finds none
fetchModesFromClient and mergeOpenCodeModes fell back to
DEFAULT_MODES ([build, plan]) whenever OpenCode discovery returned
nothing — e.g. for a freshly-created worktree whose OpenCode server
hasn't loaded the project config yet. That fabricated list let
create-time mode validation accept a stale 'plan' preference that the
provider then rejects at prompt time (Agent not found: 'plan').
Return an empty list instead: OpenCode users can rename or delete any
agent, so any hardcoded fallback risks validating a mode that doesn't
exist. DEFAULT_MODES is retained only for description enrichment and
sort ordering.
* fix(app): reconcile stale selected mode against discovered modes on create
The mode picker displays modeOptions[0] when the stored modeId isn't
in the discovered list (e.g. a globally-remembered 'plan' that a
workspace's OpenCode config no longer defines), but the create request
still submitted the raw stored modeId. Result: UI showed 'Build' while
the request sent 'plan', which the daemon then rejected.
Reconcile the selected mode against the discovered mode ids when
building the create config (both the workspace draft composer and the
workspace-setup dialog), so the submitted mode matches what the picker
shows — falling back to the first available mode when the stored one is
absent. Mirrors the existing resolveEffectiveModel reconciliation for
models.
* test(server): fix opencode mode tests after fallback change + rebase
- Replace 'available modes include build and plan' (which relied on the
removed [build, plan] discovery-failure fallback) with two tests:
one asserting discovered agents map to modes, one asserting empty
discovery yields no fabricated modes.
- Restore messageId fields in the four provider-subagent timeline
assertions. These were accidentally dropped during rebase conflict
resolution; the #2013 subagent code legitimately emits messageId, so
the expectations must include it.
* test(app): fix e2e seed helper using invalid opencode mode
createIdleAgent seeded an OpenCode agent with modeId
'bypassPermissions' — a Claude mode OpenCode never had. This worked
before only because the session create path silently coerced unknown
modes to 'build'. Now that create-time mode validation rejects modes
the provider doesn't define, use 'build' + auto_accept (OpenCode's
unattended full-access equivalent), matching the rewind-flow helper.
Fixes the e2e failures across archive-tab, command-center-host,
settings-toggle-tab-regression, workspace-agent-tab-rename,
workspace-pane-remount, workspace-navigation-regression, and
worktree-restore specs, which all seed via this helper.
* fix(app): use global codebuddy binary for codebuddy-code ACP provider
The @tencent-ai/codebuddy-code npm package ships multiple bins
(codebuddy/cbc/cbc-prewarm), so `npx -y @tencent-ai/codebuddy-code`
fails with "could not determine executable to run" and exits
immediately. Paseo's ACP catalog probe treats npx as a long-running
process, so the initialize request never gets a response and the
probe times out after 60s, leaving the model list empty.
Switch to the user-installed codebuddy CLI (matching kiro-cli,
traecli, cursor, etc.) so the probe completes and models load.
* chore: trigger CI rerun (app-tests flaky: window-is-not-defined in strategy-web.test.tsx, unrelated to this change)
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(app): add sidebar help and support menu
Give users a single place to run diagnostics, find shortcuts, and reach the support channels. Share one global diagnostic sheet between Settings and the sidebar so both entry points collect the same report.
* fix(app): keep cloned workspace navigation atomic
A newly merged clone flow still used the previous navigation signature and opened its draft tab separately. Route the cloned workspace and its draft target through the unified navigation command so attention selection cannot replace the intended tab.
* test(app): harden sidebar help coverage
Accept both the Discord invite URL and its canonical redirect, assert placement through real element geometry, and remove an unnecessary diagnostic-host callback.
* test(app): cover support redirect variants
Keep the help-menu E2E stable across prerelease versions and the valid redirect forms returned by Discord and GitHub.
* fix(ci): retry npm installs
Transient registry and package-download failures should delay a job briefly instead of requiring a manual rerun. Apply one bounded retry policy across CI, deploy, and release workflows.
* fix(ci): preserve retries for historical Android tags
Manual Android rebuilds may check out releases from before the shared retry script existed. Keep that install step self-contained and allow the shared helper's injected runner to be asynchronous.
* fix(ci): preserve retries for desktop release refs
* refactor(ci): use shared npm retry helper everywhere
* fix(app): focus explicitly opened workspace tabs
Explicit tab navigation could be overwritten by attention-aware workspace
navigation when the visible agent had just finished. Make workspace navigation
own both normal attention selection and explicit targets so callers cannot
sequence them inconsistently.
* test(app): strengthen forked tab focus assertion
* fix(codex): surface silent terminal activity
Successful no-output commands use a null output field, while terminal interactions carry their stdin separately. Preserve both so the agent timeline remains auditable.
* refactor(tests): share Codex event waiter
* fix(codex): preserve bounded terminal audit details
Keep terminal stdin through late command relabeling, cap it with other timeline content, and accept nullable output from legacy live completion events.
* fix(codex): preserve repeated terminal writes
* fix(app): preserve New Workspace prompts across target changes
The New Workspace draft was keyed by the selected host and project, so changing either target replaced the visible prompt with another draft scope. Treat the screen as one persistent draft surface and migrate the newest active legacy draft.
* test(app): express draft persistence as user actions
* fix(app): clear stale picker PR context
* fix(app): persist picker attachment ownership
* fix(app): keep workspace target context safe
* feat: clone GitHub repo into a workspace
Add an end-to-end "clone a GitHub repo and register it as a Paseo
workspace" flow: a new workspace.github.clone RPC, daemon handler,
client method, CLI `paseo clone` command, and a GitHub-repo mode in
the project picker modal. Gated behind the workspaceGithubClone
server capability flag.
- protocol: workspace.github.clone request/response schemas + feature flag
- server: handleWorkspaceGithubCloneRequest, normalizeCloneRepository
- client: DaemonClient.cloneGithubWorkspace
- cli: `paseo clone <repo> --dir <path> [--protocol https|ssh]`
- app: GitHub-repo mode, clone-protocol picker, error surfacing
Review fixes:
- CLI clone now checks the workspaceGithubClone capability and fails
fast with a clear "update the host" error instead of hanging for the
full request timeout against an older daemon.
- Replace the duplicated client-side URL-detection regex (app + CLI)
with a shared isCompleteGitRemote() in @getpaseo/protocol/git-remote,
backed by parseGitRemoteLocation so clients classify remotes
identically to the daemon (fixes confusing errors for git://, ftp://,
file:// inputs).
- Add git-remote.test.ts covering the shared classifier.
* Fix GitHub clone failure handling
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(app): give each permission mode a distinct icon
The mode selector reused shield glyphs across modes: Auto mode showed a
question mark, Always Ask a checkmark, and the same icon appeared on
several modes (ShieldCheck on both Always Ask and Plan, ShieldAlert on
both Accept File Edits and Bypass). The result read as arbitrary rather
than as a scale of autonomy.
Assign every built-in provider mode a distinct icon from one shared
vocabulary, applied consistently across Claude, Codex, Copilot, and
OpenCode:
- Shield guarded default (every action prompts)
- ShieldCheck classifier/auto-reviewer vets prompts
- ShieldPlus edit tools pre-approved, others still prompt
- ShieldEllipsis plan / read-only deliberation
- ShieldOff fully permissive, no prompts
Register the new icons in the client MODE_ICONS map. Old clients
downgrade unknown icon names to ShieldCheck through the existing
customModeIcons compat gate, so the wire contract is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(app): order permission modes by autonomy
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(chat): keep attachments visible after creating agents
Agent creation could receive the canonical user message before the client knew the new agent ID. Hand the local rich presentation to that message and preserve it through later timeline hydration.
* refactor(chat): clarify local message presentation naming
* fix(chat): clear pending state after message handoff
* feat(app): pin projects to top of the sidebar
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): dedicated Pinned sidebar section with UX fixes
Replaces the invisible float-in-place pinning with a Codex-style Pinned
section at the top of the sidebar that hoists pinned chats and projects.
- Pin-aware keyboard shortcut numbering (badges match visual order)
- Pinned chats show their project name as subtitle (context after hoisting)
- Pinned entries ordered most-recently-pinned first
- Collapsible Pinned section (persisted)
- Double-click guard on workspace pin toggle
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): move Pinned section above the Workspaces header
Pinned now sits at the top of the scroll, above the "Workspaces" section
header (which moves into the scroll below it), matching the market-standard
sidebar layout. Chevron sits beside the "Pinned" label.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): reveal Pinned collapse chevron on hover only
Extracts PinnedSectionHeader with its own hover state; the chevron hides
until hover on web and stays visible on touch/compact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): pin the active chat via keyboard shortcut and inline pin button
Adds a ⌘⇧P / Ctrl+Shift+P shortcut that pins the selected chat, mirroring
the worktree.archive action across actions/dispatcher/route-shortcut, gated
by `selected && supportsWorkspacePinning`. Also surfaces an inline pin button
on row hover (always-on for touch) alongside the kebab in the shared trailing
overlay, swapping Pin/PinOff by pinned state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(server): unify project and workspace pin handlers
Collapses the two near-identical pin.set handlers into a shared
handlePinSetRequest that owns control flow, error handling and the
workspace-update fan-out; each caller only supplies its registry apply
step and typed response payload.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): drop empty pinned project shells and empty Workspaces header
Pinning every chat of a project hoisted them into Pinned but left an empty
project header (plus its new-workspace ghost row) below, duplicating the
project. Skip projects emptied by pinning while keeping genuinely empty
projects. Also hide the "Workspaces" section header when no unpinned projects
remain, so it no longer floats above nothing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(protocol): correct workspacePinning COMPAT version to v0.1.105
Feature ships in 0.1.105 per the changelog, not the placeholder v0.1.103.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): hide redundant chat pin control inside pinned projects
A pinned project already hoists all its chats to the top, so the per-chat pin
was a no-op that just toggled a dead icon. Suppress the inline pin button, the
kebab pin item, and the pin keyboard shortcut on chats whose project is pinned;
they still work for chats in unpinned projects.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): hide project pin control on empty projects
Project pin state is derived from workspace descriptors, so a project with no
workspaces would set pinnedAt on the server but emit no descriptor update — the
pin silently never surfaced and the user got no feedback. Offer the pin only
once the project has a chat, matching how chat pins are suppressed inside
already-pinned projects.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): drop collapsed Pinned rows from keyboard shortcut numbering
When the Pinned section is collapsed its chats and projects are hidden, but the
shortcut model kept counting them: visible unpinned rows showed off-by-N badge
numbers and pressing a number key jumped to a hidden pinned chat. Exclude the
pinned rows from numbering when Pinned is collapsed, matching what renders.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): hydrate pinned sidebar rows
* fix(app): keep filtered workspace controls reachable
* feat(app): scope sidebar pinning to chats, drop project pinning
Per PR review: pinning whole projects overlapped with project reordering
and mixed projects into the pinned list. Keep chat pinning only.
- Remove the project.pin.set RPC, its server handler, the projectPinnedAt
descriptor field, and the client setProjectPinned method.
- Drop the project pin control, isProjectPinned reads, and the suppressPin
plumbing (its only source — a pinned project — is gone).
- Fold pin-aware shortcut ordering into SidebarModelProvider (the live
shortcut owner) and delete the orphaned use-sidebar-shortcut-model hook,
so pinned chats and their shortcut numbers share one projection.
- Prune the now-unused project pin i18n keys across all locales.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(server): inline the pin handler into the workspace path
With project pinning gone, handlePinSetRequest had a single caller. Fold it
back into handleWorkspacePinSetRequest and drop the project/workspace
generalization.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert: keep main's use-sidebar-shortcut-model untouched
This hook (and its test) already live on main. The scope-down had deleted
them, but that's out of scope for this PR — the pin-aware shortcut ordering
lives in SidebarModelProvider now, so main's hook is left exactly as it was.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(protocol): align workspacePinning COMPAT version in websocket-server
Match the v0.1.105 version already corrected in messages.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): reshape pinned sidebar updates
* chore(app): remove stale pin button styles
* refactor(app): simplify pinned workspace rows
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>