← Back to home@elonnzhang

dsh-expert-ai

No description

Stars
0
Language
TypeScript
Created
Aug 19, 2026
Updated
Oct 20, 2026
GitHub repo

Introduction

dsh-expert-ai

English | 中文

dsh-expert-ai keeps a library of expert identities (persona, avatar, source disclosure) for DeepSeek Harness and serves it to other plugins through the expertIdentity Cordis service. It is the identity layer meant to run together with the dsh-bot runtime, and it adds exactly one other thing: a conversation with a single expert.

Features

The plugin does two things:

  • Identity layer: persists records in the expert-ai settings namespace and each identity in $DSH_HOME/expert-ai/<id>/agents/<id>.md; provides the expertIdentity Cordis service so another plugin, such as a Bot runtime, can read the enabled identities: persona, avatar, source disclosure and revision.
  • Single-expert conversation: pick an expert in the sidebar Chat Panel, or type @ in the composer and pick one, to open a conversation bound to that expert's persona. It has no Workspace, is forced to the read-only file sandbox, and gets its opening message from the plugin.
  • Expert mention and consultation: @<id> in the composer still saves the mention as model-visible text, and the primary agent can ask one expert through the consult_expert tool, whose answer comes back through a persona subagent.

It also adds a Harness-styled settings section for creating, editing, enabling, and deleting records, including imports from agent Markdown files (YAML frontmatter is ignored, the body is the persona).

It does not add an Agent preset selector, online expert-package import, or group chats (the dsh-bot runtime owns those).

Working with dsh-bot

This plugin only supplies identities; the dsh-bot runtime runs the single and group chats. Install both into the same Profile.

  • dsh-bot asks for the expertIdentity service with ctx.inject(['expertIdentity'], …). Without this plugin it behaves like a plain ds-bot, and this plugin needs nothing from dsh-bot.
  • Identities are created and edited only here, under Settings → 专家. dsh-bot is designed to take a bound Bot's persona and source disclosure from the service and to follow expert-identity/changed.
  • This plugin registers no session tools and binds no persona to Harness's own Sessions, so a Bot Session carries one identity.
  • A linked checkout is served from lib/, which is not committed: run pnpm run build after changing the Host half.

The split of responsibilities and the service contract are in docs/expert-bot-phase1.md.

Demo

Chat PanelSettingsCreate expert
Expert Chat PanelExpert settingsCreate expert form

Record fields

Each record has an id, name, description, primary-domain category, composable tags[], agent, persona, and enabled flag. agent is the only runtime identity source. When no Agent file is supplied, saving a record writes persona to the managed <id>/agents/<id>.md file and stores that relative path in agent; when an Agent file is supplied, its Markdown body is authoritative and persona is ignored. Relative agent paths are read from $DSH_HOME/expert-ai/ first and then from the plugin package, so bundled experts/ files work in a fresh Profile; a same-named file under $DSH_HOME/expert-ai/ overrides the bundled copy. New records using the custom category should set customCategory; older records without it fall back to the display name. The Host validates the settings document; leftover features, teams, and maxBatchSize from older settings are ignored. toolFilter and model are optional: they pick the tools and the model a consultation runs on.

Presentation fields are optional, so records saved before they existed stay valid:

  • title (at most 40 characters after trimming) is shown after the name and defaults to “思考助手”.
  • avatar is { mime, data } with an image/png, image/jpeg or image/webp mime and base64 data; the whole data URL must fit in 160,000 characters. The editor crops a picked image to a centered 160 px square and re-encodes it.
  • source is { kind, note? } with kind one of public, organization or authorized; note holds at most 200 characters. A record without source counts as public, and the kind selects the disclosure the identity carries.

expertIdentity service

Another plugin reads identities with ctx.inject(['expertIdentity'], ctx => …):

  • list() returns the enabled identities whose persona can be read. get(id) returns one with its persona and disclosure, or undefined for an unknown, disabled or unreadable id.
  • revision is the sha256 of the persona. A change of title, avatar or source does not alter it; it arrives through the changed event and a fresh get.
  • disclosure is a Chinese paragraph chosen by source.kind, for the consumer to put into its prompt as is.
  • expert-identity/changed carries { ids, withdrawn? }. ids are the sorted ids that appeared, disappeared or changed. withdrawn (sorted, absent when empty) lists those whose record the user disabled or deleted, and ids still contains them; an identity whose persona merely cannot be read is not withdrawn. Listeners are not awaited, and nothing is emitted at startup.
  • The types ExpertIdentity, ExpertIdentitySummary, ExpertIdentityService and ExpertIdentityChange are exported.

