Skip to content
←Back to Open Source

OPEN SOURCE DEEP DIVE

Agent ContextLLM AgentObservabilityDeveloper Tools

dsh-context: turning DeepSeek Harness context into an auditable ledger

A DeepSeek Harness plugin for context insight and management: five surfaces cover the cross-session overview, per-session composition/trend/events/file activity and the subagent fleet; the host folds the V4 durable event log into a per-request composition timeline, meters parity-verified against the runtime own projections, and probes a per-tag compatibility matrix against real harness sources.

bowenliang123/dsh-context2.8kTypeScriptApache-2.010 min read

What it actually solves: the context window is the agent's real budget

Anyone who has run a long agent session knows the drift: replies slow down, tool calls start losing information, and after one auto-compaction the agent behaves like someone else. The culprit is almost never the model; it is what the context window holds, when each part entered, and what squeezed it out. dsh-context is a plugin for DeepSeek Harness (@deepseek-ai/dsh, the official open-source agent runtime and web UI) that does one job: turn "context" from a black box into an auditable ledger - what each request's window contained, what every part cost in tokens, which step rewrote it, and what the whole agent family billed, all in the open.

The npm package is simply dsh-context, Apache-2.0 licensed, with English and Simplified Chinese UI in the same repo. It is publicly recommended and starred by DeepSeek Harness staff. Releases move fast (0.73.x), yet each one ships with a compatibility document whose every rule carries its own evidence - rare in a plugin ecosystem.

Five surfaces, one ledger

The plugin projects the same fold state onto five surfaces, so the numbers agree wherever you enter:

SurfaceQuestion it answers
Context Insights pageCross-session overview: KPI band (sessions, billed tokens, estimated cost, cache-hit rate, turns, tool runs, active time), 7-day usage bars, 8-week activity heatmap, aggregate composition ring, filterable session cards that jump straight in
Context tabThe per-session dashboard: stats, token composition, per-request trend, event log, file activity
Fleet tabThe agent family topology: current agent, its parent chain, every subagent, one live card each
Right-sidebar Context panelThe same dashboard beside the chat, no page switch while reading
/context slash commandA centered modal with current composition plus the context browser, without leaving the chat

Plus a Preferences card: default view placement, trend granularity and mode, sort orders, and a system-prompt prefix/suffix joined around every session's system prompt for all agents including subagents - effectively hanging a per-deployment persona on the harness's own prompt assembly chain.

The Context Insights page: KPI band, 7-day usage chart, activity heatmap and session cards

Host side: folding the durable event log into a per-request timeline

The data core is a fold in src/host/fold.ts: registered as a session projection unit on the harness, the framework calls applyTimeline once per committed session/event, replaying durable events into a timeline of "what every model request's window was made of", persisted through the projection cache. Several disciplines on this path deserve naming:

  • Folds are TOTAL. Unknown event types return state unchanged; a malformed event drops whole, never half-applied; no undefined-valued property is ever materialized into persisted state. The comment explains why: the projection registry drives apply with no error boundary, so one throw stalls the unit's cells and push feed forever, and one undefined property fails every later projection-cache write for that session.
  • Same reference means no change. When an event does not change state the fold must return the identical reference - Object.is gates the change feed - and only real changes build a new reference via lazy shallow clone. State stays plain JSON and bounded, the cached-checkpoint precondition.
  • Category rules. developer/message folds into injected context, never human inputs; the skill machinery (available-skills catalog, /skill invocations, skill-tool loads) gets its own bucket so a stealthy skill's footprint cannot hide inside the injected-context and tool-result buckets; tool-result errors read message.isError alone, and a hostile envelope-only row degrades to unflagged instead of throwing.
  • Cache evolution has rules. A unit's stateVersion bumps only when later events cannot backfill the change (running totals, per-step collections flushed at step end, materialized envelopes); serve-time-derivable fields never bump, and new wire fields are additive-optional. Stale rows refold from the durable log; warm-up probes both contextActivity and contextTimeline; sessions over 2,000 events refold in in-memory slices with one write-back, byte-identical to a single-shot cold snapshot.
  • Cold reads pass a host-wide gate. FIFO, one at a time, only while the V8 heap sits below two-thirds of its ceiling; a skipped read takes the caller's designed no-data path, never a failure - observability must not crush the host it observes.

Metering parity: mirror, don't import

Every figure the plugin prints that the harness also prints is not imported (most of those functions live inside the kernel bundle with no package export) but mirrored: src/shared/estimate.ts re-implements the harness token-meter's fixed density (about 4 chars per token, +4 per content block, +4 role framing), and the compatibility suite then verifies equality differentially against the harness's own projections (sessionStats, contextBreakdown, contextPressure, tokenUsage). The headline occupancy and composition read the same official projections as the composer's context ring, so the card's percentage always matches the ring.

Cost is equally restrained: list prices come from models.dev with per-1M rates on hover; only DeepSeek splits peak/off-peak, its listed rates being off-peak with peak windows billing at 2x, judged by Beijing-time weekday/weekend windows - and the doc declares that no statutory-holiday calendar is maintained, so a holiday weekday inside a peak window prices at peak while the platform bills off-peak, stated in the tooltip instead of pretending precision. The compaction reserve mirrors compaction-basic shipped defaults (thresholdRatio 0.8, headroom 65536): when window minus reserved output minus headroom is non-positive the backend refuses the config and the bar paints no masks. Even the declared divergence is documented: the timing card's wait/generation boundary opens at the first observable instant, not the meter's first-token anchor, so marker-tiled decode buckets and the split never double-count one window.

