← Back to home@Stijnus

dsh-move-to-workspace

Move and organize DeepSeek Harness conversations across workspaces. Copies history to the target workspace and archives the original.

Stars
1
Language
JavaScript
Created
Oct 25, 2026
Updated
Oct 26, 2026

Introduction

dsh-move-to-workspace

English · 中文

npm version npm provenance release license

Move an existing DeepSeek Harness conversation into another workspace from the sidebar.

Open a conversation's … menu and choose Move to workspace…, then pick a target workspace — or register a new folder from the same dialog with New workspace… — and confirm. The conversation appears under the target workspace with its full history and title, continues there, and the original is archived in its old workspace (restorable with Unarchive).

Install · Walkthrough · Move behavior · Limits

Try it on a test conversation

After installing, use a disposable conversation with at least one message and two existing workspace folders:

  1. Wait for all turns and background work to finish.
  2. Open the conversation’s … menu, choose Move to workspace…, select the other workspace, and confirm.
  3. Check its history and title in the target workspace; the original should appear among archived conversations in the source workspace.

This is a local walkthrough, not a hosted demo. A move to a different folder creates a new session ID and leaves the original archived. It moves conversation history; it does not copy project files. Review Consequences before using it on ongoing work.

Quick start

dsh plugin --profile web add dsh-move-to-workspace

Restart the Harness (or refresh the Web UI). Each conversation's … menu gains Move to workspace….

More from this suite: Moodboard · Composer Predictions · Move to Workspace · Workspace Status · Prompt Rewrite

⭐ If this saves you a step, a star helps the next DSH user find it.

What the move does

DSH binds a conversation to a workspace through the directory the conversation started in: Workspace.attachSession validates the Session header's cwd against the workspace path, and persisted session logs are append-only. An existing conversation therefore cannot be re-pointed at a folder in place.

This plugin performs the supported equivalent:

  1. checks for running turns, subagents, background jobs and scheduled tasks,
  2. copies the complete event log into a new Session and flushes it durably,
  3. attaches the copy to the target workspace and checks that the source transcript has not changed,
  4. archives the original, restores its pin on the copy and announces the new Session.

Copy or attachment failures leave the original visible. If the source changes or starts work during copying, archival is refused; an already attached copy remains available alongside the original.

When the conversation's recorded directory already is the target workspace directory (for example an ungrouped conversation that ran there), the plugin attaches the existing Session instead of copying it, so the ID is preserved.

Consequences

  • The moved conversation has a new session ID. Links, running jobs, and scheduled tasks referencing the old ID stay with the archived original.
  • The original log stays on disk (archived, hidden by the default filter). It is not deleted; DSH has no session-deletion primitive.
  • The archived original keeps its position under the old workspace, so unarchiving returns it there rather than to the target.
  • Copies made with this plugin are ordinary Sessions: no fork marker and no invented parent link for a top-level conversation.

Choosing the target

The dialog lists every workspace except the one that currently owns the conversation. New workspace… opens the composed directory picker (the native macOS/Windows folder chooser in Desktop), registers the chosen directory as a workspace, and selects it as the target. If the chosen directory is already this conversation's workspace, the dialog says so instead of moving.

Install

From the plugin market / a profile:

dsh plugin --profile <profile> add dsh-move-to-workspace

Or, from a local checkout, install the directory as a bundle: sidebar → Plugins → Add plugin → the absolute path to this directory →

dsh plugin --profile <profile> add /absolute/path/to/dsh-move-to-workspace

A new browser half may need a page reload (⌘R) to appear. Remove it with dsh plugin --profile <profile> remove dsh-move-to-workspace.

Release provenance

