repo social banners are 2:1, so that shape fills the slot without a crop;
the recommended size is in the README/docs. Desktop AgentPluginRow gains
catalog_version to match the generated contract.
The 40-hex sha stays the release, but nobody reads one. Entries may now add
`version: "1.4.0"` (free-form, <=32 chars, never parsed) and `image:` (an https
URL on raw.githubusercontent.com / github.com / *.githubusercontent.com).
Why GitHub-only: the Desktop catalog browser deliberately never fetches from
third-party hosts, and a raw URL pinned to the entry commit is as immutable as
the sha it decorates.
Readers updated together: PluginCatalogEntry + entry_from_mapping (drop with a
warning, entry survives), validate_plugin_catalog.py (admission error), the
site extractor (drop, never fatal), the /docs/plugins card (banner + version
pill + "1.4.0 @ abcd1234" pin), the CLI table/info (pin_label), the TUI-gateway
plugin row (catalog_version -> Desktop "Update to 1.4.0"), and the Desktop
catalog detail header (image).
* fix(desktop): foreground spawn priority through requestGatewayForAgent/retainGatewayForAgent and the session-create dials
The #102281/#104139 chain tagged every activation-door dial
(openGatewayForAgent/openGatewayForProfile/ensureGatewayForAgent/
sharedPrimaryRoute) as 'foreground' so a user-initiated open takes the
pool's reserved slot instead of queuing behind background roster
hydration. requestGatewayForAgent and retainGatewayForAgent never got a
spawnPriority, so they always dialed 'background', and they are the only
dial path createBackendSessionForSend (first send on a fresh chat) and
openNewSessionTile ("New session" / tab-strip "+") use. On a saturated
3-slot local pool the user's first message or new-tab click could wait
out the background slot timeout.
Add `{ spawnPriority }` options (main's requestGatewayForProfile shape,
5eb0ed4531) to both functions and forward it into every dial they make:
requestGatewayForProfile on the scope===key and primary-registry
branches, isAttachedSharedRemote, openSecondary, and the plain-profile
retain branch's gatewayForProfile(key, true, ...) which neither #105390
nor #110354 covered. The four session-create call sites pass
'foreground'.
Tests: gateway-spawn-priority.test.ts gains both polarities for
request/retain plus the plain-profile retain branch; the
use-session-actions and default-new-session matchers pin the tagged
dial. SpawnPriority is now exported for the SDK.
* fix(desktop): canonical Bot Chat lookup dials foreground through host.requestProfile options
Clicking a bot in the roster runs findExistingCanonicalChat ->
requestForBot(bot, 'session.list') -> host.requestProfile(route, ...)
-> requestGatewayForAgent with no intent, so the first RPC of the
gesture, the one that cold-spawns the bot's backend on a local pool,
dialed 'background'. With three roster backends already hydrating the
click produced no backend activity and the fail-closed lookup surfaced
as a "try again" toast (#105104). The later host.openSession is already
foreground, but it never runs until this lookup returns.
host.requestProfile gains a fifth `options?: { spawnPriority }`
argument; `timeoutMs` stays the fourth positional so no existing caller
changes shape, and the SDK keeps its exact call arity when no options
are given. requestForBot takes the same options and forwards them only
when set, so passive roster warming (profiles.list, ui_meta) still
dials background. The canonical `session.list` and the `session.create`
that follows on a first-ever open both pass 'foreground'.
Tests: profile-routing.test.ts (options form with and without a
timeout), routing.test.ts (tag forwarded, untagged call keeps three
args), canonical-chat-registry.test.ts (session.list and session.create
carry foreground; session.title does not).
---------
Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
Co-authored-by: jxfjosh <282433633+jxfjosh@users.noreply.github.com>
Plugin entry points (Kanban palette row, statusbar count, ⌘⌥N) route
through the SDK's host.navigate, which only wrote the hash. Re-issuing
the current route fires no router event, so the page stayed behind a
focused session tile. Reveal imperatively via syncWorkspaceRoute, the
same door navigateToWorkspacePage gives the sidebar and keybinds.
Supersedes #111898.
Co-authored-by: xxxigm <tuancanhnguyen706@gmail.com>
* fix(desktop): route project writes to the live profile in All profiles
All profiles is a sidebar filter, not a write target. Appearance, rename,
add-folder, and delete now use the active gateway profile, matching create.
* test(desktop): type the all-profiles write fixture as ProjectInfo
---------
Co-authored-by: Guillaume <guillaume.eynaud@icloud.com>
* test(desktop): cover access-proxy headers on OAuth login URLs
Connections extra headers must apply to /login and OAuth-partition
requests, not only an exact remembered WebSocket URL.
* fix(desktop): send extra gateway headers on OAuth login
Cloudflare Access service tokens never reached the OAuth sign-in
window because they were injected only on defaultSession and v1
remote config. Attach them on OAuth partitions and loadURL too.
* fix(desktop): strip CR/LF from remote gateway header values
The PR this builds on sanitized header values in remote-ws-headers.ts, on
the resolution path only. That left the sibling consumers of
decryptRemoteHeaders untouched: buildRemoteConnection and both
connection-test paths still handed raw values to setHeader.
Sanitize on the canonical normalizer instead, where header NAMES are
already validated against REMOTE_HEADER_NAME_RE, so every consumer
inherits it. normalizeRemoteHeaders only trim()'d, which strips a
trailing newline but leaves an EMBEDDED CR/LF intact — the actual
request-splitting vector.
* refactor(desktop): drop the duplicate header normalizer from remote-ws-headers
Header values now arrive sanitized from decryptRemoteHeaders, so the
second sanitizeRemoteHeaderValue/sanitizeRemoteHeaderMap pair here was a
duplicate of the canonical one in connection-config.
The routing test keeps its original job (Connections headers reach
/login, longest base URL wins); the CR/LF assertions move to
connection-config.test.ts where the stripping now lives.
* perf(desktop): memoize decrypted remote header sources
onBeforeSendHeaders is now attached to every OAuth partition as well as
defaultSession, and headersForRemoteRequest decrypts EVERY registry
connection's headers. A safeStorage-encoded value costs a keychain
round-trip per read, so that was N decryptions per subresource request
where main previously did at most one behind a modeIsRemoteLike gate.
Key the collected sources off the connection config and registry cache
objects, which both readers already refresh on mtime change.
---------
Co-authored-by: xxxigm <tuancanhnguyen706@gmail.com>
A visible default-profile duplicate of the same stored session id was winning
visible-row-first resolution and swapping the main transcript onto the root
database. Prefer the verified tile owner for the active runtime, while still
letting a real visible row override a stale leftover hint.
Co-authored-by: Cursor <cursoragent@cursor.com>
scroller, but the class is unconditional and has two problems:
- Expanded (no max-h): the body grows to its content and never scrolls
itself, so `contain` has nothing to contain — it eats the wheel entirely,
leaving a dead zone where neither the body nor the thread viewport scrolls.
- Preview with short content: same dead zone when content is under max-h-40.
`contain` only "works" when the body actually overflows — exactly when a
reader most wants the wheel to keep going. And it makes thinking the only
scroller in the app that doesn't chain to the transcript (settings panels,
code blocks, tool output all chain; chaining is the macOS platform default).
Drop `overscroll-contain` entirely. The body keeps `overflow-auto`: it
consumes the wheel first and, once at top/bottom, chains to the thread
viewport like every other block. Regression test asserts no
`overscroll-contain` in preview or expanded states.
Verified with a real-wheel Playwright repro (dead zone only with the
post-#95783 classes).
(cherry picked from commit 3c5b3bbff836fe8b05a04f2d219ae550f61ceded)