addNewWorkspaceItem mounts a plugin field on the New workspace screen, and
useNewWorkspace() exposes host, project, isolation, base ref, branch and
metadata with commands that follow the composer item rules: they act on the
screen's state when they run, resolve once applied, reject while the screen
is locked, and take JSON payloads. Host and project are read-only because a
command that switched them would unmount the item that called it.
The screen's pickable state (base selection, manual isolation, branch,
metadata) moves into one plain store so pickers and plugin commands write to
the same place; the screen reports derived facts to it on each render. The
branch is sent as source.branchName only for a branch-off worktree, and
metadata on workspace.create.
Items render through ComposerControl with a new "field" surface (badge on
desktop, full-width field when compact). Combobox ships here: Paseo owns the
loading and error states, keeps previous results, and drops superseded
searches.
Draft commands read the state captured at render, so a plugin's
`await setProvider(); await setModel()` acted on a stale provider. The draft
form and options are now plain stores that commands read live, and
`setProvider`/`setModel` reject an unavailable provider or a model the loaded
catalog does not list. Draft `options` include the plugin's pending edits.
Shared composer controls own the pending state and show a rejected command as
an error toast, like the built-in controls. Callback payloads are objects
(`{ value }`, `onPress(event)`) so they can grow without breaking plugins.
The overflow sheet's content portals to the app root and lost the composer's
agent, input, toast and query contexts, so plugin items drew nothing in it.
`useComposerSheetBridge` carries them across.
A plugin registers a component with addComposerItem and reads and changes the
composer through useComposer(): provider, model, mode, thinking, options,
prompt, attachments and agent metadata. The agent, draft and New workspace
composers provide the same ComposerInput and ComposerAgent halves, so a
command reaches the right model without the plugin knowing which composer
it sits in.
Toggle, Select and Action are drawn by one ComposerControl renderer that
the thinking select and provider option controls now use too. Controls sit in
the toolbar when there is room and become rows in the existing overflow sheet
otherwise, so on a phone they appear in the overflow. Plugin items count in
the density presence so the composer condenses to make room. Model and mode
stay specialized but share the trigger, density and overflow.
Draft metadata is kept locally, sent at creation and reset when the prompt is
sent. PluginAgentSnapshot.metadata, deferred from the metadata phase, lands
here. The unused provider badge is removed.
vitest resolves @floating-ui/react-native to its ESM build: the CJS entry
requires the real react-native, whose Flow sources do not parse.
An agent.create.before hook that returned its own { config, env } dropped
metadata that an earlier hook in the chain had added, because only the
caller's original metadata was restored at the end. The omitted field is
now carried forward at each hook handoff, as agent.open.before already
does.
The agent.open.before result type also required metadata, so an older
TypeScript plugin that returned its own object no longer compiled even
though the daemon accepted it. The result type now makes it optional.
Plugins had nowhere to keep their own data on an agent or workspace, so
Phase 5 and 6 surfaces would each have invented a side store. Both now
hold an opaque Record<string, JsonValue> that Paseo stores, snapshots and
hands to hooks without reading a key.
Set at creation or changed with agent.metadata.update.request /
workspace.metadata.update.request (top-level keys replace, null removes).
Persisted on the agent and workspace records, so it survives a restart.
Reaches agent.create.before (which may add to it), agent.open.before
(read-only), agent.create.after and workspace.create.before/after.
Gated once on server_info.features.entityMetadata; the client throws
"Update the host" instead of sending to a host without it.
A plugin reporting `controls` and `providerOptions` receives every key a live
change touches, with or without a control, and applies them or rejects the
whole request. The daemon's live-key guard now runs only for a plugin still
reporting `settings`, which cannot refuse a key.
A provider plugin reports `controls` and the `providerOptions` it runs in
`session.config`, and receives running-session changes as
`session.configure` `changes.providerOptions` (null returns a key to its
default), under the same apply-or-throw contract as built-in providers.
A control may declare a `defaultValue`; a value a plugin reports at that
default for a key nobody set stays unset instead of being pinned to the agent.
A plugin that still reports `settings` keeps working: the daemon reads each
setting as a control plus an option and sends changes in both shapes
(COMPAT(plugin-provider-settings)).
The app resolves a control's icon as a Lucide name when it is not one of the
built-in kebab-case names, so a plugin's icon shows live and in drafts.
Public docs and the provider-direct example use only `providerOptions` and
controls; the migration doc gains the rows.
Bring in what v0.14.0 shipped: the 0.14.0 entry in CHANGELOG.md and
CHANGELOG.zh-CN.md exactly as tagged (replacing the 0.14.0-beta.2 series
entry), the 0.14.0 version in every package.json and package-lock.json, and
the generated F-Droid changelogs. The unreleased notes on main stay above the
entry and now say they are not in 0.14.0.
Release commit: 8c6de842a3 (tag v0.14.0)
* docs(changelog): fold 0.14.0-beta.2 UI fixes into the 0.14.0 entry
(cherry picked from commit 28bb7e29be)
* chore(release): cut 0.14.0-beta.2
(cherry picked from commit 1a05491be5)
* docs(changelog): keep unreleased main changes out of the 0.14.0-beta.2 entry
The beta.2 tag was cut from a selective hotfix branch, so #8100, #8117 and #8081 are on main but not in beta.2. Every changelog parser (app, website, release-notes sync) skips the preamble, so listing them there keeps them out of the beta.2 release body and What's new until the next release entry absorbs them.
* 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>
* 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(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>
* feat(plugins): attach tools to individual agent sessions
* feat(plugins): reopen idle agent sessions after hook changes
Defer automatic plugin changes until the next idle prompt so running turns keep steering. Expose session reload separately from snapshot refresh in the SDK.
* docs(plugins): show how to attach tools to agents
* test(agent): update permission follow-up adapter
* fix(app): keep the sidebar alive when a workspace entry has not hydrated (#7896)
ProjectBlock asserted that every placement in a project has an entry in
workspaceEntriesByKey. A retained sidebar that is inactive (compact layout
with the sidebar closed) or still waiting for sessions keeps an empty map,
and Show background stops the project list from being pruned to hydrated
entries. From the eighth workspace on, selectSidebarGroupVisibility asked
for the status of a missing entry and the app crashed on start.
Count a missing entry as Done. It renders no row, so it should not claim a
slot past the limit.
Closes#7869
Co-authored-by: brucey0x <11062155+brucey0x@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 6bc4b73c42)
* docs(release): add 0.13.1 hotfix notes
* chore(release): cut 0.13.1
---------
Co-authored-by: Budi <11062155+budivoogt@users.noreply.github.com>
Co-authored-by: brucey0x <11062155+brucey0x@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(server): release idle agent runtimes with provider safety checks
Require provider permission before releasing resumable runtimes so foreground idleness does not discard tracked background work. Preserve the saved conversation and serialize prompt admission with runtime closure; allow hosts to disable collection.
* docs: translate idle runtime cleanup configuration and provider contract
* feat(server): collect runtimes after one idle hour with optional provider vetoes
Keep callers resident while local or remote finish notification intent remains outstanding. Daemon-owned work determines baseline eligibility; provider checks add optional vetoes. Own the idle clock and sweep lifecycle in one cleanup module while the manager retains guarded closure.
* feat(agents): control idle runtime release live from host settings
Let hosts retain runtimes without restarting the daemon. Disabling invalidates pending collection decisions while preserving idle clocks for re-enabling. ACP-owned terminals and pending adapter work veto collection until they finish.
* fix(settings): clarify idle agent release with an info tooltip
The 0.13 UI entry rename dropped the reveal hook from the 0.12 runtime.
Keep it on the legacy entry until that entry is removed, with deprecated
SDK typing and migration guidance.
Closes#7577Closes#7722
Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: RoundRedDot <roundreddot@gmail.com>
Co-authored-by: Matt Cowger <1929548+mcowger@users.noreply.github.com>
ACP agents replay a loaded session's session/update notifications before
session/load resolves, but AcpRuntime.open registered nativeSessionId only
after the response, so the session filter dropped every replayed update and
restored sessions opened with an empty timeline.
Register the native id before the request, drain the notification lane once
the response arrives so the replay completes before session.opened, and
follow the open request's history contract: replayed updates rebuild the
timeline under history "replay" and are dropped under history "skip".
Replayed user_message_chunk updates now restore user messages through the
chunk fallback scheme, so multi-chunk messages keep their boundaries; live
user chunks stay dropped because admitPrompt already echoes the prompt.
Co-authored-by: nistream <nistream@users.noreply.github.com>
* feat(plugins): unify panel props and persist tab state
* refactor(plugins): make agent panel registration canonical
* docs(plugins): update Chinese panel API reference and migration
* feat(website): serve paseo.sh in Simplified Chinese
Every page of the website now has a Simplified Chinese version under
/zh-CN: the interface, docs, blog posts, comparison pages, legal pages,
all changelog releases and the homepage mockup. English URLs are unchanged.
The URL alone decides the language. There is no browser-language redirect
and no stored preference, because either would have to redirect the
unprefixed English URLs that people share and search engines index.
Internal links carry the page's language instead, and hreflang alternates
point Chinese search results at /zh-CN.
Translated headings pin the English anchor with {#id}, so section links
work in both languages. content-translations.test.ts fails when a page
has no translation or a translation's headings drift from the English.
Changelog releases and plugin listings without a translation render
whole in English under a notice, so a release never waits on one.
Plugin manifests and the registry gain optional translations and
OVERVIEW.zh-CN.md fields; older clients and registries ignore them.
* feat(website): replace the native language select with an EN/ZH menu
The header showed an OS-native select whose popup did not match the site.
The trigger now shows the language abbreviated (EN, ZH) and opens a menu in
the app's dropdown style that names each language in itself, with its name
in the page language underneath. Rows are real links to the same page in
that language; arrows, Escape, outside presses and taps behave as a menu
button, and the menu keeps itself on screen when a narrow header pushes the
trigger toward an edge.
Also drops the stray space after the full-width comma in the Chinese
homepage subtitle.
* docs(website): translate main's new docs and 0.12.0 release notes
Main added Keep run workspaces to the schedules docs, rewrote plugin icons
around inline SVG, and promoted 0.12.0 with new entries. Bring the Chinese
pages and changelog in line so no section falls back to English.
* fix(server): leave plugin listing translations to the registry
The daemon rejected manifests whose translations the registry would refuse,
so a plugin main installs could fail to load here. Paseo never reads
translations; the registry validates them at publish. Docs now say which
checks are automated at publish and which are human review.
* docs(plugins): describe the registry's final overview translation discovery
* style(server): format manifest test
* feat(plugins): translate plugin UI in each app language
Keep translation resources and subscriptions inside each installation. Resolve host labels against the presentation locale so live language changes preserve mounted drafts and RPC state. Existing descriptor types retain string defaults for source compatibility.
* test(plugins): dismiss developer chrome in second translation client
* test(app): measure sidebar rows one at a time
After a reload the developer error toast appears while expectVerticalOrder
measures every row at once. Each concurrent boundingBox runs the toast
locator handler, and only one of them resumes, so the poll timed out on
every attempt since #7424. Measuring sequentially keeps one action in flight.
* test(app): return sidebar row measuring to main
#7480 hides the developer toast instead of running a locator handler, so
measuring the rows concurrently no longer races it. Reverts 41ac23016.
* test(app): wait for the compact shell before opening compact Changes
The shell survives the breakpoint, so right after the resize the wide
Explorer could still be visible: the test skipped the Explorer toggle and
the tab then disappeared, or toggled at the wrong time. Wait for the
compact-shell marker, as the composer helpers do since #7424.
* docs(website): translate main's plugin file panel docs
* fix(files): report document MIME types for binary files
File reads now carry the document's MIME type for binary files, so a file panel can
hand the bytes to a viewer that needs the real type (application/pdf).
* feat(plugins): file panels
Plugins contribute file panels with addFilePanel. A file tab hosts the panels that claim the
file: the built-in Markdown, HTML and image panels, plugin panels, and Source, Paseo's editor,
which claims every text file and is always the last candidate. The panel is the tab body;
the bar shows the document's status and a switcher between the file's panels (side by side
up to three, two on a phone, a menu beyond that).
Paseo asks which panel opens a file only when a plugin panel is among several candidates,
once per open, and remembers the answer per extension or exact filename when told to. Open
with in the tab menu and the switcher change the tab's panel; Always use remembers it.
Opening a file at a line switches the tab to Source.
Panels read and write the host's document, so dirty state, autosave and conflicts stay
Paseo's. DocumentFrame shows bytes documents such as PDFs in the browser's viewer.
Replaces the file tab's Preview/Source mode toggle.
* feat(file-panels): choose and forget file panels in Settings > Files
Settings > Files lists each extension or filename where a plugin panel competes with
another candidate, or that has a remembered panel. Each row chooses the panel that opens
it, or Ask every time; Ask again forgets the choice. Source appears as a candidate for
text files. Panels no connected host offers can be forgotten but not chosen.
* docs(plugins): file panel examples and author reference
Adds the pdf-panel and markdown-panel examples, the File panels section of the plugin
reference, and an e2e spec that installs both examples, opens a PDF in the browser
viewer, and edits Markdown through Paseo's document across the switcher, Source,
autosave and conflicts.
* feat(file-panels): panels read and write through a file handle
The file tab stats the file and mounts its panel without reading it. Every
panel, built-in or plugin, gets a PluginFile handle: readText, readBytes, url,
version-checked writeText/writeBytes (PluginFileConflictError unless force),
and watch. useFileText, useFileBytes and useFileUrl read through it, and
Loading joins @getpaseo/plugin/client/ui. Panels own their loading state.
The host document pipeline, the explorer preview cache and the preview
lifecycle are deleted. The daemon classifies a file version from a bounded
sniff and writes base64 content as bytes behind fileByteWrites.
A deleted file keeps its panel mounted; a read confirms the deletion first,
so a remove-and-rewrite is not reported as one. Native Source decodes with
builtins and highlights in idle-time slices, the lines around the opening
line first, so a large file no longer blocks the JS thread.
* fix(file-panels): Settings offers Source where the picker does, and review follow-ups
- Settings > Files offers Source on every key except those a built-in panel of
another kind (Image) claims, so a plugin that alone claims a text extension
shows the same choices as the picker.
- Delete the unused isSourceFilePanel.
- The Markdown example warns with the file name when its unmount write fails,
rather than dropping the edit silently.
- Document that PluginFile stat fields are read at render time and that watch
or the read hooks are how a panel learns they changed.
* refactor(plugin): consolidate the client UI entrypoint
* fix(files): report document MIME types for binary files
File reads now carry the document's MIME type for binary files, so a file panel can
hand the bytes to a viewer that needs the real type (application/pdf).
* feat(plugins): file panels
Plugins contribute file panels with addFilePanel. A file tab hosts the panels that claim the
file: the built-in Markdown, HTML and image panels, plugin panels, and Source, Paseo's editor,
which claims every text file and is always the last candidate. The panel is the tab body;
the bar shows the document's status and a switcher between the file's panels (side by side
up to three, two on a phone, a menu beyond that).
Paseo asks which panel opens a file only when a plugin panel is among several candidates,
once per open, and remembers the answer per extension or exact filename when told to. Open
with in the tab menu and the switcher change the tab's panel; Always use remembers it.
Opening a file at a line switches the tab to Source.
Panels read and write the host's document, so dirty state, autosave and conflicts stay
Paseo's. DocumentFrame shows bytes documents such as PDFs in the browser's viewer.
Replaces the file tab's Preview/Source mode toggle.
* feat(file-panels): choose and forget file panels in Settings > Files
Settings > Files lists each extension or filename where a plugin panel competes with
another candidate, or that has a remembered panel. Each row chooses the panel that opens
it, or Ask every time; Ask again forgets the choice. Source appears as a candidate for
text files. Panels no connected host offers can be forgotten but not chosen.
* docs(plugins): file panel examples and author reference
Adds the pdf-panel and markdown-panel examples, the File panels section of the plugin
reference, and an e2e spec that installs both examples, opens a PDF in the browser
viewer, and edits Markdown through Paseo's document across the switcher, Source,
autosave and conflicts.
* feat(file-panels): panels read and write through a file handle
The file tab stats the file and mounts its panel without reading it. Every
panel, built-in or plugin, gets a PluginFile handle: readText, readBytes, url,
version-checked writeText/writeBytes (PluginFileConflictError unless force),
and watch. useFileText, useFileBytes and useFileUrl read through it, and
Loading joins @getpaseo/plugin/client/ui. Panels own their loading state.
The host document pipeline, the explorer preview cache and the preview
lifecycle are deleted. The daemon classifies a file version from a bounded
sniff and writes base64 content as bytes behind fileByteWrites.
A deleted file keeps its panel mounted; a read confirms the deletion first,
so a remove-and-rewrite is not reported as one. Native Source decodes with
builtins and highlights in idle-time slices, the lines around the opening
line first, so a large file no longer blocks the JS thread.
* fix(file-panels): Settings offers Source where the picker does, and review follow-ups
- Settings > Files offers Source on every key except those a built-in panel of
another kind (Image) claims, so a plugin that alone claims a text extension
shows the same choices as the picker.
- Delete the unused isSourceFilePanel.
- The Markdown example warns with the file name when its unmount write fails,
rather than dropping the edit silently.
- Document that PluginFile stat fields are read at render time and that watch
or the read hooks are how a panel learns they changed.
* fix(plugins): file events are objects, deletion is a typed error, DocumentFrame owns its fallback
- watch listeners receive { type: "changed" } | { type: "deleted" }, so later
versions can add fields without breaking them.
- PluginFileDeletedError joins PluginFileConflictError. The read hooks report a
deleted file with it, and a write over a deleted file rejects with it; Source
shows that as the deleted notice. PluginFileConflictError now means only
"changed on disk".
- DocumentFrame on Android shows Paseo's "<TYPE> files can't be shown on this
device" message for documents its WebView cannot display, so the PDF example
is useFileUrl + Loading + DocumentFrame with no platform branch.
* test(file-panels): Settings offers Source for a remembered panel no host has
* test(e2e): hide the developer error toast instead of clicking it away
The locator handler that dismissed #error-toast could match a toast that closed before its
click, then wait on it until the test timed out (agent-timeline-resume.spec.ts:45 failed every
attempt on this branch). Hiding the toast with CSS keeps it off app actions with no race.
* test(e2e): desktop browser-tabs hides developer toasts on its attached page
Stop materializing and copying the entire conversation through lifecycle hooks. Keep the deprecated timeline field empty and direct plugins to bounded timeline reads.
* Upgrade to Expo 57 and unify Explorer across layouts
* feat(app): retain Explorer across layout and appearance changes
Keep one workspace Explorer host and destination-aware navigation so folding, theme updates and compact sheets preserve the current panel. Scope pull request discovery and timeline demand to active surfaces.
* fix(app): keep Explorer resize handles stretched through the dock
The shared frame moved the divider into an absolute column, leaving its horizontal handle without height. Restore the row so pointer drags reach it, preserving the existing resize and performance contract.
* fix(app): keep Explorer drag widths out of the shared stylesheet
Carry the existing inline sizing policy into the shared frame so pointer previews do not accumulate CSS rules.
* fix(test): align isolated test runtimes with Expo 57
Hoisted React consumers need the matching DOM renderer at the repository root. Automated Metro processes must not install native DevTools into disposable homes, and development toast overlays must not cover compact actions.
Keep Explorer journeys on the shared host and synchronize setup fixtures on daemon output rather than a one-second file-marker window. A delayed real child start reproduces the readiness failure on every platform.
* fix(test): await retained shell layout after composer resize
The gesture host now stays mounted across breakpoints, so its presence no longer identifies compact layout. Report the current layout on the retained host and synchronize composer journeys with that state.
* fix(app): preserve browser interactions in retained workspace hosts
Initially disabled gesture hosts still registered pointer and wheel listeners, while hover cards used window positions inside a workspace-local sheet portal. Respect initial gesture enablement and position cards relative to their measured floating host.
Keep developer-toast dismissal independent of pointer input and replacement toast visibility so modal, hover, and focus checks retain their input state.
* fix(test): share developer toast handling with Electron browser runs
The standalone Electron browser runner bypassed the browser fixture, allowing developer errors to cover Settings. Use the same non-pointer dismissal in both runners while retaining their console logs.
Every contribution `icon` now takes a Lucide name, `{ svg }`, or a
component. Attachment sources take only the first two because composer
drafts persist the source icon.
The daemon's provider-icon check and the app's inline-SVG check are the
same function in the shared SDK root, so a plugin sees the same reasons
in both places. The app parses each icon once at registration and fails
the plugin with that reason. Rendering goes through one module that
turns any form into a component, so surfaces never branch on the form.
Preserve the bundled Hoplite ACP adapter and provisioning transformer, activate it only after Add, and provide a searchable provider grid. Restore the contributor's original icon without the copyright mark.
Co-authored-by: Odin Glynn-Martin <odin.glynn@icloud.com>
* fix(muse): show usage only for the agent account
Usage discovery ignored session scope and exposed all remembered accounts to unrelated agents. Resolve session accounts from their own launch environment while preserving global discovery.
* test(muse): own the usage scope regression in the plugin suite
* fix(usage): scope agent reports to their own logins
Account grouping also merged fetch inputs and cache entries across agent and host queries, hiding the agent login failure behind another harness. Preserve scoped login sets, collect every host failure with source-owned harness labels, and keep refresh results within their requesting view.
* test(usage): expect harness labels in discovered accounts
* fix(providers): launch plugin CLIs on Windows and tolerate Muse startup
Plugin providers bypassed the core Windows launcher and the plugin host exposed an empty server SDK module. Share the launcher through the SDK and bind it in the host. Give Muse initialization its own deadline because native startup traverses session history before answering MSP.
* fix(providers): terminate Windows launcher process trees
A command-script launcher leaves the CLI behind cmd.exe. Expose forced process-tree termination in the server SDK, use it for Muse and SDK ACP teardown, and bound Muse closing so inherited streams cannot leave callers waiting forever.
* test(muse): keep platform setup outside the teardown assertion
* refactor(plugin): expose process capabilities without quoting helpers
* fix(providers): close process trees on uncorrelated protocol errors
* feat(usage): stream reports for live agent accounts
Resolve account scope from the running harness environment without invoking lifecycle hooks. Keep the client aggregate result while delivering report updates as each fetch settles; the app presentation follows separately.
* fix(usage): retain account scope for open agent sessions
Capture the launch environment once per session, independently of lazy child-process startup or exit. Use a session UUID for registry identity. Update remaining global discovery fixtures to the scoped SDK contract.
* test(usage): update browser fixtures for scoped report streams
* feat(usage): let a usage card show window rows without pins
UsageCard takes a required pinnable prop. When false, a window row is a
plain bar: no Pressable, hover highlight, or pin glyph. The settings
Usage list passes true.
* feat(usage): show the agent's usage in the context window popover
Reports stream into the query cache as each update arrives, for the
host list and for a new per-agent query keyed by agent. The context
window popover fetches its agent's reports while open and renders one
unpinnable card per report as it lands, a loading sentence before the
first, the request error as a sentence, and nothing on hosts without
usage reports.
* feat(usage): hide the Refresh button on popover usage cards
UsageCard takes a required refreshable prop. The popover passes false
because its content takes no pointer events, so the button could not
be pressed; the card then shows its freshness inline. The settings
Usage list keeps the button.
* feat(usage): make the sidebar Usage summary opt-in
The sidebar Usage item starts hidden on every layout, not only on
compact ones, so resolveSidebarNavItems drops its compact input. While
the summary is off, the Usage list renders window rows without pins;
saved pins stay in preferences and return when it is turned on. Specs
that read the summary now turn it on first.
* docs(glossary): describe the popover usage card and the opt-in sidebar summary
* fix(usage): show a popover request error without retrying
A daemon error for an agent's usage, such as an unknown agent, does not
heal on retry, and reopening the popover fetches again. Retrying held
the loading sentence for about seven seconds before the error.
* test(usage): cover agent usage in the context window popover
The usage fixture records agentId, streams reports held behind
promises, and can switch host support between reloads. The meter spec
walks loading, streaming, a report problem, a request error and a host
without usage reports, on desktop and compact in both themes.
* fix(usage): keep the plain context window tooltip on hosts without usage
The popover widened to fit usage cards on every host. Hosts that cannot
report usage now keep the tooltip's default padding and width, as
before, and mount no usage section.
* fix(usage): let a report's Refresh replace it in the agent's list too
Refresh wrote the refreshed report only into the host's list, so a card
rendered from an agent's list kept the old report. Both lists now sit
under one host key and Refresh replaces the report in each that holds it.
* feat(usage): open the context window details in a sheet on compact screens
Wide screens keep the tooltip. On compact screens pressing the meter opens
a content-sized bottom sheet titled Context window. Both render one
ContextWindowDetails body; only the sheet's usage cards have Refresh,
since the tooltip cannot be pressed. Pins stay off in both.
* test(usage): cover the compact context window sheet
On the compact viewport the meter opens the sheet; its cards have Refresh,
a Refresh replaces the card, and Close dismisses the sheet. The glossary
names the sheet.
* feat(ui): give tooltips and the context window sheet accessible roles
Tooltip content is role=tooltip, and the context window sheet is a dialog
named Context window, so assistive tech and tests find them by role. The
agent usage message drops its test ID; it is found by its text.
* test(usage): reshape the context window and Usage switch specs as journeys
The context window spec runs one tooltip journey and one sheet journey per
theme, with no layout branches. Its fixture script, meter actions and
report builders move to a context-window helper, and it finds the meter,
tooltip, sheet and Refresh buttons by role. The theme comes from the
system colour scheme. The Usage switch test uses named helpers for the
Usage icon and the Summary in sidebar switch.
* fix(usage): say an agent's usage request failed instead of showing older reports
A failed request keeps whatever reports the query held: those from an
earlier open, or those that streamed in before the failure. The context
window details showed them as if they were the agent's current usage.
The request error now wins over held reports.
* test(usage): let a fixture clean up the context window spec's agent
The tooltip and sheet tests no longer wrap their steps in try/finally;
a spec-local fixture seeds the agent and removes it after the test.
* fix(usage): keep a refreshed report when the list it is in finishes
Pressing Refresh on one card while another source was still streaming
put the pre-refresh report back: the finished list replaced the query's
data with the copies the request had fetched when it started. A report
now keeps whichever copy was fetched later, both when a streamed copy
arrives and when the finished list replaces the streamed one.
* fix(usage): show only the login a resumed agent runs under now
An agent resumed under another login keeps its ID, so its usage query
kept the previous login's reports: reopening the context window showed
them while the new login's reports loaded, then both side by side until
the request finished. The agent's usage is now dropped as soon as its
tooltip or sheet closes, so each open loads the login the agent runs
under at that moment.
* fix(usage): stop a closed agent usage request from writing reports
Closing the context window details cancels the agent's usage request and
drops reports still on their way, so reopening after a resume under
another login sends a new request instead of joining the old one.
* feat(plugins): expose agent closed lifecycle event
Emit the event when a live agent reaches its closed state so plugins can release agent-scoped resources. Keep archive and close notifications distinct.
Co-authored-by: ilteoood <6383527+ilteoood@users.noreply.github.com>
* fix(plugins): finish close hooks during daemon shutdown
Wait briefly for pending lifecycle events before stopping plugins so agent close hooks can release their resources during shutdown.
Co-authored-by: ilteoood <6383527+ilteoood@users.noreply.github.com>
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: ilteoood <6383527+ilteoood@users.noreply.github.com>
* fix(usage): derive window names and unify pin selection
Quota slots do not identify a period. Derive numeric windows and IDs from reported duration and quota scope; keep unknown lengths neutral. Resolve source defaults and explicit pins through one model, with a persisted empty selection distinct from defaults.
* fix(plugins): declare the duration helper host requirement
* fix(usage): verify persisted selections at the storage boundary