deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
Session export & compliance archiving for DeepSeek Harness (dsh).
Turns any current or historical session into verifiable, sanitized archives in multiple formats, manages them on disk, and gives you a compliance/traffic view — all as a dependency-free dsh bundle.
Exports are only useful for compliance or archival if they do not leak. This plugin combines, in one bundle:
.env-style
lines, URL credentials and absolute file paths are masked before anything
touches disk. The same input always yields the same output, and masked values
carry a stable fingerprint so archives stay correlatable without leaking secrets.ctx.sessionPersistence / ctx.sessions).markdown, jsonl, pdf, or all.parts.json manifest); long
messages can be truncated in Markdown with an explicit marker (JSONL always keeps
the full record).| Rule | Description |
|---|---|
api-key |
sk-…, pk-…, ghp_/gho_/github_pat_ (GitHub), hf_, AKIA…, AIza…, long base64-style tokens |
jwt |
eyJ… tokens |
high-entropy-token |
any 32+ char alphanumeric run |
env-value |
KEY=value assignment lines (e.g. pasted .env) |
bearer |
Bearer/token values |
url-credential |
https://user:pass@host |
absolute-path |
Windows drive / UNC / POSIX home paths — masked, or relativized to <project-root> when pathMode: relative |
email |
opt-in |
custom |
arbitrary user regexes |
All matched values are replaced with
<redacted:<rule> sha256:<fingerprint>> where the fingerprint is a salted SHA-256
(12 hex chars). Determinism + a configurable salt keep exports correlatable across
files and machines without exposing the secret: because the fingerprint is derived
from the secret value alone (not the rule label), the same secret gives the same hex
whether it was caught by the env-value, api-key, or jwt rule. The env-value
rule runs first, so a KEY=sk-… line is reduced to KEY=<redacted:env …> in one
step (masking the value token or quoted string while preserving surrounding prose
and commands) before token rules see it.
<outDir>/
sessions/<session>/<YYYY-MM-DD>/<YYYY-MM-DD>_<HHMMSS>-<tag>_<format>.<ext>
<file>.meta.json # one sidecar per export (see ArchiveEntry)
<file>.parts.json # present when the export was split
archives.json # searchable index (rebuilt from sidecars if lost)
Files are named with a process-unique tag so two exports in the same second never overwrite each other. List/search by session, format, ISO date range, free-text query, and limit; delete by archive id, file name, or relative path — strictly confined to the archive root (path traversal is rejected). Index writes are serialized in-process, so concurrent tool calls cannot lose records.
Every export embeds a metadata header:
plugin (name, version), session (id, project, cwd, createdAt), exportedAt,
models used, sanitization (enabled, level, fired rules, salt fingerprint),
event/message counts, optional contentHash, and warnings. An optional SHA-256 hash
over the sanitized transcript is included (includeHash: true).
Note: warnings that only become known while writing (large-export chunking, PDF
fallback) cannot be embedded in the already-rendered header; they are recorded on
the archive index entry + .meta.json sidecar and re-surfaced on tool results.
A trend/audit view (/export-audit) aggregates exports by day, format, session
and sanitization level.
ctx.tools): session_export, session_export_list,
export_delete, sanitize_config.ctx.commands): /session-export,
/session-export-list, /export-delete, /sanitize-config, /export-audit.dsh-session-export/core subpath..ts test suite directly).sessions,
sessionPersistence, tools, commands).This is a standard dsh bundle: an npm package whose package.json declares the
dsh.bundle manifest and ships a cordis.patch.yml patch layer plus an entry module
exporting name/inject/apply.
# from this repository
dsh plugin --profile demo add github:JohnXu22786/session-export
# or from npm once published
npm install -g dsh-session-export
The patch inserts one row:
# cordis.patch.yml
- insert:
- id: dsh-session-export
name: dsh-session-export
config:
outDir: !!js dshHomePath('exports')
outDir defaults to $DSH_HOME/exports (i.e. ~/.dsh/exports). Override any
setting by targeting the same row id in a later patch layer (config is a whole-row
replacement, per harness semantics).
- id: dsh-session-export
name: dsh-session-export
config:
outDir: !!js dshHomePath('exports')
enabled: true
defaultFormat: markdown # markdown | jsonl | pdf | auto
includeNotes: false # include todo/command/feedback bookkeeping
includeDocuments: true # emit the document-tree appendix
maxChunkBytes: 4194304 # split big exports (0 = never)
maxMessageChars: 0 # truncate message display in Markdown (0 = never)
prettyToolArgs: false
includeHash: true
redaction:
enabled: true
pathMode: mask # mask | relative | off
projectRoots: [] # e.g. ['C:\\Users\\you\\work']
maskApiKeys: true
maskEnvValues: true
maskBearerTokens: true
maskUrlCredentials: true
maskAbsolutePaths: true
maskEmails: false
salt: "" # set a secret to harden correlatable fingerprints
customPatterns: # [{ pattern, flags?, replacement? }]
- pattern: '\\d{4}-\\d{4}'
replacement: '[CARD]'
aiMaskEnabled: false # layer a user-provided aiMaskFn on top
pdf:
engine: auto # auto | chrome | html
chromePath: "" # explicit chromium-family binary
session_export { format?, sessionId?, redact? } — export a session.
Returns artifact paths plus a compliance summary. redact: false opts out for
one-off exports.session_export_list { query?, sessionId?, format?, before?, after?, limit? }
— list/search the archive.export_delete { target } — delete by archive id / file name / relative path.sanitize_config { action?, sample? } — show the active rules or test a
sample string against them./session-export [format] [--id=<session>] [--no-redact]
/session-export-list [query] [--format=<f>] [--limit=<n>] [--id=<session>]
/export-delete <id|path|filename>
/sanitize-config [--test <text>]
/export-audit
pdf is optional and lightweight by design:
meta CSP, no scripts, @media print rules).pdf.engine: chrome (or auto), the plugin drives a headless
Chromium-family binary (chromePath or CHROME_PATH, then common install
locations) via --headless --print-to-pdf.This avoids heavy PDF dependencies (no native renderer is shipped).
contentHash is sha256 over the canonical JSON-Lines serialization of the
sanitized transcript (header excluded), so two exports of the same sanitized record
hash identically regardless of format.
npm install
npm test # node --test over test/*.test.ts (needs Node 24+ for .ts)
npm run typecheck
npm run build # tsc → dist/
Structure:
src/
index.ts # bundle entry: name / inject / apply
commands.ts # slash-command definitions
tools.ts # ctx.tools definitions + registration
adapter/ # thin dsh<->core bridge (backend, types, defineTool)
engine.ts # export pipeline orchestration
config.ts # configuration shape + normalization
core/ # harness-independent library (./core subpath)
fold.ts # event-log → transcript document
redact.ts # deterministic sanitization engine
archive.ts # on-disk archive, index, chunking, traversal guard
render/ # markdown / jsonl / html / pdf
meta.ts # compliance header
audit.ts # trend/audit aggregation
filenames.ts # platform-safe names
hash.ts # sha256 / fingerprints
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。