A preset publishes its own description, of any length, and `.cards` sizes
rows with `grid-auto-rows: 1fr` — which makes every implicit row the same
height, not just the row holding the tall card. One long description
therefore set the height of the whole roster.
The description now clamps to four lines and offers the rest through the
shared Tooltip, attached only while the element actually overflows. Card
height stays derived: with the description bounded, `grid-auto-rows: 1fr`
already equalizes, and a card carrying the broken-preset reason or a
revealed path still sizes itself.
Tooltip gains an optional `maxWidth`; its default half-viewport cap
renders a description wider than the settings dialog it belongs to.
The failed-shape-check badge reads "Failed to load" rather than "Broken":
discovery reports a composition that is missing, unreadable, or malformed,
which overstates as damage.
Fixes#2238
The /feedback acknowledgement now appends a sharing-policy sentence after the
anonymous user id. The seeded-history e2e regex anchored on the end of the
User line, and the golden snapshot did not include the disclosure. Update both
to match the new format, and re-record the module-graph translation-pairing
hash after rebasing onto master (which picked up the windows-native ACL
coverage fix in #2182).
Regenerates the ack golden for the merged acknowledgement format, records
the zh counterparts and pairing hashes for the telemetry and catalog
pages, and restores the command-feedback to session-telemetry edge and
dependency in the module graph.
The feedback-command e2e now drives the recorded prompt through a separate
all-modes test that arms whenTurnSettled before sending and writes the
fixture back via recordFixture in record mode; the acknowledgement golden
test runs only in replay/refresh. The scaffold restores the pinned DSH_HOME
on the persistence-root setup failure path, and the telemetry subsystems
page links the README's sharing-disclosure anchor.
The /feedback acknowledgement now echoes the receiving session id and
reports the mounted telemetry backend's sharing policy: the telemetry seam
exposes a backend-independent TelemetrySharingStatus through a required
abstract sharing member on the Telemetry service, the OTel backend maps its
mode onto it, and the command appends one policy-only sharing sentence
(full / feedback-only / disabled / not configured) to the acknowledgement.
The web client renders the text through the existing command row without a
client change; a new assembled-browser e2e mounts the shipped telemetry row
in FULL mode against a local dead endpoint and pins the shipped default
sentence as a keyless golden.
The Models editor card curated its pi-ai fields by what every route has,
so `displayName` and `api` — the two a hand-declared route names for
itself — were asked for at creation and then reachable only through
settings.yaml. The editor now renders both for a route the directory
reports as declared, from the same namespace schema the create card
reads. A catalog route gets neither: it defaults its name from its
catalog entry, and its models each carry their own protocol, so a
route-level one could only override all of them.
Clearing the name unsets it and the route falls back to its id, which is
what the field's placeholder shows; storing the empty string would be
refused by the adapter. A declared profile naming no protocol selects
nothing rather than reading as if it had picked the first choice.
The Provider ID stays fixed: it is the settings dict key, it is
referenced from `agent-default-model` and every logged request header,
and it is the stem of a credential reference the page can never read
back to move.
Fixes#2204
The child session now shows the preset badge its parent shows, which is the
visible consequence of recording the composition it runs. The first recording
of these two goldens was taken against a dist built before the master merge,
so it captured the fallback Chinese label instead of the English one
`newEnglishPage` pins — the web lane replays the BUILT client, and a stale
build reads as a product difference. Re-recorded after `pnpm run build`.
The delegation-pinned approval/policy: never makes a child session's knobs
match no preset, so the child conversation's Access chip truthfully reads
Custom.
The navigation-panes replay scenario clicks the trajectory toolbar export
button and asserts the real host-streamed download: filename, a single
session.jsonl entry, and byte-verbatim seed content. The toolbar golden
refreshes for the localized export button.
`session.create` also adopts an already-live session, and the preceding
commit newly allows adopting one under the preset it switched to while
blank. Its response still echoed `header.agentPreset`, so that adoption
answered with the preset the session had just left — contradicting the
request it had accepted and the row `session.list` serves for the same
session from `resolveSessionPreset()`. The echo now resolves the same way.
The `assertPresetUnchanged` parameter doc said `existing` was the preset
the session was created under; both callers now pass what it runs.
`composeFrom()` was documented as "infallible" and "cannot fail" beside two
`@throws`. It has no composition failure mode — no roster read, no mount, no
file — but it does reject a caller error, and the wording now says which.
The package-level "switched preset" test re-linked to the same preset id,
so it could not tell reading the parent's live scope chain from reading its
creation header. A second fixture preset makes the switch real.
The Web browser lane's subagent goldens gain the preset badge a child now
shows, which is the visible consequence of recording its composition. That
lane runs only under DSH_EXAMPLE_MODE=lib and was missed before.
The Agent Note records two limits found in review: a cold-resumed
continuable child joins its parent's current composition rather than the one
its header names, and `toolFilter` does not constrain a joined child. The
latter is a regression from the agent-plane move rather than anything this
change introduces — with the same tools in the global layer the filter
applies normally — and is tracked in #2185.
Refs #2185
The 导出 button fetches GET /api/session.export and saves the ZIP (root
artifact plus subagent descendants). The plugin exposes exportLog through
the view's inject face, resolves the tab label through the locale service,
and disables the button while in flight; fixture mode answers 404 so the
error bar explains the gap.
The mid golden was racing the reasoning block's stream: captureStableAria
could freeze on the pre-render gap between steering acceptance and the
assistant step, pinning a snapshot without the Think row. Wait for the
[data-variant=think] row so the golden captures the complete assistant step.
Steering bubbles render as plain user bubbles; a mid-turn steer is
recognizable by its position in the flow. The runtime SteeringMessageNode
projection and pending-steering lifecycle are unchanged.
Partially supersedes the 2026-08-04 context-source and steer marks note;
the new simplification note owns the removal rationale.
A hand-damaged preset was silent until the worst moment. An unparsable
composition listed as an ordinary selectable row and failed only at the
next session start — set as default, every new session failed. A
directory whose composition file was deleted vanished from the roster
while still occupying its id: copy answered "delete the existing preset
first" while remove answered "not found", a dead end.
Discovery now owns health: every id-shaped directory is a roster slot,
broken when its composition is missing or unloadable, checked with the
loader's own entryListSchema dialect (!!js included) so health never
rejects what the loader accepts. `broken` rides AgentPreset, the
agentPreset.list entry, and the UI row; mount/recompose/standingKeyFor
refuse broken up front with the discovery-reported reason, while
resolve/read/remove still answer. The section renders marked red cards —
unselectable, uncopyable, deletable, location kept on custom rows — and
both pickers drop broken rows entirely.
The cordis preset's persona now forbids editing the shipped install
(corrupting cordis would disable the mode itself) and points authoring
at $DSH_HOME/.agent-presets; its skill teaches preset.yml metadata, the
copy-first workflow, the one-escalation sandbox reality, and honest
verification. Exercised live: asked to edit the shipped composition the
composed agent refuses citing both rules; asked for real presets (simple
and complex) it lands them under the user root with one approved
escalation each and self-checks with the loader dialect.
Copying an existing preset — or drafting one in Creator mode — replaces
the copy-minimal skeleton hint: the creator entry exists now, so the intro
names the two real paths instead of steering everyone at one source.
The settings section gains the conversational authoring entry beside
copying: a dashed add-card (the Models page's affordance) that stages the
self-referential cordis preset and starts a new session on it, closing the
settings panel through the shell's new section owner-prop `close` — the
one shell affordance a section receives, for flows that leave settings.
The seat learns the difference between picking and staging: `select()`'s
immediate apply meets the still-current running session and drops the
stage as unservable, so the entry uses `stage()` and leaves the apply to
the list-change applier that fires when the started session becomes
current. `load()` stops regressing the display when its reply lands after
an applied stage was consumed — staged pick first, then the composition
the current session already carries, then the deployment default.
The authoring lane connects a workspace and drives the gesture to a
composed host session; the section goldens gain the entry.