Releases are built and published by GitHub Actions through npm trusted publishing (OIDC): the workflow exchanges a short-lived GitHub OIDC token for npm publish rights, so no npm token or secret is stored in the repository or in GitHub. Every published version carries a SLSA provenance attestation (https://slsa.dev/provenance/v1).

Verify the latest release:

npm view dsh-move-to-workspace@latest dist.attestations
npm audit signatures   # inside a project that installed the package

The attestation is visible on the npm package page and served by the registry at https://registry.npmjs.org/-/npm/v1/attestations/dsh-move-to-workspace@<version>. The publishing workflow is .github/workflows/release.yml; the publisher configuration and release steps are in RELEASING.md.

Rename a conversation

The row menu's Rename row is provided by the plugin and opens a confirmation dialog with the current title prefilled. The new title is saved only after you confirm; Cancel leaves it untouched. An empty title is refused, and a successful rename updates the sidebar row immediately.

Delete a conversation

The row menu's Delete conversation… sits next to Archive and permanently removes the stored session log. It always asks first: the confirmation names the conversation, warns that the deletion cannot be undone, and points at Archive as the recoverable alternative.

On confirmation the Host detaches the conversation from its workspace, clears its archive and pin membership, deletes the log directory and its projection-cache row, and removes the row from connected Clients. Deletion is refused with a stable error code while the conversation is currently open (a live Session holds the log) and for subagent child sessions. There is no undo, and unlike Archive the history is gone for good.

Organize conversations

Choose Organize conversations… from any conversation's row menu. The plugin builds a dry-run plan for every current (non-archived, non-subagent) conversation:

  • a conversation whose recorded folder is a project folder is grouped into a workspace at that folder — reused when one is registered, created when not;
  • a conversation that started in a catch-all workspace (such as the default workspace, which contains other workspaces) is matched by an unambiguous title/folder-name match against project folders under that workspace and under ~/Developer/GitHub.

The preview lists each workspace group with CREATE or REUSE, its path, the conversations it would receive, and whether each conversation is moved (copied, new session id) or attached in place (same folder, same id). Deselect any group before confirming; nothing changes until you press Organize. Conversations that are already correctly grouped, ambiguous matches, and generic locations like home or /tmp are counted as staying put rather than guessed.

Applying uses the same copy-attach-archive operation as Move to workspace…, so moved conversations keep their history and title, and the originals are archived in their previous workspace.

Files

FileRole
package.jsonBundle manifest (dsh.bundle.patch) and the browser half (dsh.client).
cordis.patch.ymlInserts the Host row move-to-workspace.
index.jsHost half: the POST /move-to-workspace endpoint, the copy/attach/archive operation, and the Session announcement.
client.jsBrowser half: the session row action, the workspace picker dialog, and its create-a-workspace row.
locale/en.json, locale/zh.jsonPlugin Manager title/description.
test/Node test-runner checks for the Host contract, the endpoint guards, and the Client registrations.
scripts/registry-doctor.mjsReports and repairs a workspace registry left with duplicate folder rows or conversations owned twice (§ Repairing a broken registry).
market-index.jsonFormat-1 market index for Discover; versions is filled after the first npm publication.

Endpoints

All endpoints are same-origin and require the owner session; failures answer {"ok": false, "code": "...", "message": "..."}.

EndpointBodyResult
POST /move-to-workspace{ sessionId, workspaceId }{ ok, sessionId, workspaceId, copied }; running work refusals include activity entries (turn, subagent, job, schedule).
POST /organize{ mode: "plan" }{ ok, plan } — workspace groups marked create/reuse, per-conversation move/attach, and the counts of conversations that stay put.
POST /organize{ mode: "apply", targets: [{ path, sessionIds }] }{ ok, workspaces, sessions } with per-workspace and per-conversation results.
POST /delete-session{ sessionId }{ ok, sessionId, removedLog, logDirectory }; refuses live Sessions and subagent children.

Repairing a broken registry

0.1.10 fixes this. Versions 0.1.4–0.1.9 could register a second workspace for a folder that was already registered under another path spelling (a symlink next to its target), and the new row adopted the folder's conversations. One conversation owned by two workspaces makes the stored registry inconsistent, and @deepseek-ai/dsh-workspace refuses to load that state — the app then shows no workspaces and no conversations at all, with no error on screen.

Conversations are still on disk. Quit the app (the Host rewrites the registry while it runs) and run:

node scripts/registry-doctor.mjs          # report only
node scripts/registry-doctor.mjs --fix    # repair, after writing a backup

It collapses folders registered more than once onto the row registered under the resolved path, and leaves each conversation with exactly one owning workspace. It touches nothing else: no conversation log, projection cache, archive or pin state is changed. Reopen the app afterwards. The same check runs on demand: npm run registry-doctor.

Limits

  • Refuses archived conversations, subagent conversations, blank conversations, and conversations with no recorded directory.
  • The move needs the target workspace directory to exist. New workspace… needs a composed directory picker; without one the dialog reports that workspaces cannot be created from it.
  • The copy runs in-process on the Host thread; very large transcripts make the request proportionally longer.

Development

npm test
npm pack --dry-run

License

MIT