dsh-chat-log
Fold DSH session logs into clean chat logs: stream fragments merged, nothing else dropped. /chat + browser download. 把 DSH 会话日志折叠成聊天记录:流式碎片合并,其余内容一行不丢。/chat 命令 + 浏览器下载。
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 25, 2026
- Updated
- Aug 25, 2026
Introduction
dsh-chat-log
中文 | English
Turn a DSH session log into a clean chat log: streaming chunk fragments are folded into normal messages, and everything else is kept verbatim. Download it with one click next to the official "Session log" ZIP button, or via the /chat command.
Why
The official @deepseek-ai/dsh-session-log-export downloads a ZIP of the raw log: session.jsonl (or multi-frame zstd), where over 80% of the lines are token-level streaming fragments (assistant/chunk, reasoning-chunks, text-chunks, tool-call-chunks). Reading or reusing that log means wading through thousands of fragment lines.
dsh-chat-log builds a readable chat view from the authoritative folded events (user/message, assistant/message, tool/call, tool/result), and keeps every other event (permissions, approvals, request context, lifecycle) verbatim in events.
A note on fidelity: fragment deltas occasionally lose characters against the authoritative fold (observed: a missing closing quote in a tool-call argument). This plugin always trusts the authoritative fold and reports the comparison result in
report.
Features
official /export | this plugin | |
|---|---|---|
| Output | raw-log ZIP (browser download) | clean nested chat JSON (dsh-chat.v1) |
| Streaming fragments | shipped as-is (80%+ of lines) | folded into complete messages |
| Everything else | mixed into the archive | verbatim in events (nothing dropped) |
| Download | ZIP only | direct .json download (same HEAD-preflight pattern) |
| Headless | ✗ | /chat command (zero tokens, human-command plane) |
| Verification | — | fragment↔fold comparison report |
- Zero runtime dependencies, single pure-data core (
lib/fold.js) — no zstd, no zip, no server. - Nothing is deleted: every non-fragment event survives byte-identical; unverifiable fragment steps fall back to keeping raw events.
- Token effect: zero.
/chatstays on the human-command plane; results never enter model history.
Requirements
- DSH Web (
dsh web) profile with Node ≥ 22.15 commands/sessionPersistence/sessionsservices (mounted by the shipped profiles)
Install
# from npm (recommended)
dsh plugin --profile web add dsh-chat-log
# from GitHub
dsh plugin --profile web add github:YupegLV/dsh-chat-log
# local (development / self-hosting)
dsh plugin --profile web add file:/absolute/path/dsh-chat-log
Restart dsh web afterwards.
⚠️ Browser download needs one host patch (idempotent)
The "Chat log" button's direct browser download relies on a host download endpoint /api/session.chat, hosted in dsh-host-apiproxy — third-party plugins cannot register their own /api/* routes. After install, run once:
node node_modules/dsh-chat-log/scripts/patch-apiproxy.mjs
# or from the plugin checkout: node scripts/patch-apiproxy.mjs
The script is idempotent (safe to re-run; backs up the original file automatically). Re-run after every DSH upgrade (a fresh dsh-host-apiproxy overwrites the patch).
Without the patch: the button fails silently (no status text), but the
/chatcommand (writes todsh-chats/) always works.
Usage
| Entry | Description |
|---|---|
/chat | Export the current session → <session-cwd>/dsh-chats/chat-<id8>-<timestamp>.json, echoes the absolute path |
/chat --id <sessionId> | Export a specific session |
/chat --out <dir> | Custom output directory |
| Chat log button (session header) | Beside the official "Session log" ZIP button; HEAD-preflights then downloads dsh-chat-<session-id>.json through the browser download manager (needs the host patch above) |
Output format (dsh-chat.v1)
{
"schema": "dsh-chat.v1",
"session": { "id": "...", "createdAt": 1787625016270, "cwd": "...", "agentPreset": "standard", "title": "..." },
"model": { "provider": "deepseek-official", "model": "...", "reasoningEffort": "max", "maxTokens": 256000 },
"turns": [
{
"index": 1,
"startedAt": 1787625120770,
"user": [ { "time": 1787625120846, "content": [ { "type": "text", "text": "..." } ] } ],
"steps": [
{
"index": 1,
"assistant": {
"time": 1787625125842,
"reasoning": "full reasoning text",
"text": "full reply text",
"toolCalls": [ { "id": "call_...", "name": "bash", "arguments": "{...full args JSON...}" } ],
"usage": { "inputTokens": ..., "outputTokens": ... }
},
"tools": [
{ "call": { "id": "call_...", "name": "bash", "arguments": "..." },
"result": { "isError": false, "text": "...", "time": ... } }
]
}
],
"endedAt": 1787625168111
}
],
"events": [
/* every non-fragment raw event, verbatim and in order:
session/turn/step lifecycles, request headers & context,
permissions, approvals, sandbox, titles, checkpoints ... */
]
}
Nothing dropped guarantee
events= every raw event except the stream fragments, byte-identical, in original order (verified at export: count and per-line identity).- Fragment content is fully carried by the authoritative folds (every fragment step must have a matching
assistant/message; steps without one keep their raw fragments inevents). - The export includes a
report: total events, folded fragments/steps, kept events, fragment↔fold comparison.
Development & verification
Plugin-development pitfalls (client bundle id must be the bare package name,
file:installs are copies not symlinks, …) live in docs/DEVELOPMENT.md.
# offline core verification (no dsh runtime; input = decompressed JSONL)
node test/verify.mjs <session.jsonl>
Layout:
lib/fold.js pure-data core: JSONL → chat tree + verification report
lib/index.js host half: /chat command, flush, readRaw, atomic write
lib/client.js browser half: "Chat log" download button
cordis.patch.yml profile patch layer (inserts the plugin row)
scripts/patch-apiproxy.mjs idempotent patch: registers /api/session.chat
docs/DEVELOPMENT.md pitfalls & mechanism notes
test/verify.mjs CLI verification suite
Known limitations
- Requires a
sessionPersistencebackend exposing raw artifacts (JSONL backend: yes; SQLite: no — same as the official ZIP export). - Output is pretty-printed JSON; roughly 1.2× the raw log size (authoritative folds + full
events), ~30% if you truncate tool results. - Direct browser download depends on the idempotent
dsh-host-apiproxypatch (see Install;/chatwriting to disk does not).
License
MIT