Commit Graph
2737 Commits
Author SHA1 Message Date
Mohamed Boudra 64ea11f61e Merge origin/main into plan-plugin-api-phase7-composers-by-ref 2026-11-06 05:10:31 +01:00
Mohamed Boudra 5abfdf7107 Record workspace creation parentage (#8890)
* feat(workspaces): record workspace creation parentage

Retain the caller workspace as informational ancestry for future sidebar nesting, independently of workspace placement and lifecycle.

* fix(app): preserve workspace parentage in replica cache
2026-11-06 10:55:56 +07:00
Mohamed Boudra 2d976b0b19 fix(plugins): a plugin reporting settings is sent every key of an options patch 2026-11-06 04:50:52 +01:00
Mohamed Boudra 746165c300 fix(plugins): a model or thinking change, an unreported option key, and a draft model reach the provider unchecked
Paseo no longer checks these in front of the provider. A running agent's model or thinking
change no longer waits for the catalog or checks it, a plugin provider is sent every option
key and refuses what it cannot apply, and a draft setModel keeps the id as the model picker
does.
2026-11-06 04:48:23 +01:00
Mohamed Boudra 09e8139f39 test(server): the test providers list two models with thinking options, and the live switches run
The fake providers listed one model and no thinking options, so once the daemon validated
selections against the catalog the live model and thinking switches had nothing valid to switch to
and skipped. Every fake provider now lists two models with thinking options, and the switches run
for all three providers without depending on installed binaries.
2026-11-05 23:25:43 +01:00
Mohamed Boudra 64146208bd test(server): live model and thinking switches use values the provider lists
The daemon rejects a model or thinking option its provider does not list, so the live-preferences
e2e can no longer switch to a made-up id. A provider that lists nothing to switch to skips the case.
2026-11-05 23:25:43 +01:00
Mohamed Boudra 8215535631 fix(plugins): a running agent's composer validates and refreshes against the agent's own catalog
setAgentModel and setAgentThinkingOption checked the provider's global catalog,
while the picker reads the catalog of the agent's directory, so a model only
the project lists was rejected. The validation input now carries the directory.

The composer read the catalog once with no observer, so reconnect repair, which
refetches only active queries, never refreshed it and a failed load never
retried. While anything watches, the composer now observes the catalog query.

The shared composer map kept every agent's composer for the session. It is now
dropped when nothing watches it and no composer is mounted.
2026-11-05 23:25:43 +01:00
Mohamed Boudra ded20eb85c fix(plugins): composers by reference follow the focused agent and reach retained composers
- `{ focused: true }` is the agent Command Center hands to agent items, published by its
  registration through plugins/focused-agent.ts and empty outside a workspace. The composer source
  reads that module instead of the remembered workspace selection, the navigation store and the
  layout store, which also removes the require cycles those imports added.
- A composer the app retains in a workspace the user left has no live input. `insertText` and
  `setPrompt` now store the text with `replaceDraftText`, which asks the mounted input to show it;
  before, the text reached the store but the retained input never displayed it.
- `model` and `thinking` read exactly what the picker resolves before the catalog loads (no
  models), then re-resolve when it lands, instead of reading null.
- The daemon validates a thinking option against the model the agent runs: the runtime model when
  the provider lists it, else the configured one.
- The composer sources subscribe to query-cache updates only. A query another component creates
  during its render is announced as `added`, and a subscriber that updates then made React warn
  about updating a component while rendering a different one. A catalog read no longer starts its
  load synchronously either.
2026-11-05 23:25:42 +01:00
Mohamed Boudra f665cc697c feat(plugins): reach a running agent's composer by reference
useComposers(selector) in any plugin component and composers(selector) on every command context return the composer of a running agent, named by id or by focus, whether or not its tab is open.

A running agent now has one store-backed composer model apart from any mounted tab. The mounted composer plugs its cursor-aware insert in while mounted; otherwise insertText and setPrompt write the agent's saved prompt. model and thinking resolve through the provider catalog with the picker's own function, so a read equals what the picker shows.

The host reads one ComposerModel from context and no longer knows whether it is in an agent, a draft or New workspace. The daemon rejects an unlisted model or thinking option for a running agent, replacing the app-side guard.
2026-11-05 23:25:42 +01:00
Mohamed Boudra d484a841ed fix(server): a branch the caller named is not renamed from the first agent context
The daemon marked every new branch-off worktree for automatic naming, recording the chosen branch
as the placeholder, so the first agent's prompt could rename a branch a plugin or client had
named through branchName. Only a branch the daemon derived from the worktree slug is a
placeholder now.
2026-11-05 22:20:09 +01:00
Mohamed Boudra c22b97506f docs(server): the handle-less recreate gap spans the agent.create.before hook 2026-11-05 21:25:10 +01:00
Mohamed Boudra 4216cdc7b5 docs(server): name the metadata gap when a handle-less agent is recreated on load 2026-11-05 21:25:10 +01:00
Mohamed Boudra e07440ae06 fix(server): reopening a closed agent keeps metadata saved just before
The loader reads the agent record, then queues a resume. A setMetadata
queued ahead of that resume saves a newer record, and the open hooks saw
it, but registration took metadata from the loader's older read and wrote
it back over the saved patch. Registration now uses the metadata read
inside the lifecycle lane.
2026-11-05 21:25:10 +01:00
Mohamed Boudra 762f5f023e test(server): fixtures for agents and workspaces carry metadata
Agent snapshots and workspace records now always carry metadata, so the
hand-built fixtures and expectations in three existing suites name it.
2026-11-05 21:25:10 +01:00
Mohamed Boudra 50e830ac01 fix(plugins): omitted metadata in a workspace.create.before result means unchanged
Same handoff as agent.create.before: a legacy hook that returned only
{ source, ... } dropped the caller's metadata and anything an earlier
hook added, and workspace provisioning then defaulted it to {}. The
omitted field is carried forward at each handoff; an explicit {} still
replaces.
2026-11-05 21:25:10 +01:00
Mohamed Boudra f2f8a84041 fix(plugins): omitted metadata in a hook result means unchanged
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.
2026-11-05 21:25:10 +01:00
Mohamed Boudra 7ddaf59aa7 fix(plugins): an agent.open.before result may omit metadata
The result schema required metadata, so a plugin written before metadata
existed that returns its own object instead of spreading the request
failed validation and the session open failed, even for agents with no
metadata. An omitted metadata now means unchanged and is filled back in
from the request so later plugins in the chain still see it; a present
value that differs is still rejected.

The restart test now sets metadata through the PaseoApi handles and
checks that agent.open.before sees the patched value after the restart.
2026-11-05 21:25:10 +01:00
Mohamed Boudra 35e136314d feat(plugins): agents and workspaces carry plugin metadata
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.
2026-11-05 21:25:10 +01:00
Mohamed Boudra f4ddcf7a5b test(server): an apply to a plugin that reports controls sends its changes as provider options 2026-11-05 20:25:11 +01:00
Mohamed Boudra 46b0466f3b fix(server): an apply never reads a requested option as one the plugin dropped
A plugin may run an option it never reports. Only a key it reported before can be
reported as removed, and a rewind reopens with the accepted request plus what the
plugin reported back.
2026-11-05 20:25:11 +01:00
Mohamed Boudra 429cf715ca fix(server): an apply reports the options the plugin reset along the way
Applying and unsolicited reports now share one conversion from the plugin's
report to a patch, so a key the plugin stops reporting is a removal on both.
2026-11-05 20:25:11 +01:00
Mohamed Boudra 4196fae928 fix(server): a plugin that stops reporting an option has reset it
The adapter only visited keys present in a new report, so a key the plugin
dropped to return to its default kept its stored value and its rewind value.
A previously reported key that disappears is now reported as removed.
2026-11-05 20:25:11 +01:00
Mohamed Boudra bbd93f76ee fix(server): a rewound plugin session reopens with its options as they stand
The reopen merged the plugin's reported options over the launch record, so a
key the plugin reset by leaving it out came back at its launch value. The
session now keeps the committed options, removals included. The provider-direct
example also refuses a combined request before changing anything.
2026-11-05 20:25:11 +01:00
Mohamed Boudra 9e70f5665a fix(server): a plugin decides which options it applies live
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.
2026-11-05 20:25:11 +01:00
Mohamed Boudra f4210f39aa fix(server): a plugin provider's reported options are stored like any provider's
A control's `defaultValue` only says what the control shows for a key with no
value. Opening and applying store every option the session reports, defaults
and values nobody set included, as built-in providers do; the special case that
left a value at the default unset is removed.
2026-11-05 20:25:11 +01:00
Mohamed Boudra 448f258583 feat(plugin): provider plugins declare option controls and receive option changes
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.
2026-11-05 20:25:11 +01:00
Mohamed BoudraandTianyu Yang 3ed57e9dd7 Change a worktree workspace base from Changes (#8642)
Change a worktree workspace's base from the Changes pane. The choice persists and diffs, counts, commits and Git actions follow it.

Supersedes #6872.

Co-authored-by: Tianyu Yang <yangtianyu.cl@bytedance.com>
2026-11-05 22:30:14 +07:00
Mohamed Boudra 41a36fa593 test(server): the MCP test agent manager resolves effective options 2026-11-05 15:30:11 +01:00
Mohamed Boudra af2fb8ab35 fix(server): a closed agent's options include the provider defaults, like a live one's
The stored snapshot projected only the options the agent owns, so an agent
that ran with a config.json default (for example Claude fast_mode: true)
showed the option as missing, and its legacy Fast control as off, until it
was resumed. The projection takes the same resolver the live agent uses
(the provider's defaults under the agent's own options).
2026-11-05 15:30:11 +01:00
Mohamed Boudra 51505a7d3b fix(app): a closed agent shows its stored options, and a provider switch asks about its own options
A closed agent's snapshot is projected from its stored record, which kept
controls and provider options but dropped them from the payload, so the
app drew no speed or other controls for it. The stored projection now
carries them, with the legacy features derived from the same source.

The draft read the form's provider and the model's options separately, so
for the render after a manual provider change it asked the new provider
about the previous provider's options. The request is now built from one
snapshot of the model and is absent until the model has observed the new
provider.
2026-11-05 15:30:11 +01:00
Mohamed Boudra 0d82eed6b5 feat(app): the client and app work on options and controls
The agent snapshot's `options` are the state and `controls` only draw them.
Running and closed agents draw from the snapshot; drafts and profile forms ask
`provider.controls.list.request` with their `providerOptions`, so aliases such
as a legacy Fast profile's `service_tier: "fast"` are resolved once, by the
daemon, and the draft keeps Fast highlighted. A draft adds only the user's
edits and each provider's last-used control values; applying a profile seeds
its non-control options (e.g. `sandbox_mode`) into the draft, and a profile
form saves them back instead of pruning to the controls it can draw.

A daemon without `server_info.features.providerOptionControls` still sends
`features` and `featureValues`. `LegacyProviderOptions` in the client package
is the one place that translates snapshots, draft listings, create and apply
requests, and daemon-config profiles, so the app never branches on it.
Persisted preferences and tab layouts rename `featureValues` through tagged
COMPAT preprocessors.

The features/controls bridge moves from the server into the protocol package
so the client can reuse it.
2026-11-05 15:30:11 +01:00
Mohamed Boudra 2ec0ac3c25 refactor(server): openCodeOpenOptionsPatch takes one named-fields object 2026-11-05 15:00:18 +01:00
Mohamed Boudra 59be8975a4 Dismiss a failed agent's red dot by reading it (#8831)
* fix(sidebar): clear the failed status once the error is read

Reading an errored agent already cleared its error attention, but the
state bucket also mapped lifecycle status "error" to failed. Lifecycle
only leaves "error" when the next turn starts, so the red dot and the
Failed group stuck until the agent was prompted again. The failed bucket
now follows unread error attention, the same way the green dot follows
unread finished attention; lifecycle status stays "error".

Because failed now depends on attention, a failure must replace unread
finished attention, otherwise a reload that fails on an unread agent
would show green.

* fix(sidebar): keep failed subagents red and let read failures be marked unread

The failed bucket now follows unread error attention, which broke three
places that relied on lifecycle "error" alone:

- Subagent track rows and provider-subagent tabs have no read state of
  their own, so failed children lost their red mark. They now map the
  child's lifecycle directly.
- A read errored workspace became "done" and offered Mark as unread, but
  the daemon only accepted idle or closed agents. Marking it unread now
  restores error attention.
- A reload that failed again while the lifecycle was already "error"
  raised no new attention. A failed reload now raises error attention
  directly instead of relying on a lifecycle transition.
2026-11-05 18:49:12 +07:00
Mohamed Boudra 6cb9685a6d fix(server): plugin option applies run one at a time, and OpenCode reports the Auto Accept legacy full-access turns on
Plugin sessions let two applyOptions overlap. The second finished and re-recorded the
settings the plugin held, which included a change the first had already made and not yet
reported; when the first then failed, nothing was left to report and the stored options
kept the old value while the plugin ran the new one. Applies now queue on the session,
the way the ACP adapter's do, so each reconciles against what the one before left.

OpenCode normalizes the legacy full-access mode to build with Auto Accept on, but the
session never said so, so the options the manager keeps did not hold Auto Accept and a
reopen turned it off. The session now reports it through openOptionsPatch on create and
resume.
2026-11-05 12:28:49 +01:00
Mohamed Boudra 902d43da7c chore(release): reconcile v0.15.2 into main (#8845)
* fix(app): hold the loading screen until first-run setup is decided

A new user saw the empty home, the sidebar and a logo jump before setup
popped in. Startup now keeps the splash while setup may still open over
the empty home, for at most 3 s and only while the setup host is
reachable. Remembered-workspace restore does not wait. Web dialogs now
fade in as they already fade out.

(cherry picked from commit a0a7620b62)
(cherry picked from commit 72d7c0bf26)

* fix(app): fade the dialog card in and land the dim at once

(cherry picked from commit 6ef05705a0)
(cherry picked from commit 71c28b431c)

* fix(app): keep the loading screen up while startup redirects

(cherry picked from commit 0b28d4cbff)
(cherry picked from commit 13cc485e6f)

* test(app): cover a setup host that drops while startup waits

(cherry picked from commit 2a906ef83e)
(cherry picked from commit 7b6e21207a)

* fix(app): type the web-only dialog enter animation style

The CSS animation props are not part of ViewStyle. CI typechecks without the
generated expo-env.d.ts, which declares them, so the cast is required there.

(cherry picked from commit b887d979d2)

* fix(app): keep the setup window mounted when the theme changes

The shell's appearance boundary remounts its dialog hosts whenever a theme
color changes, so picking a theme in setup, or a system light/dark switch,
replaced the open setup window and replayed its fade. Setup draws colors
through CSS variables and themed props, which update in place, and changes
no font size, so it now sits outside that remount.

The dialog enter fade is skipped with reduced motion, and its style is typed
for CI, which typechecks without the generated expo-env.d.ts.

(cherry picked from commit b24c6eacf8)
(cherry picked from commit cb0e9907f5)

* docs(changelog): prepare 0.15.2

(cherry picked from commit 5af1a8ff66)

* chore(release): cut 0.15.2

(cherry picked from commit e896c05b09)
2026-11-05 16:40:53 +07:00
92c8c55f31 Fix registry plugin installs through npm mirrors (#8756)
* fix(plugins): install registry npm pins through the configured registry

Registry npm pins carry an npmjs.org tarball URL. Passing that URL to npm made it rewrite the host onto the configured registry and drop any path, so mirrors such as https://mirrors.tencent.com/npm/ returned 404. Install the pinned version instead and identify the reviewed artifact by version and integrity, since a mirror records its own tarball URL.

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

* fix(plugins): address review on npm artifact comparison

Take an object parameter in isSameNpmArtifact, and clean up the mirror fixture when test setup fails.

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

* test(plugins): verify mirror artifacts with a warm npm cache

A shared npm cache lets the mirror regression reuse tarballs from earlier
fixture installations, so successful installs made no tarball requests.
Check the installed artifact's mirror URL, version and integrity instead
of requiring network transfers.

Co-authored-by: finofliu <finofliu@tencent.com>

---------

Co-authored-by: finofliu <finofliu@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-05 08:59:05 +00:00
Mohamed Boudra 72955cef60 chore(release): reconcile 0.15.1 into main (#8814)
Brings the shipped 0.15.1 changelog entries, version, and F-Droid changelog files from release/v0.15.1 into main.
2026-11-05 10:26:47 +07:00
Mohamed Boudra 8dfdb479ab fix(terminal): start the daemon when the terminal worker cannot start (#8810)
On 0.15.0 the daemon launches the detached terminal worker while it is
being created, and the worker refuses to start unless it can read its own
birth time and command line (Windows PowerShell 5.1 `Get-CimInstance`,
`ps` elsewhere). Any failure of that query used to escape
startWorkerTerminalManager and take the whole daemon down with
"Daemon failed to start (exit 1)", as two Windows machines hit after the
update: one whose PowerShell 5.1 cannot load CimCmdlets, one where
powershell.exe cannot be spawned at all. 0.14.0 never ran that query at
startup.

The launch error is now logged and the daemon starts without a current
worker. The pool already launches one lazily on the first terminal
creation, so that request reports the same error while the machine still
cannot run the query, and terminals come back once it can. The identity
requirement itself is unchanged: a worker without a verified identity is
never written or adopted.
2026-11-05 08:59:04 +07:00
paseo-bot[bot] 0e7f452c2f Keep duplicate-path setup out of the Git filter CI budget (#8753)
* test(server): prepare duplicate paths before measuring Git filtering

* test(server): clarify the observer path preparation boundary

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-04 12:25:42 +00:00
Monarch MaisuriyaandClaude Opus 5.5 6221b0c8b7 Fork from earlier replies when a plugin card was updated later (#8746)
The fork checkpoint check refused a fork when any projected row spanned the
selected reply. A plugin card re-appended with the same id keeps its first
position and stretches to its latest update, so it spanned every reply in
between, although the fork never copies plugin cards. The check now looks only
at the row kinds the fork copies, taken from the same constant as the copy.

Closes #8745

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 09:02:25 +00:00
Mohamed Boudra 8a972b8c2e fix(server): an ACP removal reports the choice the select lands on, and the Codex plan tests enable plan mode
A removal asks the select for its empty choice and compared the result against that
same empty choice, so applyOptions returned nothing and the manager stored the removal
as null while the session ran the Default choice. The difference is now computed against
what was asked (null), so the options hold the value the session runs with.

The Codex plan-mode end-to-end tests called the removed session.setFeature through an
optional call and silently skipped plan mode; they apply the option and fail loudly if
the method is gone. The MCP parity wrapper forwards listControls instead of the removed
listFeatures.
2026-11-04 09:35:24 +01:00
Mohamed Boudra cc56b392de fix(server): ACP option applies run one at a time
Two overlapping applies shared one transaction guard, so when the first was refused after the agent had moved a select, its report was dropped as 'still in a transaction' and the second saw no difference. Applies are serialized, as OpenCode's are, so each reconciles against the state the previous one left.
2026-11-04 09:35:23 +01:00
Mohamed Boudra cb331532ad fix(server): draft speed shows the tier the first turn will run with
Draft discovery reported the options as requested, so a Fast configured as a legacy fast_mode or the fast alias read as Normal while creating sent the model's own Fast tier. The draft now reports the options the scratch session opened with, as registration does.
2026-11-04 09:35:23 +01:00
Mohamed Boudra da994fb400 fix(server): profile options convert against the provider graph a write commits
A write that edited a provider and a profile together folded the profile against a shallow merge that dropped the provider's extends, so its legacy Fast was not converted. The fold now sees the same deep-merged providers the write commits.
2026-11-04 09:35:23 +01:00
Mohamed Boudra c4baf750f2 fix(server): a profile is stored in the current option keys, converted for the adapter its provider extends
Profiles in config.json are old data entering, so the store converts them (stored value and incoming patch) with the same conversion the agent manager uses. Storage and clients now share one vocabulary: resetting a legacy Codex profile's speed removes what was shown, and a malformed fast_mode rejects the write instead of breaking later config reads. The config API goes back to copying the field shape only. The extends chain is followed by one function over either the registry's definitions or config.json's providers.
2026-11-04 09:35:23 +01:00
Mohamed Boudra 31a408d108 fix(server): a malformed Codex fast_mode is rejected, and profile options are renamed for clients by the agent manager's conversion
convertLegacyOptions read any truthy fast_mode as Fast, so the string "false" ran priority. It now accepts only true, false or null. The profile store no longer renames keys: a literal codex profile was renamed in storage while an extends-codex profile was not. The config API now converts each profile's options with the manager's adapter-aware conversion, so codex and my-codex profiles read the same.
2026-11-04 09:35:23 +01:00
Mohamed Boudra 3be00f5105 fix(server): OMP turn completion publishes a Fast change, and Claude opens with Fast off on a model that has none
OMP installed runtime state in several places and only one compared Fast; every state read from the runtime now goes through one install step. Claude kept a Fast default on a model without Fast, which made unrelated options reject until the user cleared it; the session now opens with Fast off and reports it in openOptionsPatch, as the model-switch path already does.
2026-11-04 09:35:23 +01:00
Mohamed Boudra 6e704f803c fix(server): an OMP replacement runtime that refuses the session's Fast publishes what it accepted
The approval-mode restart installed the new runtime's state without comparing its Fast result to the session's, so a refusal never reached options_changed, the remembered value, or storage. The replacement now goes through the same comparison as refreshState.
2026-11-04 09:35:23 +01:00
Mohamed Boudra 949462f39b fix(server): legacy Fast reaches draft discovery and saved profiles, and overlapping or replaced options keep the state they committed
Draft controls and commands convert the config they hand to the provider, so inspecting a
legacy Fast preference no longer fails Codex's strict options. A profile naming codex converts
its saved fast_mode for the app's controls. OpenCode serializes auto_accept applies so a failed
one restores exactly what it changed, and a replacement OMP runtime starts from the Fast the
session opened with.
2026-11-04 09:35:23 +01:00
Mohamed Boudra 53d91d63da fix(server): a rejected OpenCode auto_accept leaves the session asking for permission
applyOptions committed the new flag before approving pending requests, so a failed reply left
the session auto-approving while the stored option stayed off. The flag is restored when the
apply throws.
2026-11-04 09:35:23 +01:00