DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Request Log — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

dsh-request-log

Request Log

DeepSeek Harness 插件,记录每一次 LLM 提供商请求/响应,可在 Web UI 中按调用查看。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add dsh-request-log@0.1.8
README兼容性版本

兼容性与来源证明

Request Log 以 dsh-request-log 发布,当前版本为 0.1.8。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
npm
Registry 更新时间
2026/9/20

版本

0.1.8stable
2026/9/18
0.1.7stable
2026/9/10
0.1.6stable
2026/9/10
查看其余 6 个版本收起版本
0.1.5stable
2026/9/10
0.1.4stable
2026/9/10
0.1.3stable
2026/9/4
0.1.2stable
2026/9/4
0.1.1stable
2026/9/1
0.1.0stable
2026/8/31

相关插件

正在加载相关插件…

最新版
0.1.8
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
826.4 kB
文件数
11
Surface
web
许可证
Apache-2.0
发布源
npm
GitHub
★ 0
周下载
227
安全扫描
✓ v0.1.8 扫描通过
最近提交
2026/9/10
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 models-usage 分类下经过校验的插件。

Usage@linxin666/dsh-usagedsh Web GUI 的使用统计插件:检测各提供商余额和编码计划配额,并提供实时令牌使用记录,以及当前提供商的专属宠物气泡Whale Widgetdsh-whale-widgetDSH Web 界面右下角的 DeepSeek 余额小鲸鱼挂件:余额/今日已用/峰谷定价、自定义泡泡点击序列(文本/余额/今日/峰谷/图片/随机语句与并列加权选择)、逐行样式与字体、悬浮快捷编辑、音效与每轮消耗、自定义角色/动图/音效、吸附与翻转自定义Usage Stats@ychris12138/dsh-usage-statsdsh Web GUI 的令牌使用热力图、提供商余额和订阅配额Codex Connectdsh-codex-connect用于 DeepSeek Harness 的 ChatGPT OAuth 和 Codex 模型。

README

dsh-request-log

A DeepSeek Harness plugin that records every Provider request and response — exactly what was sent, and exactly what came back.

Each time dsh calls a Provider, the plugin captures the full neutral request payload (system prompt, messages, tool schemas, sampling options) and the assembled response (content blocks, usage, finish reason, timings), persists it per session, and serves a Requests / 请求 tab in the web UI where you can browse every call and inspect it in four views:

  • Neutral — the exact provider-neutral capture (what crossed the llm/stream boundary).
  • OpenAI ChatCompletion — POST /v1/chat/completions shape (reasoning_content for thinking, role: 'tool' rows for tool results, cached_tokens details).
  • Anthropic Messages — POST /v1/messages shape (system blocks, tool_use/tool_result, cache_read_input_tokens).
  • OpenAI Responses — POST /v1/responses in the exact form the harness adapter sends (store: false, full input, native reasoning: { effort, summary }); when the prior call of the conversation is known, an annotation (and the banner) reconstruct what a stateful client could have sent through previous_response_id — clearly marked as a reconstruction, never as the real request.

A call opens in the view its provider and model imply — Anthropic Messages for Claude routes, OpenAI Responses for Codex-style ones, OpenAI ChatCompletion otherwise — so the first thing you see is the shape that call really took on the wire. Neutral is one click away, and whichever view you pick is remembered per session.

The JSON bodies render as a collapsible tree: containers fold past depth 2 by default, long strings (a 40KB system prompt) clamp behind a … +N chars toggle, and Expand / Collapse buttons control the whole view.

The wire views are reconstructed from the exact capture, mirroring the mapping dsh's pi-ai adapters perform — differences between the views are exactly the differences between the protocols. Adapters resolve some things the neutral boundary never sees (the exact Anthropic thinking mode, thinking signatures, adapter default max_tokens); where a detail is unknowable the rendering keeps a legal wire shape and marks the gap with a namespaced _note — the views show what a real client could have sent, never an invented value.

