DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

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

dsh-otel

Otel

DeepSeek Harness 插件:通过 otel_export 工具,从会话日志中导出轮次、步骤和工具调用的 OpenTelemetry span,并通过 OTLP/HTTP 或 stdout 发送

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

npx -y @deepseek-ai/dsh plugin --profile web add github:jwilson411/dsh-otel#fd44d97ecf55a611f36f5a7e8a1c875d62484f63
README兼容性版本

兼容性与来源证明

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

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/8/30

版本

0.1.0stable
2026/8/30

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
any
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/9/1
查看源码 ↗
README Badge

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

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

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

认领这个 Plugin →
报告问题

相关插件

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

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Sdk Minimal@deepseek-ai/dsh-sdk-minimal独立的最小 SDK 配置包:JSON-RPC、一个 DeepSeek 适配器、持久化 Shell 和 JSONL 会话Sdk App@deepseek-ai/dsh-sdk-appdsh SDK 配置包:基于 dsh-base 提供 stdio JSON-RPC 服务和进程生命周期管理Subagent Codex@deepseek-ai/dsh-subagent-codex基于官方 app-server 协议的一次性 Codex 子代理提供程序

README

dsh-otel

A DeepSeek Harness function plugin that exports a session as OpenTelemetry spans: one span per turn, per step, and per tool call, linked into one trace, sent to any OTLP/HTTP collector — Jaeger, Phoenix, Grafana Tempo, the OpenTelemetry Collector — or written to stdout.

The scope is one direction: the session log out, as spans. The harness already writes a durable record of what happened; Jaeger already knows how to draw a trace. What is missing is the translation, and that is all this is. Nothing here observes the harness at runtime, buffers, samples, or writes to the session log.

It is off by default, it takes no OpenTelemetry SDK as a runtime dependency — the OTLP/JSON document is written by hand — and it puts no prompts, tool arguments, or tool results on a span. A span carries names, ids, timings, and the token counts the log itself reported.

Install

dsh plugin --profile web add github:jwilson411/dsh-otel

dsh plugin forwards to pnpm inside $DSH_HOME/profiles/web, then reconciles the profile: because this package's manifest declares dsh.bundle.patch, it is appended to the profile manifest's ordered dsh.profile.bundles list and its cordis.patch.yml becomes a layer. Remove it the same way, with remove in place of add.

Note that @deepseek-ai/dsh-tools's npm latest tag still points at the older 0.0.1-rc.1; the 0.1.1-rc.2 line is published under next. Pin explicitly rather than relying on the tag.

Turn it on from the profile's own cordis.patch.yml — note that an id-targeted patch replaces the row's whole config, so restate every field you mean to keep:

- id: otel
  config:
    exporter: otlp-http
    endpoint: http://127.0.0.1:4318/v1/traces
    sessionLog: ~/.dsh/sessions/current.jsonl
    sessionId: sess-1
config keyenvironment fallbackdefault
exporterDSH_OTEL_EXPORTERoff — one of off, stdout, otlp-http
endpointDSH_OTEL_ENDPOINThttp://127.0.0.1:4318/v1/traces (used only by otlp-http)
sessionLogDSH_OTEL_SESSION_LOGunset — a call naming no log fails loudly
sessionIdDSH_OTEL_SESSION_IDunset — falls back to the log's header, else unknown

Off is the default on purpose. With no exporter configured the plugin still registers its tool and the tool still builds and counts spans — so a model can answer "how did that run go?" — but nothing leaves the process. Telemetry crossing a machine boundary should be a thing someone turned on.

The spans

spanopened byclosed byparent
dsh.sessionthe first timed eventthe last event— (root)
dsh.turnturn/startturn/enddsh.session
dsh.stepstep/startstep/endits turn
dsh.tool.executetool/callthe tool/result with the same callIdits step

Attributes:

keyonmeaning
session.idevery spanthe session the trace belongs to
dsh.turnturn, step, toolthe turn number
dsh.stepstep, toolthe step number within the turn
dsh.event.seq.start / dsh.event.seq.endevery spanthe log lines the span was opened and closed by
dsh.turn.reasonturndata.reason from turn/end, when the log reported one
tool.name, tool.call_idtoolthe tool and the call it answered
tool.errortoolwhether the result reported a failure
dsh.usage.*steptoken counts, copied verbatim from assistant/message's usage
dsh.span.unclosedanythe log ended before this bracket closed

Status is OK unless a tool result reported an error or a turn ended with a reason that reads like failure (error, failed, cancelled). The mapping is deliberately conservative: an unfamiliar reason is reported OK rather than guessed at.

dsh.usage.* is copied, never computed. No text is tokenised, nothing is summed, and a count the log did not report is simply absent.

