Merge origin/main into fix-mobile-text-selection-multi-paragraph

Both sides fixed Android inverted-chat selection scrolling (89d42e441 here,
#7131 on main); main's version of paseo-scroll wins.
This commit is contained in:
Mohamed Boudra
2026-10-16 13:17:35 +02:00
144 changed files with 5144 additions and 1158 deletions
+5
View File
@@ -43,6 +43,10 @@ So arrival sets a _target_ and the reveal rate is derived from the backlog inste
through that hook; the web viewport once skipped it and history hosts of a live tool group went
stale. A new field on `StreamLayoutItem` must be added to `areLayoutItemsEquivalent`, or sharing
silently stops.
- **Streaming cost must not grow with revealed history.** Reading older messages must not
make the next reply progressively more expensive to display. Reuse unchanged history work
while text arrives, and release obsolete window caches as the reader reveals more history;
window caches must stay bounded while their source timeline remains loaded.
## Native parsing cost
@@ -55,6 +59,7 @@ Completion restores one native text view per Mermaid-delimited segment, so selec
- **Smoothness (user-perceived):** `packages/app/e2e/browser/agent-stream-smoothness.spec.ts`, gated behind `PASEO_AGENT_STREAM_PERF_E2E=1`. Drives the mock provider's `bursty-stream` model and reports coefficient of variation of characters painted per frame (smoothness) plus p95 gap between visible updates (stalls). Both numbers are needed: a stalled stream is perfectly smooth.
- **Reproducing bursty arrival:** the `bursty-stream` model in `mock-load-test-agent.ts` emits uneven runs of tokens separated by idle gaps. Burst sizes come from a seeded generator, so a run repeats exactly.
- **Rate policy in isolation:** `computeRevealStep` in `packages/app/src/agent-stream/text-reveal.ts` is pure; `text-reveal.test.ts` covers convergence and burst flattening without a renderer.
- **Per-delta cost on a long timeline:** `npx vitest bench --project unit src/agent-stream/stream-tick.bench.ts` from `packages/app` replays 200 deltas over a 2,000-item timeline through the pure model and layout pipeline, with the default mounted window and with history fully revealed. It measures the JS work per delta, not paint.
Healthy numbers (2026-08, Expo web against a local dev daemon, real Claude Haiku agent, ~8.5s samples during active streaming). Setting `TEXT_REVEAL_HORIZON_MS` to 0 makes the reveal paint on arrival, which is how the baseline column was taken:
+3 -1
View File
@@ -124,7 +124,9 @@ This is the Android counterpart of the iOS local-simulator flow in [development.
Android focus and selection visibility requests must not reposition inverted timelines. The
`modules/paseo-scroll` package keeps React Native's scroll manager interface and returns zero for
child-reveal scroll calculations when the vertical scale is inverted. Dragging and explicit scroll
child-reveal scroll calculations when the view's transform matrix inverts the vertical axis. React
Native's Android list uses `scale: -1`, which decomposes into a 180-degree rotation with positive
`scaleY`; checking the scale property alone misses it. Dragging and explicit scroll
commands still work; non-inverted scroll views keep Android's default behavior.
Register this package before React Native's core package through its Expo config plugin. Normal
+1 -1
View File
@@ -484,7 +484,7 @@ The [Agent Client Protocol (ACP)](https://agentclientprotocol.com) is an open st
ACP agents communicate over JSON-RPC 2.0 on stdio. Paseo spawns the agent process and talks to it through stdin/stdout.
Paseo also ships an in-app ACP provider catalog for common agents, including CodeWhale, Cursor, DeepAgents, DimCode, Gemini CLI, Hermes, Qwen Code, and Kimi Code. Catalog entries create the same `extends: "acp"` provider config shown below.
Paseo also ships an in-app ACP provider catalog for common agents, including CodeWhale, Cursor, DeepAgents, DimCode, Gemini CLI, Hermes, Qwen Code, and Kimi Code. Most catalog entries create the same `extends: "acp"` provider config shown below. Hoplite activates its [bundled provider](plugins.md#built-in-plugins) instead.
### Adding a generic ACP provider
+4 -1
View File
@@ -589,7 +589,10 @@ host, kinds, and ids. Opening a cached workspace uses exact workspace and projec
directory scan. One invalid row is deleted and returned as a miss without affecting other rows.
An invalid directory row and its affected checkpoint cursor are repaired in one transaction, so a
later launch cannot accept a checkpoint for a partial baseline. Directory changes and their
checkpoint are also applied in one transaction.
checkpoint are also applied in one transaction. Reads wait for accepted changes to reach storage,
then retry only when a requested row changes during the read. Unrelated agent streams do not
invalidate the read. Directory baseline replacements and host removal or renaming still invalidate
the affected reads.
The cache is capped at 32 MiB and evicts whole hosts in least-recently-written order. Budget
bookkeeping may scan opaque row sizes during a deferred write, never during host registry startup or
+10 -11
View File
@@ -204,10 +204,11 @@ still-mounted content, paints its own surface under the safe areas, and turns
the surface translation off. The composer's single tree keeps the editing
surface at the same position in both modes, so the mounted editor, its native
buffer, selection, and focus survive the toggle and Send's clear always reaches
the live input. The one remount left is a viewport crossing the compact
breakpoint: the app shell swaps its gesture wrapper and the workspace screen
the live input. A viewport crossing the compact breakpoint still remounts it:
the app shell swaps its gesture wrapper and the workspace screen
mounts tabs in different trees, so every screen below remounts. The draft owner
restores the text and the editing session restores the caret and focus intent.
restores the text and the editing session restores the caret. Focus restoration
is limited to mouse-driven UIs, using the same fine-pointer policy as autofocus.
New workspace keys its sessions by draft in the app-level store for the same
reason. The centered form honors fullscreen by style alone: no centering, no
translation, setup fields hidden, slot filling the stationary viewport. The
@@ -217,14 +218,12 @@ sheet suspends pane focus, and the composer gives up input from the moment the
user asks for the compact agent list or explorer, before the panel's motion
settles. Pane focus itself stays put under those panels: the agent panel reads
it as "the user is in this chat" for attention clearing, and opening the agent
list must not clear the attention it shows. Focus restoration follows pane
focus, not the active composer. The editor keeps its focus intent only across
a blur that lands while its pane is not the user's (a retained tab hidden, a
sheet over it) and refocuses when the pane comes back. A blur while the pane is
theirs drops the intent in every layout mode, and the keyboard the compact
panels dismiss when they settle is such a blur, so closing the agent list or
explorer never reopens the keyboard. Keying restoration on the active composer
made that dismiss look like a hidden-tab blur and refocused on close.
list must not clear the attention it shows. On mouse-driven UIs, focus restoration
follows pane focus, not the active composer. Keying it on the active composer
made sidebar dismissal look like a hidden-tab blur and refocused on close.
Native and touch-web navigation must not replay old editor focus, including
when blur arrives after pane deactivation or the editor unmounts before blur.
Fullscreen toggles keep the editor mounted and need no focus restoration.
The band beside the inline editor where the fullscreen glyph sits is the input's
own right padding, so a tap anywhere in it focuses the editor whether or not the
glyph shows. On web the slot is a fixed box over the visible viewport, which is
+6
View File
@@ -107,6 +107,12 @@ absent from the installed plugin list and source configuration; their client bun
appear in the plugin catalog. Editing one in development requires a daemon restart.
Directory, Git, and npm installs cannot use a built-in ID.
A provider registration with `requiresConfiguration: true` participates only while its ID exists
under `agents.providers`. Hoplite uses this for catalog opt-in. Add writes `{ "enabled": true }`
without `extends`, so it selects the bundled adapter. Remove deletes the config entry; setting
`enabled: false` keeps the entry. The bundled JavaScript still loads at startup, but an absent
provider does not enter executable resolution or catalog discovery.
Provider plugins use separate installation and provider IDs. `muse-provider` registers the
selectable `muse` provider (Muse Code) and a usage source. Its `status({ launch })` reports
availability and a diagnostic after the daemon resolves the executable. Configure command,