DeepSeek Harness plugin

dsh-move-session

Cross-workspace session migration for the dsh web GUI: a Move Session header action plus dialog (target workspace picker, keep-or-archive the original), idle-session only, full context preserved.

Jump to install

Source facts

Repository
hucj09/dsh-move-session
Latest update
Aug 16, 2026
Category
Memory
GitHub stars
1
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/hucj09/dsh-move-session
Plugin: dsh-move-session
Author: hucj09

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.en.mdSource · read only
README language

dsh-move-session

English | 中文

Cross-workspace session migration plugin for the dsh Web GUI. Adds a Move Session entry to the session operations: copy the current session as-is into another workspace (full conversation log, title, agent preset and model selection preserved), with a choice to keep the original or archive the original. Only idle sessions can be moved.

> Current version v0.1.3. Same hot-pluggable convention as dsh-ssh / dsh-task-board: > mounted via cordis.patch.yml + a profile node_modules install, no dsh source changes. > Zero-dependency plain JavaScript, source-as-artifact (lib/ is the runtime code), no build step.

---

Features

RequirementImplementation
1. "Move Session" among session operationsMove Session button in the session header action row (icon + label, alongside agent preset / subagent catalog / job list); ② a Move Session entry injected into the sidebar session-row "…" menu (after rename / fork / archive; installed package only)
2. Move as-is to another workspace, full contextThe complete event log is copied verbatim (messages, tool calls, title, agent-preset/selected, request/header and every other event), recorded as a new session in the target workspace
3. Keep or archive the originalRadio choice in the dialog: Keep the original session (default) or Archive the original session
4. Proper UI interactionIcon+label button → modal dialog (target workspace picker + mode picker + error/success states), auto-navigate or offer to open the moved session; all text follows the GUI language (zh/en)

Other details:

  • Idle-only: while the session is running the button is disabled and the host re-validates.
  • Cross-tab refresh: the copy is published via agents.create (same path as the shipped fork),

which pushes host/session-added; attachSession pushes host/workspace-changed; archiving pushes host/archived-sessions-changed — every tab's sidebar updates instantly.

  • Model & preset inheritance: agentOptions come from the last request/header in the source

