dsh

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package dsh implements an event-level adapter for DSH, the DeepSeek Harness.

SURFACE. DSH keeps one append-only session log per session at ${DSH_HOME:-~/.dsh}/sessions/<project-dir>/<session-dir>/session.jsonl.zstd. The default physical encoding is a concatenation of independent Zstandard frames — one checksummed frame for the header line, then one per durable append batch — so the file is JSONL only after decompression. A backend configured `compression: none` writes the same logical lines as plain session.jsonl instead, and discovery accepts both spellings. This is the second zstd surface in the harness matrix (codex is the other) and, unlike codex's, it is compressed BY DEFAULT: a reader without a zstd decoder sees nothing at all here rather than losing only cold sessions.

WHAT IS READ. Accounting and identity records, by name:

  • `session` — the immutable header line: session id, cwd, agent preset.
  • `assistant/message` — one completed model call: `data.usage` plus the provider/model that served it. This is the ONLY usage record.
  • `tool/call` — one tool invocation the model requested, named and correlated to its step.
  • `request/context` — provider/model fallback for a later completed call.
  • `assistant/chunk` — usage-chunk sequence numbers only, to detect a completed message that lost its reported accounting.

Everything else — user prompts, tool results, packed chunk rows, request headers with their system prompt and tool schemas — is ignored by TYPE, so content never enters a decode in the first place.

SPLIT-IDENTITY TRAP (confirmed live). The same model call reports its usage TWICE: once as an `assistant/chunk` whose `chunk.type` is "usage", and once as the step's `assistant/message` `data.usage`. The numbers are identical — DSH's own token meter says so ("a final assistant-message usage for the same (turn, step) replaces that sample instead of double-counting it"). This adapter counts ONLY `assistant/message`. The message's `sourceEventSeqs` names every chunk seq it collapses, including that usage chunk, and is kept in the audit payload as the evidence that the collapse happened.

TOKEN ACCOUNTING. DSH's TokenUsage counts are DISJOINT: `inputTokens` is UNCACHED input, with cached input reported separately as `cacheReadTokens` and `cacheWriteTokens` (billed input is the sum of the three), and `reasoningTokens` is a subdivision of `outputTokens` that is never added again. So the components map straight onto the Anthropic-style ledger columns and the total is their sum; the source reports no total of its own.

FORK SEEDS. A forked session's log opens with its parent's leading events copied VERBATIM under a new session id, and `seedLength` counts them. Usage and activity keys are minted from the message identity, so those copies COLLAPSE onto the originals. Turn context cannot ride that: its value comes from the session HEADER, and the two headers can disagree about the very records they share, so the seeded prefix records no context here and leaves those turns to the log that owns them. See header.seeded.

COST ATTRIBUTION. A DSH step is, by the harness's own definition, "one model call plus the tool executions it requested", and every `tool/call` carries the (turn, step) it belongs to. So a call joins its usage row EXACTLY, with no timestamp guessing: the usage row is the `assistant/message` of the same (turn, step). When a step somehow carries no assistant message, or more than one, the calls are emitted UNATTRIBUTED rather than attached to a guess.

CRITICAL: strictly read-only. Files are opened O_RDONLY, never written, locked, rotated or repaired — DSH's own loader repairs a torn tail, and this adapter must never be the thing that does it.

Index

Constants

View Source
const HomeEnv = "DSH_HOME"

HomeEnv names the environment variable that moves the DSH home, and with it every session log this adapter reads. Exported for the same reason claudecode.ConfigDirEnv and codex.HomeEnv are: what gets collected is decided here, not by the defaults, and a supervised install must account for that.

NOTE FOR THE INTEGRATOR: add dsh.HomeEnv to cmd.discoveryEnv(). Until then TestDiscoveryEnvCoversEveryAdapterVariable fails by design — it reads the adapter sources precisely so a new discovery variable cannot be forgotten.

Variables

This section is empty.

Functions

func New

func New() adapter.Adapter

New returns a DSH adapter.

Types

type Adapter

type Adapter struct{}

Adapter reads DSH session logs. Read-only.

func (Adapter) Capabilities

func (Adapter) Capabilities() model.ToolCapability

Capabilities declares what this project can say about DSH.

Cost is COMPUTED: nothing here calls SetCost. Activity is an EXACT join — the call and the usage object it was billed under come from the same record, so every activity row names the usage row that paid for it.

func (Adapter) Collect

func (a Adapter) Collect(ctx context.Context, src adapter.Source) (adapter.Observation, error)

Collect reads one session transcript in full.

func (Adapter) CollectIncremental

func (a Adapter) CollectIncremental(ctx context.Context, src adapter.Source, cp *model.SourceCheckpoint) (adapter.Observation, error)

CollectIncremental gates the transcript on size+mtime: an unchanged file is not opened at all. Any change re-reads the WHOLE file.

There is deliberately no tail read. The default artifact is a stream of independent zstd frames whose boundaries are not derivable from a byte offset without parsing every block header, and DSH's crash recovery may TRUNCATE and RE-ENCODE the tail in place, which would leave a stored offset pointing into the middle of a rewritten frame. Re-reading is correct in both cases and the store collapses the re-derived dedup keys; the cost is one small file per changed session per cycle. A nil cp is a full read.

func (Adapter) Discover

func (a Adapter) Discover(ctx context.Context, cfg adapter.DiscoverConfig) ([]adapter.Source, error)

Discover locates every session transcript under <home>/sessions.

The layout is <sessions>/<project-dir>/<session-dir>/session.jsonl[.zstd]. The project directory encodes the session's cwd lossily (separators replaced, truncated to a filesystem component limit), so it is NOT parsed for the project: the header line inside the transcript carries the absolute cwd verbatim and is the only honest source for it. The walk is depth-agnostic and matches on the fixed transcript file names alone, which keeps a project directory rename or a deeper layout from silently emptying discovery.

func (Adapter) DisplayName

func (Adapter) DisplayName() string

DisplayName returns the human-friendly name.

func (Adapter) ID

func (Adapter) ID() string

ID returns the stable tool identifier.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL