ac0033
dsh-ctm
No description
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 16, 2026
- Updated
- Aug 16, 2026
Introduction
dsh-ctm — Context Transparency Manager
English | 中文
Turns the model's context into a visible, editable, effectiveness-scored first-class object, published as a self-contained bundle plugin for DeepSeek Harness.

Features
- Full visibility: a turn → segment flow view colored by role / tokens / cache status / effectiveness, with markdown rendering and paging.
- Token accounting (provider-measured): the KPI strip uses MECE buckets — uncached input / cache hit / output (cache write appears separately only when > 0) — plus a derived cache hit rate (hit ÷ bucket sum; hidden when the denominator is 0) and the model. All figures are whole-session cumulative, read preferentially from the
tokenUsagesession projection, falling back to a full-log event fold when the projection is unavailable (the summary'susageSourcemarks which path was taken). Turn headers show the sum of that turn's step requests; step headers show that single request's measured usage (tooltips state the exact semantics); the latest request's prompt size and context-window occupancy (when thecontextPressureprojection is readable) sit in the KPI tooltip. Segment-level token counts remain local heuristic estimates and are explicitly labeled as estimates. - Live refresh: event-driven (reactive
useSessionsubscription) — new messages, tool results and turn boundaries update immediately; no polling. - Initial system prompt: shown once as segment 0 inside the "System prompt" panel.
request/headeris not logged only once — loop boundaries (initial / resume) and request-envelope changes (model switch, prompt edits) each write a fresh full snapshot, and the newest one wins; CTM reads it viarequestHeader()for live sessions and falls back toreadSession()for historical ones. It is replaceable (see below). - Editable: replace, delete, rollback, snapshot restore, undo, effectiveness override (manual), and copy segment content (to re-send a rolled-back message, copy the original into the host input box — the host chat UI does not expose its input box to plugins). The initial system prompt is replaceable; all other system-injected content (runtime context etc.), tool results, and CTM placeholder markers are read-only in the UI.
- Effectiveness engine: automatic
effective/redundant/stale/injectedverdicts with manual override. The 2-gram similarity sets are LRU-cached (500-entry cap), never rebuilt from scratch per request. - Apply for real (off by default): when enabled, replace / delete / rollback no longer intercept requests — they are written into the session log as surface
replaceevents atagent/pre-step(same timing, same mechanism as official compaction), honoring DSH's model-visible ⟺ logged invariant: replay / fork / token accounting always match what the model actually saw. Edits are badged "pending" until logged. Deletes and rollbacks are carried by placeholderuser/messagenodes (grouped under "User input" in the UI, read-only protected — not classified as system injections); assistant revisions are carried as user messages by role demotion; tool-call/result pairs are never split (deletion always shadows a minimal balanced range: deleting a tool result absorbs the assistant message carrying its tool-call, so the model never sees a dangling call). Undo = a queued (not yet logged) edit is simply dequeued; an already-logged edit is reversed by a counter-replace with the original content (only the most recent operation is undoable); undoing a rollback or a multi-node delete uses a restore group: the placeholder node is replaced by the first shadowed item and the rest append to the tail in original order, all demoted to user messages (an append-only log cannot re-add assistant/tool roles), and a restore group is itself undoable (undoing it re-shadows the run). Logging failures are recorded and surfaced at the top of the view. Mutations carryingexpectedVersionare checked optimistically — a version mismatch rejects the request with zero side effects. - System prompt editing: a replacement of segment 0 is stored as an override and swapped into the section list by the
system-prompt/assemblewaterfall at the next prompt assembly; newrequest/headersnapshots are logged automatically by the agent loop. - i18n: Chinese / English UI.
- Zero coupling: host↔client runs over plain HTTP
POST /ctm— no Typert@Remote/dsh-api-remotes; zero runtime dependencies (the tool-pair balance check is re-implemented locally instead of depending on@deepseek-ai/dsh-compaction).
Architecture
src/
├── contract.ts # shared contract: TS types + Zod runtime validation (single source of truth)
├── host.ts # host half: POST /ctm route + the agent/pre-step & system-prompt/assemble waterfalls
├── usage.ts # MECE bucket conversion / summation / full-log fold for usage (pure logic, unit-tested)
├── surface-edits.ts # edit logging layer: replace-event construction, tool-pair balancing, edit-queue application (pure logic, unit-tested)
├── bigrams.ts # 2-gram sets + LRU cache for the effectiveness engine (pure logic, unit-tested)
└── client/ # browser half: conversation.view tab + fetch('/ctm')
contract.ts: the host validates requests with it, the client validates responses with it. Types are inferred from Zod schemas, and runtime validation provides cross-version tolerance — the key to a community plugin shipping on its own cadence. New fields are always.optional()(e.g. segmentpending,usage; stateapplyError); the single exception was the summary usage rework (oldinputTokens/cachedTokens/...→ MECEtotal/lastRequest/usageSource) — host and client ship in one package, so that one breaking change was made directly.host.ts: registersPOST /ctmviawebServer; injectssessionQuery/sessions/tokenMeter. Reads surface events fromreadSurface(after an edit lands, this is automatically the post-edit surface), and the system prompt fromrequestHeader()(live) orreadSession()(historical), inserted once as segment 0. Usage has two channels: assistant segments carry the provider-measured usage of a single request from theassistant/messageevent; session-level totals prefersessionProjections.snapshot()(an optional service captured through anctx.injectchild context — cordis throws on any read of a service not declared ininject, optional chaining does not help) for thetokenUsage/contextPressureprojections — the projection folds the complete log, immune to compaction/shadowing — falling back to summing the full log event by event (correct but O(log) per read), with the summary'susageSourcemarking the channel actually used. With apply for real on, edits enter a per-session queue and are appended group by group in theagent/pre-stepwaterfall (the turn is open and the request not yet built, so edits take effect in that very request; edits to an idle session queue up and land on the next step). The system-prompt override swaps the whole section list in thesystem-prompt/assemblewaterfall. Memory is bounded: session store LRU capped at 50, 20 snapshots per session, 50 trash entries per session.client/: registers a "Context" tab inconversation.view; every operation round-trips throughfetch('/ctm')with responses validated byctmResponseSchema; refresh is triggered reactively viauseSession. A "pending" badge distinguishes queued-but-unlogged edits from applied ones.
Wire protocol
POST /ctm; the body is an op-discriminated union (JSON), and every request carries a sessionId:
| op | extra fields |
|---|---|
getState | — |
replace | segmentId, content (UI allows replacing only the initial system prompt and non-system-injected segments; queued for logging when apply for real is on) |
delete | segmentId |
rollback | turnIndex |
restore | snapshotId |
reset | — |
undo | — |
override | segmentId, value (string | null) |
setRealtime | enabled |
Response: { ok: true, state: CtmState } | { ok: false, error: string }.
Every op may additionally carry an optional expectedVersion (the version of the last state the client applied): the host compares it before any side effect and rejects mismatches with a stale_version error notice (optimistic concurrency against multi-client / stale-page races).
Known limitation: undoing a rollback or a delete involving tool pairs restores the removed content as user messages (the first item replaces the placeholder node, the rest append to the tail) — an append-only log cannot re-add assistant/tool roles. This is the same role-demotion scheme as assistant edits.
Install / uninstall
# Install from GitHub (pinning a tag is safer; on first install pnpm will ask you
# to add the package to allowBuilds in the profile's pnpm-workspace.yaml — git
# installs pull source only, and the package's prepare script builds it at install time)
dsh plugin --profile <name> add github:ac0033/dsh-ctm#v1.0.0
dsh plugin --profile <name> remove dsh-ctm
add automatically writes the package into the profile's dependencies + dsh.profile.bundles (because it declares dsh.bundle) — no manual cordis.patch.yml edits; remove cleans up the dependency, the bundle layer and node_modules together.
Local development
pnpm install
pnpm build # produces dsh/index.js (host) + dsh/client.js (client)
Publishing
- Run
pnpm buildbefore publishing (e.g. a"prepublishOnly": "pnpm build"script); community users then receive the prebuiltdsh/index.js+dsh/client.jswith zero build steps. - Zero runtime dependencies:
zodis inlined into both bundles;reactresolves from the shell's module table. - When moving to your own scope, update both
nameinpackage.jsonand thename:row incordis.patch.yml.
License
MIT © YuanLumen