* feat(worktrees): let plugins choose creation options and placement
Await the worktree creation request hook before Git source planning so plugins can change placement, branch and base without undoing a fetch. Manage arbitrary destinations through marker and registry ownership, and restore them at their recorded paths.
Co-authored-by: Matt Cowger <matt@cowger.us>
* fix(worktrees): compare repository paths across Windows aliases
Deletion must recognize the same repository through Git native and long path spellings. Reuse ownership path equivalence instead of comparing canonical strings, and let the recovery fixture accept Git platform line endings.
Co-authored-by: Matt Cowger <matt@cowger.us>
* fix(worktrees): respect recorded ownership and creation intent
Keep branch checkout distinct from branch-off, validate plugin placement once at the daemon boundary, and share default path collision resolution. A restored external worktree's marker must never override its recorded non-ownership.
Co-authored-by: Matt Cowger <matt@cowger.us>
* test(worktrees): record ownership in session archive fixture
The fixture creates a Paseo-owned worktree, so its durable record must say so. Archive now treats a recorded false value as authoritative instead of replacing it with filesystem discovery.
Co-authored-by: Matt Cowger <matt@cowger.us>
* test(plugins): retry temp cleanup after worktree archive on Windows
Windows releases directory handles after the daemon closes, so rmdir on the
source repository can hit EBUSY. Retry like the other git temp-dir tests.
* test(plugins): compare plugin-chosen worktree paths in canonical spelling
Windows temp directories come back as 8.3 aliases while the daemon records
the resolved spelling, so normalize the expected root the same way.
---------
Co-authored-by: Matt Cowger <matt@cowger.us>
* M1: terminals survive daemon restarts: stop reasons, detached terminal worker, kill cleanup
restart_server_request.killTerminals / shutdown_server_request.keepTerminals and server_info.features.terminalsSurviveRestart; CLI daemon restart --kill-terminals and daemon stop --keep-terminals (capability-gated). The terminal worker runs detached over an owner-only, secret-authenticated socket and is adopted on daemon start. One kill cleanup contract (the reaper) by birth time, used by the daemon, the supervisor and stopDaemonInstance. Settings offers Restart and close terminals.
* M1: fix review findings: reaper session/table retry, short-command creation order, remote kill-restart cleanup report, stop test port
* M1: fix review findings: reaper-owned cleanup record, session member identity by terminal id, unreadable cleanup records, Settings restart error rendered in the card
* M2: worker protocol versions and draining, worker runs from its own copy, desktop upgrades keep terminals, out-of-date daemon notice, docs/terminal-worker.md
- Daemon classifies live workers by protocol: adopts current, drains one version old, shuts
down older, leaves a newer one running and answers list/create with
terminal_worker_incompatible (daemon status prints it). Never more than two workers.
- esbuild worker bundle (build:lib); a built daemon copies bundle, shell integration and
node-pty into $PASEO_HOME/terminal-worker/<version>-<hash>/ and launches from it; copies no
record names are pruned.
- Desktop app_update/version_mismatch stop with keep terminals on macOS/Linux; Windows and a
daemon without the capability close them.
- Out-of-date daemon sidebar notice with Update button, worded by terminalsSurviveRestart.
- PASEO-13: unreadable worker records fail status and kill-restart retry.
- PASEO-14: a session member without a terminal id is unknown, not unrelated.
- Smoke test 46 with A/B installation fixtures and per-fixture CLI shims (PASEO-9 fold).
* M2: track in-flight worker requests without leaving an unhandled rejection
detach({ afterPendingRequests }) tracked each request with request.finally(), which rejects
with the request; a fire-and-forget send that timed out surfaced as an unhandled rejection
(worker-terminal-manager.test.ts). Track settlement only.
* M2: CI round 1 fixes: worker terminal manager in the MCP key test, status cleanup field, shared socket dir
- mcp-server.test.ts (new upstream test) built the worker manager with the old fork API;
it now starts a real detached worker in a throwaway home.
- daemon-session.test.ts expected status without terminalCleanupUnconfirmed (added in M1).
- paseo-daemon.test.ts: a home under the scratch temp dir is too long for a unix socket, so
the worker listens in the per-user paseo-<uid> directory, which outlives workers by design;
the test now requires it to be empty instead of absent.
* M2: run Windows PowerShell by its install path for process identity
server-tests (windows-latest): bootstrap-provider-availability.test.ts starts a daemon with a
PATH that holds only git, and the terminal worker failed to start: 'cannot read its own process
identity: spawn powershell.exe ENOENT'. Process inspection (managed-processes.ts inspect and the
terminal worker's process table) now runs %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe.
* M2: lifecycle e2e plain stop uses the shutdown RPC on Windows
CI round 2, server-integration (windows-latest): 'stop and stop --force never signal processes
a stale or earlier-boot record names' called stopDaemonInstance without a shutdown channel,
which Windows refuses ('has no graceful shutdown channel'). Plain stops in the file now pass
one, as the CLI does.
* M2: native notice outlet, Windows desktop-update wording, worker cap counts unconfirmed cleanups, create recovers after a drain
* M2: phone sidebar mounts the callout slot (iOS notice verified), worker record reads retry transient errors
* M2: native notice preconditions use explicit working directories, one block per terminal
* M1: compact sidebar shows only native callouts on every platform; PTY round trips in the MCP key test get a 5 s window
* ci: pin Playwright browser jobs to ubuntu-24.04
The pinned Playwright (1.58) has no ubuntu-26.04 build, and forks whose
ubuntu-latest already resolves to 26.04 fail at browser install. Job names
are unchanged, so required-check contexts still match.
* ci: use ubuntu-latest for Playwright jobs, same as upstream
Reverts the ubuntu-24.04 pin; upstream main's Playwright shards pass on ubuntu-latest.
* fix(server): keep terminal worker record IO off the daemon event loop
* fix(server): restore terminal creation after worker failure
A disconnected terminal worker remained the current manager indefinitely, leaving dead sessions listed and every later create bound to a closed socket. Confirm process death before retiring the member and use the existing single launch path for replacement. Reconnect live workers and replay their screens while preserving terminal identity.
* test(server): verify orphan cleanup during worker recovery
Recovery reaps orphaned programs before a later daemon stop. The previous fixed-delay assertion raced that cleanup and failed when it completed promptly. Verify orphan removal, same-daemon replacement creation and cleanup on normal or forced stop instead.
* test(server): isolate Windows dependency fixture setup
Build the local package registry in setup so its npm packing processes do not consume the installation journey deadline. Install the platform package used by Windows runtime fixtures directly, avoiding an unused wrapper postinstall probe without a timeout.
* test(server): wait for pending watchers before disposal
Observation admission starts before asynchronous path setup reaches watcher subscription. Disposing at the admission counter races setup and never exercises late watcher cleanup. Wait for the subscriptions to be pending before disposing.
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(worktrees): copy included local files before setup and agents
Keep local-file materialization in one built-in lifecycle hook so workspace setup and the first agent see the completed copy. Reuse the community plugin algorithm with the source checkout supplied by the worktree hook.
Co-authored-by: Carlos Precioso <github@precioso.design>
* test(plugins): make worktree lifecycle checks portable on Windows
Compare source checkout filesystem identity across Git and Windows path spellings. Exercise copy rollback with a destination file blocking a directory, since Git for Windows accepts directories as exclude files.
---------
Co-authored-by: Carlos Precioso <github@precioso.design>
* refactor(plugins): make lifecycle hook timing explicit
Use one registration method and subject.operation.step names so plugin authors can read whether an operation waits. Preserve old registrations through a single SDK adapter and keep timing and payloads unchanged.
* fix(plugins): align hook notes and example fixtures with release contracts
Keep the changelog inside its versioned beta entry for app parsing. Run current plugin examples against the checkout version so their declared requirements remain valid.
* docs(plugins): align translated hook dependency guidance
* fix(plugins): align hook exports and release documentation
* feat(plugins): use lifecycle positions and await worktree initialization
* test(plugins): cover first-agent worktree initialization
* test(plugins): verify rollback against workspace catalog entries
* fix(app): show a lone tool call as itself and group from the second call
A run of one tool call or thought renders as the call. When a second call
arrives the row becomes a group that keeps the first call's id, so the row
key stays put. Tool call and group open state move into a per-agent store:
opening a lone call also opens its future group without notifying the
timeline, so a click re-renders only that tool call.
* test(app): cover lone tool calls turning into groups
Desktop journeys check that an opened lone call keeps its details open inside
the new group, that clicking it renders no timeline component, and that the
group header lands where the call was. Compact covers the call sheet and a
closed group.
Keep client plugin installations and local UI state alive through transient host disconnects. Reconcile changed bundles and removals on reconnect and refresh settings changed while offline. Connection switching still replaces the client and reinstalls plugins.
Preserves the implementation from #6731, adds a failing-before/passing-after real browser regression, and corrects the English and Chinese lifecycle documentation. Verified with focused tests, browser reconnect journeys, Android debug offline interaction, root checks and green current-head CI.
Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: RoundRedDot <roundreddot@gmail.com>
Native unit tests require an Android prebuild and Maven downloads that block otherwise completed app checks. Keep the explicit local test command while removing its CI invocation and dedicated Java setup.
* fix(app): seat the subagent sheet's parent callout like the composer
The sheet tab body padded the bottom safe-area inset, and the composer dock
padded it again, so the parent callout in a subagent sheet floated a full
navigation-bar height above where the composer sits in a tab.
The sheet body (SheetBottomInset) now owns the inset: it pads it once and
tells its content none is left, so the dock stays host-agnostic. Overlay
sheets (adaptive modal sheet, menu sheet, combobox sheet) read the inset on
the far side of their portal, so ones opened from sheet content still clear
the screen edge.
The callout takes the composer's rail and shows the parent's provider icon
through StatusIcon: no icon box, with the running ring, attention dot or
needs-input alert on the icon.
* fix(app): give the attachment lightbox its own safe area
An RN Modal passes its call site's context straight through, so a lightbox
opened from a sheet tab read the bottom inset the sheet body had already
spent (0) and drew over the navigation bar. The modal is its own window:
it now gets its own SafeAreaProvider, which measures that window, and the
image area reads its insets below it.
floating-panels.md records the rule for overlays: read insets where the
overlay draws.
* feat(app): open a subagent in place of the subagents list on compact
On phones, choosing a subagent from the subagents list now replaces the list
inside the same sheet instead of stacking a second sheet over it. Back returns
to the list, Close closes both, Android Back steps subagent -> list -> closed,
and Open parent lands on the pane the list was opened from (for a nested
subagent, its parent's sheet).
- Menu engine: fill pages (`fill: true`) take the full sheet height, Back pops
a page via `goBack`, and a page whose definition disappears falls back to
the list.
- One sheet pane (`screens/workspace/sheet-pane`) renders the header, body,
bottom inset and focus for both containers: the workspace's detached sheet
tabs and the in-list menu page. Only the container differs.
- The header drags the sheet through `SheetDragArea`; compact menu sheets use
the shared `SheetBackground`/`SheetHandle` chrome.
- `selectSheetTab` describes the sheet a reveal would show without opening it.
* fix(app): put the parent callout on one row
The Open parent button already says what the callout is, so the Parent label
goes. The parent's title shrinks to one line before the button does, so Open
parent never wraps or leaves the row.
* refactor(app): register panels once in the workspace screen
Every reader of the panel registry called ensurePanelsRegistered, which
imports every panel module. The panels render those readers (the agent panel's
subagent track and parent callout, the new-tab panel's launcher, the diff
panel's review surface), so each reader closed an import cycle through
register-panels.
The workspace screen, the only route that renders a registry reader, now
registers the panels before anything under it renders, and the readers import
only the leaf panel-registry. The pane content test registers a probe panel for
real instead of mocking the registry.
* test(app): transform the markdown WASM factory when Vite versions its URL
Importing the sheet pane barrel in retained-chat-content.browser.test.tsx made
Vite request md4c.js with a ?v= query, which the CommonJS-to-ESM transform did
not match, so the test file failed to import.
Five panels for the App Store and Google Play in iPhone, Android phone,
iPad 13-inch and Android tablet sizes, rendered from native captures of
the real app against a staged demo host.
The renderer keeps the captures and the store art reproducible: it
resizes every capture with Lanczos to the exact pixels it fills because
Chrome's own downscaling made earlier exports soft, and it fails when a
headline or subline wraps differently, because the device is placed from
that fixed text height. The README is the recapture runbook and the
store-screenshots skill points agents to it.
Android ships generated Markdown props without code.fontScale, so native measurement wraps inline code at a larger size than drawing. Regenerate both Android code structs and enforce generated-header parity for both platforms.
Constrain the sender label to the message width so long agent names do not wrap above received messages. Keep the line limit opt-in on Button and cover compact chat widths with a real-browser regression.
Native hidden overflow capped the measured content at the preview height, so long messages never offered Show more. Use the native scroll layout mode on the existing View to measure full content while preserving clipping, and store only whether it overflows.
Observation admission precedes asynchronous path resolution. Wait for subscriptions to start before disposal so the test consistently exercises late watcher cleanup.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(app): unify layout and density breakpoints
Align md styles with the existing 600pt compact/wide boundary so
600-719pt tablet windows no longer mix wide layout with compact metrics.
Keep the 500pt composer threshold local to its measured content width.
Let Settings navigation shrink before its detail pane falls below the
existing 400pt target, while retaining the preferred 720pt split budget.
Validated workspace typecheck, lint, formatting, real Chromium resize
checks, Duo portrait and landscape, and existing sidebar geometry tests.
* test(app): cover consistent docked navigation and readable Settings
Compare narrow and wide navigation density and Settings detail space
through the real browser journey, including compact Settings after resize.
The regression fails with main's split layout and density boundaries.
Co-authored-by: Elsa <zeyugao@outlook.com>
---------
Co-authored-by: Elsa <zeyugao@outlook.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(server): report reused plugin children as running again
Child turn starts were discarded after attachment, leaving the previous completed status visible during later work. Translate started to running for live events and ordered history replay.
Co-authored-by: yywl <yywl@linux.0392yy.cn>
* test(server): wait for Git observation before completing fetch
The structural fallback test advanced its debounce timer before asynchronous watcher setup and fetch completion. Wait for those stages so slow filesystem setup does not turn the intended refresh assertion into a scheduling race.
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: yywl <yywl@linux.0392yy.cn>
Reduce plugin client payloads with whitespace and syntax minification after eager host-export interop. Preserve identifiers, lowered async behavior and dependency license notices; leave server bundles readable.
Verify worker-produced code through registration, byte-reduction, immutable-export and retained-notice regressions. Carries forward #7625 on an upstream branch.
Pin Playwright-installing CI jobs to Ubuntu 24.04 after the ubuntu-latest rollout selected an unsupported browser-install platform.
Follow-up payload review motivated by Matt Cowger's plugin-loading discussion: https://discord.com/channels/1481169421832814616/1563217536269680744
Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: RoundRedDot <roundreddot@gmail.com>
Reserve-and-release port selection races other listeners during daemon startup. Bind port zero and use the existing daemon instance endpoint for readiness and callers.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(app): pad the model picker empty state on all sides
The empty state carried vertical padding only, so the provider refresh
error it renders wrapped from edge to edge and read as attached to the
model picker's left border on web and mobile.
One padding value keeps the message off the border on every platform.
* test(app): keep model discovery errors inside picker edges
Exercise a real catalog deadline through the existing Pi RPC fixture at desktop and compact browser widths. Both gutter assertions fail without the shared padding fix.
Co-authored-by: Matteo Pietro Dazzi <matteopietro.dazzi@gmail.com>
---------
Co-authored-by: Matteo Pietro Dazzi <matteopietro.dazzi@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Thoughts are grouped with tool calls only while Expand thinking is off,
yet opening a group rendered each thought with `defaultExpanded`, so the
reasoning text popped open against the setting. Grouped thoughts now open
like any other call: a closed Thinking row the reader clicks.
The folded history also treated the latest retained turn as live whenever
the agent was active without a known turn id, which unfolded the previous
turn until the new turn's rows arrived. Without head rows, the latest turn
stays live only while the active run is that turn.
Co-authored-by: Claude <noreply@anthropic.com>
Reuse verified host-scoped plugin bundles across client launches and reconstruct complete catalogs when unchanged code is omitted. Keep store ownership on the connected client, including adopted bootstrap clients, and persist in the background with bounded writes.
Remove catalog catch-up deferral and fix repeated timeline preparation that promoted cached completed history into a live head. This preserves completed plugin-rendered chat when catalog installation precedes timeline catch-up.
Carries forward #6732 on an upstream branch, with the original hash/cache contribution and integration attribution preserved.
Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: RoundRedDot <roundreddot@gmail.com>
* fix(app): preserve reading position when older turns fold
Item-based history pages can complete and collapse the row at the reading position. Transfer its anchor to the folded turn host so reaching history start does not drain older pages. Also omit reasoning from action summaries and align sender attribution with its message bubble.
* fix(app): preserve the reader when older pages extend tool groups
Resolve grouped calls and folded turns through one presentation-owned row lookup. Keep retained group indexing off the streaming path and verify item-page group replacement against its failing baseline.
* fix(app): preserve message previews when opening turns
Keep the wrapper child structure stable so React does not remount the message fragment when the indent rail appears. Remounting reset preview measurements and briefly removed the fade and Show more control.
* fix(app): separate show less from expanded message text
* fix(server): keep background Git fetches from opening login prompts
Git tries graphical askpass before consulting GIT_TERMINAL_PROMPT, so passive fetches repeatedly opened a login despite terminal prompting being disabled. Disable askpass and credential-manager interaction for this fetch while preserving stored credential lookup.
* test(server): make fetch and ACP expectations match supported Git and timeline behavior
Newer Git also creates origin/HEAD on the first fetch, so assert the authenticated branch result without excluding that valid additional ref. Main now qualifies ACP timeline identities by chunk kind while preserving the native messageId; update the plugin round-trip test to check that contract.
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Outside folder links were routed to the workspace-scoped Files explorer,
which kept an access error until Retry. Route explicit outside folders
to the local file manager or show an outside-workspace message, while
preserving existing file and inside-folder behavior.
Refs #7513
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Jacek Tomaszewski <jacek@fullstack.house>
* fix(plugin): keep ACP reasoning separate from replies
Scope explicit timeline identities by chunk kind so a provider sharing a native message ID cannot merge reasoning into the assistant reply. Preserve the native assistant messageId for consumers.
Co-authored-by: Yanlin <127009+cncolder@users.noreply.github.com>
* test(plugin): cover kind-qualified ACP history identities
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Yanlin <127009+cncolder@users.noreply.github.com>
Use upstream atlas invalidation fixes and cover ordinary output and shared-atlas clearing with real-WebGL pixel regressions.\n\nRelated to #6506; does not close the broader rendering issue.
* perf(app): make diff line wrapping responsive on large changes
Wrapping eagerly shaped every expanded line before the viewport could paint. Estimate off-screen heights and measure rows as they enter the window, preserving the scroll anchor as geometry becomes exact. Add shared layout and browser benchmarks to track toggle latency and shaping cost.
* fix(app): preserve wrapped diff anchors across bounded relayouts
Resizing can move the viewed row outside the old materialization window. Keep its estimated wrap geometry available to anchor resolution, and measure the anchored row in the same layout pass so inaccurate estimates cannot discard the exact fragment or pixel offset.
* fix(client): preserve history loading on older hosts
Whole-turn paging is optional, but requiring its capability blocked existing history reads before any request was sent. Negotiate the paging enhancement at the client boundary while preserving item-based requests for older daemons.
* fix(app): preserve row ownership during late turn updates
Late lifecycle events retagged the newest row even when they updated an earlier call, causing the next update to append a duplicate tool ID. Assign turn membership only to rows written by the event before finalizing unrelated reasoning.
Co-authored-by: luke <luopeihuan@gmail.com>
* fix(app): preserve reasoning continuity and turn expansion controls
* fix(app): hide reasoning inside collapsed turns
---------
Co-authored-by: luke <luopeihuan@gmail.com>
Raw timer gaps and elapsed callback or GC spans include Windows runner preemption. Share callback CPU accounting across the daemon hot-path probes and retain deterministic async admission, real yields, and sync-IO tracing at filesystem boundaries.
`npm audit signatures` in the quality job fails on some runners with
EMISSINGSIGNATUREKEY for @react-native/babel-preset@0.88.0. Nothing in
the repo uses React Native 0.88: the package came from a second Expo SDK
that npm placed under packages/expo-two-way-audio/node_modules because
expo-module-scripts was declared there. Root `overrides` are not applied
to packages nested under a workspace, so jest-expo's `expo@*` peer
resolved to the latest SDK and pulled 200+ duplicate packages into the
lockfile.
The failure itself is a CDN cache problem: one Cloudflare edge (ORD,
which serves GitHub runners in Azure northcentralus) cached the
attestations document for that version seconds after publish, before
the publish attestation existed, and keeps serving the provenance-only
document for a year. Every quality job that lands in that region fails;
every other region passes. The audit code path checks the registry
signature before attestations, so the key list is fine and reruns only
succeed by landing elsewhere.
Declaring expo-module-scripts in the root devDependencies puts it under
the overrides, which removes the nested SDK and the poisoned URL from
the lockfile. The module's tsconfig points its typeRoots at the hoisted
copy; its scripts resolve `expo-module` from the root `.bin`.
Closes#7730
* fix(app): keep autonomous turns distinct and readable
Hidden system prompts merged autonomous work into the previous response and reopened completed turns. Preserve canonical turn boundaries and expose their triggers, while making received messages attributable and expanded turn details readable in one step.
* fix(timeline): mount history at actual run boundaries
* fix(timeline): load and retain complete available turns
* fix(app): summarize actions and preserve conversation events
* test(timeline): verify whole-turn paging stays responsive
* fix(app): group reasoning according to thinking preference
* test(app): align history journeys with whole-turn loading
* fix(server): keep image materialization responsive
Decode and hash attachments in bounded yielding units, deduplicate before writing, and publish complete private files before provider timeline events. Await mapping through existing replay boundaries and serialize notification dispatch so turn completion cannot overtake an image.
* fix(server): reject attachment starts closed during conversion
Keep interruption terminal for prompts still being materialized, and reject a start if its session closes before provider submission. Cover both providers with real large-image conversions and make history reconciliation tests await the async timeline capability.
* fix(server): publish OpenCode snapshots before live suffixes
Commit snapshot cursors and publish each event together. Holding those events until attachment IO completed let live deltas overtake text that had already advanced the cursor. Require snapshot publishers and exercise text and reasoning interleaving against real image materialization.
* test(server): measure reconciliation callback work instead of idle gaps
Timer gaps include Windows runner scheduling waits. Keep the 20 ms gate on actual main-thread callback spans and GC, retaining raw gaps and thread CPU as diagnostics.
* test(server): exclude preemption within reconciliation callbacks
Zero-CPU scheduling waits also occur inside callbacks. Budget charged thread CPU and independently require async filesystem admission so synchronous inspection waits remain a deterministic regression.
* fix(server): keep plugin compilation off the daemon event loop
TypeScript import validation runs in esbuild callbacks on the daemon thread. Isolate preparation and compilation behind one bounded compiler queue, returning bundles and metadata only after runtime-boundary validation. Recheck lifecycle intent before executing a compiled plugin.
* fix(server): reuse the compiler worker during plugin build batches
Amortize TypeScript startup across built-ins and queued builds, then retire the worker after one idle second. Carry preparation environments explicitly across worker reuse. Keep dependency-backed example coverage with its existing app fixture.
* fix(server): retire the compiler worker after build errors
A failed parallel target can leave the other target compiling. Await worker termination before the next queued request instead of reusing an isolate with unfinished build work.
* fix(server): preserve compiler error metadata across workers
* test(server): isolate plugin validation in the loop-gap fixture
Exercise erased TypeScript syntax with a small runtime bundle so parser validation, not multi-MiB result transfer, determines the CI fixture. Keep the 20 ms gate and report event-loop active/idle time for scheduler diagnostics. Resolve the development loader at module initialization.
* test(server): probe event-loop turns during plugin compilation
Windows timer wake-up lateness measured 22 ms while the loop was idle for 21.96 ms. Use check-phase callbacks to measure loop availability, retaining the raw 20 ms limit and the failing base-branch reproduction.
* test: run plugin responsiveness probe in serial integration suite
* test: diagnose compiler probe gaps with per-thread CPU readings
* test: distinguish compiler CPU blocking from runner scheduling
* test: classify the largest compiler CPU accounting window
* test: isolate compiler responsiveness from harness GC and idle accounting
* test: measure callback blocking with a high-resolution clock
* fix(server): keep Claude history replay responsive
Load persisted history through session readiness rather than the constructor. Async descriptor-owned replay preserves ordering while serving other requests, and rebind retires the old query before rebuilding history and rewind state.
* fix(server): recover Claude history readiness after rebind errors
* test(server): calibrate Claude replay fixture for CI
* fix(server): report Claude rewind rebind failures to callers
Add a client-local Send shortcut selector with Enter and Command/Ctrl+Enter choices. Preserve Enter as the default and route the selected shortcut through the existing send behavior. Cover selection, persistence, editing, and running-agent send behavior with browser tests.
Co-authored-by: Luke LaBonte <contact@lukelabonte.com>
* fix(server): keep Git observation and worktree IO off the event loop
Resolve subscription path identities before callbacks and serialize async worktree metadata mutations by canonical ownership. Git command tracing must not probe the working directory before spawning.
* test(server): verify Git trace records without filesystem probes
* fix(server): establish Git identities at observation setup
Read-only checkout refreshes should not resolve watcher namespaces. Observation tests await setup and compare cross-workspace fan-out without depending on completion order.
* fix(server): retain literal symlink targets in Git event identity
Canonical realpaths can erase a symlink target spelling when its parent is also a symlink, such as /var on macOS. Resolve these aliases once during setup so deleted and ignored event paths still match without per-event IO.
* fix(server): preserve Windows worktree path spelling
* fix(server): retain native aliases in worktree ownership
* test(server): write Git IO trace markers asynchronously
Project settings and script health updates synchronously read unbounded user files. Share admitted async snapshots across consumers, budget script validation, and serialize revision-checked edits while keeping lexical project identity.