Commit Graph
414 Commits
Author SHA1 Message Date
AnanovoandGitHub 2be5a07aae feat(web): preview composer image attachments (#1322)
* feat(web): preview composer image attachments

* fix(web): address composer preview review
2026-08-03 18:04:51 +08:00
TEEKandGitHub 0f78bfdc9a fix(web): improve rich composer IME and line-break handling (#1328) 2026-08-03 18:04:37 +08:00
SSU-WEI HUANGandGitHub ecd73b8c10 fix(web): animate pull refresh indicator (#1337) 2026-08-03 18:02:37 +08:00
1761b696f7 feat: add cache-aware token usage dashboard (#1338)
* feat: add cache-aware token usage dashboard

Track normalized Claude, Codex, and ACP usage with incremental SQLite backfill. Exclude imported transcript history, rebuild usage after history rewrites, and expose an owner-only dashboard with cache-aware totals and breakdowns.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix: preserve usage model and local dates

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix: normalize cached usage and timezone buckets

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

---------

Co-authored-by: HAPI <noreply@hapi.run>
2026-08-03 18:02:26 +08:00
AnanovoandGitHub 518bd7a9a9 fix(web): align file search controls with outline (#1339) 2026-08-03 18:01:53 +08:00
82639c66f8 fix(web): show full voice platform rules in settings preview (#1345)
Settings truncated read-only fixtures to 800 chars so scrolling never
revealed the rest. Default preview is now the full document; explicit
caps remain for tests.

Closes #1341

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 18:01:17 +08:00
SSU-WEI HUANGandGitHub cd570adf66 Improve fork and rewind actions (#1336) 2026-08-03 12:33:06 +08:00
weixiang1862andGitHub cc39021abc feat(web): show hidden directories in workspace browser (#1331)
* feat(web): show hidden directories in workspace browser

Add optional includeHidden param to the machine list-directory RPC so the
WorkspaceBrowser can toggle hidden (dot-prefixed) entries. Default remains
filtered for backward compatibility; the toggle persists via localStorage.

* fix(web): disable show-hidden toggle while directory loading

Prevent overlapping list-directory requests with opposite includeHidden
values; the toggle is now disabled while a directory load is active.
2026-08-03 12:32:50 +08:00
SSU-WEI HUANGandGitHub a111d0bc82 feat: use official Pi agent logo (#1335)
* feat: add official Pi agent logo

* fix: honor selected theme for Pi logo
2026-08-03 12:32:39 +08:00
2d904de76e fix(web): migrate chat-path attachments on scratchlist park (#1227)
* fix(web): migrate chat-path attachments on scratchlist park (#1226)

When an image is attached via the normal upload adapter before scratchlist
mode is enabled, toggling mode swapped adapters and send() dropped the
metadata — park stored text-only and cleared chips. Migrate pending
chat-path files into hub scratchlist storage on send, and fail closed if
non-hub paths still reach the park wrapper.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): defer chat-path cleanup until scratchlist park succeeds

send() migrates before scratchlist.add; deleting the original upload
there left retries pointing at a missing chat blob when park failed.
Stamp migratedFromPath and clean up only after the park attempt result.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): park scratchlist before composer.send clears chips

assistant-ui empties text/attachments before onNew, so return false from
park could not restore retryable state and rejected cleanup deleted the
migrated hub blob. Intercept park from a live snapshot; clear only after
accept; releaseWithoutDelete so clearAttachments keeps parked hubs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): lock composer while scratchlist park is in flight

Disable input/send and hide chip remove during migrate+add so mid-flight
edits are not wiped on success and hub blobs are not deleted out from
under the accepted entry.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): do not clear composer if park snapshot changed mid-flight

Compare post-await composer state to the pre-park snapshot before
clearing; disable DragDropZone and scratchlist promote while parking
so parent paths cannot add chips the clear would silently drop.

Addresses Codex Major on #1227 (preserve post-snapshot composer changes).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): defer migrated chat-path cleanup until park snapshot clears

Return ScratchlistParkResult.beforeClear from onParkScratchlist so
finalizeMigratedScratchlistParkCleanup runs only after HappyComposer
confirms the composer was unchanged mid-flight.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): flush rich mentions before scratchlist park snapshot

Park snapshots composer.text after flushSerializedText so session
@-mention chips serialize to markdown links before scratchlist add.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): validate park snapshot before scratchlist add

Split prepare/commit/abort so mid-flight composer edits abort orphan
hub blobs instead of parking a duplicate. Reuse restored hub paths in
prepareScratchlistParkAttachments so remounted chips do not re-upload.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): ignore scratchlist toggle hotkey while park is in flight

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 10:39:01 +08:00
SSU-WEI HUANGandGitHub c3a5522207 Add realtime dictation providers (#1329)
* feat: add realtime dictation providers

* fix: cancel realtime dictation startup

* fix: refresh local dictation availability

* fix: preserve dictation on disconnect

* fix: normalize OpenAI language hints
2026-08-03 10:03:06 +08:00
3c3bffdfbd feat: message-level conversation fork and rewind (#1263)
* feat: add message-level conversation fork and rewind

Expose native Codex/Grok/Claude history controls through hub REST+RPC and web ConfirmDialog actions, without file rewind or composed forks. Also reconcile the duplicate hub V14→V15 migration so typecheck can pass.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: hydrate fork transcript and consume Claude --fork-session

Forked HAPI children now copy the source transcript prefix so navigation is not a blank thread, and Claude drops --fork-session after the first launch so relaunches do not branch again.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: harden fork/rewind concurrency and durable history points

Skip pending scheduled rows when hydrating fork transcripts, serialize fork/rewind per session, and persist conversation history points/indexes across existing-session bootstrap and Grok relaunches.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: close remaining fork/rewind races and UI anchoring

Block sends and scheduled maturation while history actions run, order fork prefixes by invocation time, inherit history locators into children, and only offer Fork current on the live tail boundary.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: address remaining fork/rewind bot findings

Materialize Claude --fork-session before the first child prompt, validate
HAPI history boundaries before native RPC, expose forkCurrent on a latest
user boundary, fully demote unsupported conversationHistory capabilities,
and fix the truncate test setup order.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: align fork-current ids and Claude fork bootstrap

Compare the latest fork boundary in assistant-ui threadMessageId space,
spawn Claude forks with the persisted session mode, and preserve
forkedFrom across existing-session bootstrap.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: close fork/rewind consistency holes at the contract layer

Hold the source history lock until Claude child binds a distinct native
id, persist Codex localId→turnId locators, and mark/block diverged
sessions when native rewind outruns HAPI truncate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: use Codex stable lastTurnId for historical fork

Map HAPI's exclusive boundary to the previous turn's inclusive
lastTurnId so native fork context matches the hydrated transcript.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: require exact Grok native resume for fork children

Reject newSession fallback when forkedFrom is set, and keep the hub
history lock until the child binds the forked grokSessionId.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: kill active fork children before failed-fork cleanup

Bind/readiness failures can leave the child process running; deleteSession
rejects active rows, so terminate first then remove the HAPI session.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: close remaining fork lock, hydrate, and todos gaps

Reject mode switches during history actions, batch-copy fork
transcripts in one SQLite transaction, and rebuild todos after
fork hydrate / rewind truncate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow Codex historical fork before the first turn

Use experimental beforeTurnId when there is no previous turn for the
stable inclusive lastTurnId boundary.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: mark Grok history busy immediately after dequeue

Hub idle checks clear once messages-consumed fires; hold the busy flag
across permission sync and rewind-points lookup before prompt starts.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: encode copied conversation history content

* fix(web): hide local conversation history actions

* style(codex): remove trailing whitespace

* fix(fork): preserve children when cleanup is unconfirmed

* fix(history): confirm cleanup and guard rewind divergence

* fix(history): probe capabilities before advertising

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 09:28:01 +08:00
AnanovoandGitHub 3f03e8daa1 feat(web): add expandable message composer (#1319)
* feat(web): add expandable message composer

* fix(web): preserve composer selection when expanding

* fix(web): keep composer toolbar actions tappable

* fix(web): preserve composer escape behavior

* fix(web): apply overflow-safe toolbar alignment
2026-08-03 09:27:12 +08:00
KorenKritaandGitHub 413fbb8714 fix(web): preserve queued edits across cancellation (#1324) 2026-08-03 09:25:45 +08:00
KorenKritaandGitHub 52dda73045 fix(web): align rich composer DOM and Unicode boundaries (#1325)
* fix(web): align rich composer DOM offsets

* fix(web): truncate session titles safely

* fix(web): preserve session title size bounds

* fix(web): normalize empty wrapper offsets
2026-08-03 09:25:25 +08:00
KorenKritaandGitHub fb3988a81f fix(web): restore failed sends atomically (#1326)
* fix(web): restore failed sends atomically

* fix(web): wait for composer draft hydration

* fix(web): count restored attachments after success

* fix(web): keep scratchlist copy available

* fix(web): move suppressed retry errors to target session
2026-08-03 09:25:01 +08:00
wushenghuaandGitHub 0725fabe84 feat(web): pin running sessions in an in-progress section with state badges (#1315)
* feat(web): pin running sessions in an 'in progress' section with a live badge

* feat(web): show project name on pinned running session rows

* feat(web): make the pinned 'in progress' section collapsible

* fix(web): don't auto-expand directory groups when opening pinned running sessions

* fix(web): keep running section open while searching; clear auto-expand guard when selection leaves a group

* feat(web): show machine label on pinned running session rows

* fix(web): make running-section toggle keyboard-accessible with correct filtered state

* feat(web): split pinned running section into working/pending/idle groups with distinct badges
2026-08-03 09:24:44 +08:00
SSU-WEI HUANGandGitHub 9d07857570 Add provider-backed dictation mode (#1327) 2026-08-03 06:05:58 +08:00
AnanovoandGitHub a6f302ebd1 fix(web): preserve mobile scroll intent after pointer cancellation (#1312)
Keep touch and pen input eligible to cancel initial bottom settling when native panning dispatches pointercancel before scroll. Add interaction coverage for the mobile event sequence and unrelated global cancellations.
2026-08-02 19:53:31 +08:00
SSU-WEI HUANGandGitHub 5b91504263 Add read-only session status panel (#1301)
* feat(web): add session status panel

* fix(web): include nested session activity

* fix(web): preserve incomplete status details

* fix(web): handle anonymous terminal completion

* fix(web): classify pending status accurately

* fix(web): retain ambiguous terminal candidates
2026-08-02 17:33:54 +08:00
KorenKritaandGitHub b7da8d3ab2 fix(web): show Pi reasoning effort (#1303) 2026-08-02 17:33:29 +08:00
KorenKritaandGitHub 147102a877 fix(web): preserve rich composer caret (#1305) 2026-08-02 17:33:15 +08:00
AnanovoandGitHub 68299631f9 feat(web): customize composer toolbar visibility (#1298) 2026-08-02 13:14:00 +08:00
AnanovoandGitHub 74ec247b92 fix(web): make inactive session notices flush with headers (#1299) 2026-08-02 13:04:27 +08:00
KorenKritaandGitHub 1ca7af44d2 fix(pi): keep archived sessions visible (#1297) 2026-08-02 10:00:15 +08:00
SSU-WEI HUANGandGitHub f213bc348c fix(web): require user intent to cancel initial scroll (#1291)
* fix(web): require user intent to cancel initial scroll

* fix(web): snapshot scroll intent before consumption
2026-08-02 09:27:37 +08:00
weishu 9b299eaab7 feat(web): migrate to @assistant-ui/react 0.14 (tap 0.9.8 with update-depth fix)
- @assistant-ui/react ^0.11.53 -> ^0.14.29, react-markdown ^0.11.9 -> ^0.14.7
- resolves @assistant-ui/tap 0.9.8, which ships the upstream fix for
  bulk message prepends (per-scheduler MAX_UPDATE_DEPTH guard, PR
  assistant-ui/assistant-ui#5370) that the local patch covered for 0.3.5
- API migration: useAssistantApi -> useAui, useAssistantState -> useAuiState
  with s.* selector access; TextMessagePart type-guard for content.find;
  portable DefaultComponentsMap annotation for memoizeMarkdownComponents

Verified: tsc clean, 1762 unit tests, history-load e2e 12/12 against the
unpatched upstream scheduler.
2026-08-02 00:12:29 +08:00
weishu 58ff91e5c4 fix(web): correct context window calculation for local-mode sessions
Remote sessions get an authoritative context_window injected into usage by
the CLI (from SDK result modelUsage), but local-mode sessions forward raw
transcript JSONL whose usage has no context_window, so the web fell back to
getContextBudgetTokens — which could not tell Fable's 1M window from its
bare id (only the "[1m]" suffix was recognized) and received session.model,
which is usually null for local sessions, defaulting to a 200k budget. A 1M
Fable session with ~256k of context showed 135% used with an over-limit
warning instead of ~26%.

- modelConfig: recognize Fable ids (fable, fable[1m], claude-fable-*) as 1M
- reducer: LatestUsage carries the usage-bearing message's own model
- StatusBar: new contextModel prop feeds the fallback heuristic only;
  the model prop's other semantics are unchanged
- HappyComposer forwards it; SessionChat passes
  latestUsage.model ?? session.model
2026-08-02 00:00:55 +08:00
AnanovoandGitHub 0537ddf84a feat(web): make session header metadata configurable (#1267)
* feat(web): make session header metadata configurable

* fix(web): align mobile header metadata priority
2026-08-01 23:26:17 +08:00
AnanovoandGitHub 6aae44498d fix(web): align browse button and select chevrons (#1279)
* fix(web): align new-session browse button

* fix(web): inset select chevrons
2026-08-01 22:55:21 +08:00
Junmo KimandGitHub 084d3462cf fix(web): keep line numbers clear when code wraps (#1260)
* fix(web): reserve code gutter padding

* fix(web): expose diff wrap controls

* test(ci): run terminal wrap regression
2026-08-01 17:11:40 +08:00
AnanovoandGitHub bbcef8c300 fix(web): preserve share export layout fidelity (#1277)
* fix(web): preserve share export layout fidelity

* fix(web): keep mobile and multi-image exports responsive

* fix(web): distinguish coarse pointers from touch desktops
2026-08-01 17:10:51 +08:00
AnanovoandGitHub 01bffa3c8c fix(web): stabilize chat scrolling and mobile alignment (#1278)
* fix(web): stabilize chat scrolling and mobile alignment

* fix(web): scope chat scroll containment
2026-08-01 17:10:38 +08:00
AnanovoandGitHub fd20e584d6 feat(web): refine composer status bar (#1281) 2026-08-01 17:09:49 +08:00
weishu 51d8c04e0a feat(web): collapse session machine filter into header menu on mobile
Replace the always-visible wrapping chip row below the md breakpoint with a
filter icon button in the session list header (right side, next to the new
session button). The button opens a radio menu with per-machine counts and
an inline health summary, shows an active-filter dot, clamps to the
remaining viewport/safe-area space, and supports Escape/Arrow-key
navigation with focus restore. Desktop keeps the one-tap chip bar.
2026-08-01 13:26:15 +08:00
weishu b6897ee736 feat(web): replace session list refresh button with pull-to-refresh
Remove the refresh icon button from the session list toolbar and make
the list itself the refresh affordance via a touch pull gesture.

Gesture (SessionList):
- Touch listeners on the scrollable list container; pull engages only
  at scrollTop 0, with 16px feedback / 64px trigger thresholds, and
  fires on release past the trigger. Mirrors the established
  pull-to-load-older pattern in HappyThread.
- Touch-only by design: desktop has no overscroll bounce, so a wheel
  pull feels broken; desktop keeps relying on SSE live updates and
  query focus refetch.
- onRefresh widened to () => Promise<unknown> | void so the indicator
  tracks the in-flight refetch and ignores re-entrant pulls.

Feedback:
- Status pill over the list (role=status, aria-live) shows
  pull/release/refreshing states with a spinner while refreshing; it
  also covers the initial useSessions load (isLoading), which lost its
  only busy indication when the toolbar button was removed.
- The success toast is dropped (the pill is the feedback); the failure
  toast is kept. handleRefresh now returns its promise.

Empty states (review P2s):
- SessionsEmptyState and the no-results message move from the shrink-0
  header container into the scroll container, so the gesture works on
  the visible empty state (retry path after a failed initial fetch)
  and short viewports scroll instead of crushing the gesture area.
- SessionsEmptyState is gated on !isLoading so a slow initial request
  no longer flashes the final empty state with active actions.

i18n: add sessions.refresh.pull/release/refreshing (en + zh-CN),
remove now-unused button.refresh and sessions.refresh.success.*.
Desktop wheel pull was implemented and then reverted after review.
2026-08-01 10:37:09 +08:00
AnanovoandGitHub 39da5c4b8e fix(web): align compact dialog titles (#1253) 2026-07-31 18:17:57 +01:00
SSU-WEI HUANGandGitHub 7ad454f9a2 fix(web): prioritize session names in narrow sidebar (#1029) 2026-07-31 18:17:23 +01:00
da921821ee fix(web): show Auto instead of Default for Cursor model picker (#1248)
* fix(web): show Auto instead of Default for Cursor model picker

Cursor CLI uses `auto` for automatic model selection; labeling it
"Default" in HAPI was confusing and inconsistent with `agent --list-models`.

Fixes #1247

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): align Cursor unavailable copy with Auto label

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: re-trigger Codex PR review

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(web): update Cursor catalog JSDoc for Auto label

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 18:17:11 +01:00
SSU-WEI HUANGandGitHub 259d69cd8f fix(web): show composer status for Cursor (#1250) 2026-07-31 18:16:52 +01:00
weishu 3f73a5f6ef feat(web): collapse session search into the sidebar toolbar row
Move the session-list search out of its dedicated full-width row and
into the sidebar toolbar, so the sidebar gets one compact header row
instead of two stacked rows.

Behavior:
- Collapsed by default: a search icon sits at the left of the toolbar
  row, on the same line as the existing tool icons (codex import,
  refresh, browse, settings, new session).
- Clicking the icon expands the input to fill the entire row width
  (tool icons hide) and focuses the input; when focus leaves the
  search container the input collapses back to the icon.
- Query and date range stay applied while collapsed; the icon shows
  an indicator dot whenever any filter is active.
- The date-range picker stays inside the expanded input.

Implementation:
- SessionList gains a headerActions prop and renders a single header
  row: [collapsible search] [spacer] [optional renderHeader plus]
  [headerActions].
- SessionsPage drops its own toolbar wrapper and passes its five
  buttons through headerActions instead.
- The safe-area top inset moves to the router container wrapping both
  the error banner and the list, so iOS PWA error text no longer sits
  beneath the status bar/notch.

Focus handling (review follow-ups):
- Controls that unmount themselves on click (clear-query X, picker
  backdrop, range-end selection, picker footer Clear) silently moved
  focus to <body>, leaving the search expanded but no longer
  collapsible via blur. Each now returns focus to the input; the
  picker footer Clear is wired through a dedicated onClear prop.
- Header actions can no longer be suppressed by a stale expanded
  state: they render whenever the search control itself is absent
  (e.g. the session list empties via SSE), and the expansion resets
  when the list becomes empty.

i18n: add sessions.search.open (en / zh-CN).

Tests: adapt existing search/date-filter tests to expand the search
before interacting; add regressions for collapse-on-blur query
persistence, focus restore after clearing the query and after
clearing the date range, and header actions surviving an emptied
session list.
2026-07-31 19:52:27 +08:00
bc543dc494 feat: rich composer session @-mentions + inspect_peer (#1228)
* feat(web): feature-flagged rich composer for inline session @ mentions

Custom segmented contenteditable (not TipTap) inserts caret-local session
atoms from the existing @ picker and serializes to markdown links on send.
Textarea path remains default until flag parity dogfood.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): rich composer mention boundary + #1215 refs

Treat U+FFFC mirror atoms as word boundaries so @ after a session
token still opens autocomplete. Point comments at Fixes #1215.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(web): peer-stack e2e for rich composer session @ mentions (#1215)

Smoke: flag on, @ picker inserts inline session atom chip (not prose dump).
Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): preserve newlines in rich composer Enter-newline mode

Chromium splits contenteditable on Enter into block divs; serialize those
as \\n and insert <br> when parent leaves Enter unhandled (Shift+Enter /
enter-inserts-newline).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): show @ badge when rich composer mentions flag is on

Dogfood was invisible: flag-off looks like a normal textarea, and flag-on
had no chrome. Surface a small @ badge when enabled.

* fix(web): rich session composer on by default (not a user setting)

The plan dual-path was an engineering kill-switch, not an opt-in. Default
to the segmented composer; only richMentions=0 disables. Drop the flag
badge and record a peer-stack motion proof covering chips + baseline UX.

* fix(web): make rich composer Shift+Enter create a visible newline

Trailing <br>+empty text node was a silent no-op at EOL. Use
insertLineBreak (ZWSP pad fallback), assert real \\n in peer e2e.

* feat(web): hover tooltips on rich composer session chips

Show full title, status, short id, and path on chip hover via a portal
bubble fed by live useSessions lookup (drafts fall back to title + id).

* fix(web): dismiss rich composer chip tooltips on mouse leave

contenteditable pointerout/relatedTarget was flaky so tips stuck after
leaving the chip. Hit-test on pointermove, clear on prose/input/leave.

* fix(web): address cold-review Blocker/Majors on rich composer

Exclude peer e2e from default Playwright; force plain-text paste; restore
newline hard-stop in findActiveWord; fix root-anchored selection mapping
and nested-block serialize; cover with unit tests.

* chore: drop accidental .cursor files from rich-composer tip

* fix(web): close remaining cold-review gaps on rich composer

Drop absolute peer e2e tooling imports, prove chip→markdown send, and
harden paste/EOL/focus/tooltip/Enter edges before Meta rematerialize.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: absorb soup playwright.config union for clean remat

Keep fork peer-stack timeouts/annotated-video wiring and add testIgnore
for e2e/peer so the next driver rematerialize does not conflict.

Co-authored-by: Cursor <cursoragent@cursor.com>

* revert: drop fork playwright tooling from upstreamable tip

Peer-stack annotated-video + HAPI_PEER wiring stay on fork main / soup.
Product tip only needs testIgnore for e2e/peer (see docs/tooling/peer-stack.md).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): fix rich composer Shift+Enter double newline and paste space

Prefer manual newline+pad over execCommand insertLineBreak, and stop
applying autocomplete trailing-space on paste/drop paths.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): pad EOL Shift+Enter after Range.insertNode split

insertNode always leaves an empty text sibling, so !nextSibling never
saw EOL; detect meaningful trailing content and cover with jsdom tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): drop custom onDrop from rich composer

Intercepting drop without caretRangeFromPoint landed text at EOF or
no-oped in-editor moves. Native CE drop is enough for #1215; paste
still forces plain text.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(web): sidebar-parity tooltips on rich composer session chips

Reuse SessionRowSummary (flavor, thinking/attention, schedule, todos,
relative ago, path) for chip hover so the tip matches the session list.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: keep peer-stack e2e off the upstreamable tip

Peer specs and playwright.peer.config stay on fork main per
docs/tooling/peer-stack.md; default config still testIgnore's e2e/peer.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: cite sessions with UUID wire + inspect_peer for agent/overseer

Rich composer chips already serialize to [title](/sessions/<id>); flush
before send so the agent prompt never gets title-only chip text. Add
inspect_peer (MCP + hapi inspect-peer) as the read twin of ping_peer so
that same id is immediately usable for overseer/agent peer lookup, with
system-prompt glue from citations to inspect/ping.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): gate inspect_peer behind permission approval

Cross-session history reads need the same prompt path as ping_peer:
keep inspect_peer off Claude --allowedTools and treat it as sensitive
in ACP/OpenCode read-only mode so prompt injection cannot silently
enumerate peer transcripts.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: clarify playwright peer testIgnore is upstream-safe

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): keep session UUIDs on rich composer copy/cut/paste

Copy/cut write wire markdown so chips do not collapse to @title-only
clipboard text; paste reparses session links back into atoms.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 19:42:56 +08:00
AnanovoandGitHub c5f1928eb0 fix(web): consolidate Codex import in new session (#1240)
* fix(web): consolidate Codex import in new session

* fix(web): clear imported Codex history selection
2026-07-31 18:12:30 +08:00
AnanovoandGitHub 567a6c9499 fix(web): improve session date picker visibility (#1254)
* fix(web): improve session date picker visibility

* fix(web): pair selected date theme colors
2026-07-31 18:11:49 +08:00
TEEKandGitHub 212e58cba2 fix(web): allow reasoning scroll chaining (#1264) 2026-07-31 16:08:49 +08:00
AnanovoandGitHub 0db7d68b37 feat(web): add stepwise session preview controls (#1266)
* feat(web): add stepwise session preview controls

* fix(web): avoid no-op session preview collapse

* fix(web): expand previews from rendered count
2026-07-31 16:08:27 +08:00
weishu b3a1f5fafd fix(web): hide background message sync indicator
Remove the top-right Loading messages pill shown while tail reconciliation runs with cached conversation content.

Keep background tail synchronization silent so it does not compete with older-history loading feedback or imply that the visible conversation is blocked. Cold-start skeletons, pull-to-load guidance, older-page loading state, unseen-message navigation, and global reconnect feedback remain unchanged.
2026-07-31 15:59:42 +08:00
weishu 584d1647d0 fix(web): harden older history loading 2026-07-31 15:59:42 +08:00
Haoqing WangandGitHub 3a931e3c81 fix(web): count unseen messages by rendered block, not raw message (#1255)
* fix(web): count unseen messages by rendered block, not raw message

The "N new messages" pill counted raw DecryptedMessages while the
timeline renders folded blocks, so the two never agreed. A subagent run
is dozens of sidechain messages but a single Task card; a tool_use and
its tool_result are two messages and one card; consecutive tools collapse
into one group. The pill could read "47 new messages" when scrolling down
revealed two new rows.

collectNewUnseenIds never inspected isSidechain, and it could not: the
reducer's grouping is stateful (it needs the Task tool_use before it can
map parentToolUseId), so a per-message predicate in the store cannot
reproduce it. Adding an isSidechain check there would also invert the
error for orphan sidechain messages, which tracer.ts falls back to
emitting at the top level.

Instead, drop the store's unseen bookkeeping entirely and count what the
renderer actually produced. Watermark the visible blocks when the user
scrolls away from the tail, then count the blocks past the last one they
had seen.

The count is anchor-based rather than timestamp-based because the blocks
array is not monotonic in createdAt: messages sort by invokedAt ??
createdAt, so a queued message carries an old createdAt while sitting at
the end. Anchoring also makes prepended history free, since older blocks
land before the anchor.

Known limit, documented at the call site: once the history window fills
up, mergeIntoWindow trims incoming messages off the tail, so the pill
reports 0 instead of a count. Under-reporting is preferable here, and
returning to the tail force-refetches the latest page anyway.

* fix(web): keep unseen watermark stable across optimistic id replacement

The watermark snapshotted only block.id, but that id is not stable for
the user's own messages: mergeMessages replaces an optimistic row with a
stored row that keeps localId under a new server id, and the user block
renders with the message id. Scrolling into history while an own message
was still optimistic meant its echo anchored one block earlier and bumped
the pill by one, with no new rendered row.

Track localId alongside id in the watermark and match on either.

Reported by HAPI Bot on #1255.

* fix(web): count joined assistant cards, not pre-join blocks

visibleBlocks is still not one-to-one with rendered rows: assistant-ui
joins a run of adjacent assistant-role blocks into a single card, so a
response made of reasoning + text + a tool call was reported as three new
messages instead of one, and appending another block to an in-flight
response bumped the pill without adding a row.

Walk the blocks after the anchor and only start a new row where the
assistant run breaks.

Role assignment is the part that would drift, so rather than restating it,
visibleBlockRole moves from assistant-runtime.ts to toolGroups.ts (next to
the VisibleChatBlock definition it describes) and both the runtime and the
counter import the one copy.

Reported by HAPI Bot on #1255.
2026-07-30 23:24:01 +08:00
AnanovoandGitHub 99814e9668 fix(web): make share image previews interactive (#1229)
* fix(web): make share preview controls interactive

* fix(web): preserve lightbox modal focus
2026-07-30 23:20:23 +08:00