Iambatman1928/dsh-xingye ↗★ 0

dsh-xingye

将 DSH 会话转为本地聊天 Agent 的插件 适合希望在本地创建多角色人设、分线聊天且注重隐私保护的用户。

套件
dsh-xingye
相容性
待驗證
Harness 依賴範圍
>=0.1.0-rc.1 <0.1.1 || >=0.1.1-rc.1 <0.1.2 || >=0.1.2-rc.1 <0.2.0-0
Cordis 依賴範圍
^4.0.2
版本
1.0.0
授權
MIT
最近更新
2026年9月16日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Iambatman1928/dsh-xingye

Xingye (星野) · a chat agent for DSH

English | 中文

test

Xingye turns one DeepSeek Harness session into a chat agent. Install it, open a new session with the 星野 agent preset selected, and you are talking to Xingye herself. Inside her session you can create other characters, write their personas, give one character several separate archive threads, and keep several personas for yourself to switch between. Everything stays on this machine — nothing is sent to a server beyond your own model calls.

Characters · Create · My personas · Conversation · Archives

Install

dsh plugin --profile web add 'github:Iambatman1928/dsh-xingye'

Then restart DSH and:

  1. open Settings → Plugins and confirm dsh-xingye is listed;
  2. open a new session and pick the 星野 agent preset — this step is not optional, the preset is what gives that session its identity and its tool set;
  3. just start talking. Xingye greets you with her built-in persona.

A character pill (avatar + name + current archive) sits in the session header — click it to open the full-page character manager.

Why does one install command still need a preset step? DSH agent presets are discovered from the filesystem only; there is no plugin registration API for them. So on first load this package copies its bundled preset to /.agent-presets/xingye/ ($DSH_HOME defaults to ~/.dsh). That copy happens only when the target is missing, never overwrites a preset you have edited, and logs exactly what it did. To opt out, set installPreset: false on this plugin's row and copy node_modules/dsh-xingye/preset/ to ~/.dsh/.agent-presets/xingye/ yourself.

A prebuilt tarball is attached to every release, which skips pnpm's build-approval step:

dsh plugin --profile web add 'https://github.com/Iambatman1928/dsh-xingye/releases/latest/download/dsh-xingye.tgz'

The interface

Five pages, built from four design mockups — colours, radii, spacing and shadows are copied from them (warm white #fffbf7, brand purple #8b5cf6, 16px card radius, 56px avatar, 64px nav).

PageHow to reach itWhat it does
Charactersclick the character pill in the session headerone card per character: avatar / name / tags / last line; click a card to switch and keep chatting; the "N archives" control opens the archive page; "+" starts a new character
CreateCharacters → "+"avatar upload, name, blurb, personality, opening line, tags; on save Xingye really creates the character and speaks the opening line
My personasbottom nav "personas"current persona card plus the pickable list; create one; clear it
Conversationclose the full pageavatar plus white left bubbles (Xingye) and purple right bubbles (you); the "+" beside the input is the story menu (retry / rewind / restart / recap / undo)
Archives"N archives" on a character carda character's several conversation threads: switch and continue, start a new one, delete the character (two confirmations)

Leaving a full page: tap the active tab again, press Esc, click the scrim on tablet/desktop, or use the "⌄" in the header.

Responsive: phone (/xingye-data/ by default ($DSH_HOMEdefaults to~/.dsh`):

xingye-data/
  index.json                     global default: last character / archive / user persona
  config.json                    retention policy (hand-editable)
  sessions.json                  session id → character / archive / user persona binding
  ui-state.json                  snapshot the UI reads (written by the plugin)
  characters//
    character.json
    persona.md                   persona text
    source/                      original copies of imported files
    avatar.* portrait.* background.*   character art (drop a file in and it is used)
    archives//
      archive.json
      messages.jsonl             transcript — the source for rewind and undo
      summary.md                 summary of older turns after cleanup
      trash.jsonl                undone content (recoverable)
      eventbook.jsonl            event book
      branches/
.json     cut story saved as a branch
  user-personas/
/
    persona.json
    persona.md

Back up by copying xingye-data; start over by pointing dataRoot at an empty directory.

Changing the data root: this package's cordis.patch.yml and the agent preset's row config each carry a dataRoot. Left unset, both fall back to /xingye-data. To move it you must set both, or the UI reads a different directory than the agent writes.

Image generation (optional)

Configure an OpenAI-compatible endpoint on the agent preset row:

- id: xingye
  name: './plugin/index.js'
  config:
    image:
      baseURL: 'https://your-endpoint/v1'
      apiKeyEnv: 'YOUR_API_KEY_ENV'
      model: 'your-image-model'
      size: '1024x1024'

With model empty it stores prompts without generating. The key is read from the environment and never written to disk.

Known limits

DSH's session log is append-only: there is no API to delete, truncate or roll back historical events, and no deletion path for session.jsonl.zstd on disk.

So the precise meaning of "undo the last 30" is:

  • the archive's local record is genuinely rolled back — that data belongs to Xingye, so deleting it deletes it (recoverably);
  • the model receives an explicit invalidation notice — the undo tool's result stays in the conversation, telling Xingye those turns are void, not to quote them, and to wait for you to say it again;
  • but the original events still exist in DSH's session log (they are simply declared void from that turn on).

Other limits:

  • The preset must be the right one. In a session running a different preset the UI still renders, but its actions cannot reach the agent — Xingye's tools are not in that session.
  • The UI is a full-frame overlay and therefore sits above every session. When it is not bound to a Xingye session the pages stay readable but the buttons do not send commands, and the header says why — deliberately, so a line like "use this persona" can never be injected into an unrelated session.
  • Editing client.js requires a page reload; editing the Host half (index.js) requires a DSH restart.

Layout

dsh-xingye/
├─ index.js             Host half: /xingye/api routes + preset materialisation
├─ client.js            Client half: the full pages, character pill and story menu
├─ cordis.patch.yml     bundle patch that registers the plugin in a profile
├─ preset/              the bundled `xingye` agent preset
│   ├─ agent.cordis.yml session composition: identity + tool list
│   ├─ preset.yml       display name and blurb
│   └─ plugin/          17 `xingye_*` tools (zero dependencies, `node:` builtins only)
└─ tools/               three offline test suites

The division of labour: the preset half is the backend (characters, archives, undo, event book — all on the agent plane) and the profile plugin half is the frontend (the UI plus a local route that hands UI actions to the session). The UI reimplements no business logic, so there is exactly one source of truth — which is why the code under preset/plugin/ may only import node: builtins: a preset directory is not on the deployed package's node_modules resolution chain.

Development

node tools/verify.mjs                  # preset plugin, full flow (fake ctx, no DSH needed)
node tools/preset-install-test.mjs     # bundled-preset materialisation, in a throwaway $DSH_HOME
node tools/ui-client-render-test.mjs   # client half rendered offline (real React + a mini runtime that runs effects)
node tools/ui-host-route-test.mjs      # host half routes (fake req/res)

The UI tests read client.js from this repository root and look for React under ~/.dsh/profiles/*/node_modules and similar locations. Point them elsewhere by passing arguments: node tools/ui-client-render-test.mjs .

License

MIT. Not affiliated with DeepSeek.