510 Commits
Author SHA1 Message Date
EDDYCRAZY-CC fb28142973 fix(web): resolve the open issue batch 1724, 1730, 1732, 1740 and 1742
/api/dsh-web-all/degraded answered an empty list while /rows still listed the
plugin, and the task board could only translate its absent routes into
"not mounted ... restart the DSH service" - advice that cannot work while
another process holds $DSH_HOME/task-board/ledger-v2.lock. A degraded record
now carries a bounded one-line reason and the task board reads it to report
the owning pid with actionable guidance; a bare 404 stays the only signal
that the routes are absent, and the one-owner-per-ledger contract is
unchanged (no lock change, no cross-process write).

The other four issues live in the skin center and are fixed in that
repository (satellites/dsh-skins, commit 82f42bd): the applied-skin contract
(#1740), the ORCA stage geometry and the semantic adapter's late anchors
(#1732), the maid-atelier trim over the plugin manager page head (#1742), and
the composer frost that made the card a containing block for the shell's
fixed tooltips (#1724). This commit moves the gitlink and rebuilds what the
market reads from it.

Also rebuilt: packages/dsh-web-all/lib (its sources moved), the market dist
for the four touched skins, and the recorded lib fingerprints.

Validation: pnpm typecheck, pnpm test, pnpm test:standards, pnpm docs:check,
pnpm i18n:check, pnpm emoji:check, pnpm aggregate:check, pnpm libs:check,
pnpm sync-shared:check, market-build --check, and the skins suite all pass.
2026-09-28 10:43:10 +08:00
zhu1090093659 30089082e1 feat(dsh-market): reuse the official plugin surfaces in the Workshop store
The store installed community plugins through its own writer chain (family
`pluginManager` service to the loopback gateway to the `dsh plugin`
CLI), which the packaged Desktop client refuses. Install through the
official in-process manager's remote face when the host publishes it, keep
the family face as the fallback, add a manage action that hands an
installed bundle to the official Plugins page via
`pluginNavigation.openBundle`, and bridge both faces with `ctx.inject`
as contract observations instead of cross-package imports.
2026-09-27 20:47:32 +08:00
zhu1090093659 b424f237e3 feat(task-board): fold the task form into regions and surface the agent preset
The create/duplicate/subtask dialog stacked every field in one scrolling
column. It now groups its configuration into collapsible regions, task
content open by default, and every collapsed header keeps a one-line summary
of the values it holds, so the default layout needs no scrollbar; only the
modal body keeps an overflow fallback for short windows, and a region holding
a blocking error is forced open instead of reporting it out of sight.

The agent-preset pin is renamed from "Mode" to "Agent preset" in the create
dialog and the task detail. Its picker keeps the runtime roster as the
authority and groups it into built-in and user presets, shows the four
built-in rows under localized names, and defaults to an inherit choice that
names the preset a run without a pin actually uses. zh/en copy stays in the
package and ru mirrors it in dsh-i18n.
2026-09-27 20:47:14 +08:00
zhu1090093659 0a18cf62d4 feat(task-board): start task runs with dsh built-in /goal
A task row now carries an optional goalRun flag, on by default: the new-task
dialog starts checked and the task detail can turn it off. Only an explicit
false is stored, so a card that never touched the option - and every card
written before the field existed - keeps starting its runs as goal runs.

When it is on, HostExecutionRunner queues the task prompt first and then arms
dsh's built-in /goal with the same composed prompt as its objective, so the
session keeps working automatic continuation rounds until the agent marks the
goal complete. The inspection loop settles such a run from session/projections
(active stays pending, blocked fails with the goal's own reason, and
complete/paused/no goal keeps the turn verdict) instead of the first turn end,
which would have reported unfinished work as done and let a scheduled card
start a second concurrent session for the same objective.

A refused or unacknowledged /goal command is reported and the run continues as
a plain single turn; goalObjective prefixes an objective the command's own
parser would read as a goal operation.

Also updates the agent-tool surface, the zh/en locales, the dsh-i18n ru
dictionary, the package README pair and AGENTS.md, the rebuilt aggregate client
bundle with its fingerprint record, and adds the owning Agent Note plus the
goal-run test suite.
2026-09-27 20:47:14 +08:00
zhu1090093659 dc7e26b9f0 refactor(shared): drop dead shared artifacts and realign references
- Remove the sync-manifest entry for shared/host/run-guarded.ts and the three
  generated copies under packages/{dsh-usage,dsh-task-board,dsh-git-graph}/src/host/:
  nothing in this repository imported the module (the satellite repositories
  carry their own copies), so the entry only kept three unread files in sync.
  The shared source and its spec stay.
- Delete the unused mobileBundle helper and its node:module createRequire import
  from shared/tsdown.client.ts; the standalone mobile bundle has been gone since
  0.4.0.
- Update the sync composition guard in scripts/sync-shared.test.mjs (99 -> 96
  copies, 48 -> 45 host copies) and refresh scripts/lib-artifact-fingerprints.json.
- Re-point the notes, READMEs, CONTRIBUTING and docs that still linked the
  skin-center contract files to the dsh-skins repository, and prune the stale
  screenshots.
- Record the decision in
  .agents/notes/implemented/simplification/2026-09-26-dead-shared-artifacts-removed.md
  and correct the run-guarded facts in the aggregate fault-isolation note.
2026-09-27 20:47:07 +08:00
EDDYCRAZY-CC 97bcd4e610 fix(client): resolve layout injection in skill-explorer, 0.1.7 settings in usage, and fence error in task-board 2026-09-27 20:29:50 +08:00
zhu1090093659 1898979187 chore(skins): pin the re-anchored skin rows and publish the market
Follows the dsh-skins fix (that repository's `a342b8e`) that re-anchors the
plugin sidebar rows on the shell's native `sidebar.panellist` rows. Moving the
gitlink is what the market build reads, so the four affected skins' assets and
their try-on transforms are regenerated here together with the aggregate
bundle (which inlines the child plugins' client sources and now carries the
`data-dsh-panel-entry` glyph anchor) and the recorded lib fingerprints.

pnpm market:check, pnpm libs:check, pnpm test:standards, pnpm i18n:check and
pnpm docs:check all pass at this revision.
2026-09-26 22:14:28 +08:00
zhu1090093659 32e6a83a39 refactor(panels): render every family panel through the native layout seats
ssh is the last panel that took the center column over at the DOM level. It
now registers a row in the shell's own panel list (sidebar.panellist) and a
keyed page in the layout's main slot, like the task board and the skill
center, so the shell owns the row box, label, highlight, rail and switch.
That retires the whole takeover apparatus:

- shared/client/panel-mount-core.ts and shared/client/sidebar-entry-core.ts
  are deleted along with every synced copy; ssh's mount.tsx, sidebar-entry.ts,
  panel-mount-core.ts and sidebar-entry-core.ts go with them. The
  body-mutations hub now serves only the aggregate shell and the usage card.
- The dsh-panel-activate handshake and PANEL_FAMILY occupancy table are gone:
  the layout's keyed main slot is the single occupancy authority, so the board
  and the skill center no longer coordinate with anyone.
- The terminal survives the move because the PTY session is host-owned (see
  the previous commit): the layout unmounting a deselected page costs the view
  its xterm instance, not the remote shell, and the terminal tab reattaches by
  session id. The panel's tab, connect request and session id live in the
  controller rather than in component state.

Tests move with the design: the takeover specs (center-panel-lifecycle,
center-column-css, sidebar-entry, the layout specs, board/ssh coexistence) are
replaced by native-panel-registry specs that drive the real SlotCore, plus a
controller-state spec for ssh's view state.

Validation: pnpm typecheck; pnpm test (ssh 202, task-board 561, skill-explorer
121, shared 107, all packages green); pnpm test:scripts 342/342;
pnpm test:standards; pnpm docs:check; pnpm i18n:check; pnpm aggregate:check;
pnpm libs:check; pnpm market:check.
2026-09-26 17:23:04 +08:00
zhu1090093659 6965f49d95 fix(scripts): make the relink range guard real semver comparison
The guard rejected any satellite version above the range's base (for
example 0.4.3 under ^0.4.2) because the caret branch demanded component
equality with the base before its upper-bound check. Rewrite the
comparison as ordered lower/upper bounds: caret bumps the leftmost
non-zero component, tilde the minor, x-ranges their first hole, and
prerelease suffixes compare by release core. Covers the shapes the
aggregate declares (caret, tilde, OR alternations, x-ranges, exact
pins); regression tests pin 0.4.3-in-^0.4.2 accepted and 0.5.0
rejected.
2026-09-26 17:20:09 +08:00
zhu1090093659 b510711e80 fix(scripts): repoint the aggregate's satellite rows at local checkouts
The desktop host resolves the external rows the aggregate's patch
contributes from the aggregate package's own node_modules, where pnpm
links the satellite dependencies to published registry tarballs. A
satellite commit followed by a host restart therefore kept loading the
released copy: the reported skin-center picker error and the macOS
system-wallpaper list survived a restart even though the local checkout
carried the fix.

link-profile.mjs now repoints those four links at satellites/<repo> when
the checkout has a built lib/ whose version satisfies the declared
range, so the same script that already wins the profile layer for
family rows also wins the aggregate layer for satellite rows. pnpm
install reverts the links; rerunning the script repairs them. The flow
is documented beside the satellite commit rules it complements.
2026-09-26 17:15:47 +08:00
zhu1090093659 208bb2c10e feat(skill-explorer): render the skill center through the native layout seats
The skill center stops taking the center column over at the DOM level. It
contributes a row into the shell's own panel list (sidebar.panellist) and a
keyed page into the layout's main slot, and drives ctx.layout.selectPanel —
the same shape the task board already uses, so the shell owns the row box,
the label, the active highlight and the collapsed rail. mount.tsx, the
hand-drawn sidebar row, and the package-local panel-mount-core /
sidebar-entry-core / body-mutations copies are gone.

The panel's tab and editor target move from SkillPanel component state into
PanelController. The layout mounts a keyed page only while its panel is
selected, so component-local state dropped the open tab and any in-progress
edit on every panel switch; SkillPanel now reads the controller snapshot
through useSyncExternalStore. panel-state.spec.ts locks that contract, and
native-panel-registry.spec.ts drives the registration against the real
SlotCore.

ssh still takes the column over at the DOM level, so native-panel.tsx keeps a
transitional dsh-panel-activate handshake with it (and the task board keeps
its side). Both go away when ssh moves to the native seats too.

sync-shared loses the skill center's three copies: the copy-count buckets are
102 total / 42 client, and ssh is now the only consumer of
sidebar-entry-core and panel-mount-core.

Validation: pnpm typecheck, pnpm test (skill-explorer 121, ssh 210,
task-board 565+1 skipped), pnpm test:scripts 342/342, pnpm test:standards,
pnpm docs:check, pnpm i18n:check, pnpm aggregate:check, pnpm libs:check and
pnpm market:check all pass.
2026-09-26 17:10:00 +08:00
zhu1090093659 68b095272b Merge origin/dev into dev: one center-column panel family
Brings in the 11 upstream commits, chiefly 305a3863 (skill-explorer moves
into the center column). Both sides had reworked the same panel subsystem
from opposite directions, so the merge integrates rather than picks a side.

- shared/client/panel-mount-core.ts keeps the upstream PANEL_FAMILY table
  (one occupancy row per panel instead of pairwise sibling options) and
  documents that the task board left the core for the native layout seats.
- The board keeps its native panel registration and its dsh-panel-activate
  handshake, widened from ssh alone to every DOM-takeover family panel:
  coordinateWithFamilyPanels plus TAKEOVER_PANEL_NAMES now cover
  skill-explorer too, so the new third panel can no longer paint over the
  board while its row still looks selected. panel-coexistence.spec.ts
  covers the new direction.
- sync-shared drops the board's body-mutations / sidebar-entry-core /
  panel-mount-core copies and keeps ssh plus skill-explorer for the core;
  copy-count buckets are 105 total / 45 client.
- The panel-mount-core extraction note records the merged design and
  refreshed facts; both sides and the sidecar are re-recorded.

Merge conflicts: 14 files. Validation: pnpm typecheck, pnpm test
(ssh 210, task-board 565+1 skipped, skill-explorer 115), pnpm docs:check,
pnpm i18n:check, pnpm test:scripts 342/342, pnpm libs:check,
pnpm market:check, and the panel suites all pass.

pnpm test:standards reports 3 new violation groups, all in
dsh-plugin-manager gateway specs that another session has unstaged; they
are not part of this merge.
2026-09-26 16:37:10 +08:00
zhu1090093659 da32f628a5 feat(task-board): render the board through the native layout panel
Move the task board off its DOM takeover: the board now registers a
sidebar.panellist row and a keyed main page through the official slots
system, and the package-local panel-mount-core, sidebar-entry-core and
body-mutations copies are retired. The shared panel-mount-core returns to
the single ssh consumer with sibling activation names.

Also folds in the plugin-manager update-patch rework (the settings tab is
replaced by a patch row on the official Plugins page, plus the desktop
launch-profile repair) and the matching Agent Notes.

Committed as a checkpoint before merging origin/dev.
2026-09-26 16:14:14 +08:00
EDDYCRAZY-CC 828bb6bdd4 fix(remote-web-ui): supervise a running tunnel by its public URL
A connector that loses its Cloudflare edge registration keeps its process and its metrics port alive while the minted hostname stops resolving. The lifecycle judgement covered only a URL timeout and a process exit, so the manager reported 'running' forever, the relay kept forwarding paired phones to the dead address, and the phone got Cloudflare 530 / Error 1016 instead of the relay's offline page - measured in issue #1723 for over four hours with no self-healing path.

TunnelManager now probes the public URL of a running tunnel every 60s and, after two consecutive failures (DNS failure, refused connection, timeout, or a Cloudflare 5xx such as 530/1033 - an unauthenticated 401/403 still counts as alive), calls the existing fail() path: the process is stopped, the phase goes to 'failed', and the ordinary backoff restart mints a new URL that re-registers the relay. The probe, its interval, its failure budget and its timeout are injectable seams.

The public URL is probed rather than cloudflared's local /ready endpoint: the package's Tunnel handle exposes no readiness face, and the public URL is the address the phone actually uses, so one measurement covers DNS, edge registration and origin reachability.
2026-09-25 22:41:43 +08:00
EDDYCRAZY-CC ad90e46b28 fix(settings): bind family cards to the profile entry the Host serves
A family settings card addresses its form by PROFILE ENTRY ID, but each card
knew only its own namespace. When the family binder is not loaded the cards
fell back to ctx.configForms.get(namespace) — an entry the Host does not serve
on an aggregate install. The write was rejected with "No configurable plugin
entry \"liangshen\"" while the card reported "the deployment did not accept
these values", which is what the LiangShen settings page showed for every edit.

The fallback now lives once in shared/client/settings/settings-entry-form.ts
(synced to the six packages whose card binds a family namespace) and binds the
entry id the shared describe mirror justifies, REBINDING when the mirror names
a different one of the package rows. A one-shot guess cannot be right for every
deployment: the mirror answers asynchronously — at plugin activation it usually
holds nothing yet, and the aggregate mounts its client children in order — the
bare namespace is no entry id on an aggregate install, and the aggregate row id
is wrong on a standalone one, whose row id is the namespace or the package own
ui-* row. An unanswered or EMPTY mirror counts as unanswered, not as absence;
only a mirror that answers with other packages rows falls back to the
namespace itself, which is the pre-0.1.7 keying shape.

Affected cards: dsh-liangshen, dsh-remote-web-ui, dsh-task-board, dsh-market,
dsh-usage and dsh-session-archive (the last three carried the same defect).
The aggregate and market specs now model a mirror that names the served row
instead of asserting the namespace.

Verified in the real Web GUI: the save now posts ns "web-ui-liangshen" and the
Host answers ok. The refreshed aggregate lib/ and the new fingerprints ship
with the change; the owning 0.1.7 cohort note records the binding rule.

pnpm typecheck, pnpm test, pnpm docs:check, pnpm i18n:check,
pnpm aggregate:check, pnpm libs:check, pnpm sync-shared:check,
pnpm test:standards, pnpm runtime-deps:check and pnpm emoji:check pass.
pnpm test:scripts fails only in the tar --force-local suites, which Windows
bsdtar rejects (GNU flag) and which this change does not touch.
2026-09-25 15:39:12 +08:00
EDDYCRAZY-CC 67f421a5cc test(sync-shared): move the copy-count buckets for the third panel core
The skill center's panel-mount-core.ts copy is a 102nd generated copy and the
42nd under src/client/, so the two bucket assertions in the sync-shared test
move with the manifest. Caught by CI: the assertion reported 102 against the
stale 101.
2026-09-25 15:00:18 +08:00
EDDYCRAZY-CC 305a38634c feat(skill-explorer): move the skill center into the center column
The skill center was a body-level overlay modal and the only family surface
that did not share the center column. It is now a center-column panel like ssh
and the task board: a header with the back-to-conversation control, a
skills/create tab bar, and the family's control vocabulary, still
theme-token-only.

Panels now share one occupancy table: shared/client/panel-mount-core.ts owns
PANEL_FAMILY (activation name and html attribute per panel) instead of the
pairwise sibling options. Opening a panel clears the other rows' attributes and
broadcasts its own name; an open panel closes when the broadcast name is not its
own. The pairwise shape could not express three panels, so a panel that did not
name the third one stayed logically open while invisible and its sidebar row
needed a second click to reopen.

The skills list adopts the family's row treatment, the refresh control hides
while a load is in flight, and the create tab resolves the workspace it needs
when it is the first tab opened. The semantic-attributes contract drops
card/head; the wallpaper-exclusive skin re-anchors its skill center rules on the
panel root plus skill-row (committed in the dsh-skins repository, pinned here).

Generated copies follow their sources: ssh and the task board mounts lose the
sibling options, the skill center gains a panel-mount-core copy, and the
aggregate artifact plus the market dist are rebuilt.

Authored by yupeng.jia <yupeng.jia@momenta.ai> as ad47d3a4 on the contribution
fork; re-applied here on the current dev (the original was based on 53ec2ed1,
108 commits behind) with the generated artifacts rebuilt rather than taken from
the stale base, and the skin-center content re-applied in its own repository.
2026-09-25 14:47:18 +08:00
EDDYCRAZY-CC 607d207b5c fix(liangshen): declare the preset when the registry arrives late
The agent-preset registry's publication is decided by its own dependency chain
(the official registry injects loader and sessionProjections, and reaches
settings through ctx.inject), so a cold start can activate this row BEFORE the
service exists. The single declaration attempt then warned and gave up, and the
preset stayed missing until an unrelated settings write happened to re-arm the
row -- which is exactly the report: toggle any switch and it appears, and only
then (issue #1721).

The registry cannot simply be injected: a deployment may compose none at all,
and waiting on it would pend the row forever, which the host's boot gate turns
into a failed web boot rather than one dead plugin (issue #1712). So the row
keeps probing and degrading, and ctx.inject supplies only the late edge.

That recovery is gated on a registryMissing flag rather than re-arming
unconditionally: cordis runs the callback asynchronously even when the service
was already present at activation, so an ungated re-arm would release and
re-declare the declaration the activation had just registered. The child fiber
the callback runs under never makes this row pending, so the boot gate stays
out of it.

Also refresh the aggregate lib/ output and its fingerprints, and record the
decision in the 0.1.7 cohort note that owns the volatile settings model.
2026-09-25 13:58:06 +08:00
EDDYCRAZY-CC 3615f779d5 fix(remote-web-ui): stop the footer seat box from breaking the settings row
The wide-foot rules treated the sidebar.footer.action seat as this plugin's
own icon row and gave it a box (flex: none, order, align-items). The seat is
a shared container: as soon as any other plugin registered into it, the seat
became a tall column and the shell's own settings trigger -- an element this
plugin does not own -- was squeezed to 102px and vertically centred inside it,
with the whole footer growing to 515px over the session list (issue #1710).

Drop the seat's box (display: contents) so its occupants answer to the foot's
own wrapping layout, give a non-family registrant a full line of its own, and
keep the family trigger intrinsic-width on the settings line. The intended
result stands: settings on the left taking the remaining width, the family
trigger beside it, every other registrant on its own row above.

Also bump the dsh-skins pin to carry the skin-center and blue-fantasy 0.1.7
adaptations, and refresh the aggregate lib/ output and its fingerprints.
2026-09-25 13:40:41 +08:00
EDDYCRAZY-CC 8225d9f2ce chore(libs): rebuild the aggregate for the mobile drawer fix 2026-09-25 11:25:34 +08:00
zhu1090093659 e09bd211d3 feat(dsh-update): split the self-update surface into its own plugin row
The self-update surface lived inside dsh-remote-web-ui, so disabling or removing the remote-access plugin also removed the update entry, and the desktop application - which owns its own updater - had no way to keep the seat off one page while keeping it elsewhere. It now ships as @linxin666/dsh-update with its own bundle row (update / web-ui-update) and its own host half behind the loopback fence.

The aggregate gains the child (aggregate.yml, cordis.patch.yml, children.*), the shared-file sync manifest carries the new consumer targets, the ru dictionary moves its update namespace to the new file, the three old note records are updated in place for the new owner of the surface, and the market's committed lib is rebuilt for the shared guard it carries.
2026-09-25 10:22:27 +08:00
zhu1090093659 ce231d230a chore(libs): re-record the aggregate fingerprint after the task-board fence change
A child package's sources are part of the aggregate's recorded inputs, so the task-board fence change made packages/dsh-web-all read as stale. The committed value is recorded from the committed tree; the wider value that also covers the pending aggregate and child sources stays in the working tree, where it belongs with the commit that lands them.
2026-09-25 10:10:27 +08:00
zhu1090093659 9f2f7f1921 fix(dsh-web-all): keep the macOS window draggable under family decorations
The official desktop stylesheet turns every direct body child into a
-webkit-app-region: no-drag region, so a body-level element spanning the
viewport subtracts the whole window from the macOS draggable region and
cancels the official [data-window-drag] chrome rows with it: double-clicking
the title area stops running the system zoom action and the window stops
dragging by its title bar. The skin center mounts six fixed decoration layers
plus a backdrop-blur veil that way at controller creation (no active visual
required), and the aggregate boot splash joins them on every load; all of them
are declared non-interactive, which does not exempt them from that
computation.

The aggregate compat layer now opts those non-interactive body-level overlays
out with an "initial !important" declaration, scoped to the darwin platform and
to direct body children. Rebuilt lib/, refreshed lib fingerprints, added the
responsive-contract regression case and the Agent Note.
2026-09-25 07:30:53 +08:00
zhu1090093659 27e138d1f7 fix(release): bring the root alias version along with the release tag
The root package.json is private and unpublished, so the release bump never
moved its version: a git or link install of the repository kept reporting
0.1.1 in the plugin manager long after the family reached 0.4.x.

The root alias is part of the released contract, so pin its own version the
same way its aggregate dependency is pinned: verify-version now fails the
publish when the root version drifts from the tag, and the release bump
covers the root manifest beside the family packages.
2026-09-24 23:15:52 +08:00
zhu1090093659 30ab8692a9 chore(sdk): advance the plugin cohort to 0.1.7-rc.2
Move every family manifest, the plugin scaffold and the shared workspace
package onto the 0.1.7-rc.2 cohort, together with the surfaces that state
the same fact:

- the dsh.engines.dsh floors and the matching @deepseek-ai/dsh host peers
- the release-age exclusion ledger and the two packageExtensions keys
- the root README badges, the CI/release mount-smoke pins and the
  docs/publish-prep.md and docs/plugins.md prose

Two rc.2 facts were verified against the published artifacts rather than
inferred from the version number:

- the shell's frozen static module table is unchanged (the same nine
  specifiers in the dsh-web-frontend rc.2 dist bundle), so
  shared/web-platform.ts keeps its list and only its provenance comment
  moves; the new dsh-client-shortcuts is an ordinary client plugin, not a
  static module
- @deepseek-ai/dsh-client-ui-primitives@0.1.7-rc.2 imports a further
  undeclared face, @deepseek-ai/dsh-util-code-language, so the
  primitives packageExtensions patch gains that pin

The satellite peer floors in pnpm-lock.yaml still read >=0.1.7-rc.1: they
belong to the satellite packages and move when those repositories release
the aligned version.
2026-09-24 22:26:05 +08:00
zhu1090093659 46ee2736ab fix(remote-web-ui): keep every application-delivered page out of the pairing fence 2026-09-24 21:56:52 +08:00
zhu1090093659 64beab4ad5 chore(desktop): remove the repository's Electron desktop app and its packaging lane 2026-09-24 21:48:46 +08:00
EDDYCRAZY-CC 8d9010f7b6 chore(web-all): rebuild the aggregate for the document-relative client routes
The aggregate inlines in-repo child client sources, so the route change in
skill-explorer, git-graph, usage, session-archive, plugin-manager, market,
web-all and task-board requires a rebuild of the committed lib/ together with
the recorded fingerprints. pnpm build, pnpm libs:write and pnpm libs:check all
pass.

Refs #1707
2026-09-24 20:47:43 +08:00
zhu1090093659 a621ef4819 Merge origin/dev into dev
Generated-artifact conflicts (packages/dsh-web-all/lib/client.js.map and scripts/lib-artifact-fingerprints.json) were resolved by regenerating rather than hand-merging: pnpm build on the merged sources, then pnpm libs:write. The rebuilt aggregate bundle carries both the usage balance-origin fix and the child mount-claim release.
2026-09-24 19:51:16 +08:00
zhu1090093659 fcfd9b8958 fix(dsh-web-all,task-board): keep family surfaces across in-place reloads
The client module system replaces a rebuilt application entry in place:
it disposes the old fiber and re-applies the new code without reloading the
page. The aggregate's mount registry never released the claims of the dead
instance, so its successor skipped every family child — usage, market,
plugin-manager and session-archive vanished from the page and
dsh-task-board's DOM outlived the dictionaries it renders through, leaving
a live sidebar row and board showing raw keys (entry.label, board.title,
board.showSubtasks).

- mount-children.ts releases exactly the claims its own fiber added, so a
  successor mounts the children again while a sibling instance keeps its own
- dsh-task-board binds its DOM mounts and its settings subscription to the
  fiber, matching the discipline dsh-ssh already followed
- regression specs for both, verified to fail against the pre-fix sources
- live A/B on the running host: an in-place replacement of the pre-fix
  artifact kills the whole family in an open page, and the same replacement
  on the fixed artifact keeps one row, the usage card and the complete
  settings nav with no reload and no duplicates

Agent Note: .agents/notes/implemented/bug-fix/2026-09-02-aggregate-client-children-mount.md
2026-09-24 19:42:33 +08:00
EDDYCRAZY-CC 6c089fc586 fix(remote-web-ui): name both settings locations in the lan-required hint
Closes #1700.

The lan-required banner told every user to open "Settings -> Web Plugins ->
Remote access". That section only exists when dsh-web-settings is
installed (the family aggregate): a standalone install places the same card
on the plugin's row under the official Plugins page, so following the hint
led nowhere. The copy now names both deployment shapes.

Verified: pnpm --filter @linxin666/dsh-remote-web-ui test (429 passed) and
pnpm i18n:check (zh/en/ru keys stay at parity).
2026-09-24 18:59:57 +08:00
zhu1090093659 687f618a2b feat(dsh-task-board): subtasks, agent tools and Agent Team runs
Subtasks and cascade runs
- TaskRecord.parentId with a Host-owned lineage gate (existence, cycle, and
  the maxSubtaskDepth limit, 1..3, default 1) shared by every caller in
  src/core/subtask.ts.
- run/rerun open one execution per participant under a single runGroupId; a
  parent settles only after its own turn and every direct subtask, with
  failure dominating. Cron takes the same path.
- Creation inherits the parent workspace, preset and model but NOT the
  permission: the binding resolves from the live ancestors at launch, so
  detaching a subtask cannot leave a confirmed elevated card behind.
- Subtree archive/restore, delete-with-subtasks refusal, link repair on load,
  and the client affordances (link/detach a subtask, badges, parent-only
  default view with a roll-up badge, filter auto-expand).

Agent tools
- Eight model-facing tools (task_board_list/get/create/update/set_parent/run/
  manage/schedule) drive the same Host ledger the browser drives. They call
  no separate business rules, so every gate holds identically.
- Registration follows the enabled switch and resolves the tool registry as an
  optional service, so a deployment without one still mounts the board.
- There is deliberately no confirm-permission tool: the human gate stays human.

Agent Team execution (task-level opt-in)
- teamRun switches one cascade from one session per participant to a single
  Lead session plus one teammate per subtask, spawned through the optional
  agentTeams service; each teammate session is attached to that subtask
  execution so the ordinary session monitor settles it.
- Every run states its shape in the launched prompt: the independent sessions
  a cascade opens, or the Lead and its named teammates.
- Fail closed: a missing service refuses a manual team run by name (and is
  recorded for a scheduled one), and a subtask that pins its own above-default
  permission is refused because a teammate cannot carry that pin.

Verification: pnpm typecheck, pnpm test (task-board 551 passed),
pnpm test:standards, pnpm docs:check, pnpm i18n:check, pnpm emoji:check,
pnpm aggregate:check, pnpm libs:check. Takes effect after the user restarts
the running DSH service.
2026-09-24 17:44:42 +08:00
zhu1090093659 cdf79e8c44 fix(dsh-web-all): make shelled family rows configurable
A family row mounts the aggregate shell, so the row's Config belongs to the shell and
the Host settings surface served no form for it: a settings save on a family entry was
rejected with 'No configurable plugin entry', and the browser cards fell back to a
config form the host never served. The shell now declares Config = z.any().volatile():

- 'any' keeps the wrapped plugin's fields at the row config root, where a standalone
  install of that package keeps them, so one profile shape serves both mount paths and
  no family client needs a path prefix. scripts/aggregate.mjs emits that flat shape.
- '.volatile()' puts the entry on the settings surface at all (a schema with no volatile
  field is skipped) and admits a write to any path. The root-volatile update also goes
  through the live path: the Loader commits the new config into the running entry's
  reference and emits loader/volatile-update, and the shell re-mounts the family plugin
  with the committed config instead of remounting the entry.

The wrapped plugin's own schema still validates the values it receives, so a refused
value leaves that one row degraded rather than taking the boot down. The rebuilt lib/
replaces the superseded shell chunk, and the recorded fingerprint covers the new sources.
2026-09-24 16:54:50 +08:00
zhu1090093659 3abd4dd87a docs(satellites): finish the four-repository wording
AGENTS.md still introduced `satellites/` as "those three repositories" and the
market input fetcher's header named only the skin and pet content. Both now
name the four satellites this branch carries (skins, pet, community index,
presets).

Gates: docs:check, emoji:check, test:scripts.
2026-09-24 13:14:39 +08:00
zhu1090093659 21f3b4d72a refactor(preset-center): consume the extracted preset center instead of carrying it
The preset center now lives in its own repository (dsh-presets) and is consumed
here as the published npm package @linxin666/dsh-client-ui-preset-center, the
way the skin center, the pet plugin and the community plugin index already are.

- packages/dsh-preset-center leaves this tree (plugin sources, tests and the
  presets/ catalog; history stays in this repository);
- the aggregate mounts it through an external rows: entry (web-ui-preset-center)
  with a hand-written ^0.4.1 range, and ./preset-center stays in the exports map
  as a tombstone so profiles written against the patchFrom era keep importing;
- market-inputs.lock.json gains a presets input pinned to the
  satellites/dsh-presets gitlink, and scripts/market-build reads
  .market-inputs/presets with the in-repo fallback kept for the fixture tests;
- scripts/sync-shared.mjs stops mirroring five host helpers into the package
  (101 -> 96 copies); the lib fingerprints, coverage baseline, test-standards
  baseline, runtime-deps scan and i18n audit each shrink by one package;
- the contribution gate has no exception left: reject-non-content-pr.yml
  redirects a 新预设收录 declaration to dsh-presets, and the PR template,
  CONTRIBUTING.md, PR_TRIAGE.md and ISSUE_TRIAGE.md now state that this
  repository accepts no external contribution directly;
- docs, the family-satellite owning note and the two preset notes follow, and
  the new Agent Note records the split.

Gated by: typecheck, aggregate:check, libs:check, sync-shared:check,
runtime-deps:check, i18n:check, emoji:check, docs:check, market:check,
test:standards, test:scripts (340 tests), test (every package) and build.

Not yet safe to merge on its own: the satellites/dsh-presets gitlink needs that
repository to exist on GitHub, so the satellite commits and this pin have to be
pushed together.
2026-09-24 13:13:17 +08:00
EDDYCRAZY-CC 2560825092 fix(remote-web-ui,git-graph,task-board): resolve seven reported open issues
Issues #1675, #1696, #1682, #1678, #1677, #1690, #1672.

remote-web-ui

- #1675 (boot crash): writeLanBind only stripped an empty `[]` placeholder,
  so a profile the plugin manager had rewritten into a NON-empty flow array
  got the managed block concatenated onto it and the file became two root
  documents (YAMLException 7:1 at dsh web startup). A flow-style root is now
  re-emitted in block style before the block is appended, which keeps exactly
  one valid document; an unparsable base is refused with the file path rather
  than written half-valid. Adds the `yaml` dependency, the same document
  round-trip dsh-client-ui-plugin-manager already uses.
- #1696 (device-authorization loss): the constructor restored the persisted
  device table and immediately capped it at the cap it happened to hold (the
  schema default of 4 until the saved settings row is applied), then a normal
  heartbeat/sweep wrote the trimmed table to disk. Restore now keeps every
  valid persisted session; the cap is enforced in accept(), which evicts FIFO
  until a slot is free.
- #1682 (desktop GUI fenced): the local-page test only compared the hostname,
  so the DSH Desktop shell's `dsh-app://app/` page was treated as a LAN/tunnel
  origin and the whole GUI was replaced by a pairing fence the shell can never
  satisfy (it strips every set-cookie). The predicate now also accepts a page
  on a desktop delivery scheme or one the official transport already declared
  the host owner, and the inlined boot script applies the same rule. The
  transport hook alone never unfences a network page: this plugin's own
  device-gated landing grants the same hook to a paired LAN/tunnel remote,
  and that page must keep riding the gated channel.
- #1678 (rail seat misaligned): the shell renders sidebar.footer.action as a
  centered horizontal row in the rail too, so a second registrar made the seat
  78px wide against a 35px icon column. A rule scoped to the collapsed frame
  and anchored on this plugin's own data-rail occupant stacks the seat.
- #1677 (registry probe burst): the update status call fanned out ~20 registry
  probes at once; the fan-out is now a bounded pool of four that preserves the
  caller's order.

git-graph

- #1690: auto-isolation probed `workspaces.startSession`, which 0.1.7 moved to
  the navigation service `uiWorkspace`. The probe returned null on every boot,
  so the feature stayed disabled and warned on every page load. The wrapper
  now reads both faces (uiWorkspace for startSession, workspaces for the rows)
  and degrades only when one is genuinely missing.

task-board

- #1672: a gateway that withdrew the strict session/list definition spent the
  full service-unavailable retry window and then printed a console error the
  user has no action for. That condition is now classified as a quiet,
  warn-once roster degradation.

Every change ships with tests that pin the new behavior, including the
regression cases the reports described. The aggregate client bundle and the
committed lib fingerprints are rebuilt in this commit because the child
client sources the aggregate inlines have changed.

Verified: pnpm typecheck, pnpm test, pnpm build, pnpm test:standards,
pnpm docs:check, pnpm i18n:check, pnpm emoji:check, pnpm aggregate:check,
pnpm market:check, pnpm libs:check all green.
2026-09-24 10:42:39 +08:00
zhu1090093659 67d55c96f9 test(ci): record the liangshen baseline CI actually measures
The dispatched nightly came back with one regression: dsh-liangshen functions
80.64 here against 80.07 on the runner, 0.57 over the gate's half-point
tolerance. The gap is not noise. benchmark-live-run.test.ts guards two cases
with it.runIf(harnessInstallAvailable()), so a machine that has the harness
installed covers functions a Linux runner never runs, and recording from here
puts the baseline above what CI can reach.

Record the four liangshen metrics at the runner's values, which is what the
ratchet has to hold on, and say so where the test rules live: when the two
sides disagree, the CI value is the one to record.
2026-09-24 10:04:56 +08:00
zhu1090093659 fc0be24611 test(scripts): keep the category-line fixture in sync with the template
The 插件功能 category line lost its 宠物 item when the pet plugin moved to dsh-pet; the template, both pr-review-routes.json strings and this fixture are meant to repeat the line verbatim, so the fixture carries the same text again.
2026-09-24 10:01:25 +08:00
zhu1090093659 9640ec8dbb test(ci): re-record the coverage baseline the current fleet measures
The ratchet has been red on dev since at least the 2026-09-21 nightly: three
nightly runs in a row failed on the Coverage ratchet job while the
flake-detection job passed. The recorded numbers are stale for the fleet that
produced them rather than platform-specific: the values a Linux runner reports
and the values this machine reports agree to within the gate's own noise.

Re-recorded with the gate's own --write-baseline under the CI cohort's Node 22.
Fourteen packages move, 42 metrics up and 12 down; the ones moving down are
named here so the loosening is visible rather than implied: dsh-liangshen
functions 91.57 -> 80.64 and branches 81.29 -> 71.4; dsh-model-capabilities
branches 87.83 -> 84.59; dsh-session-archive lines 76.02 -> 70.5, statements
73.32 -> 68.06, functions 68.18 -> 57.54 and branches 60.65 -> 55.02;
dsh-skill-explorer branches 78.16 -> 67.95; dsh-ssh functions 72.43 -> 68.62 and
branches 76.8 -> 57.36; dsh-task-board functions 73.51 -> 67.62 and branches
82.06 -> 67.33.

The setup repair that lands before this one makes Node 25 measure these same
numbers, so the recording holds for both the pinned cohort and a contributor's
newer interpreter: the gate reports no package regressed on either.
2026-09-24 09:55:36 +08:00
zhu1090093659 57d1499ddf test(vitest): give the family test setups a usable localStorage
Node 25 defines localStorage on the global object, and without a usable
--localstorage-file its getter returns an empty object: getItem, setItem and
clear are all missing. Vitest's jsdom environment fills in globals only where
the name is still free, and under vitest window is globalThis, so that stub
survived into every DOM test: the packages whose code reads storage took their
degraded paths instead of the code under test, and dsh-task-board's specs failed
outright on window.localStorage.clear.

The shared setup now installs a Storage in a DOM environment and removes the
stub where there is no DOM, which is what Node 22 leaves behind. dsh-git-graph
and dsh-task-board join its copy manifest, and dsh-skill-explorer's own setup
imports the shared source.

Measured on Node 25 against Node 22, the repair is what moves the numbers back:
dsh-git-graph 79.53 -> 81.32 lines, dsh-remote-web-ui 69.66 -> 70.34,
dsh-skill-explorer 77.61 -> 80.22, dsh-web-settings 78.66 -> 82, with branches,
functions and statements following. Coverage no longer depends on the
interpreter that ran the suite, which is what makes one recorded baseline mean
the same thing in both.
2026-09-24 09:54:51 +08:00
zhu1090093659 a3f783b886 Merge origin/dev into dev
Brings in the satellite pin move onto the end-of-line fixes, the profile
linking for the three satellite packages, the skin and pet path repointing, and
the attestation-lane fixes, alongside this branch's package-root cleanup.

Resolutions:
- scripts/official-tokens-snapshot.mjs: both sides repointed the contract at
  the dsh-skins submodule. Took the incoming version, which carries the same
  paths and root with a tighter explanation of which install resolves what.
- scripts/pr-review.mjs and scripts/pr-review.test.mjs: this branch retires the
  skin review lane (e2cc4fdf — reject-non-content-pr.yml redirects those PRs, so
  the lane can never fire here), which deletes the functions fd504852 patched to
  read a dsh-skins layout. Kept the retirement; the incoming fix only has a home
  if the lane comes back.
2026-09-24 09:50:51 +08:00
zhu1090093659 843bd04902 fix(market): drop the deploy lane's post-deploy asset verification
The step ran through the worker's attestation route, but the call still leaves
the runner, and the zone answers that vantage with a managed challenge before
the Worker sees it: both attested runs came back cf-mitigated=challenge with
the Just a moment... interstitial and a ray, and a retry does not clear it. The
step could therefore only report the challenge and turn a completed deploy red.

The lane now deploys and stops there. The attestation route, its secret binding
and the sweep stay, and the maintainer runs the sweep on demand from a network
the zone's policy allows, which is where the whole 3075-path check passes. This
leaves a named coverage gap: a partial upload, or a manifest the origin cannot
serve, is caught by market:check against the pinned inputs and by that sweep,
not by CI. scripts/market-verify-assets.mjs stays in the lane's path trigger
because the lane runs its tests under pnpm test:scripts.
2026-09-24 09:48:46 +08:00
zhu1090093659 b4944353a5 refactor(scripts): drop the packages/skins root the satellites took
The skin, pet and community packages left this repository, so packages/skins/
never exists in a checkout of it. Every scanner that still walked it as a
second root did so defensively — family-packages.mjs and
coverage-gate.discoverPackages listed it first and guarded the walk with
existsSync, aggregate.mjs looked for a second aggregate manifest and a second
package index under it, e2e-mount-rewrite searched it for workspace packages,
and seven test files built fixtures under it. The walkers now name packages/ as
the single root and the fixtures and comments follow; modules that only carried
the root in a doc comment are reworded.

scripts/coverage-baseline.json loses the three packages no longer measured here;
its sixteen keys now name exactly the sixteen package directories on disk.

The in-repo fallback paths in scripts/market-build stay untouched: they are the
documented route for building from a checkout that still carries the packages,
and the fixture-based market-build tests populate them deliberately.
2026-09-24 09:48:23 +08:00
zhu1090093659 9a13a2c9bd chore(scripts): drop the e2e wording the skin review lane left behind
The reviewer's only e2e step was the skin preview lane, so the header, the --workdir help and the worktree-root comment no longer promise e2e artifacts beside the worktrees; docs/development.md loses the same phrase. The workspace keeps its ~/remote-e2e name.
2026-09-24 09:46:46 +08:00
zhu1090093659 eee7de29a6 docs(scripts): read the submodule tree when previews are not pinned yet
capture-previews writes previews into the dsh-skins working tree, so market:fetch --force does not pick them up: --force only re-materializes an input whose worktree already sits on the pinned commit, and a worktree elsewhere is ignored in favour of the pinned tarball. The header now names the local-content mode for looking at fresh previews before the commit is pinned (and that the resulting market/dist must not be committed), and the pinned flow for the copy that ships. docs/development.md's skin section carried the same --force step.
2026-09-24 09:45:20 +08:00
zhu1090093659 e2cc4fdf9b refactor(scripts): retire the skin review lane from the PR reviewer
Skin, pet and community-index contributions are submitted and gated in dsh-skins, dsh-pet and dsh-community-plugins, and reject-non-content-pr.yml redirects PRs that declare them here, so pr-review.mjs could never see one: its skin detection matched packages/skins/skin-center/skins/<id>/, which no longer exists, and the visual lane it fed ran capture-previews (now writing into the satellite checkout) inside a PR worktree.

checkSkinChanges, checkCopyright, checkSkinPreviews, judgeVisualMetrics, skinVisualVerify and analyzePixels are gone, along with the result's visual field, the header note and their tests. The reviewer keeps the checks that still apply to what this repository accepts: size, secrets, emoji, CI files, lockfile, template evidence and the worktree gate sequence.

The retire-legacy-skin-gallery note named checkSkinPreviews as shipped reality; its fact now records that the reviewer carries no skin check, and the pair hashes are re-recorded.
2026-09-24 09:45:20 +08:00
zhu1090093659 a2542a08c5 fix(scripts): resolve the split repositories where they now live
Three references here still led into directories that moved to the satellite
repositories. The social-preview banner loaded the skin artwork from
packages/skins/skin-center and the pet atlas from packages/dsh-pet, so both
backgrounds rendered empty and the shot only escaped notice because the page
check looks for a missing img element, not a failed background. The token
snapshot read and wrote the skin center contract under packages/skins/
skin-center, so --check could not open the contract it compares against.

The banner points at satellites/dsh-skins and satellites/dsh-pet, the snapshot
takes the contract, the generated registry and the theme dependency base from
satellites/dsh-skins, and the pnpm-workspace glob for the empty packages/skins
tree is gone.
2026-09-24 09:40:55 +08:00
zhu1090093659 e6fa938561 fix(scripts): send the preview loop through the local content mode
capture-previews told the developer to run `market:fetch --force` after
committing previews in dsh-skins, which reads the pinned commit rather than the
tree that just gained the screenshots; `--local --force` reads that tree and
still re-materializes an already-ok cache. The skin skill also named the
screenshots .png where the manifest, the tool and the skins use .jpg.
2026-09-24 09:39:24 +08:00
EDDYCRAZY-CC cdbaed0d77 feat(scripts): link the satellite packages into the dsh profile too
The split moved the skin center, the pet plugin and the community index out of
packages/ into satellites/, so the three profile links those names carried were
left pointing at directories that no longer exist. They are rows in the
aggregate, and the loader resolves those rows from the profile layer, so a
dangling link pushes the local DSH back onto the published copies instead of the
checkout.

satellitePackages() walks satellites/* for family-scoped manifests and main()
links them like the in-repo family; the shared family walker keeps seeing the
sixteen packages this repository releases. A satellite's lib/ has to exist —
its own `pnpm install` builds it through the prepare script.
2026-09-24 09:39:20 +08:00
EDDYCRAZY-CC fd504852ea fix(scripts): read a skins PR in both skin directory layouts
Skin contributions moved to the dsh-skins repository, where a skin lives at
`skins/<id>/`, but `pr-review.mjs` still matched only the pre-split
`packages/skins/skin-center/skins/<id>/`. `--repo` can point at either
repository, so the change detection and the preview check now accept both
layouts; a dsh-skins PR gets the copyright and preview reminders again.

Verified: pr-review.test.mjs covers the satellite layout for both checks.
2026-09-24 09:27:15 +08:00