Archive finished subagents across the whole track - #3368
Merged
Conversation
Make the track action clean up finished managed and provider-owned children as one operation while preserving running work. Managed archives remain sequential and provider-owned dismissal stays client-local.
Contributor
|
| Filename | Overview |
|---|---|
| packages/app/src/subagents/archive-finished.ts | Introduces the sequential archive controller, eligibility checks, progress reporting, outcome classification, and retry-state reconciliation. |
| packages/app/src/subagents/use-archive-finished.ts | Connects the controller to authoritative session state, the existing archive mutation, and provider-row dismissal. |
| packages/app/src/subagents/track.tsx | Renders archive progress and retry status while preventing duplicate submissions. |
| packages/app/e2e/browser/archive-finished-subagents.spec.ts | Covers optimistic managed-child removal, daemon settlement, rollback, visible retry state, and recovery through a real browser and daemon. |
| packages/app/e2e/support/helpers/subagents.ts | Encapsulates archive synchronization, daemon rejection, and authoritative archive-state assertions behind domain-level E2E helpers. |
Sequence Diagram
sequenceDiagram
participant U as User
participant T as Subagent track
participant C as Archive controller
participant P as Provider store
participant D as Daemon
U->>T: Archive finished
T->>C: archiveFinished()
C->>P: Hide finished provider rows
loop Each eligible managed child
C->>D: Archive child
D-->>C: Success or failure
C-->>T: Update progress
end
C-->>T: Idle or retryable failure state
Reviews (2): Last reviewed commit: "test(app): clarify subagent archive asse..." | Re-trigger Greptile
desmond-rai
added a commit
to desmond-rai/paseo
that referenced
this pull request
Aug 15, 2026
* feat(app): render mermaid diagrams in agent chat with pan/zoom (getpaseo#2306) * feat(app): render mermaid diagrams in agent chat with pan/zoom Fenced mermaid blocks in assistant messages now render as diagrams instead of code blocks. Web: mermaid renders inline via dynamic import (code-split, loads on first diagram). Drag to pan, ctrl/cmd+wheel or trackpad pinch to zoom about the cursor, double-click to reset; plain wheel still scrolls the chat. Rendered SVGs are cached across virtualization remounts. iOS/Android: diagrams render in a WebView running a self-contained generated bundle (build:mermaid-webview, mirrors the native terminal's xterm pattern — no CDN, mermaid ships in the app). The inline preview is touch-shielded and height-driven by a bridge message; tapping opens a fullscreen viewer where the platform WebView owns pinch-zoom. Streaming source that doesn't parse yet falls back to the highlighted code block and keeps the last good render; renders are serialized through one queue with stale-task skipping so token-by-token updates cost at most one in-flight layout. Security: mermaid can fetch external resources during layout (image shapes, CSS url()) before output sanitization runs — see mermaid-js/mermaid#7645 — so resource-bearing source is rejected up front (containsUnsafeMermaidSource) on every platform, htmlLabels stay off, securityLevel is strict, and the mermaid secure list is extended so directives can't override app theming or themeCSS. The lockfile diff includes npm 11.17 normalization churn beyond the mermaid closure; a no-op npm install produces the same rewrite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): serialize native mermaid renders and pin viewer to last good source Serialize render messages inside the mermaid webview entry so a newer streamed update can't re-initialize global mermaid state (e.g. flip the theme) while a prior layout is still running; superseded queued renders are skipped. Track the last successfully rendered source on the RN side and open the fullscreen viewer with that, so tapping mid-stream while the latest chunk is still invalid can't show a blank viewer. Addresses PR review feedback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): pair native mermaid renders with their source via request id A "rendered" bridge event was credited to the newest sent source, but an older render can complete while a newer (possibly invalid) chunk's send is in flight, mis-crediting the older render's success to the newer source. Render messages now carry a request id that the entry echoes on rendered/renderError; the RN side resolves the source by id, so the fullscreen viewer always opens the exact source that produced the visible render. Addresses PR review feedback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): harden mermaid guard and native preview per adversarial review - Guard bypass (blocker): mermaid accepts quoted and unicode-escaped object keys, so quoted "img": evaded the resource denylist while still parsing into a real img property. The guard now decodes u/x escapes and strips quoting characters before matching, and tests both raw and normalized source. - structuredClone polyfill banner in the webview bundle: the app's iOS floor is 15.1 but structuredClone exists only from 15.4; esbuild targets don't polyfill runtime APIs. - Preview height sizes the inner content view so fence padding/borders no longer clip the reported SVG height, and measurement happens at the final content width (measuring overlay keeps the fence insets). - The invisible measuring overlay is hit-test transparent, so an invalid diagram's fallback code block keeps its copy button and gestures. - renderError now prunes all superseded source-map entries, matching the rendered path, so a long broken stream can't accumulate sources. - The entry's render chain can no longer be poisoned: the whole task body is inside try/catch and the chain appends a trailing catch. - "Mermaid diagram" accessibility label is translated in all locales (message.mermaidDiagram) on web and native. - esbuild pinned in packages/app devDependencies so the generated webview artifact doesn't depend on hoisting layout; </script is escaped in the generated JS. Addresses adversarial review round 2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(app): mermaid source toggle and visible zoom controls Web: a hover control cluster on the diagram (zoom in/out anchored to the viewport center, reset view, view source) mirroring the code block's hover-copy idiom; always visible on compact form factors where hover doesn't exist. View source swaps to the highlighted code block (with its own copy button) and a view-diagram button swaps back. Native: the fullscreen viewer gains a source toggle beside the close button, swapping the webview for the highlighted source in a scrollable view. All control labels are translated in every locale under message.mermaid.*; the flat mermaidDiagram key from the previous commit is folded into the same group. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): align mermaid control icons with the app's icon vocabulary Network is the settings Connections icon — reusing it for "view diagram" gave one glyph two meanings; use Workflow instead. RotateCw is the app's established reset/reload glyph (RotateCcw had no precedent). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): use Scan for mermaid reset-view control RotateCw is the app's reload glyph (19 uses) — a reset-zoom button wearing it invites a reload misread. Scan (corner-bracket fit-to-frame) is unused and carries the intended fit/reset semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): remove raw NUL byte from mermaid cache key separator A literal 0x00 in the template string made git classify the file as binary, rendering the PR diff for it unreviewable. Use the escaped sequence instead — identical runtime key, plain-text source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): align mermaid source-view toggle with the copy button The source view wrapped the code block while the fence margins stayed inside it, so the floating view-diagram toggle hung in the margin whitespace above the visible box — misaligned with the code block's copy button and outside its hover region. Hoist the margins onto the wrapper (both buttons now share the box edge) and render the toggle in the copy button's plain style so the pair reads as one control row. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(app): close mermaid review findings * chore(app): finish Mermaid PR rebase cleanup * refactor(app): isolate diagram rendering and stabilize streaming * fix(app): keep Mermaid previews stable while streaming * fix(nix): update npm dependency hash * fix(app): preserve streamed rows through completion --------- Co-authored-by: Damon Meledones <dmeledon@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> * Make large diffs open quickly and scroll as one surface (getpaseo#3212) * perf(app): virtualize large diff rendering Render bounded diff row groups through one vertical list and one horizontal surface. Bound retained list ranges when expansion grows the data set so large diffs paint promptly without sacrificing scrolling behavior. * refactor(app): normalize working tree diff options * fix(app): keep wide diff text reachable * Let users choose the metadata generation model (getpaseo#3215) Expose Automatic and Manual metadata model selection in Host Settings, preserve existing configured fallbacks, and keep structured generation resilient through discovered candidates. Includes Playwright coverage, translations, public documentation, and settings-row alignment polish. * fix(app): keep theme selection consistent (getpaseo#3214) Theme registration, the appearance picker, persistence, and shortcut cycling drifted as new choices were added. Make one ordered catalog own those paths so Auto and Pure Black participate consistently, while lifting the Pure Black selected-workspace surface above its sidebar. * Show the source branch on development builds (getpaseo#3216) Development web and Electron builds now show their source branch in the expanded sidebar, including a branch icon and safe truncation. Dev launchers inject the label at build time; production builds remain unchanged. * docs: fix typos across contributing, product, and skill docs (getpaseo#3153) Fix spelling and grammar errors found in a full sweep of the repo's English markdown files. - CONTRIBUTING.md: "contributing to to it", "accpeted", "wil" - .github/PULL_REQUEST_TEMPLATE.md: "value provider to" instead of "value provided to" - docs/product.md: "vendor-lock in" instead of "vendor lock-in" - packages/expo-two-way-audio/CONTRIBUTING.md: "prescreptive" - packages/expo-two-way-audio/README.md: "can bee added" - skills/paseo-committee/SKILL.md: "considered and reject" Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * feat(app): switch sidebar Group by from the command center (getpaseo#3063) Switching the sidebar between Project and Status grouping takes three clicks today: open the display preferences menu, open Grouping, pick the mode. It is the only sidebar preference with no fast path. Typing "group" in the command center now offers a single entry that switches to the mode you are not in, so it reads "Group by status" while grouped by project and the reverse once you are in status mode. With two modes a "cycle grouping" label would not say where you land, and an entry that reads the same in both states looks like it does nothing. The entry is query-only, so the default Cmd-K list is unchanged. It does not clear the focus restore element, because it navigates nowhere and focus has to return to whatever you were typing in. The contribution is built by a pure function in root-contributions.ts so its branches can be unit tested, mirroring workspace-contributions.ts. The registration subscribes to groupMode with a narrow selector to avoid re-registering every root action when host filters are reconciled. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * Add reusable agent profiles (getpaseo#3208) * Search models across all providers from the picker root * Store agent profiles in daemon config and apply agent config in one RPC An agent profile is a named bundle of provider, model, mode, thinking option, feature values and notes, stored host-wide next to terminal profiles. It carries no system prompt: AgentSessionConfig.systemPrompt is creation-only, so a profile holding one would apply when starting an agent and silently do nothing when applied to a running one. Applying a profile through the four single-field RPCs can fail halfway and leave an agent matching no profile, so agent.config.apply takes the whole bundle. Model runs first because a thinking option only means anything against the model that offers it. Old daemons parse their persisted config strictly and would drop an unknown agentProfiles key, making a save appear to succeed and vanish, so both capabilities are advertised on server_info for the client to gate on. list_profiles exposes the profiles to orchestrating agents, which read the notes to pick one and copy its values into create_agent. * Add the agent profiles settings section Profiles live behind one module boundary so removing the feature is a directory plus one mount, which is what favourites never was. The picker and composer will ask for rows and an apply function rather than learning what a profile contains. The edit form follows the form-model pattern instead of the terminal profile modal's per-field state, because provider selection cascades into which models, modes, thinking levels and features exist below it. The section is gated on server_info.features.agentProfiles: an older daemon drops the key on write, and a save that appears to work and then vanishes is worse than an unavailable state. * Document agent profiles and name the four senses of profile The glossary reserved Profile for HostProfile, terminal profiles owned the user-facing word, and custom-providers.md used it for provider aliases. Agent profile is now the term, with all four cross-referenced. data-model.md never documented terminalProfiles either, so both profile lists are backfilled together along with the whole-list patch semantics and why absent and empty differ. * Pin agent profiles to the model picker and delete favourites Applying a profile writes its values into the agent controls and is then forgotten, so rows carry no checkmark and no selected state: after applying, nothing is selected. Chevron still means navigate deeper, so the absence of one is what marks a row as an action. A running agent is its provider's process and agent.config.apply carries no provider, so the live picker only offers profiles matching the agent's provider rather than showing rows that cannot do what they say. The draft path has no such limit and switches provider freely. Draft feature values go through preferences rather than the feature callback alone, because the draft feature hook clears local values whenever the provider changes and a provider-switching profile would lose them. Favourites are deleted rather than migrated: they were device-local AsyncStorage keyed by provider and model, while profiles are host-wide daemon config. ProviderSelectionModelRow.favoriteKey stays as row identity and moves to its real owner; renaming it is its own commit. * Cover agent profiles and cross-provider search with E2E specs Model rows expressed selection only as a checkmark glyph, which assistive tech cannot see and a spec cannot assert. Rows that draw the indicator now carry aria-selected; profile rows deliberately carry none, because a profile is an action rather than a selection. That is what makes "nothing is marked selected after applying a profile" a real assertion instead of a tautology, with the materialized model one level down as the control. The search fixture extends claude rather than mock: provider-config.ts only allows the six shipped providers as extends targets, so a derived provider cannot inherit the dev-only mock provider. * State what applying a config bundle does on partial failure applyBundle is not a transaction and the earlier commit message implied it was. A step that rejects aborts the rest and the steps that already applied stay applied, which a profile omitting modelId can reach: provider setters validate against the agent's live model, so mode can land while a feature that model does not support is rejected. Nothing is hidden by that. Every setter emits agent state before returning, so what applied is streamed to the client next to the rejected response. Rolling back would re-invoke the setters that just failed, usually against a session that is gone, and pre-flight validation would restate each provider's model-dependent checks where they would drift from the setters that own them. What sending the bundle in one request removes is the multi-round-trip failure class: interruption between hops, and interleaving with another client. The test already pinned this behaviour; its name now says so. * feat(agent-profiles): refine profile discovery and appearance Make profiles discoverable from empty and provider-scoped model pickers, keep global search stable and virtualized, and move profile settings under Agents. Replace emoji identity with a shared Lucide icon and host-color picker, while requiring concrete provider selections so profiles materialize predictably. * fix(agent-profiles): defer profile swatch styles Resolving Unistyles proxies at module load can freeze the temporary startup color scheme before the persisted theme is restored. Keep only style-key names at module scope and resolve the selected swatch during render. * Always create a fresh worktree (getpaseo#3224) * fix(server): always create requested worktrees A slug is a preferred name, not workspace identity. Let the worktree creator suffix occupied paths and branches so duplicate and detached-slug requests still create fresh worktrees. * fix(server): suffix occupied checkout branches Fresh creation also applies when checkout targets are already in use. Create a suffixed branch from the requested branch instead of rejecting the request. * test(server): align occupied branch coverage * test(server): remove stale worktree reuse expectations * Show live task progress while agents work (getpaseo#3227) * feat(agents): surface live task progress Keep current task state above the composer and turn task snapshots into semantic timeline activity. Normalize provider task protocols while preserving genuine plan flows. * fix(agents): admit tasks through stream sequencing * test(agents): expect normalized task status * feat: Implement workspace rename cli (getpaseo#3209) * feat(cli): add workspace rename command `paseo workspace rename <workspace-id> <title>` sets a workspace's user-visible title through the existing workspace.title.set RPC, and `--reset` clears the override so the name reverts to the branch or directory. The command reads the descriptor back so the output shows what a reset reverted to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(cli): harden workspace rename against read-back and arg failures Review findings on the rename command: - A failed workspace list sweep after a successful title change reported the whole command as failed. The read-back is display-only, so it now runs outside the mutation's error path and degrades to a null name. - Only --reset needs the descriptor read back; a set title is already the resolved display name, so the common path no longer pages the list. - Dropped the `"code" in error` rethrow. Nothing in that block throws a CommandError, but DaemonRpcError always carries a `code` property even when it is undefined, so the guard let those through to render as UNKNOWN_ERROR with a stack instead of WORKSPACE_RENAME_FAILED. - Commander 12 allows excess positionals, so an unquoted multi-word title renamed to its first word and dropped the rest. The command now rejects excess arguments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(cli): report the rename RPC response instead of reading back Reading the descriptor back to show what --reset reverted to was the only reason rename needed a paged workspace sweep, and that sweep dragged in everything around it: a failure path that could report an applied rename as failed, a nullable name, and a bespoke warning sink to tell the two null cases apart. Sibling commands don't do this. `workspace archive`, `agent mode`, and `schedule update` all report what the RPC returned and stop; the one command that reads back is `agent update`, which has the same "applied but reported as failed" bug. So rename now mirrors `workspace archive`. `--reset` reports `title: null` rather than naming what it reverted to; `paseo workspace ls` shows the resolved name. This also reverts the `collectWorkspaces` extraction, whose only second caller was rename. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * fix: terminalize Codex compaction on turn end (getpaseo#3211) * fix: terminalize Codex compaction on turn end * fix: ignore stale Codex compaction completions --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> * Notify callers when watched children close and cap responses (getpaseo#3192) Wake caller agents when watched children close and cap embedded child responses at 4,000 characters. Co-authored-by: Hanjun(Tony) <54878785+wilgon456@users.noreply.github.com> Co-authored-by: Thong Van <minhthong@gmail.com> * fix(omp): negotiate RPC protocol v2 so large model catalogs don't overflow (getpaseo#3184) * fix(omp): negotiate RPC protocol v2 so large responses don't overflow OMP caps protocol-v1 single-line RPC frames at 1 MiB. get_available_models returns the full effective model catalog (hundreds of models), which exceeds that cap, so OMP replies with an overflow error ("RPC response exceeded the transport limit") and the OMP provider fails to start sessions. Negotiate RPC protocol v2 after OMP's startup `ready` frame when it is supported: v2 lifts the single-frame ceiling to 64 MiB by chunking oversized frames. Add rpc_chunk reassembly to the shared JSONL transport so chunked responses are reconstructed before routing, and gate the negotiation on the ready frame so it is in place before the first request. Protocol v2 is negotiated only when the peer advertises support in its ready frame; peers that stay on v1 (e.g. Pi's --mode rpc) are unaffected. * fix(omp): make protocol startup atomic --------- Co-authored-by: pi3123 <pi3123@users.noreply.github.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> * Remove unused session message processing (getpaseo#3231) * perf(app): remove unused session message state The legacy message accumulator had no readers but copied its full array for each activity and updated Zustand for every assistant chunk. Keep only the live voice-abort notification and voice transcription behavior. * test(app): use live session action in task subscription test * Reduce storage writes during agent streaming (getpaseo#3232) * perf(app): skip transient replica cache writes The replica cache subscribed to the entire session store, so live stream-head updates forced repeated JSON serialization and browser storage writes even though the head is not persisted. Select the cache's persisted inputs once and schedule writes only when that projection changes. * test(app): move perf cleanup into fixture Keep the replica-cache performance scenario as a direct setup, stream, measure, and assert flow while the spec-local Playwright fixture owns deterministic mock-agent cleanup. * Use saved Project Settings for new worktree setup (getpaseo#3233) * fix(worktrees): honor saved setup configuration Project Settings writes paseo.json in the source checkout, but existing committed copies in new worktrees previously won silently. Copy the saved project config before lifecycle setup so the command shown to users is the command that runs. * fix(worktrees): replace config symlinks safely A selected ref can place a symlink at paseo.json, so copying over that path may overwrite a file outside the worktree. Materialize the saved source config with atomic rename semantics to replace the path entry without following it. * fix(server): keep daemon workers alive across host sleep (getpaseo#3235) The worker watchdog treated wall-clock time spent in host sleep as evidence that the worker was unresponsive. Count consecutive observed heartbeat misses instead so sleep does not destroy live terminal sessions while genuinely stalled workers still restart after the same active observation window. * fix(app): bound Android provider snapshot storage (getpaseo#3234) Android AsyncStorage's 6 MiB SQLite ceiling was exhausted by per-workspace provider snapshots with no eviction. Raise the native cap to 10 MiB and bound snapshots to 4 MiB with indexed eviction, clearing legacy unindexed data without scanning payloads on every save. * Improve Markdown file preview readability (getpaseo#3240) * feat(app): improve markdown file previews Keep rendered documents on the chat reading rail, surface YAML front matter as readable metadata, and make shared Markdown links retain the chat accent and hover treatment. * fix(app): preserve unsafe frontmatter source * test(app): cover markdown link behavior * fix(app): avoid unsupported Android array sorting (getpaseo#3241) Hermes in the production Android app does not provide Array.prototype.toSorted. Multi-host project selection and provider cache eviction could therefore crash when they reached deterministic sorting paths. * Help agents diagnose Paseo provider problems (getpaseo#3243) * feat(skills): add Paseo help and provider diagnostics Give support agents a topology-aware troubleshooting entry point backed by the deployed documentation index, and expose the daemon provider diagnostic through the CLI so agents can inspect the affected host directly. Agent-launching skills now use configured profiles instead of the removed orchestration preferences. * test(cli): keep provider diagnostics on real boundaries Remove the internal client mock and preserve its normalization and host-targeting assertions through the existing isolated-daemon coverage. * Preserve daemon sessions when workers stall (getpaseo#3263) Stop treating heartbeat silence as proof that a live daemon worker should be destroyed. Crash recovery, explicit lifecycle requests, and supervisor-loss cleanup remain unchanged. * Document per-execution Hub worktree branch names (getpaseo#3267) * docs(hub): document per-execution worktree branch names Hub now materializes `${{ paseo.execution.id }}` inside a daemon environment's `worktree.newBranch` before it persists or dispatches the launch, so a reusable branch-off environment can get a stable branch per execution instead of every trigger contending for one literal name. The reference states the constraint precisely because it is the part the YAML cannot show: `newBranch` accepts that one execution-scoped path and nothing else, and any other expression fails bundle activation with the authored file and field in the error. Literal branch names stay valid. * docs(hub): reshape the per-execution worktree branch docs Split the two pages by the job each reader is doing. The daemon page is a task guide, so it now carries the worktree YAML and only the two facts that reader needs to choose a branch name, then links out. The reference owns the exhaustive contract at the `newBranch` field: a table contrasting a literal with the template, then the accepted expression scope and what activation rejects. Dropped the render-before-dispatch ordering. Readers cannot act on where Hub materializes the value; the reachable behavior is that a retried or recovered execution keeps its branch, which is what the table says. * docs(hub): make the per-execution worktree branch the only guidance A branch-off Hub environment that aims every execution at one branch name is not a configuration worth teaching, so the docs no longer present it as an alternative. Both pages now show `newBranch: trigger-${{ paseo.execution.id }}` and say what it does, with no literal-branch example and no comparison. The reference keeps the field's type as a bare syntax fact, along with the accepted expression, what activation rejects, and the authored field the failure names. * fix(usage): read Cursor token from modern state.vscdb key via node:sqlite (getpaseo#2704) * fix(usage): read Cursor token from modern state.vscdb key via node:sqlite Settings → Usage showed Cursor as unavailable for a normal Cursor desktop login. The quota fetcher read the access token from the legacy `cursorAuthStatus` key, but current Cursor builds store a plain JWT under `cursorAuth/accessToken` with no such legacy row, so the lookup returned null. Read the token with built-in node:sqlite instead of shelling out to a `sqlite3` CLI (absent by default on Windows) — a second, independent cause of the same "token not found → unavailable" failure. Prefer the modern key, fall back to the legacy `cursorAuthStatus` blob for older builds, and log read failures at debug so an unavailable card stays diagnosable. Fixes getpaseo#2586 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(usage): cover Cursor sqlite text and blob auth --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> * fix(desktop): shut down cleanly with workspace terminals (getpaseo#3268) Workspace terminal shutdown only signaled an npm wrapper, leaving Electron alive with broken output pipes. Make the signal-handling runner the terminal-owned process and route external Electron termination through the existing quit lifecycle. * fix: stop completed Codex subagents appearing active (getpaseo#3188) * Revert "Make large diffs open quickly and scroll as one surface (getpaseo#3212)" This reverts commit b599d38. * fix(app): align track header hover states The subagent track used a custom hover path that left its label and chevron muted. Reuse the ghost button interaction so track headers promote their content consistently. * fix(app): restore file tree layout Restore the established tree rails while retaining file actions. Keep Escape local to generic editable fields so cancelling a file draft cannot interrupt an agent. * fix(app): align agent profile controls Keep trailing edit controls from changing section heading rhythm, and center each profile's icon with its own text block so one-line and noted rows share the same visual alignment. * Restore model-default variant selection (getpaseo#3281) * fix(providers): restore implicit model variant selection Provider catalogs list only explicit overrides, so treating the first override as the default made base model behavior unreachable. Represent the base choice in the catalog and translate it back to an omitted variant at the provider boundary. * test(providers): exercise variant catalog boundary The regression concerns the model catalog callers receive, so verify normalization through fetchCatalog instead of coupling the test to its internal builder. * docs(changelog): add 0.4.0-beta.1 * chore(release): cut 0.4.0-beta.1 * fix: update lockfile signatures and Nix hash [skip ci] * Preserve opened subagents when parents are archived (getpaseo#3279) * fix(subagents): preserve opened children on parent archive Opening a managed child represents user ownership only while that client's tab remains open. Track that intent per client, detach surviving children during parent archive, and keep single and bulk tab-close behavior consistent. * fix(subagents): mark generic tab openings Centralize managed child tab ownership in agent navigation so list, command-center, deep-link, notification, and subagent-track openings all mark ownership before creating the tab. Use stable localized errors for lifecycle failures. * fix(subagents): classify cold tab openings Fetch uncached agents before deciding whether tab ownership must be recorded, so notification, desktop, and cold-route openings cannot bypass parent archive protection. * fix(app): translate navigation fallback errors * fix(navigation): allow offline workspace intents * refactor(app): derive subagent ownership from open tabs * fix(server): serialize subagent lifecycle ownership * fix(app): invalidate incompatible replica cache Todo timeline entries gained required activity metadata in beta.1 while the persisted cache format remained unchanged. Reject version 3 snapshots so upgrades hydrate authoritative state instead of rendering legacy rows. * docs(changelog): add 0.4.0-beta.2 * chore(release): cut 0.4.0-beta.2 * fix: update lockfile signatures and Nix hash [skip ci] * test(app): remove unintended replica cache case * Keep new worktrees clean when setup changes are uncommitted (getpaseo#3311) * fix(worktrees): preserve selected base config New worktrees were overwriting the selected ref's paseo.json with source-checkout bytes, which could create an accidental revert in an otherwise fresh workspace. Keep committed base config authoritative and warn when locally saved setup changes still need to be committed. * fix(worktrees): seed config exclusively * fix(projects): refresh config status on focus * fix(app): validate persisted client state (getpaseo#3289) * fix(app): validate persisted client state Persisted replica data could satisfy TypeScript through an assertion while missing fields required by renderers. Treat every AsyncStorage value as untrusted input, clear values that fail strict schemas, and version the replica DTO so incompatible caches cannot hydrate. * fix(app): preserve validated legacy state * fix(app): preserve validated preferences Preserve supported legacy location fields and every shipped theme while keeping unknown persisted fields rejected. Align browser test seeds with the current preference shape so strict validation exercises the intended provider. * fix(app): migrate legacy agent preferences Released clients stored host/location fields and per-provider thinkingOptionId together. Migrate those exact strict bytes into current preferences without admitting unknown fields. * fix(app): preserve drafts with legacy reviews Accept the exact strict shape released for workspace review attachments so migration can discard that obsolete attachment without clearing every persisted draft. * Let Claude apply its native MCP timeouts (getpaseo#3315) * fix(claude): preserve native MCP timeouts Paseo forced Claude MCP startup and tool waits to ten minutes, turning unavailable servers into long silent first turns. Let Claude apply its own defaults while preserving explicit user environment overrides. * test(claude): isolate MCP timeout regression Exercise timeout precedence through the existing provider runtime environment input without mutating process-wide test state. * test(workspace-git): await refresh boundaries Synchronize fetch observation tests with initial snapshot and fetch completion before advancing fake timers. * Disconnect daemons locally when Hub is unreachable (getpaseo#3321) * fix(hub): make daemon disconnect unilateral Local relationship removal must not depend on reaching the Hub. Fence execution authority, make one bounded courtesy revocation attempt, then clear local state so status and reconnect eligibility agree immediately. * fix(hub): preserve disconnect intent across crashes Persist the terminal disconnect marker only while the bounded Hub notification is in flight. Startup removes that marker, so a crash cannot restore the old relationship or create a revocation retry loop. * Make provider catalog refresh deadlines configurable (getpaseo#3322) * fix(providers): make catalog refresh deadlines configurable Provider startup can legitimately exceed a fixed short timeout when catalog discovery has substantial initialization work. Give each provider refresh one configurable deadline covering availability and the complete catalog probe, and cancel named child operations before allowing retries so timed-out work cannot accumulate in the background. * fix(opencode): abort catalog server acquisition A provider refresh could expire while the helper server was still starting because acquisition only tracked the activity without observing its abort signal. Reject acquisition before transferring a server reference, while leaving the manager-owned startup reusable by the next caller. * test(file-observer): allow native burst recovery * Stop idle workspace Git refreshes from starving daemon requests (getpaseo#3323) * fix(server): trust healthy workspace git watchers Quiet observed workspaces were periodically scheduling full Git refreshes, saturating the daemon-wide process pool as workspace count grew. Keep only cheap observation re-ensure work, refresh ignore paths from watcher events, and detect silent metadata watchers with a one-shot canary before accepting the subscription. * fix(server): refresh watcher ignores from git metadata Repository exclude and Git configuration changes also affect the standard ignore set. Route those metadata events back to linked working-tree observations so their excluded directory lists stay current without periodic Git polling. * test(server): drive ignore updates through watcher events The integration contract now exercises the production event-driven ignore refresh instead of waiting for the removed periodic audit. * fix(server): normalize git metadata paths on windows File observation returns platform-native relative separators. Normalize those paths before matching Git metadata names so repository exclusion changes refresh working-tree ignores on Windows too. * fix(server): trail concurrent ignore refreshes Remember ignore-source events that arrive during an in-flight Git query and recompute once more before considering the working-tree observer current. * Add guided Hub setup (getpaseo#3318) * feat(cli): add Hub init wizard Reuse the existing login, daemon enrollment, project listing, and deploy paths so onboarding stays compatible with current Hub servers. Generated triggers require an explicit sender allowlist and existing bundles are never modified. * fix(cli): stop Hub init until daemon is ready A durable Hub relationship can be reconnecting without being ready to execute workflows. Keep enrollment skipped, but stop the wizard until daemon status reports connected. * fix(cli): wait for Hub daemon readiness Both fresh enrollment and reconnect paths can expose a durable relationship before the execution socket is connected. Poll the existing status RPC with a bounded wait before resolving projects or deploying. * fix(cli): make hub init detect resolvable resources * Keep agent profile settings valid across providers (getpaseo#3331) * fix(app): keep profile modes scoped to providers Applying a profile through sequential field setters allowed a provider change to race mode persistence, poisoning modeless provider preferences with another provider's mode. Apply profiles as one form transition and heal invalid provider-scoped mode preferences when profiles are used. * test(app): cover profile features across providers * feat(app): unify task list status styling Use one task-row presentation across message and composer lists so running, pending, and completed states keep the same hierarchy and status language. * fix(providers): allow two minutes for catalog refreshes Provider catalog discovery can exceed one minute during cold startup. Increase the default deadline while preserving explicit config and environment overrides. * fix(server): prefer portable project icons Project icon discovery could select an ICO that native clients cannot decode even when a renderable SVG or PNG was available. Keep ICO as the final fallback and recognize common sized icon names. * fix(app): support copying Hermes resume commands (getpaseo#3300) * docs(release): require PR context for changelogs * fix(app): open sole provider model picker directly Running chats expose one provider, so an intermediate provider screen and back action are redundant. Restore the direct model-list entry behavior while preserving the multi-provider picker. * docs(release): make changelog wording factual * docs(changelog): prepare 0.4.0 * chore(release): cut 0.4.0 * fix: update lockfile signatures and Nix hash [skip ci] * Stabilize live relay status coverage (getpaseo#3337) * test(cli): stabilize live relay status coverage The regression fixture deleted the supervisor's active PID lock to model a reachable daemon without local process state. Under CI load, worker readiness could race that deletion and make the supervisor tear down the daemon while status and pairing were probing it.\n\nLaunch the real worker directly so the no-PID state exists naturally, then wait on the CLI-visible live relay state before asserting behavior. * test(cli): wait for complete relay probe state Status readiness alone does not guarantee the next pairing RPC or the foreign-home identity probe will finish inside the CLI's per-call timeout under CI contention. Retry each exact observable outcome within the fixture deadline and resolve the real worker entry through the server package. * test(cli): name retry mechanics once Keep the relay scenarios expressed as CLI probes and exact outcomes while one file-local helper owns the shared deadline, worker-liveness check, and retry cadence. * Restore host directories instantly before reconnect (getpaseo#3259) * feat(sync): restore host directories before reconnect Persist complete project, workspace, and active-agent replicas so every registered host can paint immediately, including offline hosts. Reconcile those replicas through monotonic per-entity cursors on the existing directory RPCs without retaining an event journal. * fix(sync): preserve active turn identity during catch-up Own Agent-to-wire projection beside the existing snapshot normalizer so replica persistence does not reach through directory-sync internals. This also keeps identified active turns intact when unchanged agents are folded into an incremental directory response. * test(e2e): inspect the real IndexedDB replica Keep cache setup, reload, and hydration coverage on the production browser storage backend after the replica moved off localStorage. Measure writes at the actual IndexedDB put boundary as well. * fix warm reconnect cache paths * feat(sync): persist project icons and deepen directory sync * fix(sync): preserve project update order * fix(cache): preserve validated replica persistence * fix(sync): retain cached agents during catch-up * feat(app): make pinned workspaces sortable (getpaseo#3341) Reuse the existing workspace drag interaction for pinned rows and persist their local order across grouping modes. Keep pressed and dragged row scrims aligned with the active backdrop, and preserve memoized row rendering on web. * fix(desktop): keep element selectors available on loaded pages (getpaseo#3187) * fix(desktop): keep element selectors available on loaded pages Annotate element and Screenshot element returned early when a cached load signal said the guest was not ready, even if the current document was complete and executable. Let executeJavaScript own the readiness boundary and keep selector cleanup available in the same stale-signal state. Extend the real Electron browser regression to reproduce the complete-document/stale-signal mismatch through both toolbar actions. * test(desktop): await browser selector injection Poll the guest selector state through the browser bridge instead of assuming injection completes within 250 ms. This keeps the Electron regression deterministic on slower CI hosts. * Harden browser selector session lifecycle * Keep selector E2E fixtures independent * fix(desktop): harden selector and annotation lifecycle * fix(desktop): keep selector PR focused * refactor(desktop): own element selector lifecycle --------- Co-authored-by: Daniel G. Kang <180234654+dgk-dev@users.noreply.github.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> * fix(app): make composer input IME-safe and paste-aware (getpaseo#3343) * fix(app): preserve native composer input semantics Native text was round-tripped through application state on every keystroke, which could overwrite an input method's in-flight edits. Keep native text ownership inside the input and reserve application-driven replacement for explicit draft mutations.\n\nRoute native clipboard images through the existing persisted attachment pipeline so keyboard and system paste actions behave like other image sources. * fix(nix): refresh dependency hash The native composer dependency changes the fixed npm dependency closure used by desktop builds. * fix(app): keep composer locked across image pastes Count concurrent native image persistence operations so submit remains disabled until every pasted image is attached. * fix(app): replace composer text through the DOM on web Web TextInput refs are DOM elements, so application-driven draft replacement must update their value and selection without native setNativeProps. * fix(app): preserve web IME composition Carry the Chinese IME fix from getpaseo#2990 into the web composer input boundary. Co-authored-by: Jimmy Lee <jimersylee+github@gmail.com> --------- Co-authored-by: Jimmy Lee <jimersylee+github@gmail.com> * Resume cached timelines without replaying their tail (getpaseo#3329) * perf(timeline): resume cached history from its exact range Warm launches were repainting cached history and then downloading the same bounded tail to rediscover that nothing changed. Persist synchronization authority only when the complete current canonical window can be encoded losslessly; otherwise retain the existing display-only fallback. * test(timeline): hide replica setup behind E2E helper * fix(timeline): bound persisted resume overflow * test(timeline): cover workspace eviction catch-up order * test(timeline): read persisted range from IndexedDB * Add managed local plugin lifecycle (getpaseo#3222) * feat(plugins): add local plugin surfaces and RPCs Keep plugin execution scoped to its daemon while allowing one local TypeScript entry point to contribute validated RPCs and native client surfaces. Paseo owns host selection, navigation chrome, and per-installation query caches. * feat(plugins): add Linear attachment source * fix(nix): refresh npm dependency hash * test(server): exclude internal sessions from Hub counts * test(server): distinguish Hub execution sessions * feat(plugins): make local plugins explicitly opt-in Keep plugin loading dormant until configuration enables it, and collect unsupported examples under one directory. * refactor(plugins): establish host boundaries * fix(nix): refresh plugin dependency hash * fix(plugins): complete packaging and startup rollback * feat(plugins): add managed local lifecycle Make trusted local plugins recoverable and authorable through daemon-owned lifecycle operations, host settings, and a capability-gated CLI. Reloads fully stop the previous plugin before starting new code, while generated projects provide strict TSX checking without changing runtime dependency ownership. * fix(nix): refresh plugin dependency hash * Archive finished subagents across the whole track (getpaseo#3368) * feat(app): archive finished subagents together Make the track action clean up finished managed and provider-owned children as one operation while preserving running work. Managed archives remain sequential and provider-owned dismissal stays client-local. * test(app): clarify subagent archive assertions * Reload daemon configuration without restarting (getpaseo#3365) * feat(daemon): reload configuration without restarting Keep config validation, reloadability classification, override handling, and atomic runtime application inside the daemon so local and remote clients share one authoritative workflow. * fix(daemon): preserve reload resource ownership Keep every materialized provider client under daemon-lifetime ownership and classify speech overrides from resolved providers. Restore the complete WebSocket config-store test contract exposed by CI. * fix(acp): isolate Hermes state per Paseo agent --------- Co-authored-by: Damon <17677912+dmeledon@users.noreply.github.com> Co-authored-by: Damon Meledones <dmeledon@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> Co-authored-by: Christoph Leiter <christoph@leiter.io> Co-authored-by: Martin Hanzík <martin@hanzik.com> Co-authored-by: Edi Hasaj <34984294+edihasaj@users.noreply.github.com> Co-authored-by: Hanjun(Tony) <54878785+wilgon456@users.noreply.github.com> Co-authored-by: Thong Van <minhthong@gmail.com> Co-authored-by: pi3123 <subhashve4@gmail.com> Co-authored-by: pi3123 <pi3123@users.noreply.github.com> Co-authored-by: QuteSaltyFish <41997780+QuteSaltyFish@users.noreply.github.com> Co-authored-by: James Strain <jstrain@nvidia.com> Co-authored-by: paseo-ai[bot] <266920839+paseo-ai[bot]@users.noreply.github.com> Co-authored-by: Daniel G. Kang <kangmumu@gmail.com> Co-authored-by: Daniel G. Kang <180234654+dgk-dev@users.noreply.github.com> Co-authored-by: Jimmy Lee <jimersylee+github@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linked issue
None. Related provider-row persistence work: Refs #3137.
Type of change
Reasoning
Archive finished previously dismissed only finished provider-owned rows, despite presenting managed Paseo subagents in the same track. Users had to archive finished managed children one at a time, so the bulk action did not match the surface it belonged to.
This change makes the action cover every finished row. Managed children archive sequentially through the existing optimistic lifecycle mutation; provider-owned children remain a client-local dismissal. The workflow and retry state live in a non-React controller so the UI only subscribes, renders status, and dispatches the action.
Goals
Non-goals
QA
Automated behavior coverage:
npx vitest runagainst the four focused subagent files — 59 tests passed.npm run format:check:files— passed.npm run lint— 0 warnings, 0 errors.npm run typecheck— all workspaces passed.git diff --check— passed.Platform coverage:
The visible change is limited to transient numeric progress and localized retry copy in the existing track header; no layout or status-icon design changed.
Risk surface
The main risk is lifecycle coordination during partial failure. The controller rechecks each managed child before archive and classifies retryability from the authoritative store after rejection. Tests cover optimistic removal, sequential settlement, mixed managed/provider rows, resumed work, missing/archived/reparented children, partial failure, and retry recovery.
Checklist
npm run typecheckpassesnpm run lintpassesnpm run formatpasses