Commit Graph
2656 Commits
Author SHA1 Message Date
Mohamed Boudra cc2012be7c fix(app): prioritize side panel launcher actions
Lead with supporting workspace tools when the launcher is in the Side panel, while ordinary panes continue to lead with agent creation.
2026-08-22 22:24:35 +02:00
Mohamed Boudra b139046a3e fix(app): show the New agent shortcut
The command center resolved New agent through the New tab binding, so it advertised Cmd+T despite creating an agent. Resolve the target-agent binding instead and cover the mapping.
2026-08-22 22:17:13 +02:00
Mohamed Boudra 482bbb2910 fix(app): order new tab launcher actions by workflow 2026-08-22 22:16:48 +02:00
Mohamed Boudra 4a5a8d469c fix(app): keep compact model options scrollable
The compact model sheet had no sheet-aware scroll owner when profiles filled its viewport, leaving the controls below them unreachable. Keep the root provider view inside Gorhom's scroll container so native sheet gestures work on both platforms.
2026-08-22 22:06:27 +02:00
Mohamed Boudra c60fa098ac Give every pane a browser-style New tab (#3715)
* feat(app): replace empty panes with New tabs

Give every retained pane an independently identified New tab, replace it in place when a destination is chosen, and preserve split close and move invariants. Keep existing workspace startup and compact explorer routing explicit, with focused unit and browser regression coverage.

* test(app): count content tabs in subagent integration

* fix(app): preserve workspace shortcut ownership
2026-08-22 21:30:15 +02:00
Mohamed Boudra 33cf6cf337 perf(app): defer replica persistence until user inactivity
Whole-replica capture and serialization blocked the JS thread during navigation and streaming. Defer that work until the user has been inactive while allowing long-running streams to persist without becoming the idle signal.
2026-08-22 20:57:13 +02:00
Mohamed Boudra 65fbe38dd3 Restore compact and stable chat timelines (#3716)
Restore compact message and footer spacing, reserve independent composer-pill clearance for transcript content and floating controls, and keep visible web tool-call expansion from shifting the reading position.
2026-08-22 20:50:20 +02:00
Mohamed Boudra a69dca2907 Expand workspace panel actions and keyboard navigation (#3685)
* fix(app): keep file navigation in place

Retargeting a File tab allocated a new tab identity, causing the tab track to unmount and remount during navigation. Preserve the identity for File-to-File changes while retaining new instances for cross-kind replacements.

* feat(app): expand workspace panel actions

Keep the Command Center aligned with workspace tab and pane primitives. Add explicit panel placement, focused open behavior, keyboard navigation for empty panes, and a Changes pill that toggles the visible Side panel without closing its tab.
2026-08-22 14:15:23 +02:00
Mohamed Boudra 3029b5991a Surface workspace changes from agent panes (#3682)
* feat(app): refine workspace pane actions

Prioritize Changes when available, keep Files useful before a selection, and reserve desktop diff clicks for selection so comments require the explicit affordance. Move the Changes shortcut to Shift+G to avoid consuming a likely copy variant.

* feat(app): improve access to workspace changes
2026-08-22 12:20:21 +02:00
Mohamed Boudra b4e0ff7685 fix(app): restore native composer auto-growth (#3681)
Native multiline inputs were always scrollable, which kept iOS content-size measurement pinned to one line. Keep native sizing intrinsic below the cap and enable internal scrolling only after the composer reaches it.
2026-08-22 11:36:22 +02:00
Mohamed Boudra 4c6b79dc44 feat(app): refine agent profile creation (#3680)
Keep profiles as the richer primitive while restoring the familiar star cue and clearer guidance for agent selection. Account for borders in shared field geometry so controls preserve their declared heights across form consumers.
2026-08-22 11:16:28 +02:00
Mohamed Boudra c763b80824 fix(files): keep large source previews responsive (#3665)
Large read-only files bypassed the editor's virtualization and eagerly mounted every highlighted token, which could stall the renderer for seconds and consume gigabytes of memory. Route source previews through bounded platform renderers and reject reads above their display budgets before accumulation.
2026-08-22 10:13:31 +02:00
Mohamed Boudra 02f59b1a26 test: remove relay enable transient state assertion 2026-08-21 17:23:13 +02:00
Mohamed Boudra f660fcf2e5 chore(release): cut 0.5.0-beta.4 2026-08-21 16:35:11 +02:00
Mohamed Boudra 75b0eaf8a8 test(app): use platform-aware editor chords in file-editing e2e
CodeMirror's mac keymap binds doc start/end to Cmd and gives Ctrl-a the
emacs line-start behaviour, so the Control chords in these specs did
nothing on macOS and only passed on Linux CI.
2026-08-21 15:20:15 +02:00
Mohamed Boudra 2caecdaae8 Use a dropdown for default send behavior (#3644)
* fix(app): make default send a dropdown

Keep the compact three-choice setting behind one anchored control while preserving the selected behavior and persistence flow.

* test(app): select default send behavior from its menu

The agent-message submission coverage configures send behavior through Settings, so it follows the dropdown interaction rather than the removed segmented buttons.
2026-08-21 15:10:14 +02:00
Mohamed Boudra 29883cb797 fix(app): lay out the tab modified dot inline
The dot was absolutely positioned at the chip's right edge, so it painted
on top of a truncated label. Render it as the last flex child of the tab
content row and widen a modified tab by the dot plus its gap so the label
keeps the room it had.
2026-08-21 15:04:14 +02:00
Mohamed Boudra c527b203c4 test(app): stabilize consecutive turn e2e 2026-08-21 13:58:21 +02:00
Mohamed Boudra 602860625c Give readable content its own text size (#3637)
* feat(app): separate content typography from interface size

Readable prose benefits from its own scale without inflating navigation and controls. Preserve existing users’ rendered size during migration while giving fresh desktop installs a 15px content default.

* test(app): cover independent content typography

The appearance journey now verifies that changing interface size leaves content size intact. Timeline traversal yields when a test deliberately holds the requested page, so the helper no longer waits on its own gate.

* test(desktop): cover independent content typography

The desktop appearance regression follows the renamed Interface control and verifies it does not alter Content size.
2026-08-21 12:35:23 +02:00
Okan YILDIRIM e1805ef542 feat(plugins): add theme contributions
Adds plugin-contributed light and dark app themes, including persistence, host-aware coalescing, fallback behavior, documentation, examples, and browser/iOS QA.
2026-08-21 11:48:12 +02:00
Mohamed Boudra a7f3bf7abb fix(app): open Changes on the populated comparison (#3636)
* fix(app): restore smart Changes comparison selection

The tab migration persisted the selected comparison, pinning Changes to an empty view after checkout state changed. Keep comparison overrides ephemeral per surface and expire them when dirtiness changes, while preserving only presentation state in tabs.

* chore(app): date Changes compatibility cleanup
2026-08-21 11:23:43 +02:00
Mohamed Boudra 04624cb419 fix(app): show Setup tabs only for failures (#3634)
* fix(app): only surface failed workspace setup

Running and successful setup snapshots were treated as reasons to seed a background Setup tab, adding noise to every workspace with setup commands. Seed only failed setup and classify that tab as main-workspace content so the failure stays visible without stealing focus.

* fix(app): surface failed setup once

Keep the failed-setup surfacing claim with the setup snapshot so closing its background tab remains respected after the workspace remounts. Explicitly prefer the main pane for a new failure tab without moving a user-placed existing tab.

* fix(app): keep setup tabs out of side panel

A preferred main-pane placement can fall back when users remove that pane. Keep that implicit fallback out of the focused Side panel when another workspace pane is available.
2026-08-21 11:17:04 +02:00
Mohamed Boudra 32d6b18dbf Open summarized tool calls in mobile sheets (#3619)
* feat(app): open summarized tool calls in mobile sheets

Keep compact timelines readable by moving summarized tool-call groups into a live sheet while preserving the existing inline desktop interaction. Individual calls reuse the current detail sheet and stack above the group.

* fix(app): keep mobile tool call sheets current

Restore live group-sheet auto-follow, replace the mocked component coverage with real compact and desktop browser journeys, and remove the touched theme subscriptions.
2026-08-21 08:53:42 +02:00
DanielWalnutandMohamed Boudra cf4664ddf1 feat: add MiniMax Code ACP provider (#3457)
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-08-21 08:53:24 +02:00
Christoph LeiterandClaude Opus 5 c3d37eba4b feat(app): filter the sidebar by project (#3563)
The sidebar can be pinned to a set of hosts or to a set of labels, but
not to a set of projects. If you keep many projects on one host, the
project is the filter you want.

Add a Project page to the sidebar display menu, next to Host. It
multi-selects projects, and an empty selection means every project.

Apply the filter at two points. Project grouping reads the projects
array, but Status grouping builds its rows from the workspace entries.
A filter applied to only one of the two leaves the other mode
unfiltered.

Apply nothing when a stored selection names no visible project. The
project list is narrowed by the host filter, and it is empty before any
host connects, so dropping keys that do not match would destroy the
selection on every cold start.

Count the project filter in the sidebar header gate. Pinning every chat
in a project drops that project from the unpinned list. Filtering to
that project then removed the header, and the header carries the display
menu, which is the only way back to the Project page.

Generalise the label filter empty state to all sidebar filters, and move
its strings out of the labels namespace.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:51:35 +02:00
Tuzhibaev TimurandMohamed Boudra 33224ace47 Russian localization fixes by a native speaker (#3586)
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-08-20 23:22:19 +02:00
Mohamed Boudra 19a7d26349 feat(app): make the side panel user-directed (#3605)
* feat(app): let the file panel close its tree rail

The file panel's tree rail was permanent on desktop — the changes panel
had a folder-tree toggle in its toolbar, the file toolbar had nothing, so
the rail was there whether you wanted it or not.

The changes panel's private DesktopTreeToggle becomes a shared
TreeRailToggle so both toolbars render the same affordance; that also
drops the treeToggleStyle prop threaded through ChangesToolbarProps,
which recomputed exactly what the component already built internally.
The i18n keys move out of workspace.git.diff into workspace.tree, since a
shared component reaching into the git namespace would rot.

The two rails keep independent flags — the file rail in panel-store, the
changes rail in useChangesPreferences — so closing one leaves the other
alone. FileTreeToggle wires itself to the store rather than taking a
callback: the bar renders several components below FilePanel, where the
rail actually lives.

Both rails also get narrower (320 -> 260 default, 200 -> 180 minimum).
The persist version bump resets treeRailWidth on purpose: nearly every
existing install carries the old 320 default, so without the reset the
narrower rail would only ever reach new users. Anyone who dragged their
rail to a deliberate custom width loses it once.

Note that MIN_CONTENT_WIDTH still clamps the rail to
`containerWidth - 240`, so in a narrow pane the new default never
applies — that floor, not the default, is what sets the width there.

* feat(app): make the side panel an empty workspace surface

The fixed, hideable side panel keeps supporting context beside an agent without seeding content. Pane-local choices stay local, while implicit supporting opens follow a persisted routing preference.

* test(app): exercise side panel policy through its capability boundary

The browser rail journey already covers the real tree-toggle behavior. Side-panel policy tests now pass their platform capability through the public boundary instead of replacing a production module.

* test(app): expect an empty side panel after upgrade

* test(app): expect an empty first split pane

* test(app): account for the empty side panel in split retention

* feat(app): make workspace panel tabs independent

Give explicit New actions fresh tab instances, keep panel state opaque to tab management, preserve File navigation context, and stabilize uncached File transitions.

* fix(app): preserve file conflicts during preview refresh

Keep the current preview mounted for same-target reads and conversions so the editor retains its conflict observation state. Clear retained previews only when the file target changes.
2026-08-20 21:49:23 +02:00
Mohamed Boudra 2a0a0ff656 Keep mobile workspace switching responsive with long chats (#3610)
* perf(app): bound native workspace mount work

Native workspace switches retained inactive React trees and mounted large chat histories in aggressive list batches, making Hermes and Fabric work scale with prior usage.

Retain only the active native workspace, reveal a shared turn-aligned history window locally before daemon pagination, preserve desktop retention semantics, and pace native list mounting.

* test(app): cover bounded history journeys

Exercise local history reveal and hidden outline jumps through the production browser viewport while preserving held-page pagination and scroll anchoring.

* fix(app): reveal fetched outline targets

A bounded merged jump could load its target before the mounted history window, leaving the pending jump unable to scroll. Reveal fetched hidden targets through the existing history-window capability, then scroll once they mount.

* test(app): follow anchored history reveals

History-start helpers could stop at a transient edge before local window expansion restored the content anchor. Retry the real wheel or keyboard action after geometry settles until the timeline remains at history start.

* test(app): wait for history anchor release
2026-08-20 20:52:12 +02:00
c6b04f302a fix(server): stop a steered prompt from deadlocking on a permission (#3585)
* fix(server): stop a steered prompt from deadlocking on a permission

Claude parks its turn inside the blocking canUseTool callback while a
plan approval or a tool prompt waits for an answer. The SDK stops to
read its input stream there, so a message sent at the open card is
queued and never read. The user must reject the card or interrupt the
agent by hand, and then send the message a second time.

Deny the pending permissions after the steer is admitted. The prompt is
already in the SDK input queue when the permission callback returns, so
Claude reads it in the same turn. Plan mode stays on and no turn
restarts.

One pass is not enough. A turn that made several tool calls opens its
next request 10 to 300 milliseconds after the previous one is answered,
so a single snapshot advances the batch by one and parks the turn again.
The Claude adapter already tracks the steers the SDK has not read, so
expose that as an optional AgentSession.hasUnreadSteer and answer each
new request while that method reports the prompt as unread. The release
stops the moment the prompt is read, so a permission the agent asks for
after it acts on the message still reaches the user.

Only the two entrypoints that carry a message from a person set the
flag. A scheduled prompt, a child-finished notification, and an
agent-to-agent prompt must not answer a card the user is looking at.

Denial left no timeline row, so a denied plan disappeared from the
transcript. Claude and Codex now push a plan_approval row with
approved: false. For Codex the row comes from resolvePlanPermission,
which is the path that dismissPendingPlanApprovals also uses.

In the app, queue mode held the message until the turn ended, and a
parked turn never ends. The composer now sends while a permission is
pending, and the button label agrees with the submit path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(server): keep a post-prompt permission out of the release

The release loop answered the pending requests before it asked whether
the agent had read the prompt. The agent can read the prompt during a
poll interval and then open a permission request because of it. The next
pass denied that request, so a decision that belongs to the user was
made for them.

The loop now asks first and answers second. The first pass stays
unconditional, because a provider that does not report unread steers
must still get one release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(server): make steer permission clearing provider-owned

Keep the daemon policy explicit while letting each provider queue the steer and release permissions that block it. This removes timing-based polling and preserves rejected plans as canonical timeline rows.

* fix(server): preserve omitted steer options

Only construct provider steer options when permission clearing is requested. System prompts otherwise keep the existing undefined options contract.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-08-20 14:02:00 +02:00
Mohamed Boudra 975dbd433b Fix project icon cache reuse in New Workspace (#3600)
* fix(app): reuse project icon cache in new workspace

New Workspace omitted each host placement's effective icon revision, so its icon queries could not share the sidebar's memory or persistent cache entries. Carry that revision through the target projection.

* refactor(app): centralize project icon targets

Project icon consumers constructed equivalent cache targets independently. One target factory now carries each host placement revision through the sidebar, New Workspace, Projects, and Project Settings.

* test(app): inline project icon fixture
2026-08-20 13:15:58 +02:00
Mohamed Boudra dd8efba552 chore(release): cut 0.5.0-beta.3 2026-08-20 09:12:30 +02:00
Mohamed Boudra 4391c47615 fix(app): clear composer text input upon sending on mobile (#3564)
* fix(app): clear mobile composer input on submit

Submitting a prompt on mobile failed to clear the native text input because EditingTextInput.replaceText previously only called setNativeProps({ text }), which is ignored on Fabric and PasteTextInput.

Fix this by:
- Delegating replaceText in EditingTextInputNative to inputRef.current.replaceText when available, clearing via clear() when text is empty, and falling back to setNativeProps.
- Patching @mattermost/react-native-paste-input to expose replaceText(text, selection) backed by native Commands.setTextAndSelection, updating clear() to use live event counts, and preventing useImperativeHandle closures over null refs during initial mount.
- Registering the patch in scripts/postinstall-patches.mjs and adding unit tests for native text input replacement and clearing.

* fix: update nix npm dependency hash
2026-08-19 18:53:26 +02:00
Mohamed Boudra ccd991b3e3 chore(release): cut 0.5.0-beta.2 2026-08-19 16:30:43 +02:00
Mohamed Boudra 345638dc7f fix(app): retire the docked explorer sidebar
Upgrading to 0.5.0-beta.1 left the old docked explorer sidebar showing
next to the new explorer pane, with no way to dismiss it.

The sidebar rendered off the persisted `desktop.fileExplorerOpen` flag.
#3287 rerouted the explorer toggle to the workspace-layout explorer pane,
so nothing on a wide layout ever wrote that flag false again, and no
migration cleared it. Assistant directory links and review attachments
could still set it. On Windows and Linux the sidebar's close button was
suppressed because the OS window controls own the top-right corner, so
there was no affordance at all.

The explorer is now one surface picked from two facts — is the layout
compact, and can the platform render split panes:

  compact                -> slide-over overlay (panel store)
  non-compact + splits   -> dedicated right pane (layout store)
  non-compact, no splits -> ordinary tabs in the focused pane

`workspace-tabs/explorer-surface.ts` owns that choice and every entry
point routes through it, so the toggle, Ctrl+E, Ctrl+., directory links,
review attachments and side file opens can no longer disagree about where
the explorer lives.

Pane splits stay gated on `isWeb`: they depend on dnd-kit and DOM-backed
accessibility helpers. Native tablets are non-compact without splits, so
they get explorer tabs rather than a second pane — deliberately trading
side-by-side agent and Changes for a surface that only renders what the
single-column tab row can actually reach.

Ctrl+. is handled by the workspace screen rather than the app shell. The
pathname alone cannot identify the active workspace: desktop cold-starts
at "/" and restores the remembered workspace from route params, so a key
derived from the pathname is null exactly when a workspace is mounted,
and the explorer reads as permanently closed.

`desktop.fileExplorerOpen` and `explorerWidth` are dropped by a migration
to panel-state v15 but stay declared in the persisted schema. That schema
is a strict object and the validated storage deletes the whole entry when
parsing fails, so removing the keys outright would wipe every upgrading
user's sidebar width, expanded paths and diff-collapse state.
2026-08-19 15:52:51 +02:00
Mohamed Boudra 929d2b717e fix(plugins): publish the plugin SDK under the @getpaseo scope
The daemon imports the plugin SDK at runtime, but the workspace was named
@paseo/plugin and marked private, so it was never published. 0.5.0-beta.1
shipped @getpaseo/server depending on a package that does not exist and
`npm install @getpaseo/cli@beta` failed with a 404 for every user.

Two things hid it. sync-workspace-versions only rewrites deps matching
@getpaseo/*, so the "*" range on @paseo/plugin survived untouched into the
published tarball. And `npm pack --dry-run`, which is all release:check and
CI run, lists files without ever resolving the dependency graph.

Renaming to @getpaseo/plugin rather than publishing the old name: @paseo is
not our scope on npm, so that name could never have been published at all.
The rename also brings the package under the version-sync filter, which is
what pins it correctly from here on.

Plugins scaffolded against the old name still import @paseo/plugin. Both
spellings resolve through plugin-sdk-specifiers.ts, tagged COMPAT with a
removal date, so existing plugins keep loading.

This does not repair 0.5.0-beta.1 — a published tarball's dependencies
cannot be changed. The package publishes with the next release.
2026-08-19 15:52:51 +02:00
Mohamed Boudra ce905d9483 fix(app): open new tabs in the focused pane
Preserve ambient placement for reconciliation and background opens while routing user-directed tab creation through the focused visible pane. Defer terminal reconciliation during creates so a push cannot preempt the intended pane placement.
2026-08-19 15:52:51 +02:00
Mohamed Boudra e883b692b0 chore(release): cut 0.5.0-beta.1 2026-08-19 12:40:33 +02:00
Mohamed Boudra e55dd5a212 fix(search): match complete paths in file and project search
Treat slash-separated fragments as searchable text and use the same full-path fuzzy matcher for daemon results and remembered project paths.
2026-08-19 12:18:23 +02:00
Mohamed Boudra 91024bc029 fix(plugins): install local plugins on packaged desktop
The plugin compiler spawned esbuild through app.asar, and the subprocess
imported the React client barrel. Point esbuild at the unpacked binary,
add a react-free @paseo/plugin/server entry, and pass a typed PluginTheme
so plugin UI follows the active theme on desktop and mobile.
2026-08-19 11:54:55 +02:00
Mohamed Boudra 556bd0d36f feat(app): open composer subagent tabs in the explorer pane
Tapping a subagent or provider subagent from the composer track now opens its tab in the explorer companion pane when desktop split panes are active, keeping the parent agent visible in the primary pane.

Mobile and compact layouts continue using single-pane tab navigation, and cross-workspace subagents continue routing to their own workspace.
2026-08-19 11:54:55 +02:00
Mohamed Boudra 863090e0d7 chore(app): regenerate terminal webview bundle
The checked-in bundle could not be reproduced from a clean checkout. It
was built from a working tree with hand-modified xterm sources in
node_modules that were never captured as a patch, so it carried terminal
behaviour that exists nowhere in the repository.

It was also one revision stale against our own source: 04fe30a3e flipped
the ResizeObserver call in terminal-emulator-runtime.ts to
shouldClaim: false and never rebuilt the bundle afterwards.

Rebuilding from the published @xterm/xterm picks up that resize fix and
puts the artifact back in sync with the sources anyone can build from.
2026-08-19 10:29:39 +02:00
Mohamed Boudra c689f8bb94 fix(app): stop quadratic diff text measurement freezing the renderer
Both advance-measurement paths measured a line by re-measuring a growing
prefix per grapheme, so one n-grapheme line shaped n^2/2 characters. The
web canvas measurer and the model fallback each carried a copy, and the
native Skia branch had the same shape via getRectsForRange(0, end) per
grapheme. Nothing caps line length client-side, so one long generated
line in a diff pinned the renderer main thread for minutes and the app
read as frozen while compositor animations kept running.

Advance measurement now has one owner in text-measurement.ts: 64-grapheme
chunks measured against an accumulated base, with chunk boundaries snapped
to shaping-safe seams so ligatures and contextual scripts keep their exact
prefix-measured widths (the xfi/Arabic contract in model.test.ts is
unchanged). A 1.2M-char line drops from a ~331-minute projection to under
a second; measurement-cost.test.ts pins the linear shaped-character
budget so the quadratic cannot come back.
2026-08-19 10:14:00 +02:00
Mohamed Boudra b098a2640d fix(app): tighten the mobile gap under composer track pills
The pill bar floats at the foot of the stream section and the composer has
no top padding, so the bar's paddingBottom is the entire pill-to-composer
gap. Desktop was tuned down to 10 earlier; compact kept the original 16 and
read as a hole on phone. Drop it to 8.

Same constant paddings the import pill row, which tightens with it.
2026-08-19 09:47:25 +02:00
Mohamed Boudra b638f7d3ee fix(app): stop PR detection from stealing explorer pane focus
observePullRequest used to reveal and focus the explorer pane, and the persisted focus leak then made ambient tab opens (sidebar agent clicks, reconcile auto-open) insert agent tabs into the explorer pane while the main pane sat empty.

PR detection now only background-adds the PR tab to the explorer pane, with no reveal and no focus change, so a tab the user already opened elsewhere stays where it is.

Placement policy is centralized in workspace-layout-actions.ts (resolvePlacementPane / insertNewTabIntoPane): explicit paneId wins, and ambient entity opens (agent, provider_subagent, terminal, draft, browser) reroute from the explorer pane to the primary pane. explorerPaneId is a required input so callers cannot silently fail open.

Pane-rail + affordances thread explicit paneId instead of focus-then-open.
2026-08-19 08:50:31 +02:00
Mohamed Boudra e9373c026b feat(app): create agent profiles from the model chooser (#3533)
* feat(app): create agent profiles from the model chooser

Restore quick model pinning by creating an agent profile directly from a
model row. A trailing plus on unprofiled model rows opens the pre-filled
profile form; rows that already name the provider+model show the profile
glyph and open it for edit.

- seed the profile form model with provider, model, and suggested name
- expose useAgentProfileEditor from @/agent-profiles for the write path
- hover-refactor model rows per docs/hover.md and add the trailing action
- wire the composer desktop selector and compact model sheet (close-then-open)

Closes #3529

* fix(app): match favourites star position for model row profile action

The star sat after the selection checkmark at the far-right edge. Put the
profile create/edit action in the same slot instead of before the checkmark.

* fix(app): disambiguate per-model create profile action

The per-model + buttons shared the accessible name "Create profile" with
the empty-state row, which broke the empty-host picker test (strict mode
matched five buttons). Name the action "Create profile from <model>" so it
is specific and screen readers get the model context.

* fix(app): keep per-model create label out of model row names

Naming the action "Create profile from <model>" put the model label into a
second button on the row, so regex selectors targeting a model row by name
matched both. Use a generic "Create profile from this model" label that
neither collides with the empty-state "Create profile" row nor with model
row names.
2026-08-19 07:46:47 +02:00
Mohamed Boudra 60706feb34 fix(test): stabilize tool call shimmer lifecycle gate in browser e2e 2026-08-18 23:34:05 +02:00
Mohamed Boudra 936fd4b6da fix(plugins): avoid eager React Native spread during client evaluation
Spreading ReactNative at module scope evaluated getters for legacy or unlinked modules (such as PushNotificationIOS), which called new NativeEventEmitter(null) and crashed native startup. Pass ReactNative directly to runtimeRequire instead.
2026-08-18 23:26:51 +02:00
Mohamed Boudra f1eeef32ac test(e2e): unbreak tool call shimmer gate and increase eviction test timeout 2026-08-18 22:51:11 +02:00
Mohamed Boudra 25fbc939e0 feat(app): give composer track pills a segment per state
A mixed fan-out used to collapse the pill to its single most urgent
state, so a red dot beside "1 failed" over a child that was still
working read as the whole fan-out having stopped. Every state present
now gets its own mark and count, in the sidebar's status-group order.
2026-08-18 21:51:25 +02:00
Mohamed Boudra 5731f016c2 feat(app): default to steering when sending into a running agent
Steer is the better default for Enter while an agent is running, but
flipping DEFAULT_CLIENT_SETTINGS alone would only reach new installs.
Client defaults are materialized into storage — the first load writes the
whole default blob and every save re-stamps it — so every existing install
already has "interrupt" on disk, and a stored "interrupt" cannot be told
apart from one the user actually chose. A one-time migration flips them
all; picking "interrupt" afterwards sticks.

The applied-migration marker sits in its own storage key rather than in the
settings blob: StoredAppSettingsSchema is strict and readValidatedJson
clears the value when parsing fails, so an unknown field there would wipe
every setting for anyone who downgrades.

migrateAppSettings owns both of its writes so the settings blob always
lands before the marker. A failed marker write leaves the migration to
re-run harmlessly; a failed settings write must leave the marker unwritten,
or the migration is lost and the user keeps "interrupt" for good.
2026-08-18 17:12:24 +02:00