Ids are derived, not generated

The trace id is sha256(sessionId) truncated to 32 hex characters; a span id is sha256("<sessionId>/<key>") truncated to 16, where the key is the bracket's own coordinates in the log:

turn:1                      → the turn span
turn:1:step:2               → its second step
turn:1:step:2:tool:call_7   → the tool call inside that step

So exporting the same log twice produces the same tree. If the collector was down the first time, re-run the export — you repair the gap rather than duplicating it. With no session id anywhere, ids derive from the literal string unknown.

What the reader tolerates

  • A line that is not JSON is counted, reported in malformed_lines, and skipped. A tail that caught a partial write does not cost you the trace.
  • An event type this plugin does not know is ignored.
  • A bracket the log never closed is ended at the last event's instant and marked dsh.span.unclosed, rather than dropped — an in-flight tool call at EOF is usually the one you came for.
  • A step/start with no turn/start above it synthesizes its turn, so the tree never has a dangling parent.

Try it against Jaeger

docker run --rm -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest
dsh-otel export --log ~/.dsh/sessions/current.jsonl --session sess-1 --exporter otlp-http --endpoint http://127.0.0.1:4318/v1/traces
# UI: http://127.0.0.1:16686

The otel_export tool

Cordis plugin idotel (the row id in cordis.patch.yml)
Injectstools — a hard dependency; the plugin waits rather than degrading
Toolotel_export
Argumentslog (string, optional), session_id (string, optional)

log defaults to the configured sessionLog and session_id to the configured sessionId, else the id the log's header names. A call with no log from either place fails loudly rather than exporting an empty trace.

It returns log, session_id, trace_id, exporter, endpoint, spans, exported, turns, steps, tools, malformed_lines, and status. exported is how many spans actually left the process, so it is 0 whenever the exporter is off while the counts still describe the run.

The tool reads a file and, at most, POSTs to the endpoint you configured. It needs no API key.

The CLI

The same export from a shell. It imports nothing outside node: and this package, so it runs against a checkout with no dependencies installed — useful when the trace is the thing you need and the harness is the thing that is broken.

dsh-otel export --log <path> [--session <id>] [--exporter off|stdout|otlp-http]
                [--endpoint <url>] [--service <name>] [--json]

stdout carries the OTLP document and nothing else, so a redirect is always a valid file; the run's report goes to stderr, as text or, with --json, as JSON. The CLI defaults to the stdout exporter — you asked for an export at a prompt, so printing it is the least surprising thing it can do, and it still sends nothing anywhere.

$ dsh-otel export --log ~/.dsh/sessions/current.jsonl > spans.json
session sess-1 → trace 4f8c…
14 span(s): 3 turn(s), 6 step(s), 4 tool call(s)
14 span(s) written to stdout
skipped unparseable log line(s): 92

Out of scope

  • A hosted product. This is a file reader and an HTTP POST.
  • A UI. Jaeger, Phoenix, and Tempo already draw traces.
  • Replacing or annotating the session log. The log is the source of truth and this package only reads it — no sidecar, no cursor file, no state.
  • A Langfuse clone. No prompt capture, no evals, no datasets, no scoring.
  • Runtime instrumentation. Spans come from the log after the fact, not from hooks inside a running turn.

Layout

package.json        manifest + `dsh.bundle.patch` — what makes this a bundle
cordis.patch.yml    the bundle's patch layer: one insert, one plugin row
src/session.js      reading the session log as JSONL, tolerating bad lines
src/spans.js        events → spans, with ids derived rather than generated
src/otlp.js         the OTLP/JSON document and the three exporters
src/export.js       one export: read, build, send, report
src/index.js        the plugin: `name`, `inject`, `apply(ctx, config)`
bin/                the CLI
test/               offline tests over a checked-in fixture log
package-lock.json   the pinned dependency tree `npm ci` installs in CI

Tests

npm install
npm test

Offline by construction. Every test reads the checked-in test/fixtures/session.jsonl; the stdout exporter writes to an injected sink and otlp-http posts through an injected fetch, so the whole export path is exercised without a collector, a socket, or a key.

Only test/plugin.test.js needs a dependency: it registers the plugin against a stub context and validates the tool's result with the real @deepseek-ai/dsh-tools, pinned to 0.1.1-rc.2 in devDependencies and in package-lock.json so the contract is tested against one known API. Everything else — the library, the CLI, and the other three test files — imports nothing outside node: and this package.

CI (.github/workflows/ci.yml) runs npm ci and npm test on Node 22 and 24 from the committed lockfile, against the public registry only. It needs no credentials and the suite reaches no network.

License

MIT — see LICENSE.