Install from GitHub

dsh-expert-ai is installed into a DeepSeek Harness Web Profile. The commands below are run from the Harness checkout, not from the plugin repository.

Prerequisites

  • A working DeepSeek Harness checkout
  • Node.js and pnpm versions supported by that Harness checkout
  • A GitHub URL for this repository

Install

cd /path/to/deepseek-harness
pnpm dsh plugin --profile web add https://github.com/elonnzhang/dsh-expert-ai.git

The command installs the plugin into $DSH_HOME/profiles/web, adds its bundle to the Web Profile, and applies its Host and Client halves on the next boot.

Start the Web Profile:

pnpm dsh web --port 3080

Open http://127.0.0.1:3080, then go to Settings → 专家.

GitHub installs run the package's prepare script and build lib/ from source. For a reproducible release, run the checks locally before pushing:

cd /path/to/dsh-expert-ai
pnpm install
pnpm run check
git add .gitignore .npmrc src scripts docs package.json pnpm-lock.yaml tsconfig.json tsconfig.types.json tsdown.config.ts tsdown.client.ts cordis.patch.yml README.md README.zh.md
git commit -m "build: update plugin contract"
git push

With pnpm 10+, a GitHub install may require an explicit allowBuilds entry in the target Profile; see docs/publish.md.

Update or uninstall

From the Harness checkout:

# Update the installed GitHub dependency
pnpm dsh plugin --profile web update dsh-expert-ai

# Remove it from the Web Profile
pnpm dsh plugin --profile web remove dsh-expert-ai

Restart pnpm dsh web after installing, updating, or removing the plugin.

Upgrading from a release that offered run modes

The features run-mode switch is gone: the identity layer, the Chat Panel and the @ expert source are always on, and the expert-ai-mode Agent preset, online expert-package import (expert/catalog, expert/import) and the category/review/team features before them have been removed. No data migration is needed. Stop the Profile and drop the fields that are no longer read from the plugin's patch row:

- id: expert-ai
  name: dsh-expert-ai
  config:
    stateDir: …        # keep
    provider: spawn    # keep: the subagent provider for consult_expert
    records: […]       # keep
    # no longer read; safe to delete
    features: [identity, conversation]
    teams: […]
    maxBatchSize: 3

The data directory is still $DSH_HOME/expert-ai. Its .expert-packages/ directory holds packages imported online by an older release; projects/ and sessions/ keep working. History you no longer need can be deleted:

rm -rf "${DSH_HOME:-$HOME/.dsh}/expert-ai/.expert-packages"
rm -rf "${DSH_HOME:-$HOME/.dsh}/expert-ai/.expert-reviews"

Local development

Link the checkout into the profile once, then iterate against the running server:

cd /path/to/dsh-expert-ai
pnpm install
pnpm run check

cd /path/to/deepseek-harness
pnpm dsh plugin --profile web add /path/to/dsh-expert-ai
pnpm dsh web --port 3080

Build

pnpm run build runs tsdown over both halves and then refreshes the declaration compatibility tree:

ArtifactEntryConsumer
lib/index.jssrc/index.tsthe Host, applied by cordis
lib/index.d.tsHost declarationsTypeScript consumers
lib/invariant.jssrc/invariant.tsexports["./invariant"]
lib/client.jssrc/client/index.tsthe browser loader, which requests the dsh-expert-ai/client.js bundle through the versioned combo URL in the boot manifest
lib/types/client/index.d.tsClient declarationsTypeScript consumers of ./client

