dsh-session-link
September 20, 2026 · View on GitHub
· 中文 · 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-linkManual 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
dshURL protocol handler, clicking adsh://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 theagent/pre-stepseam. When a claimed direct user prompt contains a session deep link, every supported link form is normalized into canonicaldsh-session:mentions, parsed into structured references, snapshotted viasessionReferenceResolver.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 toagent/pre-stepfor canonical mentions itself; this listener runs outermost (prependplus thesessionReferenceResolverinjection) 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.clientdeclaration) rendering the copy button inconversation.session.header.actionsand opening/?session=<sessionId>deep links (legacy/s/<sessionId>and#/s/<sessionId>forms included) by selecting the target session once the list has loaded.
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.0-rc.8, which ships the session-reference service in its web bundle. On older versions (≤ 0.1.0-rc.7) add the session-reference row manually — see the manual-install note below.
# 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-linkin 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. On dsh ≥ 0.1.0-rc.8 nosession-referencerow is needed (the web bundle provides it); on older versions add it manually beforesession-link:- id: session-reference name: '@deepseek-ai/dsh-session-reference'
Usage
- Click the 🔗 button in a conversation header to copy its deep link.
- 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). - 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 theagent/pre-steplistener through a real cordis waterfall (dsh://links, both web link forms, canonical URIs, plain text, malformed URIs, prepare failures, a resolver withoutadditionalContext), asserts the bundle patch never re-insertssession-reference, and drives the legacy/s/<id>redirect route (302 / 404 / 405).client-half.test.mjs— loads the browser bundle under a DOM shim and checks the plugin surface, header-action registration, all four deep-link URL forms (?session=,/s/, and both hashes), and the copieddsh://value.resolver-integration.test.mjs— mounts the real shipped resolver (which listens onagent/pre-stepitself since dsh 0.1.0-rc.8) beside this plugin on one cordis context: asserts thedsh://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 reportssession-referenceevents (useful for verifying injection).
Limitations
- Links resolve only on the machine whose
$DSH_HOMEholds 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.
- The browser deep link needs that browser to hold this
dsh web's login cookie (visit the token URL printed bydsh webonce; 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 withoutwebServer); 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).