Client boundary: re-prove every shape at the edge

The browser half trusts nothing the host pushes: timelineSource.ts re-proves collections at the boundary, keeps whole-value absence as null (the loading screen), narrows structurally instead of blind casting, and skips elements that fail the shape instead of throwing; per-item work is isolated over hostile objects, and every async read resolves to data or a retryable failure note - never an eternal spinner; a pushed revision decrease resets the detail ledger so stale reads never merge into the new head.

Capability mounting is equally conservative: every capability rides an optional seam (deferred ctx.inject, the face re-proved before use, guarded registration) - no service, no capability, no pending fiber, no throw; at most one mount per page (issue #137: the client-plugin runner force-assigns a fresh shadowing priority per registration, so the slot registry's same-priority duplicate guard never fires across a double mount); session jumps use the sidebar's own verb uiWorkspace.openSession, not the open(id) removed by the 0.1.6 selection refactor; and fetch routes ride browser-relative spellings (issue #159), because behind a path-prefixed reverse proxy a root-absolute /api/... 404s at the gateway while the relative form keeps the prefix.

Compatibility engineering: shape-driven, never version-branched

docs/compatibility.md is the hardest document in the repo: it supports exactly 0.1.7-rc.2, 0.2.0-rc.2 and 0.2.1-rc.2 (all V4 durable-log generation) and mandates one shape-driven code path that never branches on a detected harness version; it trusts the harness's migration guarantee, carries no pre-V4 readers anywhere, and refuses unconvertible logs with SessionFormatUnsupportedError rather than serving them raw. A below-floor harness is caught again at runtime by the plugin itself: zeroed fallback units plus an upgrade modal; an undetectable version fails open into the real units - a probe misfire must never blank a working deployment.

Verification lives in the compat vitest project: per baseline tag it stages the harness's real sources, boots the built plugin into that tag's actual registries on its vendored cordis, probes host and client seams, asserts retired seams absent (settings.register, settings get(ns), sessions open(id)), and asserts every event family fold.ts switches on is declared by every baseline; CI fails on a missing tag. A manual per-release install check completes the picture: in a disposable DSH_HOME, dsh plugin add must yield exactly one composed row in --dump-config, and remove must yield zero.

Context Trend: one stacked bar per request, step briefs and compaction marks

Reading the surfaces

Context Trend draws one stacked bar per model request - finer than per-message - with a three-row plain-language step brief under the chart (User recalls the opening message, In lists what newly entered, Response shows the reply and tools called); clicking a row opens that exact message in the browser. Compactions and prunes are pinned with scissors to the bar where they happened; granularity switches Step/Turn, mode switches Total/Delta (in Delta mode a compaction dives below the baseline); the Overview toggle squashes the whole session into one continuous stacked ribbon; DNA mode lays the context out flat in the model's reading order, each row carrying its prompt position, category, introducing step and token price.

Context Browser opens the assembly box of any request: seven collapsible categories, each element expanding again into its actual content - the system prompt text, each tool's JSON schema, message text, reasoning, tool arguments and tool outputs. Every tool-schema row carries a source chip (tool-* first-party packages, dsh-* capability packages, mcp:<server> proxies, or a plugin watched live); signed delta badges diff against the previous turn; steps older than a compaction are reconstructed from the removed-message archive, and the card admits when a step's makeup is only approximate.

File Activity lists what the agent did to your files: per-purpose count chips double as filters, every edit/write contributes an estimated +added/-removed line delta per file and summed; native tools, the Minimal preset's str_replace_editor, and nested calls inside PTC run_code programs all fold into per-tool rows; searches land on real files with hit counts; clicking a row expands the full operation log, each op jumping to the exact tool result in the browser.

The Fleet tab's agent network: parent chain and seven subagent cards, live

Fleet draws the family: one card per agent (title, live context size and window occupancy, composition bar, runtime and step count), edges fanning from each parent in the lineage's color; hover lights the whole chain, click jumps into that session's own Context tab; running agents breathe with a green pulse on their incoming edge. Narrow panes fold the family into tidy rows - never a horizontal scrollbar.

Install, limits, and why it belongs in the harness domain

Installation is two steps: search dsh-context in the Add plugin wizard of the Harness web or desktop app and click Install, or run dsh plugin --profile web add dsh-context then dsh web - no build, no restart. Known limits are stated openly: after image offload and before compaction, the fold reads raw durable events, so an offloaded message keeps its pre-offload image count and price; a deployment registering a complete system-prompt section keeps only that section, dropping prefix/suffix.

The reason it belongs in the harness capability domain is direct: the competitive frontier of harness-grade infrastructure is moving from "can it run agents" to "can it show what agents consume". What dsh-context offers is not another log layer but a metering discipline verified equal to the runtime's own projections, a per-tag probed compatibility matrix, and a plugin engineering paradigm of shape-driven code, boundary re-proof and fail-open probes - precisely the three scarcest things in harness ecosystems today.

As an Amazon Associate, we earn from qualifying purchases.