DSH plugin right sidebar: tab types, routing and keepMounted

Plugin DevelopmentPublished 2026-10-17Author: DeepSeek Plugin Market
DeepSeek HarnessDSH pluginright sidebarpane-tabsidebarRighttab routing
How a DSH plugin extends the right sidebar: one surface per session, (kind, address) identity, seven fields, and a three-tier ordered claim.

A DSH plugin does not "add a panel" to the right sidebar; it registers a tab type into a dock surface that has identity, routing and lifecycle. Three concepts decide everything: each session has exactly one dock surface (stored in a session-scoped slot store and restored after refresh); tab identity is (kind, address) (same type and address reuse, different types coexist); and routing is an ordered claim (priority tier, then longest pattern, then registration order). Layer keepMounted for lazy keep-alive and canOpen for a synchronous veto, and you control both "who may open this address" and "does the body survive a switch."

DSH plugin right sidebar: one dock surface per session

Bottom line: one surface per session, stored in a session-scoped slot store, restored after refresh.

  1. Composition: a column of panes and tabs beside the conversation, with address-addressed content that can open, split, float and close.
  2. Ownership: the sidebar package owns the surface, the tab type registry and the navigation service; the layout engine is an internal dependency.
  3. Root controller: keeps separate seats for the selected session and for background sessions with initialized keepMounted bodies, but only the foreground session is visible.
  4. Change records: every surface change is one history record from a pure planner.
  5. No empty panes: an empty root pane is automatically filled.

DSH plugin addresses and tab identity

Bottom line: callers supply resource addresses, the sidebar writes page addresses, and identity is (kind, address).

Address familyFormGenerated by
Resource addressdsh-resource://<type>/…Callers such as the file tree
Page addresssidebar://<kind>The sidebar's openTab(kind)
  1. Resource scoping: the file protocol starts with session/<id> or absolute.
  2. Do not assemble page addresses: use openTab('guide') or openTab('files').
  3. Identity rule: same address through the same type reuses a tab; through two types it creates two.
  4. contentId: the claim records the address verbatim.
  5. No other navigation addresses exist.

DSH plugin pane-tab registration: seven fields

Bottom line: the definition is static, with no runtime hooks and nothing per-tab or per-session.

FieldPurpose
idUnique identity, naturally the package name; body and title seats register under it
kindType discriminant, not unique; built-ins are guide, text and files
patternsOptional resource address globs; page types omit them
priorityextension (highest by default) / builtin / fallback
canOpenSynchronous veto called on every routing decision
titleChip text captured at open time and never rewritten
keepMountedLazy keep-alive across hide, session and dock switches
  1. Register inside an effect: ctx.sidebarRightTabs.register(definition).
  2. Duplicate ids throw.
  3. Glob details: patterns containing : match the whole address; others match only the URL path portion at any depth; case-insensitive, dotfiles not hidden, picomatch POSIX syntax.
  4. Extension takeover: one kind may hold both a builtin and an extension, and the builtin returns when the extension unregisters.
  5. Guide entry: an optional guide box that opens the contributing type as a page.

DSH plugin routing: an ordered claim

Bottom line: tier, then longest matching pattern, then registration order; no claimant throws as a wiring error.

  1. Candidates: candidates(address) filters matching types not vetoed by canOpen.
  2. Sorting: priority tier first, then longest matching pattern, then registration order.
  3. Claim: claim(address, kind?) takes the first candidate; a named kind skips globs but still honors canOpen.
  4. Result: { kind, contentId: address, title }.
  5. Unclaimed addresses throw: that means no type can handle the address — a wiring error, not a user action.

DSH plugin keepMounted and the guide

Bottom line: keep-alive applies only to visited bodies; guide boxes are a type's own entry points and take no tab.

  1. Scope: visited bodies survive hiding, session switches and dock switches while their View holds the session reference.
  2. No pre-mounting: unvisited bodies never render just because keepMounted is set.
  3. Pairing with resources: the sidebar pins an open tab's address for the tab's lifetime, so switching tabs does not close streams.
  4. Guide shape: { order, title(), description?, icon? }; clicking opens the type as a page.
  5. Verify: switch away and back to confirm the body survives, and confirm new tabs are not kept alive.

DSH plugin right sidebar troubleshooting

Bottom line: separate no claimant, wrong claimant and body not kept alive.

  1. Opening throws — no type claims the address; check patterns and canOpen.
  2. Wrong type opens — check priority tier and pattern length; extensions beat builtins by default.
  3. Two tabs for one address — two kinds opened it; identity is (kind, address).
  4. State lost after switching — keepMounted is off or the body was never visited.
  5. Duplicate registration — the same id is registered twice.
  6. Repair status, stated honestly: address rules, registration fields, three-tier ordering and keep-alive semantics follow the official Right sidebar page; confirm extension slots and built-in types against that page and the package README.

Sources: Right sidebar, dsh-client-ui-sidebar-right README, Client resources.

FAQ

What determines the identity of a DSH plugin right sidebar tab?

A DSH plugin right sidebar tab's identity is the (kind, address) pair: the same address opened through the same type finds the existing tab, while the same address through two types produces two tabs. The claim uses the address verbatim as contentId.

How do resource addresses and page addresses differ in the DSH plugin sidebar?

A DSH plugin resource address is a dsh-resource:// URL whose host is a protocol, while a page address is sidebar://<kind> written by the sidebar itself during openTab(kind). Callers never compose page addresses.

Can an extension take over a built-in DSH plugin tab kind?

Yes: one DSH plugin kind can have both a builtin and an extension registration, and the extension wins for claims, get, openTab and the guide. When the extension unregisters, the builtin comes back without re-registration.

How does the DSH plugin sidebar choose among several matching tab types?

DSH plugin claims sort by priority tier (extension, builtin, fallback), then by the longest matching pattern, then by registration order. Registration order only matters when the first two tie and carries no other semantics.

When does keepMounted keep a DSH plugin tab body alive?

DSH plugin keepMounted is lazy keep-alive: an already-visited body survives hiding, session switches and dock switches while its View holds the session reference. It never pre-mounts a body that was never visited.

Related Terms

sidebar://<kind>
sidebar://<kind> is the DSH plugin right sidebar page address written by the sidebar during openTab(kind), used for tabs opened by type rather than by resource. Callers should not assemble it.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/sidebar-right
tab identity (kind, address)
DSH plugin tab identity combines the type discriminant and the address: the same type and address reuse one tab, while different types for the same address produce two tabs.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/sidebar-right
keepMounted
keepMounted is an optional lazy keep-alive for a DSH plugin sidebar tab type: an already-visited body survives hiding, session switches and dock switches while its View holds the session reference.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/sidebar-right
canOpen
canOpen is the synchronous veto in a DSH plugin registration definition, applied to glob matches and called on every routing decision. Returning false skips the type even when its pattern matches.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/sidebar-right

Sources