DeepSeek Harness Plugin

PwnKY/dsh-session-link

Stars ★ 7 Downloads (30d) 1,085 Category Sessions & Messages Added 2026-08-14 npm dsh-session-link

Copy and open `dsh://` session deep links, or paste them into another conversation to inject a bounded, read-only snapshot of the referenced session.

Install

# from npm (prebuilt)

dsh plugin --profile web add dsh-session-link

# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)

dsh plugin --profile web add github:PwnKY/dsh-session-link

Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).

README

· 中文 · English

Codex-style session deep links for DeepSeek Harness (dsh)

Copy a link from any conversation, then paste it into a different conversation — the referenced session's context is snapshotted and injected as bounded, read-only background context right before your prompt. The same link also opens the conversation in the browser.

Install

# One command: installs the package, auto-joins the profile's bundle layer,
# and auto-applies the composition rows — no manual yml edits.
dsh plugin --profile web add dsh-session-link

# Restart the web GUI and refresh the page.
dsh web

The package declares a dsh.bundle patch (cordis.patch.yml); dsh plugin add detects it and adds the package to dsh.profile.bundles, so the session-link row composes automatically at boot. The upstream session-reference service is composed by the shipped web bundle since dsh 0.1.0-rc.8 — this package no longer re-inserts it (a duplicate id fails the boot).

Generic npm install (package only, not wired into a profile): npm install dsh-session-link Manual install (without the bundle mechanism): see Quick start.

┌─ session A ──────────────┐        ┌─ session B ──────────────────────┐
│  🔗 copy session link    │        │  user: please see this session:  │
│  → dsh://session/session-│ ─────▶ │         dsh://session/session-…  │
│    …abc                  │  paste │                                  │
└──────────────────────────┘        │  model: (receives snapshot of A  │
                                    │         + the prompt, with @label)│
                                    └───────────────────────────────────┘

Features

  • 🔗 One-click copy — a "Copy session link" button in the conversation header copies dsh://session/<sessionId> (codex:// / claude:// style).
  • 📖 Cross-conversation context — paste the link into any conversation; the source session's conversation is snapshotted (bounded, read-only) and injected right before your prompt.
  • 🖱️ Clickable deep links (Windows) — with the registered dsh URL protocol handler, clicking a dsh:// link opens the web GUI and selects that session.
  • 🛡️ Fail-open — malformed links, unreadable sessions, or self-references never break your turn; the link stays as plain text and the failure is logged.

How it works