lib/index.d.ts and lib/invariant.d.ts are generated by tsdown. build:types additionally emits the per-file lib/types/** declarations required by the dual-face Client export. The Client runtime entry itself remains a loader-registration artifact rather than a normal importable browser library.

The client artifact is not an ordinary browser bundle. The web shell's module loader requires it to register itself by calling window.__ModuleLoader__.load({ id, factory }); one that loads without registering fails plugin boot with bundle … loaded without registering "dsh-expert-ai". tsdown.client.ts produces that wrapper (CJS output inside a factory banner and footer), keeps the shell's module-table specifiers external so they survive as require() calls the loader can answer, and compiles every stylesheet into a style-injection module with hashed class names. Replacing it with a plain browser-bundle config breaks plugin boot rather than just the styling.

Hot reload

A running dsh web stat-polls every client bundle it serves, so rewriting lib/client.js is the entire trigger — no server restart and no page refresh:

cd /path/to/dsh-expert-ai
pnpm run dev

On a content change the Host broadcasts a rebuilt frame over its GET /plugins/events channel and the browser half swaps the cordis fiber in place: it drops the stale factory, refetches the bundle, drains the old fiber's disposers, removes the <style data-plugin="dsh-expert-ai"> tags, and re-applies. Component state inside the swapped subtree is lost; the rest of the page keeps running.

Two kinds of change stay outside that chain and need dsh web restarted: the Host half, because node-side plugin reloading is not mounted in the Web Profile, and cordis.patch.yml, because composition is read at boot.

Do not reach for the Harness's own pnpm run dev:web here. It is the watch build for packages inside the Harness checkout, it must not run concurrently with pnpm run build there, and it rebuilds nothing for an out-of-tree package.

For the package contract, local overlays, and distribution choices, see docs/build.md, docs/load-into-dsh.md, and docs/publish.md.

Usage

1. Configure experts

Open Settings → 专家 after the Web Profile starts. Add or edit a record with:

The plugin UI follows the Harness locale and provides complete English and Simplified Chinese dictionaries. Switching the application language updates Settings and the Chat Panel without translating user-authored names, descriptions, or Persona content.

  • ID: lowercase letters, numbers, and hyphens, for example zjh
  • Name: the display name, for example 张剑辉
  • Description: a short responsibility summary
  • Category: business, science, culture, or custom
  • Custom category: recommended when Category is custom
  • Tags: searchable capabilities such as 储能系统, 工程化
  • Persona: the initial identity and response rules; converted to a managed Agent file when Agent is empty
  • Enabled: only enabled records reach the expertIdentity service and the Chat Panel

You can also import an Agent Markdown file. Its YAML frontmatter is ignored and its Markdown body is the identity. A file selected through the browser is imported into the managed file because the browser does not expose a path that the Host can reopen. When no external path is entered, the Persona is written to:

$DSH_HOME/expert-ai/<id>/agents/<id>.md

An external Agent file remains the sole source of identity and is not copied or overwritten by the plugin.

The settings document is the Profile's Cordis patch, not $DSH_HOME/settings.yaml:

$DSH_HOME/profiles/<profile>/cordis.patch.yml

Saving on the settings page rewrites the expert-ai row of that file.

2. Chat with an expert

Type @ in the composer and pick an expert, or open Chat Panel in the sidebar and choose one; either way the plugin starts a new conversation with that expert. The plugin creates an isolated project under $DSH_HOME/expert-ai/projects/<datetime>, then creates a Session and binds the expert's persona on the Host:

  • The Session receives a record-specific system prompt through Host binding; the opening message is written by the plugin and does not repeat the full persona.
  • The Session is forced to the read-only file sandbox: files can be read, not changed.
  • The binding is saved under $DSH_HOME/expert-ai/sessions/, so reopening the Session after a Host restart keeps its persona.

3. Consult one expert from a conversation

With @<id> in the composer, the mention stays model-visible text and the primary agent decides whether a consultation helps. consult_expert(id, question) starts a persona subagent for that expert and returns its answer; the subagent provider is the 子代理 Provider setting (default spawn). The expert answers once, without follow-up questions.

4. Update the plugin

After publishing a new GitHub revision, build the plugin before pushing. Then update it from the Harness checkout:

pnpm dsh plugin --profile web update dsh-expert-ai
pnpm dsh web --port 3080

If the profile still uses an old linked installation, remove and add it again:

pnpm dsh plugin --profile web remove dsh-expert-ai
pnpm dsh plugin --profile web add https://github.com/elonnzhang/dsh-expert-ai.git

Model Experience

An expert conversation receives a record-specific system prompt and is set to the read-only file sandbox. The role instruction is carried by the system prompt and remains durable and model-visible in that Session.

consult_expert adds its schema to the primary agent's model request. A consultation starts one subagent, installs the expert's persona as the subagent persona, and returns the answer text.

Known Limitations

  • Expert conversations start in a new Session each time, either from the @ menu or from the Chat Panel; the current Session keeps only the @<id> mention text.
  • The Chat Panel lives in the sidebar footer action Slot: the current public sidebar has no additive Slot above the workspace browser.
  • Expert conversations use the deployment's default Agent preset; the plugin no longer installs one of its own.