# dsh-showreel — Usage Guide dsh-showreel (鲸迹) turns the most recent **completed turn** of a DeepSeek Harness agent session into a shareable, vertical (1080×1920) recap video. It runs entirely on your machine: the host reads your session, builds an editable storyboard, redacts sensitive facts, and the browser renders and exports the final MP4/WebM plus a PNG cover. Nothing is uploaded to the cloud, and your session never leaves your computer. --- ## 1. Prerequisites | Requirement | Value | |---|---| | DeepSeek Harness | `0.1.0-rc.7` series | | Node.js | `^22.19` or `>=24` | | DSH profile | `web` | | Browser | Chromium 126+ recommended (H.264/AAC MP4); other browsers fall back to WebM | --- ## 2. Installation Install the plugin into your Web profile: ```bash dsh plugin --profile web add dsh-showreel dsh web ``` DSH installs the package and — based on the `dsh.bundle` declaration inside the package — adds `dsh-showreel` to the target profile automatically. ### Installing from a local checkout (development) ```bash pnpm install && pnpm build dsh plugin --profile web add /absolute/path/to/dsh-showreel ``` --- ## 3. Quick start 1. Start the Web UI: `dsh web`. 2. Open any session that has at least one **finished** turn. 3. Click the 🎬 button in the session header bar. 4. A five-scene storyboard is generated and rendered live on a canvas. 5. Optionally edit the title and scenes, enable AI polish and/or narration, then click **Export video**. Exports land in your browser's downloads: - `{title}.mp4` — 1080×1920, 30 FPS (Chromium, when H.264/AAC is available) - `{title}.webm` — fallback container when MP4 is not supported - `{title}-cover.png` — first-frame PNG cover, 1080×1920 --- ## 4. The storyboard editor ### Scenes Each storyboard contains five scenes derived from the last completed turn: | Kind | Content | |---|---| | `goal` | What the task asked for | | `work` | What the agent did (tools, files, model, tokens) | | `verification` | What was verified — only what actually happened is claimed | | `result` | The outcome and deliverables | | `outro` | Closing summary | Per-scene constraints enforced by the host (edits are validated against the authoritative snapshot of the session): | Field | Constraint | |---|---| | `headline` | 1–60 characters | | `body` | 1–4 lines, each ≤ 500 chars, total ≤ 500 chars | | `durationMs` | 1,000–15,000 ms | | `enabled` | boolean — disable a scene to skip it | Invalid edits are rejected with `400 INVALID_STORYBOARD` instead of being accepted silently. ### AI polish The **Polish** button asks the same model that ran the task to refine the copy. Only **redacted, structured facts** are sent — never raw session content. If the model is unavailable or the call fails, the plugin keeps the rule-based copy and shows why. ### Narration Toggle **Auto narration** to synthesize a voiceover from the (redacted) scene text using an OpenAI-compatible `/audio/speech` endpoint. If TTS is not configured, times out, fails, or the audio cannot be decoded, the export silently degrades to a video without narration. ### Export - **Export video** — real-time canvas rendering at 30 FPS via `MediaRecorder`, with progress feedback and a cancel option. - **Cover** — exports the first rendered frame as a PNG. --- ## 5. Configuration — narration (TTS) Narration is optional and off by default. To enable it, overlay the installed row in your Web profile's `cordis.patch.yml`: ```yaml - id: dsh-showreel config: tts: baseUrl: https://api.openai.com/v1 model: gpt-4o-mini-tts voice: alloy credentialRef: DSH_SHOWREEL_TTS_API_KEY timeoutMs: 60000 maxAudioBytes: 10485760 ``` ### `tts` config keys | Key | Required | Description | |---|---|---| | `baseUrl` | yes | Versioned service root, e.g. `https://api.openai.com/v1`. The plugin appends `/audio/speech`. Remote URLs must use HTTPS; local `localhost`/loopback may use HTTP. | | `model` | yes | TTS model id | | `voice` | yes | Voice id | | `credentialRef` | yes | Reference to an API key managed by the DSH credentials provider | | `timeoutMs` | no | Request timeout (default 60 000) | | `maxAudioBytes` | no | Response size cap (default 10 MiB) | ### Credentials Hand the actual key to the DSH credentials provider — e.g. the environment variable `DSH_SHOWREEL_TTS_API_KEY` or a locally hosted credential file. `credentialRef` is only a reference: the key is resolved **host-side on every request** and never enters the config file, the session, the browser, or exported artifacts. The browser cannot supply a TTS endpoint or key through any request field. --- ## 6. Privacy model The plugin is strictly **read-only** over your session and workspace. By default it never uses or shows: - `reasoning` / `thinking` content - full command output - full diffs - API keys, bearer tokens, common access tokens, private keys, password fields - absolute paths (home directory, workspace, etc.) All facts are redacted **on the host first**, and only then flow into rule-based copy, AI polish, and narration. The editor displays the redaction count and warnings, and export always requires an explicit user action. --- ## 7. Local HTTP API All endpoints are same-origin, mounted under `/showreel/api`. Request bodies are limited to 64 KiB; audio responses to 10 MiB. | Method | Path | Purpose | |---|---|---| | `GET` | `/showreel/api/sessions` | List readable session summaries | | `POST` | `/showreel/api/storyboard` | Build a storyboard for a session/turn (`sessionId`, optional `sourceTurn`) | | `POST` | `/showreel/api/polish` | Polish redacted copy with the task's model (`storyboard` payload) | | `POST` | `/showreel/api/narration` | Synthesize audio from text (`text` payload) using the host-saved TTS config | Cross-origin browser requests are rejected and responses are not cached. The narration endpoint ignores any endpoint fields submitted by the browser. ### Core types ```ts interface ShowreelStoryboardV1 { version: 1 sessionId: string sourceTurn: string title: string scenes: ShowreelScene[] stats: ShowreelStats privacy: { redactionCount: number; warnings: string[] } } interface ShowreelScene { id: string kind: "goal" | "work" | "verification" | "result" | "outro" durationMs: number headline: string body: string[] enabled: boolean } interface ShowreelStats { provider?: string model?: string durationMs: number toolCalls: number filesChanged: number inputTokens: number outputTokens: number cacheReadTokens: number cacheWriteTokens: number reasoningTokens: number } ``` --- ## 8. Browser & recording notes - The canvas is rendered **in real time at 30 FPS** in your browser; there is no server-side video processing. - **Chromium 126+** supports the MP4 container via `MediaRecorder`; the actual H.264/AAC codec availability still depends on the current system capabilities. When MP4 is unavailable the plugin shows an explicit notice and falls back to **WebM**. - Narration is mixed into the recording through Web Audio. If audio setup fails (e.g. undecodable audio), the export continues silently. - Video recording can be cancelled mid-render; cleanup (recorder, tracks, `AudioContext`) is always attempted. --- ## 9. Troubleshooting | Symptom | Likely cause / fix | |---|---| | No 🎬 button in the session header | The session has no finished turn yet; or the plugin is not loaded in the active profile (`dsh plugin --profile web list`) | | Export produces `.webm` instead of `.mp4` | The browser/system lacks MP4 (H.264/AAC) recording support — WebM is the expected fallback | | Video has no narration | TTS not configured, credential missing, timeout, or audio decode failure — export degrades to silent by design | | Polish does nothing different | Model unavailable/error — the rule-based copy is kept and the reason is surfaced | | `400 INVALID_STORYBOARD` | An edit violates the field constraints (see §4) or the payload no longer matches the authoritative session snapshot | | `400 INVALID_SESSION` / `INVALID_STORYBOARD` on storyboard | `sessionId` is unknown or `sourceTurn` is invalid | --- ## 10. Development & verification ```bash pnpm install pnpm test # unit tests pnpm typecheck pnpm build # tsdown → lib/ npm pack --dry-run ``` On macOS + Chrome you can regenerate the real demo assets with `pnpm demo` — it calls the exact Canvas/`MediaRecorder` export functions used in production rather than pre-baked animations. Tests cover official rc.7 `SessionEvent` structures, last-finished-turn selection, success/failure/no-tool/cancelled/model-error/collapsed and thousand-event sessions, redaction, tool classification, token aggregation, storyboard stability, AI/TTS degradation, API same-origin and size limits, route hot-unload, timelines, and MP4/WebM capability detection. --- ## 11. How it works (architecture) - **Host**: reads persisted events via `ctx.sessionQuery.readSession()`, builds `ShowreelStoryboardV1` as a pure function, and redacts everything before it leaves the host. It never hooks into or modifies the Agent Loop. - **Web client**: registers a single button in the official `conversation.session.header.actions` additive slot. Canvas, `MediaRecorder`, and Web Audio all run locally in your browser. - **No cloud**: no auto-upload, no cloud sharing, no background music, no landscape templates, no template marketplace, and no full-session replay. --- ## License [MIT](./LICENSE)