The feature reuses the shipped @deepseek-ai/dsh-session-reference service, which already owns canonical session URIs (dsh-session:<base64url>), mention parsing, snapshot projection, and byte-budget retention. This package wires that service into the live agent loop and the web surface:

  • Host half (lib/index.js) — a cordis plugin subscribing to the agent/pre-step seam. When a claimed direct user prompt contains a session deep link, every supported link form is normalized into canonical dsh-session: mentions, parsed into structured references, snapshotted via sessionReferenceResolver.prepare(), and the aggregated read-only snapshot context is placed immediately before the direct prompt. The hook is transport-agnostic, so pasting a canonical URI into the TUI works the same way. Since dsh 0.1.0-rc.8 the service subscribes to agent/pre-step for canonical mentions itself; this listener runs outermost (prepend plus the sessionReferenceResolver injection) and only resolves the deep-link forms upstream does not know about (dsh://, web links), so one link never injects twice.
  • Browser half (lib/client.js) — a static client package (dsh.client declaration) rendering the copy button in conversation.session.header.actions and opening /?session=<sessionId> deep links (legacy /s/<sessionId> and #/s/<sessionId> forms included) by selecting the target session through ctx.uiWorkspace.openSession() once the catalog has loaded (bounded ~10 s retry, with a warning on final failure).

Link formats

Form Example Purpose
Deep link dsh://session/<sessionId> copied by the button; clickable via the protocol handler; parsed when pasted
Browser URL http://<host>:3080/?session=<sessionId> what the protocol handler opens; also accepted when pasted
Legacy browser URL http://<host>:3080/s/<sessionId> redirected (302) to the form above; also accepted when pasted
Canonical URI dsh-session:<base64url(JSON sessionId)> the lossless URI of dsh-session-reference; also parsed when pasted
Markdown mention @[label](dsh-session:…) parsed and rendered as @label (TUI mention form)

Only links carrying a harness-shaped session id (session-…) are treated as references, so unrelated dsh://… or /s/… text is never hijacked.

Why the browser URL is ?session=: since dsh 0.1.1-rc.2 the web server answers unknown paths (including /s/<id>) with 404 — the old SPA fallback is gone — so only / (and the configured index) boot the app. The session marker therefore rides on the index route, and the host half registers a /s/<id> → /?session=<id> 302 redirect so old links, bookmarks, and history entries keep working.

Quick start

Requires DeepSeek Harness dsh (any profile with the web surface) and dsh ≥ 0.1.7-rc.1: that minimum is for the ctx.uiWorkspace.openSession() navigation seam the browser half depends on (earlier versions lack it, and the old ctx.sessions.open was removed upstream). The session-reference service is not tied to that release — the shipped web bundle has provided it since dsh 0.1.0-rc.8.

# 1. One command: installs the package, auto-joins the profile's bundle layer,
#    and auto-applies the composition rows (see "Install" above).
dsh plugin --profile web add dsh-session-link

# 2. Restart the web GUI and refresh the page.
dsh web

# 3. (Windows, optional) make dsh:// links clickable:
powershell -ExecutionPolicy Bypass -File register-protocol.ps1

Manual install (without the bundle mechanism): pnpm add dsh-session-link in the profile directory, then add to the profile's patch layer (e.g. ~/.dsh/profiles/web/cordis.patch.yml):

- insert:
    - id: session-link
      name: 'dsh-session-link'

Then restart dsh web. No session-reference row is needed: the shipped web bundle has provided it since dsh 0.1.0-rc.8.

Usage

  1. Click the 🔗 button in a conversation header to copy its deep link.
  2. Paste it into another conversation and send — the model first receives the referenced session's read-only snapshot, then your prompt (the link is replaced by its readable @sessionId).
  3. Or click the dsh:// link anywhere to open that conversation in the browser.

Windows dsh:// protocol handler

register-protocol.ps1 registers the per-user dsh URL protocol (HKCU, no admin rights) so clicking a dsh://session/<id> link anywhere (browser, chat app, terminal) opens http://127.0.0.1:3080/?session=<id>, which selects that session. The launcher is dsh-open.cmd.

# register
powershell -ExecutionPolicy Bypass -File register-protocol.ps1
# unregister
powershell -ExecutionPolicy Bypass -File register-protocol.ps1 -Uninstall

The web GUI (dsh web) must be running for a link to open a session.

What the model sees

Two consecutive user-role messages: the ## Referenced sessions untrusted snapshot (capped at 64 KiB of JSON per source, older non-checkpoint messages dropped first, long messages head/tail-truncated with an exact omission notice), followed by the direct prompt with the link replaced by its readable @sessionId label. Instructions, permission claims, or tool requests inside a snapshot are not followed unless the current user repeats them.

Configuration

Defaults of the underlying service apply (max 3 references per message, 64 KiB per source). The session-reference row comes from the shipped web bundle; override it by id in your profile's patch layer (patches target ids, so the override need not live in the same layer), e.g.:

- id: session-reference
  config:
    maxReferenceBytes: 131072

Tests

pnpm install
npm test
  • host-half.test.mjs — drives the agent/pre-step listener through a real cordis waterfall (dsh:// links, both web link forms, canonical URIs, plain text, malformed URIs, prepare failures, a resolver without additionalContext), asserts the bundle patch never re-inserts session-reference, and drives the legacy /s/<id> redirect route (302 / 404 / 405).
  • client-half.test.mjs — loads the browser bundle under a DOM shim with controllable fake timers: checks the plugin surface and inject (including uiWorkspace), header-action registration, that the copy button still emits the dsh:// value, and drives ctx.uiWorkspace.openSession() against a fake context deliberately without sessions.open (all four deep-link URL forms plus precedence, encoded/malformed/empty/unrelated URLs, a delayed catalog, a transient synchronous navigation failure, a transient getSnapshot throw, a missing target that warns after ~10 s with a bounded budget (50 retries of 200 ms), ctx.effect teardown cancelling queued retries plus a late already-dequeued callback after disposal, and exactly-once navigation). It also checks the package manifest's dsh.client.inject and ^0.1.7-rc.1 dependency minimums, and asserts the installed real dsh-client-ui-workspace's openSession(target: SessionTarget): void declaration (the installed version is reported but not pinned).
  • resolver-integration.test.mjs — mounts the real shipped resolver (which listens on agent/pre-step itself since dsh 0.1.0-rc.8) beside this plugin on one cordis context: asserts the dsh:// link injects once and a canonical URI is injected only by upstream (no double injection / ordering regression), and that self-references and unreadable sessions stay fail-open.
  • inspect-logs.mjs <sessions-dir> [sessionId…] — decompresses concatenated-zstd session logs and reports session-reference events (useful for verifying injection).

Limitations

  • Links resolve only on the machine whose $DSH_HOME holds both sessions; session ids are opaque and local.
  • The browser deep link opens sessions present in the current session list and not archived; an archived target is cleared by upstream and sessions outside the list are not auto-resumed. When the target never appears in the catalog, the client gives up after a bounded ~10 s retry and warns, leaving the default view in place.
  • The browser deep link needs that browser to hold this dsh web's login cookie (visit the token URL printed by dsh web once; the cookie then lasts its full lifetime); otherwise the request answers 401.
  • The legacy /s/<id> form depends on the plugin's redirect route (not registered in compositions without webServer); the new ?session=<id> form does not.
  • If a referenced session cannot be read (missing, budget exceeded, self-reference), the link stays as plain text and the message still sends; the failure is logged on the host.
  • Text-only projection: images and other non-text blocks are not propagated across sessions (upstream service limitation).

License

MIT © PwnKY. Built on @deepseek-ai/dsh-session-reference (MIT, DeepSeek).

Content from the project README on GitHub ↗

Links

More in this category

View the whole category →

Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.