* fix(web): re-subscribe push when VAPID key changes (stale hub subscriptions)
* fix(web): prune stale push endpoint from the hub after VAPID re-subscribe
* fix(web): record VAPID key only after hub registration succeeds
* fix(web): preserve push registration on unsubscribe failure
via [HAPI](https://hapi.run)\n\nCo-Authored-By: HAPI <noreply@hapi.run>
* 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>
ElevenLabs only passed bootstrap context through dynamicVariables without
a matching {{initialConversationContext}} prompt placeholder, so Brief me
connected with no session history. Stream deferred chunks then push bootstrap
on all backends; add the placeholder for newly created ConvAI agents.
Co-authored-by: Cursor <cursoragent@cursor.com>
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>
* 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.
* 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>
* 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>
* 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
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.
- @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.
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
EventSource reports an error the moment a connection drops, including
while it is already retrying by itself - readyState is still CONNECTING
and the stream is typically back within a few seconds. useSSE forwards
that straight to onDisconnect, and App turned it into a full-width
"reconnecting" banner synchronously, so a blip the browser recovered from
on its own still read as a broken network.
Route the disconnect through a small hook that waits out a grace period
first. Genuine outages still surface the banner, just a moment later;
recoveries that beat the timer stay silent. The sibling syncing banner
already debounces this way in useSyncingState.
The grace period is anchored to the first drop, so repeated failed
retries cannot push the banner back indefinitely.