The profile must already compose a runnable agent. A profile carrying only @deepseek-ai/dsh-base
has no runtime: add @deepseek-ai/dsh-headless (or another runnable bundle) alongside it, otherwise
the profile boots into a configuration with no agent loop and this plugin observes nothing.
dsh plugin --profile <name> add @deepseek-ai/dsh-headless@0.1.1-rc.2
dsh plugin --profile <name> add dsh-ocsf-forwarder
dsh --profile <name> --dump-config # verify the row is mounted
Pin @deepseek-ai/dsh-headless explicitly: the npm latest tag of the @deepseek-ai/dsh-*
libraries still points at 0.0.1-rc.1, so an unpinned install silently resolves to a much older
harness.
CI runs the whole end-to-end suite — a real dsh subprocess with this plugin mounted — against
every one of these:
| dsh | Notes |
|---|---|
0.1.0-rc.6, 0.1.0-rc.7, 0.1.0-rc.8 |
0.1.0-rc.8 is where the session vocabulary grew the four team/* types. |
0.1.1-rc.2 |
The latest dist-tag of the @deepseek-ai/dsh CLI. |
0.1.2-alpha.5 |
The line where Session.events became Session.snapshotEvents() and header.seedLength became Session.inheritedEventCount. This plugin reads whichever the resolved version has. It is also the first line whose vocabulary carries all 51 of the types that line knows, session-log-deepseek/delivery-accepted among them. |
0.1.2-rc.1 |
The last release before the session format migrated v3 to v4. |
0.1.5-rc.3 |
npm’s latest. 56 event types: five added, tool/code-dispatch{,-start} renamed to tool/ptc-dispatch{,-start}, and assistant/chunk replaced by the different assistant/attempt. |
0.1.7-rc.2 |
npm’s next, and what the devDependencies and the event table are checked against. 59 event types. The line where tool/result’s isError moved onto the message, and where session-log-deepseek began uploading by default. |
The 0.1.0, 0.1.1 and 0.1.2 rows are claimed and not currently demonstrated. Every
0.1.2-rc.1 install we could build — the CI recipe, the default pnpm linker, and a byte copy of a
cache built weeks earlier — dies during boot with dsh: user patch-layer watching requires the
Cordis HMR service, before any plugin loads. The same failure reproduces against this package’s
own 0.8.0 release, so it is not this plugin, and pinning @deepseek-ai/cordis and
cordis-plugin-hmr back to the versions that line shipped with does not change it. The rows stay
because a diagnosed failure is a better reason to drop a supported line than an undiagnosed one.
Peer ranges. @deepseek-ai/dsh-session and @deepseek-ai/dsh-session-telemetry are
^0.1.0-rc.6 || ~0.1.1-rc.0 || ~0.1.2-alpha.0 || ~0.1.5-alpha.0 || ~0.1.7-alpha.0
which is longer than it looks like it should be because node-semver lets a prerelease satisfy a
range only when some comparator in the same set carries a prerelease tag and the identical
major.minor.patch. ^0.1.0-rc.6 therefore admitted 0.1.0-rc.8 and 0.1.1 but not
0.1.1-rc.2 — the version the CLI’s latest tag points at — and not 0.1.2-alpha.5 either:
npm error ERESOLVE unable to resolve dependency tree
npm error Found: @deepseek-ai/dsh-session@0.1.2-alpha.5
npm error Could not resolve dependency:
npm error peer @deepseek-ai/dsh-session@"^0.1.0-rc.6" from dsh-ocsf-forwarder@0.6.0
What the range cannot cover. One comparator set per prerelease patch tuple is the only way to
express this, so 0.1.3-alpha.2 and the whole 0.1.6 line are not admitted even though both
sit between admitted lines. That is deliberate: each prerelease line is untested until someone
runs the suite against it, and widening the range is where that gets noticed.
@deepseek-ai/cordis is ^4.0.1 rather than exactly 4.0.1. The exact pin was there so the
service graph every registration goes through is one graph — but a peer range does not install
anything, so the exact pin achieved the opposite: @deepseek-ai/dsh-session@0.1.2-alpha.5 peers
@deepseek-ai/cordis@^4.0.2, and a project on that line could no longer add this plugin at all.
Every file 4.0.2 ships is byte-identical to 4.0.1; only its own package.json differs. ^4.0.1
is what DSH’s own packages declare, and being stricter than the application that owns the graph
buys nothing.
Install from the registry or a packed tarball, not from a git spec.
dsh plugin add github:CharlotteN7/dsh-ocsf-forwarder resolves and writes the
dependency, but lib/ is a build output that git does not carry and no
prepare script rebuilds it, so the row mounts and then fails to load. To
install from a checkout, build first and add the tarball:
git clone https://github.com/CharlotteN7/dsh-ocsf-forwarder && cd dsh-ocsf-forwarder
pnpm install && pnpm run build && pnpm pack
dsh plugin --profile <name> add ./dsh-ocsf-forwarder-0.5.1.tgz # the version in package.json
The bundle patch defaults the spool to dshHomePath('ocsf/session.ocsf.jsonl'). A profile patch
layer replaces a row’s whole config, so an override must restate every key it wants:
- id: dsh-ocsf-forwarder
config:
spoolPath: /var/log/dsh/session.ocsf.jsonl
seedReplay: full
privacy:
hmacKey:
source: env
variable: DSH_OCSF_HMAC_KEY
otlp:
endpoint: https://collector.internal:4318
headers:
authorization: 'Bearer ${COLLECTOR_TOKEN}'
To ship to Splunk instead, replace the otlp block with a splunk one. Exactly one destination
may be configured: two would share one cursor file and each step it past the other’s deliveries,
so naming both fails at load.
- id: dsh-ocsf-forwarder
config:
spoolPath: /var/log/dsh/session.ocsf.jsonl
fleet:
tenantUid: platform-eng
labels: [prod, eu-west]
tags:
owner: soc
splunk:
endpoint: https://splunk.internal:8088
index: dsh_security
token:
source: env
variable: DSH_SPLUNK_HEC_TOKEN