liangmianya/dsh-synapse
liangmianya/dsh-synapseDeepSeek Harness 的可视化非线性对话工作区:把会话、追问与分支变成可浏览、可拖拽的对话地图。
撤回/编辑重发/重新生成,加上单会话版本化:每次回退的时间线、产物回退(git 优先、快照兜底)、分叉图、会话谱系,以及实时看门狗(并发写入第一时间快照日志)。编辑前自动停止运行中的 agent(官方 cancel/whenIdle),回退不会损坏持久化日志、也不会破坏 /compact。
Recall · Resend (restart from here) · Regenerate, plus write-safe in-conversation versioning — the Agent business layer (production-grade guarantees) for DeepSeek Harness.
English · 简体中文
Recall / resend / regenerate — the three moves every conversation deserves. But rewinding is not just "delete a message": DeepSeek Harness stores conversations in an append-only event log, so a recall only rewinds the context while changed artifact files stay changed. dsh-retrace versions the conversation and its artifacts together, and keeps every new rewind legal — it cannot dirty the log, and new markers create no token-meter pairing debt (two-segment atomic pairs land by construction).
⚠️ Honest scope (matches the companion contract's own note): sessions that
already contain legacy single-segment markers — written by older versions —
are known design debt. Before/compact, run the companioncheckand
clean them up (fix --remove-markers); otherwise the host's own pre-compactionself-check blocks compaction. New rewinds do not add to that debt.
🛡️ Write safety · 🔍 Deep offline checks · 🔄 Detect → repair → guard — see below.
Requires DeepSeek Harness with the dsh CLI. Restart DSH after install (a running app does not hot-reload).dsh plugin --profile desktop add dsh-retrace # DSH Desktop
# or Web: dsh plugin --profile web add dsh-retrace
# or from an unpacked checkout / ZIP:
# dsh plugin --profile desktop add ~/plugins/dsh-retraceNo command line? Install the community plugin market once, then find dsh-retrace in Settings → Plugin Market and install it with one click:
dsh plugin --profile desktop add dshmarket # one timeAfter the restart, hover any assistant reply → ↩ / ↻; any user message → ✎. Full steps in 📦 Installation.
· Capability · What it means 🛡️ · Write safety · Every rewind passes a three-layer pre-write contract guard; a running agent is stopped automatically (official cancel/whenIdle, with a bounded wait); turn-interval markers are wrapped in a temporary step — new rewinds cannot dirty the log and add no token-meter pairing debt; legacy single-segment markers are known debt (run the companion check + fix --remove-markers before /compact) 🔍 · Deep offline checks · Companion dsh-log-contract ships 30+ contract rules (token-meter pairing / cross-step references / physical order / inbox replay), validated against real corrupted-session fixtures — it finds the class of problem that makes /compact permanently fail 🔄 · Detect → repair → guard · A watchdog snapshots the log at the first sign of concurrent writes; offline fix neutralizes problem markers and clips cross-step references in place; pre-write validation stops bad events before they land
Action · Where · What happens ↩ Recall · hover any assistant reply, or the row under any user message · Opens a confirmation first (destructive: this message and everything after it leaves the conversation). On confirm, if a turn is still running the plugin pauses it first through the official stop entry and waits (bounded); if the pause fails or times out the recall still continues, with a visible notice. The whole exchange round (the input and the agent's output, tool rows included) is then removed from both the model context and the conversation view; the input text is echoed into the composer so you can re-ask or re-edit immediately. A transient notice marks the rewind and disappears once you keep typing. ✎ Resend (restart from here) · row under any user message · The primary meaning is restart from this message: this round and everything after it is rewound and the new text is re-sent, so the conversation continues on a new timeline branch. If that range is refused by the guard (too large to rewrite in place), the error offers a fallback action — Change this text only (keep what follows). A collapsed "original input" reference sits right under the new message — click to expand, configurable off. ↻ Regenerate · hover any assistant reply · The reply (and everything after it) is rewound and hidden, then the original prompt is re-sent so the agent answers again.
Action · Scope · What happens ↺ Restore display · view only · Puts the rows hidden by that marker back into the conversation view. The log only grows, the model context is unchanged, and the recall trace stays. Available on the marker row. ⏪ True restore · log level · Neutralizes the two marker segments in place (sequence numbers are not renumbered), so the recalled range returns to the model surface and its edit / resend / recall actions work again. Requires an idle session that is not the one you are currently viewing; once it succeeds, switch away and reopen that session (or refresh the page) to see the restored content. Refusals are explained with a next step (session running / held by another process / already re-shadowed by a later marker / host does not provide the capability).
Checkpoints & rollback (live in 0.4.x) — every rewind is also recorded as a checkpoint:
· What · 🕘 · Checkpoints · a Checkpoints tab in the conversation view: every change (type, time, message count, file-change badges), pushed live via session/projection (no polling), windowed for long histories; nested history is browsable outline-style ↩️ · Artifact rollback · context-only / artifacts-only / both with dry-run preview; git-first + content-addressed snapshot fallback; a rollback is itself a new checkpoint (restore). Preview refuses while the agent is running (so "preview says yes, confirm says no" cannot happen), and preview/rollback failures are shown in the UI instead of failing silently 🧭 · Jump-to-conversation · one click from a checkpoint to that point in the conversation (auto-loads history, anchor highlight); when the point is out of the auto-load budget the reason is reported instead of nothing happening 🧹 · Bounded storage · snapshots keep the most recent N checkpoints (default 50); throttled background sweep prunes truncated ones
Rollback on a damaged log is not silent: the host's strict replay is the
authority, but a single malformed event (for example a sourceEventSeqs rowwritten by a third party with range encoding) can poison the whole replay. In
that case the rollback degrades to a mirror replay, logs both the degradation
and the original error, and flags the result as degraded on the wire; if eventhe mirror cannot run you get a structured replay-failed error, not a genericinternal.Why it's different (the interaction layer — the guarantees above are the storage layer):
The package declares a dsh.bundle manifest, so it installs through the official plugin path into any profile:
dsh plugin --profile <name> add dsh-retrace⚠️ Restart required. The install writes the new files and re-renders the
profile composition, but a running app does not hot-reload bundles — quit
and reopen DSH Desktop (or restart the dsh process for a standalone Webdeployment) to load the plugin. To uninstall:
dsh plugin --profile remove dsh-retrace (then restart again).dsh CLI)The same result with plain file edits and pnpm — exactly the steps dsh plugin add performs for you:
Downloaded this repo as a ZIP? Unpack it somewhere stable (e.g.
~/plugins/dsh-retrace), then eitherdsh plugin --profile desktop add ~/plugins/dsh-retrace, or follow thesteps below with the dependency line pointing at the folder:
"dsh-retrace": "file:~/plugins/dsh-retrace"./profiles/desktopon DSH Desktop, /profiles/web for standalone Web — where ` is $DSH_HOME when set, otherwise the **active session base** ($DSH_HOME and any base configured through $DSH_SESSION_BASES, falling back to ~/.dsh); ~/.dsh/profiles` is only the pre-migration fallback) and add both the dependency and the bundle-layer entry:
{
"dependencies": {
"dsh-retrace": "^0.4.131"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-retrace"
]
}
}
}(Keep whatever entries your profile already has; only add the two dsh-retrace lines.)
cd "$DSH_HOME/profiles/<name>" && pnpm install # or the active base you usedsh process (see above).For local development, point the dependency at a checkout instead of the registry: "dsh-retrace": "file:/path/to/dsh-retrace" — or let dsh do it: dsh plugin --profile add /path/to/dsh-retrace. For the latest commit without a release, add a git dependency using the repository URL from this package's repository field (pnpm syntax "dsh-retrace": "github:/dsh-retrace") in the same dependencies block, then pnpm install.
npm i dsh-retraceAdd the package to the harness composition (cordis.yml of the app/deployment you use):
- name: 'dsh-retrace'The client half is picked up automatically from the package's dsh.client metadata and bundled into the Web client (a client-module rebuild happens automatically when the composition changes). The Host half registers the same-origin HTTP route /api/plugins/retrace/* for the browser UI.
Use the dynamic entries shipped in the package. In the session where you want the feature:
lib/dynamic-host.js(Host half) and lib/dynamic-client.js (Client half).
The dynamic host registers the same operations behind the package-private harness.handle RPC (retrace.recall / retrace.editAndResend / retrace.regenerate).
package.json(@deepseek-ai/dsh-session, @deepseek-ai/dsh-client-*, cordis, react — the current build targets the 0.1.7-rc.2 generation).
dsh-log-contract >= 0.3.12 (installed automatically as a dependency).inject(sessions, agents, webServer, fs, subprocess, sandboxPolicy, jobs). A missing capability degrades the corresponding feature (for example, no subprocess ⇒ rollback uses the built-in snapshots only); it does not block boot.
Before you exit or reload, know what is still running:
· What · 🛡️ · Running-work detection · every session is scanned for live work: agent running, queued inbox items, background jobs, unclosed turns 📋 · Running banner · sessions with live work show a persistent in-page banner (short session code + reasons), so you can see it before quitting ⚠️ · Exit prompt · on plugin dispose (app exit / reload) a Chinese notice lists each running session and why it is considered busy — it only warns, it never cancels your running agent 🔒 · Page-close interception · The desktop never arms the host's native confirm dialog; it uses a page-drawn confirm gate instead: it draws the box and verifies visibility synchronously before blocking (preventDefault); if it cannot draw, is not visible, or the page is hidden ⇒ lets the close through immediately; the dialog waits for your choice (Esc = cancel); a Web-Worker watchdog (immune to background throttling) is only the *last* resort. Turning off "Exit confirmation (close guard)" recovers immediately (no restart) 🔎 · Query surface · retrace.runningState (host RPC) + GET\ · POST /api/plugins/retrace/runningState (HTTP) — same shape on both transports; the all-sessions shape also carries the host-reported page surface (surface / quitVeto)
How the close confirm behaves (user-visible): whether the host pops its own
native quit confirmation is decided jointly by the host and the page. On a
desktop shell the plugin does not use the host's native dialog — it draws its
own in-page confirmation (Esc cancels). If the page cannot draw that box, the