# dsh-recent-prompts > Get back what you told the AI: browse recent conversations, search **all** your history, copy a prompt in one click, and hide the noise. A [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) web plugin. It adds a bubble icon at the bottom of the sidebar; clicking it opens a full page of "what I told the AI recently". [中文说明](./README.md) · [Changelog](./CHANGELOG.md) · [MIT License](./LICENSE) --- ## Why The most reusable artifact of working with an AI is often **the message you wrote yourself**: a constraint, a spec, a prompt you keep coming back to. They scatter across hundreds of sessions. This plugin does three things: 1. **Browse** — what you said recently, in full text (not a summary); 2. **Search** — find it again in every session you ever had; 3. **Keep** — copy what is useful, hide the noise and the conversations you no longer want to see, restore later. ## Features | Feature | Detail | | --- | --- | | Recent window | Loads the **6 most recent sessions** × their **latest 6 turns**, grouped by session, chronological inside a group, full text | | Load earlier | Each session group can page further back, ~6 turns at a time, down to its very first message | | Search all history | Searches **every turn of every session**; hits are grouped by session with `#turn + opening snippet` | | Load full text | A hit is only fetched when you ask: the page is anchored at the next turn's start, so it returns exactly that turn | | Copy | Copies the full prompt to the clipboard (`navigator.clipboard`, with a selection fallback) and confirms with "Copied" | | Hide a prompt | Hides one turn, including its opening snippet in the list | | Hide a session | Hides the whole group from the recent list and from search results | | Hidden / restore | A "Hidden N" tab lists both sessions and prompts, with per-item and restore-all, plus copy for prompt records | | Include hidden | Toggle next to the search box, **off by default**; when on, hidden items reappear in results marked "Hidden" and can be restored in place | | Read-only | Only calls Session read APIs; never writes the session log; no network access | | Themes / locale | Host theme tokens only (light & dark); Chinese and English UI text | ## Install Requires DSH `0.2.x` (uses `session.list` / `session.projections` / `session.page`, all read-only). ### Option A — let DSH install it (recommended) Use the **Plugins** page, or ask an agent to install the bundle from the local directory: ``` plugin_manager install_bundle ``` ### Option B — command line ```bash git clone https://github.com/hujiyo/dsh-recent-prompts.git dsh plugin --profile web add ./dsh-recent-prompts ``` Then append `"dsh-recent-prompts"` to `dsh.profile.bundles` in `$DSH_HOME/profiles/web/package.json`: ```json "dsh-recent-prompts" ``` Refresh the page; a bubble icon labelled "Recent prompts" appears at the bottom of the sidebar. ### Uninstall 1. Remove `"dsh-recent-prompts"` from `dsh.profile.bundles`; 2. `dsh plugin --profile web remove dsh-recent-prompts`. ## How it works The plugin is client-only; its Host half is a no-op. Everything is read through existing read-only Remote APIs: ``` session.list() → every session: title, updatedAt, ~50-char opening snippet per turn ├─ recent window: the 6 newest sessions │ session.projections({sessionId}) → exact log cut (asOfSeq) + title + turn outline │ session.page({throughSeq, turnWindow}) → roughly the latest 6 turns of events └─ search: local match against each turn's opening text "Load full text" on a hit session.projections({sessionId}) → cut session.page({beforeSeq: next turn}) → exactly that turn ``` Events are then reduced to **the text you actually typed**: - only `user/message` events; - with `data.source.kind === 'user'`, which excludes agent-injected context, goal continuation rounds, tool results, command echoes; - text comes from the `text` blocks of `content`; images and files become placeholders. Hidden records live only in the browser: - `localStorage` key `dsh.recent-prompts.hidden.v1`; - shapes: session `{kind:'session', sessionId, sessionTitle, …}`, prompt `{kind:'turn', sessionId, seq:, body, …}`; - the session log and Harness state are never touched, so restores are always possible and other clients are unaffected. ## Privacy - All reads go through the local Harness Remote APIs; the plugin makes **no network requests**; - it **never writes** the session log; hidden records stay in this browser's site storage; - changing browser/device or clearing site data loses the hidden list (your sessions are unaffected). ## Known limitations - **Search granularity**: matching uses each turn's ~50-character opening text (the projection outline's granularity), so a keyword buried in the middle of a long message is not found. Arbitrary-position matching needs the host's full-text message index (`ctx.sessions.search`); not implemented yet. - **Cache dependency**: a session never covered by the projection cache may have no outline and therefore no search hits; opening that session once fills the cache. - **Fixed window**: the 6 sessions × 6 turns, and the 60-session search cap, are constants at the top of `client.js`. - **Many sessions**: `session.list()` returns the whole catalog (hundreds of sessions cost a little time on first load) — expected. - Hiding is view-level: it changes what this plugin shows, not the DSH sidebar's session list. ## Tunables Top of `client.js`: ```js const RECENT_SESSIONS = 6; // recent sessions shown by default const WINDOW_TURNS = 6; // turns per loaded window const WINDOW_MESSAGES = 400; // message budget per history request const SEARCH_GROUPS = 60; // maximum matched sessions rendered const CACHE_MS = 60 * 1000; // session catalog cache lifetime ``` ## Layout ``` dsh-recent-prompts/ ├─ package.json # manifest: dsh.bundle.patch + dsh.client ├─ cordis.patch.yml # inserts the plugin row into the profile ├─ index.js # Host half (no-op; rendering is client-side) ├─ client.js # all UI and reads (React, no build step) ├─ icon.svg # Plugins-page artwork ├─ locale/zh.json # plugin metadata (title/description) ├─ locale/en.json ├─ README.md # 中文说明 ├─ README.en.md # English (this file) ├─ CHANGELOG.md └─ LICENSE ``` There is no build step: `client.js` is a plain `window.__ModuleLoader__.load({ id, factory })` file, and React comes from the host's module table. ## Development 1. Install the repo as a link into a profile (see Option B); after that, editing `client.js` only needs a page refresh. 2. No test suite, no build. 3. UI copy lives in the `zh` / `en` dictionaries inside `client.js`; `locale/*.json` is metadata for the Plugins page. 4. Styling uses only `--dsw-alias-*` theme tokens; no host component package is imported. ## Contributing Issues and PRs are welcome. Please keep the plugin **read-only** (never write the session log), keep it free of host Client package imports, and never render through an iframe. ## License [MIT](./LICENSE) © 2026 hujiyo