dsh-archive-nudge
English | 简体中文(README.zh.md,随包附带)
A DeepSeek Harness plugin that puts a tiny archive-box icon next to a session
title in the sidebar when that session is probably finished — you can see which
conversations are safe to archive without opening any of them.
Reminds only, never archives for you: no popups, no auto-open, no session
state touched. Hover the icon for a one-line reason.
Screenshot
The illustration lives in the repository only (docs/screenshot.png, source
docs/mockup.html): a grey archive box on an idle session, an amber box on a
session the agent marked, and the hover card repeating the reason. It is a
mockup — the session titles are made up — not a real screenshot, and it is
not part of the npm tarball, so it is not embedded here.
Why
Long session lists rot. You usually know which conversations are dead, but
finding them again is scrolling work. The plugin answers one question at a
glance: is this one done?
When the icon shows up
| Signal | Condition | Look |
|---|
| Idle too long | the last activity is older than idleHours (default 144 h = 6 days) | grey, dimmed; tooltip reads "Idle for 6 days — safe to archive" |
| The agent says it is done | the agent calls the mark_session_archivable tool | amber; the tooltip carries the agent's own one-line reason |
Both only render while that row is idle (nothing running, nothing waiting for
approval), because that slot is where the status dot normally lives — the two
never fight for the same pixels.
Hover the row and the hover card repeats the reasoning: how long it has been
idle, the threshold in effect, and — for an agent mark — when it was made and
the agent's own words.
Context-size and turn/cost signals are deliberately not implemented: the
session list only exposes idle time, title and "is it running"; computing tokens
or turns would mean activating every session. The agent can already see its own
context usage, so it can call the tool at exactly the right moment.
Configuration
Everything lives in one file:
%DSH_HOME%/archive-nudge/marks.json
(%DSH_HOME% defaults to ~/.dsh.)
{
"config": { "idleHours": 144, "allowArchiveApi": false },
"marks": {
"<sessionId>": { "at": 1792775972791, "reason": "Goal delivered, the rest is a new task" }
}
}
| Key | Default | Meaning |
|---|
idleHours | 144 | Hours of inactivity before the grey mark appears. 0 marks a session as soon as it goes idle (handy to test the icon). Takes effect within a minute — the sidebar polls once a minute. |
allowArchiveApi | false | Master switch for the optional archive endpoints. Accepts true / 1 / "true". |
144 pairs with auto-archive plugins that use autoArchiveDays = 7: the grey
mark lights up one day earlier, meaning "this one is gone tomorrow". Without
such a plugin, 24–48 is a friendlier number.
- Mark entries older than 30 days are dropped on the next read.
- Writes are atomic (temp file + rename), so several windows writing at once
never produce half a JSON file; a corrupt file is treated as empty instead of
crashing the plugin.
Settings page
Settings → Archive nudge edits the same file for you:
- change
idleHours (saved when you leave the field, effective within a minute),
- list every agent mark with its session, reason and timestamp,
- clear one mark, or all of them — marks disappear from the sidebar immediately.
The page talks to the two write routes below, so the file stays the single
source of truth and hand-editing still works.
Also nearby: %DSH_HOME%/archive-nudge/nudge.log — a small operation log that
rotates to nudge.log.1 past 256 KB (one backup, never grows).
Useful lines: boot: host half loaded … (proves which code got loaded),
tools available/unavailable, deny loopback …, batch archive ….
Install
# from npm (recommended — the published package)
dsh plugin add --profile <profile> dsh-archive-nudge@0.2.1
# from GitHub (only if you have access to the repository — it is private)
dsh plugin add --profile <profile> github:yuyu13251/dsh-archive-nudge#v0.2.1
Manual install, if the CLI is not usable (for example when the desktop app owns
the profile):
- unpack the released package (or copy this repository, while developing) into
<DSH profile>/node_modules/dsh-archive-nudge — a symlink / junction works too,
- add the package to
dsh.profile.bundles in the profile's package.json,
- restart DSH (a page reload is enough for client-only changes).
Requirements: DSH 0.2.x web UI and Node.js >= 22. The plugin has no runtime
dependencies — Node built-ins only.
Uninstall
- delete
<DSH profile>/node_modules/dsh-archive-nudge,
- remove the
dsh.profile.bundles entry from the profile's package.json
(and the dependencies entry, if you installed it that way),
- reload the page or restart DSH,
- optionally delete
%DSH_HOME%/archive-nudge/ (marks and log).
Privacy
- Everything stays on your machine:
%DSH_HOME%/archive-nudge/marks.json — session ids plus a one-line reason,
%DSH_HOME%/archive-nudge/nudge.log — operation log.
- No telemetry, no outbound requests, no reading of session content. The plugin
never opens a session's chat data.
- Every write endpoint (
/config, /marks/clear and the optional archive API)
only accepts requests from this machine; the archive API is off by default
and answers 403 / 503 otherwise.
GET /marks and GET /config are read-only and ungated so a remote Web UI can
read them. That means: don't expose the port to the internet. Session ids and
the reasons you wrote are not secret, but they are not for public relay either.
HTTP API
For tinkerers and other local tools:
| Route | Method | Gate | Purpose |
|---|
/dsh-archive-nudge/marks | GET | none (read-only) | { ok, config, marks } |
/dsh-archive-nudge/config | GET | none (read-only) | { ok, config } |
/dsh-archive-nudge/config | POST | loopback | {"idleHours": 12} or {"allowArchiveApi": true} |
/dsh-archive-nudge/marks/clear | POST | loopback | {"sessionIds": ["…"]} or {"all": true} |
/dsh-archive-nudge/archive | POST | loopback + allowArchiveApi + workspaceRegistry | {"sessionIds": ["…"]} |
/dsh-archive-nudge/unarchive | POST | same three gates | {"sessionIds": ["…"]} |
The archive endpoints call the host's workspaceRegistry.archiveSession /
unarchiveSession, so a session with active work is refused by the host and
reported per id — one failure never aborts the batch. On a successful archive,
the plugin also drops that session's "ready to archive" mark (marksCleared in
the response).
Adding or removing routes needs a DSH restart; changing config values
does not.
The tool
mark_session_archivable { reason: string, clear?: boolean }
reason is shown verbatim in the icon's tooltip; clear: true takes the mark
back. One mark per session — calling it again replaces the reason.
Compatibility and known limits
- Built for the DSH 0.2.x web UI; it uses the public slot
sidebar.session.row.leading.
- If the icon disappears after a DSH upgrade, check
window.__archiveNudge in
the browser console — if that is gone too, the client half did not load, and
most likely the slot name changed.
- The host half only hard-depends on
webServer. A missing tools service just
means the tool is not registered; a missing workspaceRegistry just means the
archive endpoints answer 503. The sidebar nudge keeps working in both cases.
workspaceRegistry is loaded asynchronously: if it shows up late, the archive
endpoints start working without a restart.
- UI text ships in English and Simplified Chinese. The browser half registers its
copy through DSH's
ctx.locale.register(), so the settings page, hover card and
labels follow the interface language; the host half (tool description, tool
replies, error hints) picks from DSH_LOCALE / LANG, because the host has no
locale service.
Development
npm run check # node tools/check.mjs
npm test # node --test "test/**/*.test.mjs"
check parses every .js / .mjs in the repository with node --check (no code
is executed) and validates that package.json is well-formed JSON. test runs
the unit tests: store tolerance (missing / corrupt / expired marks, atomic
writes), the loopback fence and every route, the tool, the client half's three
slots, dictionary key parity and the idle decision. No dependencies, no install
step — both commands work on a bare Node 22+.
CI runs the same two commands on Node 22 and 24 (.github/workflows/check.yml).
Third-party notices
The loopback request guard in lib/index.js is derived — rewritten, simplified
and split into standalone pure functions — from shared/host/loopback.ts in
@linxin666/dsh-session-archive,
which is licensed under the Apache License 2.0. See the NOTICE file and
LICENSE-Apache-2.0.txt, both shipped inside this package. Everything else is
original work.
License
MIT — see the LICENSE file shipped inside this package.