The Client entry re-exported ConfirmState, InstallState, ManagerNotice and
PluginManagerState from the controller store although no other package names
them; the page, its tests and the presentation module import them from the
store directly. Only the face, the page props and the locale key stay public.
The dsh manifest gains no description field: a bundle's card shows the
package.json description, and the shipped optional bundles carry no
display title or description of their own until localized metadata is
designed. The plugin author tutorial shows the bundle declaration alone.
OPTIONAL_BUNDLES in app-boot lists the bundles the installation ships
switched off; the plugin manager, the default-product isolation gate,
the workspace constraints, and the packed-install check read that one
list instead of a dsh.optionalBundles field in apps/cli/package.json.
app-boot no longer emits profile/reconciled after a reload, and the plugin
manager no longer turns such a generation into a plugin-manager/changed
notice: the Web page learns of a CLI or hand edit on its next read. The
change event keeps only the reasons a manager operation produces.
The retry that allows blocked install scripts drops the failed run from
the dialog instead of stacking the new run under it. The dialog no longer
grows past the viewport: the card is capped and everything under the
head scrolls, so the close and edit controls stay reachable however long
pnpm's output is.
reconcileProfilePatches finds the root Include only through the registry
the module that mounted it keeps, as it did before the Web page: a tree
mounted by another copy of app-boot was a test-harness situation, not a
product one. The web e2e scaffold now mounts and resolves the profile
through the built app-boot, loaded with Node's own require so it shares
the module map with the Host plugins the profile loads, instead of the
source copy a bare import resolves to.
When pnpm leaves a dependency's install scripts undecided, the failed
screen lists the pending packages and offers to allow them and retry in
place of plain retry; the Host saves the permission in the profile before
running pnpm again, and the installed screen names what was allowed. A
failure the Host could not name falls back to the manual instruction, and
a stale approval is worded like the other refusals. A new web lane drives
the whole flow through a fake pnpm that blocks, then installs.
The installation lists @deepseek-ai/dsh-experimental-auto-review beside
the two Agent Teams layers under dsh.optionalBundles, so a blank Web
profile offers it in the built-in group switched off. The bundle gets a
display title and one-liner, and its README says where it is switched on.
The dsh installation lists under `dsh.optionalBundles` the bundles it
ships for a person to switch on. The plugin manager reports them as
`optional`, and the Web plugin page shows them first, in a built-in
group with an official tag, off until selected and never removable.
Agent Teams ships this way, so a blank profile offers both of its
layers. Default-product isolation, the workspace constraints, and the
packed-install check accept that one dependency edge and nothing else
experimental.
A bundle's title and one-liner come from `dsh.title` and the new
`dsh.description`, else from the package name and `description`. A
dependency without a bundle patch is no longer listed unless the
profile selects it. The install dialog asks for a package name or
address; a guide under the field shows the three accepted forms with
an example each and ends in a security warning, and the copy no longer
speaks of a plugin ID.
The composer's paste path used RangeSelection.insertText, which keeps every
pasted newline inside one text node. In that document the browser reports the
collapsed caret's geometry as the whole block's, so deleting the draft's last
line moved the capped scrollport to the draft's head while the caret stayed at
its end.
RangeSelection.insertRawText splits the input into text and line-break nodes,
the document a typed draft already has.
Drop the "built-in components it changes" section from a bundle's page:
the rows a patch overrides are a composition detail nobody acts on from
the page. A failed enable, disable, uninstall, or row switch now says
which it was, and the Chinese toasts lose their trailing period. The
install dialog is worded around the plugin ID, which is the package
name, with a hint on what an ID looks like and no real plugin as the
example; the spec forms it names are the ones the Host's inspect takes.
A bundle's title is its `dsh.title` or a shortened name, so the page
never said which package a bundle is, and nothing on it could be typed
into an install elsewhere. The name now sits under the title as the exact
package spec.
Rows of the changed-files card open a `changes-diff` right-Sidebar tab that compares the file at turn start and turn end. The workspace-changes recorder copies every file a file tool edits into the Session's temporary directory before the first edit and again at turn end, so files git does not cover are compared without git and counted first-to-last; `maxFileBytes` caps copies and snapshot reads, and `diffTimeoutMs` degrades a slow line comparison to whole-file replacement.
Ported from fix/plugin-manager-ui-polish (cce193cff3, 4ed0f1c9cb) onto
the page as it stands after the #4182 re-port: a 32px rhythm on the page
and the detail sections; a four-arc pinwheel icon on the cards, the
detail head, and the sidebar entry; the version as a tag beside the
detail title and the crumb leading back only; a red-tinted uninstall pill
with its trash icon and a plus icon on Add plugin; a round refresh action
and caption-toned counts; 12px hover rounding across the rail controls;
and the group copy renamed to Installed. The non-bundle group and its
info tooltip are not carried, since the page lists bundles only.