Features

  • Every attempt recorded — including each retry of a failed call (retries are correlated by a request hash and numbered attempt: N).
  • Step numbers — each ordinary call wears a #N badge numbering its place in the session's conversation loop (what was called at step 1, step 2, …); retries share their step, auxiliary calls take none. The detail view shows the same step in its call card.
  • Auxiliary calls too — session-title and compaction calls are tagged with their purpose.
  • Usage statistics — 统计图 — an initially open, switchable chart panel above the ledger plots every loaded call: cache-hit rate per step (fixed 0–100 % axis), token volumes (in / cache-hit / cache-write / out, drawn as stacked columns, floor-up — one per call on the step axis, one per wall-clock window on the time axis), latency phases (total vs TTFT), output speed; a model filter narrows the summary, the charts and the ledger to one model's calls. Two x axes — by time (token columns total each window; 1/5/10/30m or a custom width) or by step — and nothing else to set. Hover for exact values, click a point to center and select its ledger row, error/aborted calls draw gaps instead of fake zeros, auxiliary calls stay off the axis (badge counts them). The open state, active metric and axis ride the same per-session memory.
  • Timing — start time, TTFB (first chunk), and total duration per call.
  • Usage — input/output/cache-read/cache-write tokens exactly as the provider reported them.
  • Durable — one JSONL file per session under $DSH_HOME/request-log/, with retention (default 14 days, or never to keep everything the way dsh keeps its own session logs), a per-session call cap (default 2000), and a per-session byte cap (default 128 MiB, counting each line plus the object bytes that append added; oldest records trimmed first).
  • Fail-soft — capture and storage failures never break a model call; a partially-written line is repaired on the next append; the read API is optional (a headless composition simply skips it).
  • Fenced — the read API serves the loopback host (plus any trustedHosts you configure) only; DNS-rebinding and cross-site requests are refused before any read, and a loopback-named Host is honored only when the connection itself is loopback (a LAN peer cannot spoof it).

Privacy

This plugin persists the complete plaintext of every model call — your full prompts, conversation history, tool schemas, model outputs, reasoning traces, and the provider's own failure text (common credential shapes in it are redacted on capture, but treat it as sensitive anyway) — under $DSH_HOME/request-log/, kept for retentionDays (default 14; never keeps files indefinitely). The content lives in two places: the <sessionId>.jsonl envelopes and the deflate-compressed objects in objects/ — compression, not encryption. Deleting a session's .jsonl does not erase its content: the objects stay until a sweep's GC reclaims them, and once the plugin is uninstalled no sweep ever runs again — to erase everything, delete the whole request-log directory.

Anyone with access to that directory can read it all; files are created owner-only where the platform honors modes, but assume no more than the directory itself grants. The read API serves the loopback host plus any trustedHosts you configure — every trustedHosts entry is an unauthenticated grant of the full transcripts to every device that can reach that authority. When the harness web server is bound to a non-loopback interface, remote peers are refused unless they arrive under a configured trusted host: a spoofed loopback Host header does not pass, because loopback trust rests on the connection, not the header. Images are recorded as attachment references only, never bytes. Control everything with the directory / retentionDays / maxFileBytes / maxCallsPerSession settings below.

Install / Update

dsh plugin --profile web add dsh-request-log

Or to update:

dsh plugin --profile web update dsh-request-log@latest

Then start the web UI with dsh web (restart it if it was already running) and open any session's Requests tab.

Use it