log (better than the shipped fork's "current default model"); the agent world is composed from the source's preset (last agent-preset/selected event, else header.agentPreset).

  • Lineage preserved: the copy header records parentSession = source id and

seedLength = full event count, matching the shipped fork lineage semantics; timestamps and event order are preserved byte-for-byte.

  • Session-safe: the copy gets a fresh id (session-mv-<time36>-<seq36>); nothing is

overwritten or deleted; keep-mode leaves the source untouched.

  • Same-name distinction: only when the target workspace already holds a session with the

same title is the copy's title suffixed with [MS<n>] (e.g. My session [MS1]); n is one above the highest [MS<n>] already present there, so repeated moves ascend without colliding and re-moving an already-marked copy replaces the marker instead of accumulating. No same-name means the title stays untouched; untitled sessions are left alone. Clearly different from the fork numbering (1) / (1).

  • Empty workspace support: the move does not depend on the target workspace having any

sessions — a freshly added workspace is a valid target (its directory must exist).

![Move session example](docs/images/example1.png)

---

Install

Option 1: after npm release (recommended)

dsh plugin --profile web add @hucj/dsh-move-session

Option 2: local path (development)

git clone git@github.com:hucj09/dsh-move-session.git   # or use the existing source directory
cd dsh-move-session
npm run check     # syntax check + all unit/structural tests (zero build — lib/ is the artifact)

Then install:

dsh plugin --profile web add link:/path/to/dsh-move-session

link: installs a symlink: after changing the source, re-run npm run check and restart dsh. (file: installs a one-time copy instead; later source changes are not synced automatically.)

After installing, restart dsh web (new bundles only load on next start) and hard-refresh with Ctrl+F5; open any session — the header action row showing the "Move Session" button means the install succeeded.

> Note: dsh.client.inject is empty; the plugin itself declares > inject: ['slots', 'sessions', 'locale'] as hard dependencies, so the host needs the standard > web runtime (@deepseek-ai/dsh-client-runtime etc., present in default deployments).

Uninstall

dsh plugin --profile web remove @hucj/dsh-move-session

The command does three things: 1. removes the dependency from dependencies 2. removes the bundle row from dsh.profile.bundles 3. deletes the node_modules/@hucj/dsh-move-session install directory

Then restart dsh web.

---

Usage

1. Open an idle session. 2. Click the Move Session button in the session header action row (the sidebar session-row "…" menu also has a Move Session entry). 3. Pick the target workspace (the current workspace is excluded; path and session count shown). 4. Choose what happens to the original: - Keep the original session (default): the source stays untouched, a full copy is created in the target workspace, with an "Open moved session" action; - Archive the original session: the source enters the archive set (hidden from all grouping surfaces; log and accounting retained) and the view navigates to the moved session. 5. The sidebar updates instantly; the copy's messages, tool calls, model and preset match the source; the title matches unless the target workspace already holds a same-named session, in which case a [MS<n>] marker is appended (e.g. My session [MS1]).

---

How it works (brief)

  • Host half (lib/index.js): a loopback-only HTTP route POST /api/dsh-move-session/move,

mirroring the shipped session.fork handler step by step: idle check → flush + read the full log → target/same-workspace validation → target directory pre-check (via the fs service; a stale registry entry is rejected before any write) → mint a new identity (fresh id + target cwd + lineage) → agents.create publishes the copy (seed = all events, setup composes the source preset via agentPresets.resolve/mount) → same-name title suffix (sessionQuery reads the target titles, sessionTitle.rename appends [MS<n>] on collision) → attachSession accounting → archiveSession on request.

  • Browser half (lib/client.js): a standard web plugin bundle registering into the

conversation.session.header.actions button slot and the shell.overlay dialog slot (both official additive slots, replaceRisk: none); the sidebar row-menu entry is injected via ARIA role anchors (the official menu is a portaled [role="menu"]; the session id is recovered from the row's React fiber) — no dependency on official CSS class names.

  • Log integrity: migration copies the full log event by event; run

npm run test:integrity to verify any source/copy pair (real-data check: 1042 events + 212 chunk records preserved 100%).

---

Development & testing

npm run check          # syntax check + all unit/structural tests (node --test); must be green before commits
npm run test:watch     # dev mode: re-runs all tests automatically on file changes
npm run test:ui        # Playwright interaction tests (dialog + row-menu injection + theme; needs a local browser)
npm run test:integrity # real migration log event-level consistency check (Python)

Automated gates: .githooks/pre-commit runs npm run check automatically before every git commit (a failing gate blocks the commit); .github/workflows/ci.yml runs npm run check on every push/PR to GitHub.

Collaboration rules for maintainers and AI assistants live in AGENTS.md (versioning, testing, commits, code invariants); release history in docs/CHANGELOG.md.

---

Error codes

codemeaning
invalid-session / invalid-target / invalid-modemissing or invalid request parameters
unavailablerequired host services not mounted (agents / sessionPersistence / workspaceRegistry)
session-busysession is running; only idle sessions can be moved
session-not-foundsession absent from session persistence
target-not-foundtarget workspace does not exist
same-workspacesession already belongs to the target workspace
target-missing-dirtarget workspace directory missing or stale (e.g. a temp directory cleaned up); rejected before any write — no orphan copy
preset-unavailablesource agent preset cannot be resolved (no writes happen)
copy-failedcopy creation failed (incl. unbalanced-log seed validation)
attach-failedcopy created but workspace accounting failed (same semantics as the shipped fork)
internalunexpected error

---

Limits & boundaries

  • Only idle sessions can be moved; the client disables the button while running and the host

rejects as well.

  • The source log must be balanced (no open turn/step or dangling tool call) — idle sessions

satisfy this naturally; otherwise the copy creation is rejected by dsh's seed validation (same strictness as the shipped fork).

  • The session id changes (session-mv-*): a necessary consequence of copy semantics — dsh keys

persistence by id, and a duplicate id across workspaces would corrupt lists/accounting. Lineage is kept via parentSession.

  • The copy stays live (idle agent) in memory, like a shipped fork child; it does not die with this

plugin.

  • Only the session log is copied; attachments/files are unaffected (attachments are read on

demand; the references in the log are preserved).

  • "Archive the original" uses the registry-global archive set (same mechanism as the sidebar

archive action); the unarchive position is retained, but this plugin provides no unarchive entry (consistent with the official UI).

---

License

MIT