Open any session and click the Requests / 请求 tab:

  • The list is a chronological ledger — the newest call sits at the bottom (like the Trajectory tab), with status dot, model, step badge (#N), finish reason, TTFB / total latency, token counts, and a messages/tool-cells column where the tool number counts invocations — the tool-call blocks the response made, with a run_code program's inner tools.x(...) dispatch sites standing in for the transport call (hover for the per-tool breakdown). It loads the whole session — the first page paints while the older ones are still arriving in 1000-row offset pages, and the summary strip's N of TOTAL counts them in; your scroll position is anchored while older rows prepend, unless you are pinned to the newest call; retries show a ×N badge; auxiliary calls show their purpose. When a session ran on more than one model, a chip row above the summary strip filters by model — one aperture drives the summary, the charts and the ledger together, and the model you would switch back to stays offered even when its calls have not been paged in. One subgrid derives every column from its widest header or value; the resulting content-width table is centered in the pane and scrolls sideways only when the pane is narrower than that content floor, never by squeezing or truncating a column. The Time column is a bare clock within one day; once the loaded window spans days, every row leads with its date (times never repeat ambiguously), and full timestamps — row hover, the detail's started-at — carry their UTC offset.

  • The summary strip reads as headline figures — calls, failed / retried when non-zero (the only red and amber in the strip, hover splits errors from aborts — the health reading dsh's own footer has no equivalent of), total input, hit rate, output, and Disk added — over one breakdown line that names what it decomposes (Input 1.45M uncached · 99.82M cached · 0 written). Billed input is those three summed, so they sit under it rather than beside it as equal siblings. Disk added is what this session added to the store (envelope lines + the compressed objects its appends created), with the split and its share of the per-session cap on hover. Marginal, not the transcript's weight — a retry adds 0, and pieces two sessions share bill to whichever wrote them first. The same tooltip names the file those bytes went into (~/.dsh/request-log/<sessionId>.jsonl, the compressed objects beside it under objects/), and clicking the figure copies that path — home is written ~, so neither the strip nor a screenshot of it carries your account name.

  • The strip's toolbar carries ↑ / ↓ jump controls (back to the summary, on to the newest call — the latter also re-arms the auto-refresh pin), greyed out when there is nowhere to jump. The strip itself pins to the top of whichever box actually scrolls, so the totals stay on screen however far down the ledger you are.

How it works

  • Capture — a transparent llm/stream waterfall listener. Every real attempt (retry attempts re-enter the waterfall) is recorded and passed through unchanged.
  • Storage — one JSONL line per settled attempt, appended to $DSH_HOME/request-log/<sessionId>.jsonl. Each line is a v3 envelope carrying one tree hash that names the whole ordered request piece list, the response body's hash, and zn — the compressed bytes that append actually materialized in the object store. Bodies and trees both live in the git-style content-addressed object store (objects/<xx>/<sha256>.drl, deflate-compressed); a tree is a keyframe holding the full piece list, after which each turn writes a small delta naming only the pieces it added (chained by parent pointer, re-keyframed every 32 nodes), so the envelope line stays flat however long the session runs — over a real store's 2,570 lines: 774 B median, 656 B min, 1,147 B max, and the longest session's 164th line (722 B) is no bigger than its first (778 B). What remains is the fixed part, not history: in a 702 B line, 200 B of usage counts, 146 B of hashes, 89 B of timings, the rest ids and model. v2 instead re-listed every message hash per line and grew linearly with history (97.6 % of envelope bytes on a real store). zn also makes the byte cap track materialized content exactly: a piece already on disk bills nothing, a retry bills 0 — which also means pieces two sessions share bill to whichever wrote them first. Reads stay cheap: list pages project from the envelope alone; only the detail view resolves the tree (a bounded parent walk) and reassembles blobs. On-disk occupancy runs higher than the content figure — until the sweep packs it: 97 % of objects are ≤ 4 KiB and so occupy a full filesystem cluster whatever their compressed size, so budget roughly 2× the compressed size while objects are loose — on one real store (2,570 calls), 11,279 objects storing 15.34 MB occupied 30.86 MB loose. The sweep's pack pass then moves cold objects — reachable, and older than the GC grace floor, so a pending append can never lose its body — into immutable solid-block pack files under objects/packs/ (1 MiB blocks; a pack file grows to 64 MiB), ordered by the call that first introduced each object — the pieces come out of the tree nodes, since an envelope line names only its tree and its response body — because neighbouring calls re-send nearly the same conversation and order alone swings the packed size by 36 %, and unlinks the loose copies only after the pack and its index are both durable. The same store swept: 7.51 MB of pack bytes plus a 0.52 MB index across 37 solid blocks — ~1.0× occupancy, no cluster slack — and a detail read touches p50 4 / p90 6 blocks (max 9: many sessions interleave, so a conversation's pieces scatter) in the ~8 ms class. A pack at least 8 MiB in size whose live share falls under half is rewritten without its dead weight, and is the way back out — it stops packing and gradually unpacks existing packs to loose objects, so the format is never a one-way door. Packs (like the JSONL layer) assume : two processes sweeping the same could interleave pack writes into a torn block. v1 and v2 files stay readable forever and migrate lazily to v3 within a per-cycle byte budget (newest sessions first, 64 MiB of legacy source per sweep — enough to clear a large backlog well inside the retention window); a daily sweep garbage-collects unreachable objects, marking transitively through tree chains, and skips every reclaiming phase whenever it could not build the complete reachable set — missing a cycle costs a day of disk, deleting on partial knowledge costs the record forever; and freezes legacy behavior if you ever need the old format byte-for-byte.

Configuration

The loader row's config: block (cordis.patch.yml) accepts:

fielddefaultmeaning
directory$DSH_HOME/request-logwhere the JSONL files live — pointing this at a synced folder (OneDrive/Dropbox) or a network share puts the plaintext in the cloud / on the wire
retentionDays14delete session files untouched this long, in days (1–3650). never keeps every file forever — dsh's own session logs are never deleted, so this is what following the host looks like. 0 is refused: keeping nothing and keeping everything must not be one keystroke apart
maxCallsPerSession20000line-count guard, not the bound — maxFileBytes is the bound. The sweep deletes the oldest records past this many lines, keeping the newest N; a session that runs past it loses its beginning, silently. Measured on a live store, 2,034 calls cost 5.6 MB of objects against the 128 MB byte cap — the old 2,000 default cut history roughly 23× earlier than the bound meant to do the bounding. Reads stay incremental (mtime+size cache, tail-only parse, model rollup cached with it), so 20,000 lines costs a one-time full parse per restart, not a per-poll walk
maxFileBytes134217728per-session cap on envelope-line bytes plus the compressed object bytes each append materialized — a piece already on disk bills nothing, so a retry bills 0 and shared pieces bill to whichever session wrote them first. Budget it as marginal content, not as disk: real occupancy runs ~2× the compressed size while objects are loose and ~1× once the sweep has packed them (see Storage above). Oldest records are trimmed first, and the raw .jsonl is held under the same number by a separate file-size guard
formatautorecord format for new writes: auto = v3 deduplicating tree envelopes (+ lazy migration of old files), v1 = legacy full-body JSONL
packautowhether the daily sweep packs cold reachable objects into solid-block pack files under objects/packs/ (packing waits out the GC grace floor, so fresh writes stay loose): auto packs and repacks, off stops packing and gradually unpacks existing packs back to loose objects — the rollback door for downgrading to a build that cannot read packs; either way, one writer process per store directory

Development

pnpm install
pnpm run test     # typecheck + vitest
pnpm run build    # lib/index.js (host) + lib/client.js (browser)
pnpm run watch    # rebuild on change; run alongside `pnpm run dev:web` from a dsh checkout for client HMR

Register the local checkout into your web profile:

dsh plugin --profile web add .

Compatibility

Built against @deepseek-ai/dsh 0.1.1-rc.2 (@deepseek-ai/dsh-llm 0.1.1-rc.2 wire types). The client UI uses CSS subgrid for the ledger table and :has() to suppress dsh's chat-width drag strips over the ledger, so the browser needs Chrome/Edge 117+, Safari 16+, or Firefox 121+ (all 2023 — :has() is what raises the Firefox floor).

License

Apache-2.0

  • Auto refreshes the list every 3s while the session is running (paused while the tab is hidden). A transient refresh failure keeps the loaded data and shows a stale-data banner instead of clearing the ledger.

  • 统计 / Charts — a toggleable panel above the ledger, open for a fresh session, switches between four per-call charts: Hit rate (each step's cache-read ÷ billed input, on a fixed 0–100 % axis — cold starts, post-compaction drops and error gaps are all visible at a glance), Tokens (in / cache-hit / cache-write / out, drawn as stacked columns — one column per call, segments floor-up: cache hits floor it (they are the part of the input you did not pay full freight for, so the height above the green segment is what the call really cost) and the column top is billed + output. Columns rather than lines because token volumes are per-call quantities, not a signal sampled over time: a line between two calls interpolates tokens that were never spent, and on a 98 %-cache-hit session four stacked lines land within a couple of pixels of each other and read as one thick line. A legend chip drops its band out of every column, the segments above closing the gap; a segment thinner than half a pixel draws nothing but still counts toward the ones above it. What a column measures follows the axis: one call on the numbered axis, one wall-clock window on the clock axis. Reasoning is not broken out: it is a subset of output, and a model that never thinks reported a permanent 0 chip — the per-call detail card still shows a reasoning row when the provider reports a non-zero count), Latency (total vs TTFT), and Speed (output tokens/s of the stream phase). Hover anywhere for exact values (crosshair + tooltip); click or tap the snapped point to center and highlight its exact ledger row. On a retry-collapsed step this selects the attempt that supplied the plotted value. Calls that never reported usage draw gaps, never fake zeros.

    A two-segment 按时间 / 按步骤 (By time / By step) control picks the x axis; both segments are always on screen with the drawn one lit, and the choice rides the same per-session memory. By time is the default: every call sits at its real wall clock, so a burst of calls clusters tight and an idle hour is a visible stretch of empty axis — the shape of when you asked for what. A timestamp is only a coordinate, never a call identity: concurrent main and auxiliary calls that start in the same millisecond remain separate plotted points. Ticks land on local wall-clock boundaries (and carry the date once the span leaves one day); each retry attempt plots separately, because in time a retry is another request; and compaction / session-title calls join the chart, since a compaction is usually what explains a hit-rate cliff. By step is the numbered axis: every conversation turn gets equal width and a #N, retries collapse into the step they retry, and auxiliary calls drop off the axis (an ⓘ badge counts them).

    On the clock axis the Tokens columns are per-window sums — how much was burned between 14:00 and 14:05, not what one call spent — with a 时段 / Window control (1m / 5m / 10m / 30m chips plus a custom width up to a day) that appears only there, because the window is what the column measures. One column per call at its real timestamp would take its width from neighbour spacing, drawing 1px slivers inside a burst and a fat slab around an isolated call; the numbered axis already gives the per-call reading properly. A window is a sum over the calls that started inside it, so a call that reported nothing contributes zero instead of punching a gap, and a window with no calls renders as an empty column — an idle hour looks idle. Windows anchor to local midnight, so they land on wall-clock boundaries in half-hour-offset zones too. The other three groups are sampled signals and stay per-call lines on both axes.

  • Click a row to open the detail view: a summary card (provider, model, effort, timing, usage, finish) plus the full request/response JSON — switch between the four views (it opens on the protocol detected for that call, not on Neutral), toggle line wrapping, or copy the JSON.

  • Your place is kept — the tab only renders while active, but per-session view memory (in-page + sessionStorage) reopens the call you were viewing, its request/response side and format, the model filter, and the Auto toggle when you switch tabs — or even refresh the page. Each session remembers its own view.

  • pack: 'off'
    one writer process per store directory
    dsh
    $DSH_HOME
    format: 'v1'
  • Read API — same-origin GET routes under /dsh-request-log/ (/health, /sessions/:id/calls, /sessions/:id/calls/:callId), served by the harness web server behind a Host/Origin trust fence (loopback + trustedHosts). The list route also reports the session's storage footprint — envelope bytes, materialized object bytes, their sum, the maxFileBytes in force, and the session file's path (absolute, but with the home prefix written ~, so the host account name never crosses the wire) — the same accounting the cap bills, so what the UI shows is what a trim would act on. It is marginal: a piece already stored bills nothing, so per-session figures never sum to the store directory's size.
  • trustedHosts[]non-loopback authorities the read API may serve (host or host:port); every entry grants unauthenticated full-transcript reads to every device that can reach it