@deepseek-ai/dsh-session-telemetry-otel 0.1.7-rc.2 → 0.2.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -9,8 +9,8 @@
9
9
  en: f8ea869792c8b207
10
10
  zh: bc076172ce22ba9b
11
11
  /deepseek-ai-dsh-session-telemetry-otel/summary:
12
- en: 1c5d2f15cb13e318
13
- zh: 5e061a16cd38c4c5
12
+ en: 828903209ef34983
13
+ zh: 8eac63f1e8df15d8
14
14
  /deepseek-ai-dsh-session-telemetry-otel/table-of-contents:
15
15
  en: d152484eb41ac6b4
16
16
  zh: 09388d293f9be9cb
@@ -21,29 +21,29 @@
21
21
  en: bc7e598148a7f8c3
22
22
  zh: 0209c6aa258ec134
23
23
  /deepseek-ai-dsh-session-telemetry-otel/use-this-package/minimal-configuration:
24
- en: 3de0d5db3156a7e4
25
- zh: 71199133cd9998c4
24
+ en: 11845fd50dc0a494
25
+ zh: ee990bfca3621bd3
26
26
  /deepseek-ai-dsh-session-telemetry-otel/use-this-package/what-leaves-the-machine:
27
- en: b94ca8299ad60c5d
28
- zh: 3791131f3440e3b3
27
+ en: 2a5ddd227e400455
28
+ zh: 74b7bd9b9161b054
29
29
  /deepseek-ai-dsh-session-telemetry-otel/use-this-package/failures-and-shutdown:
30
- en: 20c6e78df8ad2c2a
31
- zh: fd6a00f98c4cc05c
30
+ en: a66831cb9de57e04
31
+ zh: 3ada28bb868e0932
32
32
  /deepseek-ai-dsh-session-telemetry-otel/understand-the-implementation:
33
33
  en: ee672a6a6f0fa02d
34
34
  zh: c3bab8e6b60e768c
35
35
  /deepseek-ai-dsh-session-telemetry-otel/understand-the-implementation/design-concept:
36
- en: d5e1f8249d3eea34
37
- zh: 7a6915e33390f4ae
36
+ en: 34f8a88224a1f6cf
37
+ zh: 40839dabaea39602
38
38
  /deepseek-ai-dsh-session-telemetry-otel/understand-the-implementation/source-map:
39
39
  en: 0b99d2e5c27213cd
40
40
  zh: 3cbdaa7f6a45d81a
41
41
  /deepseek-ai-dsh-session-telemetry-otel/understand-the-implementation/capture-wiring:
42
- en: 9bdc01d0fa130b3a
43
- zh: 63bf30c4cc983ca0
42
+ en: 5c85eb0992eb5b2b
43
+ zh: a19cdd5814c0e2b9
44
44
  /deepseek-ai-dsh-session-telemetry-otel/understand-the-implementation/field-mapping:
45
- en: 38590f3afee40bb8
46
- zh: b2fc1f4341008d79
45
+ en: a42e9e081fb93bee
46
+ zh: b8e817c2455278b9
47
47
  /deepseek-ai-dsh-session-telemetry-otel/further-exploration:
48
48
  en: 155b75490ec65d86
49
49
  zh: 07d37d6e4ac90c89
@@ -57,5 +57,5 @@
57
57
  en: e43087aea7af4c9e
58
58
  zh: f8b529e2012b4c63
59
59
  /deepseek-ai-dsh-session-telemetry-otel/known-limitations-and-deferred-work/dev-note:
60
- en: a770daec456212ab
61
- zh: 1b861566d591837d
60
+ en: 0065df2648b09ac9
61
+ zh: b5bac41fc3aec70e
package/README.md CHANGED
@@ -9,7 +9,9 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-session-telemetry-otel` exports session records through the OTel JS SDK only after new explicit feedback, for all users and providers, including `deepseek-official`. `FEEDBACK_ONLY` releases the canonical prefix through that feedback, including context; later records wait for the next explicit feedback. `DISABLED` constructs no transport. SDK batching can finish an authorized upload without another user interaction or model call. Deployments own their redaction rules.
12
+ The adapter injects `otel`; the [shared OTel plugin](../../telemetry/otel/README.md) creates its independent Session-log channel. Authorization, redaction, identity, scope version, configuration, and the shutdown deadline remain owned here.
13
+
14
+ `dsh-session-telemetry-otel` exports session records through the OTel JS SDK only after new explicit feedback, for all users and providers, including `deepseek-official`. `FEEDBACK_ONLY` releases the canonical prefix through that feedback, including context; later records wait for the next explicit feedback. `DISABLED` constructs no transport. Scheduled batching can finish an authorized upload without another user interaction or model call. Deployments own their redaction rules.
13
15
 
14
16
  ## Table of Contents
15
17
 
@@ -38,7 +40,7 @@ Programmatic TypeScript configuration uses the exported `SessionTelemetryMode` e
38
40
 
39
41
  ### Minimal configuration
40
42
 
41
- Uploading modes require an exporter URL and accept the SDK option blocks verbatim:
43
+ Uploading modes require an exporter URL. Processor settings control the independent Session-log queue; routing headers are explicit `exporter.headers` values.
42
44
 
43
45
  ```yaml
44
46
  - id: sessionTelemetry-otel
@@ -46,31 +48,34 @@ Uploading modes require an exporter URL and accept the SDK option blocks verbati
46
48
  config:
47
49
  mode: FEEDBACK_ONLY # optional; defaults to FEEDBACK_ONLY
48
50
  shutdownTimeoutMillis: 3000 # optional; defaults to 3000
49
- exporter: # passed verbatim to the SDK's OTLP/HTTP log exporter
51
+ exporter: # explicit SDK transport settings
50
52
  url: https://collector.example.com/v1/logs
51
53
  headers:
52
54
  authorization: !!js `Bearer ${process.env.OTLP_TOKEN}`
53
- processor: {} # optional; passed verbatim to BatchLogRecordProcessor
55
+ processor: {} # optional; byte/count batching and per-request watchdog
54
56
  ```
55
57
 
56
58
  | Field | Default | Meaning |
57
59
  |---|---|---|
58
60
  | `mode` | `FEEDBACK_ONLY` | Sharing policy: `FEEDBACK_ONLY` or `DISABLED` |
59
61
  | `exporter.url` | required in uploading modes | Full OTLP logs endpoint; must parse as `http(s)` |
60
- | `exporter`, `processor` | — | Passed verbatim to the SDK exporter and batch processor |
61
- | `shutdownTimeoutMillis` | `3,000` | Outer deadline for the SDK's complete shutdown sequence |
62
+ | `exporter`, `processor` | — | SDK transport plus byte/count batching; headers/TLS identity are not inherited from the environment. Agent factories own their returned agent settings, including keepAlive |
63
+ | `shutdownTimeoutMillis` | `3,000` | Outer deadline for all queued HTTP requests; remaining queued sends stop at expiry |
64
+ | `maxRequestBytes` | `4,000,000` | Maximum complete OTLP JSON request bytes before gzip; may only be lowered |
62
65
 
63
66
  Direct `ctx.sessionTelemetry.emit()` calls are no-ops in every mode and cannot bypass feedback authorization. Inherited parent feedback does not authorize a child export: the child needs new feedback of its own. Its authorized prefix then includes inherited context.
64
67
 
65
- Model requests, request headers, Session creation or adoption, restoration, and plugin mount or HMR do not authorize capture. Stored feedback alone triggers nothing. SDK scheduled flush and shutdown may finish batches authorized earlier, but never capture new records.
68
+ Model requests, request headers, Session creation or adoption, restoration, and plugin mount or HMR do not authorize capture. Stored feedback alone triggers nothing. Scheduled flush and shutdown may finish batches authorized earlier, but never capture new records.
66
69
 
67
70
  ### What leaves the machine
68
71
 
69
- In uploading modes, records carry the complete `event.data` as the seam's `sessionTelemetry/record` waterfall returns it — message content, tool arguments and results, the system prompt and tool schemas, todo text, compaction summaries, feedback text, and the session `cwd`. Provider credentials never appear: adapter API keys are constructor parameters, not session events, so they are structurally absent from the log and therefore from telemetry. `DISABLED` constructs no SDK pipeline and hands no capture to a backend.
72
+ Each Session event becomes one `eventName: "session-log"` record. `attributes.sessionId` is the collector Session identity; `attributes.content` encodes the complete event envelope with redacted `event.data`. JSON values are preserved, not the original JSONL bytes or key ordering. Legacy `session.id`, `event.seq`, and `event.type` metadata remain for existing consumers. Resources carry application and anonymous-user identity; scope carries the backend package name and version. The base profile uses `https://dsh-otel-collector.deepseeksvc.com/v1/logs`; `DSH_TELEMETRY_OTLP_URL` overrides it. No channel header is added implicitly.
73
+
74
+ The shared OTel channel measures each record once with the SDK OTLP JSON serializer, including its resource/scope envelope, then greedily packs requests using those conservative sizes. A single oversized event produces one rejection diagnostic without truncation. Session logs never mix with product analytics in a request. Capture handoff and shutdown are not collector acknowledgements.
70
75
 
71
76
  ### Failures and shutdown
72
77
 
73
- Misconfiguration fails at plugin load: a missing or non-`http(s)` `exporter.url`, a non-positive-integer `processor.maxExportBatchSize` (which the SDK accepts but then hangs on at shutdown), and an invalid `shutdownTimeoutMillis` all reject before any record is exported. During shutdown, OTel awaits `exporter.forceFlush()` before the processor's bounded completion promise; if that transport promise never settles, this package abandons the wait at `shutdownTimeoutMillis`, logs the contained failure, and lets application teardown continue — records still pending then may be lost at process exit.
78
+ Invalid byte limits, non-positive queue/timer values, `maxExportBatchSize > maxQueueSize`, invalid endpoints, and invalid shutdown deadlines fail at load. Processor defaults are 2,048 queued records, 512 records per request, a 1,000 ms scheduling delay, and a 30,000 ms request watchdog. Byte limits may split a count batch into multiple serial HTTP requests. Each request waits for SDK export-queue cleanup after its callback before the next starts. `exportTimeoutMillis` warns per request but never frees an unsettled transport slot; the SDK transport owns network timeout/retry. Shutdown drains these requests until `shutdownTimeoutMillis`; expiry discards the remaining queue and prevents further sends, while an active request may still settle. Large prefixes can therefore remain partially unsent at CLI exit.
74
79
 
75
80
  -----
76
81
 
@@ -84,7 +89,7 @@ This section explains the backend's composition; the observable behavior is full
84
89
 
85
90
  ### Design concept
86
91
 
87
- The backend is a thin adapter over the OTel JS SDK: it owns feedback authorization, resource identity, and an outer shutdown deadline. Canonical ledger records use the `@deepseek-ai/dsh-session-telemetry-otel` instrumentation scope; this backend captures no operational records. Resource identity carries `service.name`/`service.version` from `dsh-llm`'s `APP_IDENTITY` plus the anonymous `user.id` (from `$DSH_HOME/.anonymous-user-id`), once per export batch rather than per record.
92
+ The backend owns feedback authorization, identity, and shutdown. The shared OTel service owns channel creation, byte/count scheduling, record construction, JSON transport, compression, and retries. Resource identity carries `service.name`/`service.version` from `APP_IDENTITY` and anonymous `user.id`; the scope retains this package’s name and version.
88
93
 
89
94
  ### Source map
90
95
 
@@ -94,11 +99,11 @@ The backend is a thin adapter over the OTel JS SDK: it owns feedback authorizati
94
99
 
95
100
  ### Capture wiring
96
101
 
97
- The backend uses on-demand capture with stored history included. Only new own `feedback/record`, `feedback/message-put`, or `feedback/message-delete` events trigger live capture, bounded by that event. A cold `feedback/committed` notification supplies its committed canonical snapshot without publishing a live Session or Agent. Same-object handoff cursors suppress repeated capture. The backend implements no `flush()`; the SDK owns batching and shutdown drain.
102
+ The backend captures history on demand through new own feedback events or committed cold snapshots. Its private reporter serializes queued HTTP requests even after a watchdog fires; an outer shutdown deadline stops the remaining queue. It exposes no extra flush entry point.
98
103
 
99
104
  ### Field mapping
100
105
 
101
- Each telemetry record maps to one SDK log record with captured timestamp, severity, body, and attributes. Feedback authorizes the complete unhanded prefix, not only the feedback payload.
106
+ Each capture record supplies a separately copied event envelope and redacted payload to `SessionLogReporter`; serialization retains optional surface metadata and the event sequence/time. Feedback authorizes the complete unhanded prefix, not only the feedback payload.
102
107
 
103
108
  </details>
104
109
 
@@ -148,4 +153,4 @@ None.
148
153
 
149
154
  </details>
150
155
 
151
- **Runtime invariant:** No companion is published. Mode selection changes capture handoff, SDK setup, and local diagnostics without mutating session or service state an independent companion can compare. Export remains inside the SDK past the backend boundary.
156
+ **Runtime invariant:** No companion is published. Mode selection changes capture handoff, SDK setup, and local diagnostics without mutating session or service state an independent companion can compare. Collector delivery cannot be inferred from local queue state.
package/README.zh.md CHANGED
@@ -9,7 +9,9 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-session-telemetry-otel` 仅在新的显式反馈后通过 OTel JS SDK 导出会话记录,适用于所有用户和提供方,包括 `deepseek-official`。`FEEDBACK_ONLY` 释放截至该反馈的权威日志前缀,包含上下文;后续记录等待下一次显式反馈。`DISABLED` 不构造传输。SDK 批处理可完成已授权的上传,无需另一次用户交互或模型调用。部署方负责脱敏规则。
12
+ 适配器注入 `otel`;[共享 OTel 插件](../../telemetry/otel/README.zh.md) 创建其独立 Session 日志通道。授权、脱敏、身份、scope 版本、配置和关闭期限仍由本包负责。
13
+
14
+ `dsh-session-telemetry-otel` 仅在新的显式反馈后通过 OTel JS SDK 导出会话记录,适用于所有用户和提供方,包括 `deepseek-official`。`FEEDBACK_ONLY` 释放截至该反馈的权威日志前缀,包含上下文;后续记录等待下一次显式反馈。`DISABLED` 不构造传输。定时批处理可完成已授权的上传,无需另一次用户交互或模型调用。部署方负责脱敏规则。
13
15
 
14
16
  ## 目录
15
17
 
@@ -38,7 +40,7 @@ kind: "package-reference"
38
40
 
39
41
  ### 最小配置
40
42
 
41
- 上传模式需要导出器 URL,并原样接受 SDK 选项块:
43
+ 启用上传的模式必须提供 exporter URL。processor 设置控制独立的 Session 日志队列;路由头通过 `exporter.headers` 显式配置。
42
44
 
43
45
  ```yaml
44
46
  - id: sessionTelemetry-otel
@@ -46,31 +48,34 @@ kind: "package-reference"
46
48
  config:
47
49
  mode: FEEDBACK_ONLY # optional; defaults to FEEDBACK_ONLY
48
50
  shutdownTimeoutMillis: 3000 # optional; defaults to 3000
49
- exporter: # passed verbatim to the SDK's OTLP/HTTP log exporter
51
+ exporter: # explicit SDK transport settings
50
52
  url: https://collector.example.com/v1/logs
51
53
  headers:
52
54
  authorization: !!js `Bearer ${process.env.OTLP_TOKEN}`
53
- processor: {} # optional; passed verbatim to BatchLogRecordProcessor
55
+ processor: {} # optional; byte/count batching and per-request watchdog
54
56
  ```
55
57
 
56
58
  | 字段 | 默认值 | 含义 |
57
59
  |---|---|---|
58
60
  | `mode` | `FEEDBACK_ONLY` | 共享策略:`FEEDBACK_ONLY` 或 `DISABLED` |
59
61
  | `exporter.url` | 上传模式必填 | 完整 OTLP 日志端点;必须能解析为 `http(s)` |
60
- | `exporter`、`processor` | — | 原样传给 SDK 导出器与批处理器 |
61
- | `shutdownTimeoutMillis` | `3,000` | SDK 完整关闭序列的外层截止时间 |
62
+ | `exporter`, `processor` | — | SDK 传输及字节/条数聚合;不继承环境中的头部和 TLS 身份。agent 工厂负责返回实例的设置,包括 keepAlive |
63
+ | `shutdownTimeoutMillis` | `3,000` | 所有排队 HTTP 请求的外层期限;到期后停止剩余排队发送 |
64
+ | `maxRequestBytes` | `4,000,000` | gzip 前完整 OTLP JSON 请求的最大字节数;只能调低 |
62
65
 
63
66
  直接调用 `ctx.sessionTelemetry.emit()` 在任何模式下都是空操作,不能绕过反馈授权。继承的父会话反馈不授权子会话导出:子会话需要新的自身反馈。授权后的前缀包含继承的上下文。
64
67
 
65
- 模型请求、请求头、Session 创建或接纳、恢复,以及插件挂载或 HMR(热模块替换)均不授权捕获。仅凭已存储的反馈不会触发任何操作。SDK 定时刷新和关闭可以完成先前已授权的批次,但绝不捕获新记录。
68
+ 模型请求、请求头、Session 创建或接纳、恢复,以及插件挂载或 HMR(热模块替换)均不授权捕获。仅凭已存储的反馈不会触发任何操作。定时刷新和关闭可以完成先前已授权的批次,但绝不捕获新记录。
66
69
 
67
70
  ### 哪些数据会离开本机
68
71
 
69
- 在上传模式中,记录携带 seam 的 `sessionTelemetry/record` waterfall(瀑布式事件)返回的完整 `event.data`——消息内容、工具参数与结果、系统提示词与工具 schema、todo 文本、压缩(compaction)摘要、反馈文本,以及会话 `cwd`。提供方凭据绝不会出现:适配器的 API key 是构造函数参数而非会话事件,因此它们在结构上就不存在于日志中,也就不存在于遥测中。`DISABLED` 不构造 SDK 流水线,也不把任何捕获内容交给后端。
72
+ 每条 Session 事件对应一个 `eventName: "session-log"` 记录。`attributes.sessionId` 是 collector 使用的 Session 身份;`attributes.content` 编码完整事件 envelope 和脱敏后的 `event.data`。保留的是 JSON 值,不保证原 JSONL 字节或键顺序相同。为现有消费者保留 `session.id`、`event.seq` 和 `event.type` 元数据。Resource 携带应用和匿名用户身份;scope 携带后端包名和版本。基础配置使用 `https://dsh-otel-collector.deepseeksvc.com/v1/logs`,可用 `DSH_TELEMETRY_OTLP_URL` 覆盖。不隐式添加 channel 头。
73
+
74
+ 共享 OTel 通道使用 SDK 的 OTLP JSON 序列化器对每条记录计量一次,包含其 resource/scope envelope,再按保守大小顺序组包。单条超限事件产生一次拒绝诊断且不截断。Session 日志不会与产品埋点混在一个请求中。捕获交接和关闭完成不代表 collector 确认。
70
75
 
71
76
  ### 失败与关闭
72
77
 
73
- 配置错误会在插件加载时失败:缺少或非 `http(s)` 的 `exporter.url`、非正整数的 `processor.maxExportBatchSize`(SDK 会接受该值,随后却在关闭时挂起)以及无效的 `shutdownTimeoutMillis` 都会在任何记录导出前被拒绝。关闭期间,OTel 会先等待 `exporter.forceFlush()`,再等待处理器有界完成 promise;如果该传输 promise 始终不结算,本包会在 `shutdownTimeoutMillis` 到期时放弃等待、记录已隔离的失败,并让应用继续拆卸——届时仍待处理的记录可能在进程退出时丢失。
78
+ 无效字节上限、非正队列/计时值、`maxExportBatchSize > maxQueueSize`、无效 endpoint 或关闭期限在加载时失败。processor 默认队列为 2,048 条、单请求最多 512 条、调度延迟 1,000 ms、请求监测期限 30,000 ms。字节上限可能将按条数划分的批次拆成多个串行 HTTP 请求。每个请求在回调后等待 SDK 导出队列清理完成,才启动下一个请求。`exportTimeoutMillis` 针对每个请求告警,但不会释放未结束的传输槽位;SDK transport 负责网络超时和重试。关闭时发送这些请求直到 `shutdownTimeoutMillis`,到期后丢弃剩余队列并禁止后续发送,在途请求仍可能结束。因此较大的授权前缀在 CLI 退出时可能只发送了一部分。
74
79
 
75
80
  -----
76
81
 
@@ -84,7 +89,7 @@ kind: "package-reference"
84
89
 
85
90
  ### 设计理念
86
91
 
87
- 后端是对 OTel JS SDK 的薄适配层:它拥有反馈授权、资源身份与外层关闭截止时间。权威 ledger 记录使用 `@deepseek-ai/dsh-session-telemetry-otel` 插桩作用域;此后端不捕获运维记录。资源身份携带 `service.name`/`service.version`(来自 `dsh-llm` 的 `APP_IDENTITY`)以及匿名 `user.id`(来自 `$DSH_HOME/.anonymous-user-id`),按导出批次携带一次,而非逐条记录。
92
+ 后端负责反馈授权、身份和关闭。共享 OTel 服务负责通道创建、字节和条数调度、记录构造、JSON 传输、压缩与重试。Resource 身份携带 `APP_IDENTITY` 中的 `service.name` / `service.version` 以及匿名 `user.id`;scope 保留本包的名称和版本。
88
93
 
89
94
  ### 源码地图
90
95
 
@@ -94,11 +99,11 @@ kind: "package-reference"
94
99
 
95
100
  ### 捕获接线
96
101
 
97
- 后端使用包含存储历史的按需捕获。只有新的自身 `feedback/record`、`feedback/message-put` 或 `feedback/message-delete` 事件触发活跃会话捕获,并以该事件为上限。冷会话 `feedback/committed` 通知提供已提交的权威快照,不发布存活 Session 或 Agent。同对象交接游标抑制重复捕获。后端不实现 `flush()`;SDK 负责批处理和关闭排空。
102
+ 后端按需捕获历史,由新的自身反馈事件或已提交的冷会话快照触发。私有 reporter 串行发送排队 HTTP 请求,即使监测期限已触发也不会重叠;外层关闭期限停止剩余队列。不额外暴露 flush 入口。
98
103
 
99
104
  ### 字段映射
100
105
 
101
- 每条遥测记录映射为一条 SDK 日志记录,携带捕获的时间戳、严重级别、正文和属性。反馈授权的是尚未交接的完整前缀,而非只有反馈载荷。
106
+ 每条采集记录向 `SessionLogReporter` 提供单独复制的事件信封和脱敏后的载荷;序列化保留可选的呈现元数据及事件序号和时间。反馈授权整个尚未交接的前缀,而不只是反馈载荷。
102
107
 
103
108
  </details>
104
109
 
@@ -148,4 +153,4 @@ kind: "package-reference"
148
153
 
149
154
  </details>
150
155
 
151
- **运行时不变式:** 不发布伴生入口。模式选择只改变 capture handoff、SDK setup 与本地 diagnostics,不改变可由独立 companion 对照的会话或服务状态。导出在越过后端边界后仍由 SDK 内部处理。
156
+ **运行时不变式:** 不发布伴生入口。模式选择只改变 capture handoff、SDK setup 与本地 diagnostics,不改变可由独立 companion 对照的会话或服务状态。无法根据本地队列状态推断 collector 是否收到记录。
package/lib/index.js CHANGED
@@ -4,25 +4,17 @@ import { Session } from "@deepseek-ai/dsh-session";
4
4
  import { SessionTelemetryBackend, SessionTelemetryCoordinator } from "@deepseek-ai/dsh-session-telemetry";
5
5
  import { APP_IDENTITY } from "@deepseek-ai/dsh-llm";
6
6
  import { getOrCreateAnonymousUserId } from "@deepseek-ai/dsh-anonymous-user-id";
7
- import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
8
- import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
9
7
  import { SeverityNumber } from "@opentelemetry/api-logs";
10
- import { resourceFromAttributes } from "@opentelemetry/resources";
11
8
  //#region lib/types/index.js
12
9
  /**
13
10
  * OpenTelemetry Service Provider for the DeepSeek Harness telemetry capability.
14
11
  *
15
- * Composes the OTel JS SDK as-is — a `LoggerProvider` with a
16
- * `BatchLogRecordProcessor` and an OTLP/HTTP log exporter — and maps each
17
- * record handed over by the capture coordinator onto `logger.emit()`. After that call,
18
- * batching, retry, queueing, and loss policy use the SDK's documented behavior, configured
19
- * verbatim through the `exporter`/`processor` passthroughs. This package owns
20
- * capture mode and an outer shutdown deadline: the SDK's export timeout does
21
- * not bound its preceding `forceFlush()` wait.
12
+ * Authorizes feedback-bounded capture and hands complete event strings to the
13
+ * Session-log reporter. This plugin owns resource identity and an outer
14
+ * shutdown deadline; the reporter owns byte-bounded SDK delivery.
22
15
  *
23
16
  * @module @deepseek-ai/dsh-session-telemetry-otel
24
17
  */
25
- const { version } = createRequire(import.meta.url)("../package.json");
26
18
  /** Session-sharing policy selected by {@link Config.mode}. */
27
19
  var SessionTelemetryMode;
28
20
  (function(SessionTelemetryMode) {
@@ -67,34 +59,25 @@ function sharingStatusFor(mode) {
67
59
  }
68
60
  /**
69
61
  * Schemastery validator for {@link Config}; cordis runs it before the plugin
70
- * starts. It checks only the top-level fields; value checks live in the constructor
71
- * so their errors name the fields. Both SDK option objects pass through unchanged:
72
- * the SDK defines and validates their fields. Re-declaring them here would
73
- * silently drop every field this plugin did not repeat.
62
+ * starts. The constructor validates endpoint and shutdown requirements; the
63
+ * reporter validates Session byte and queue limits. SDK transport and
64
+ * processor settings retain their upstream types.
74
65
  */
75
66
  const Config = z.object({
76
67
  mode: z.union(Object.values(SessionTelemetryMode)).default(DEFAULT_TELEMETRY_MODE),
77
68
  exporter: z.any(),
78
69
  processor: z.any(),
79
- shutdownTimeoutMillis: z.number()
70
+ shutdownTimeoutMillis: z.number(),
71
+ maxRequestBytes: z.number().step(1).min(1).max(4e6)
80
72
  });
81
73
  /** Default outer allowance for the SDK's complete shutdown sequence. */
82
74
  const DEFAULT_SHUTDOWN_TIMEOUT_MILLIS = 3e3;
83
75
  const MAX_TIMER_DELAY_MILLIS = 2147483647;
84
76
  /** Severity mapping from the Service Definition's three-level vocabulary to OTel severity numbers. */
85
77
  const SEVERITY = {
86
- info: {
87
- severityNumber: SeverityNumber.INFO,
88
- severityText: "INFO"
89
- },
90
- warn: {
91
- severityNumber: SeverityNumber.WARN,
92
- severityText: "WARN"
93
- },
94
- error: {
95
- severityNumber: SeverityNumber.ERROR,
96
- severityText: "ERROR"
97
- }
78
+ info: SeverityNumber.INFO,
79
+ warn: SeverityNumber.WARN,
80
+ error: SeverityNumber.ERROR
98
81
  };
99
82
  /**
100
83
  * The backend plugin — the only entry a deployment loads. It always registers
@@ -103,7 +86,7 @@ const SEVERITY = {
103
86
  * SDK state and listens only to warn when recorded feedback stays local.
104
87
  */
105
88
  var OpenTelemetrySessionBackend = class extends SessionTelemetryBackend {
106
- static inject = ["sessions"];
89
+ static inject = ["sessions", "otel"];
107
90
  static Config = Config;
108
91
  provider;
109
92
  shutdownTimeoutMillis;
@@ -134,24 +117,40 @@ var OpenTelemetrySessionBackend = class extends SessionTelemetryBackend {
134
117
  const shutdownTimeoutMillis = config.shutdownTimeoutMillis ?? 3e3;
135
118
  if (!Number.isFinite(shutdownTimeoutMillis) || shutdownTimeoutMillis <= 0 || shutdownTimeoutMillis > MAX_TIMER_DELAY_MILLIS) throw new Error(`session-telemetry-otel: shutdownTimeoutMillis must be a positive finite number no greater than ${MAX_TIMER_DELAY_MILLIS}, got ${String(shutdownTimeoutMillis)}`);
136
119
  this.shutdownTimeoutMillis = shutdownTimeoutMillis;
137
- this.provider = new LoggerProvider({
138
- resource: resourceFromAttributes({
120
+ const { version } = createRequire(import.meta.url)("../package.json");
121
+ const reporter = ctx.otel.createSessionLogReporter({
122
+ scope: {
123
+ name: "@deepseek-ai/dsh-session-telemetry-otel",
124
+ version
125
+ },
126
+ exporter: {
127
+ ...config.exporter,
128
+ url
129
+ },
130
+ ...config.processor === void 0 ? {} : { processor: config.processor },
131
+ ...config.maxRequestBytes === void 0 ? {} : { maxRequestBytes: config.maxRequestBytes },
132
+ resourceAttributes: {
139
133
  "service.name": APP_IDENTITY.product,
140
134
  "service.version": APP_IDENTITY.version,
141
135
  "user.id": getOrCreateAnonymousUserId()
142
- }),
143
- processors: [new BatchLogRecordProcessor({
144
- ...config.processor,
145
- exporter: new OTLPLogExporter(config.exporter)
146
- })]
136
+ },
137
+ onFailure: (message, error) => {
138
+ ctx.logger.warn(message, error);
139
+ }
147
140
  });
148
- const ledger = this.provider.getLogger("@deepseek-ai/dsh-session-telemetry-otel", version);
141
+ this.provider = reporter;
149
142
  const enqueue = (record) => {
150
- ledger.emit({
151
- timestamp: record.time,
152
- observedTimestamp: record.time,
153
- ...SEVERITY[record.severity],
154
- body: record.body,
143
+ if (record.sourceEvent === void 0) {
144
+ ctx.logger.warn("Session log record withheld: redaction removed sourceEvent");
145
+ return;
146
+ }
147
+ reporter.reportSessionLog({
148
+ sessionId: record.sourceEvent.sessionId,
149
+ event: {
150
+ ...record.sourceEvent.envelope,
151
+ data: record.body
152
+ },
153
+ severityNumber: SEVERITY[record.severity],
155
154
  attributes: record.attributes
156
155
  });
157
156
  };
@@ -185,13 +184,10 @@ var OpenTelemetrySessionBackend = class extends SessionTelemetryBackend {
185
184
  */
186
185
  emit(_record) {}
187
186
  /**
188
- * Ask the SDK to drain and quiesce, but reject after the backend-owned
189
- * deadline. OTel's processor export timeout wraps `exportCompleted` only;
190
- * shutdown awaits `exporter.forceFlush()` first, which can remain pending
191
- * when the transport never obtains a socket. The provider promise remains
192
- * observed after the deadline so a later rejection cannot become unhandled.
193
- * `DISABLED` has no provider and resolves immediately.
194
- * @returns resolves when the SDK pipeline quiesces or is disabled, or rejects at the configured deadline.
187
+ * Drain queued HTTP requests until the deployment deadline. The watchdog
188
+ * never releases an unsettled transport slot. At the outer deadline,
189
+ * queued records are abandoned and no further requests may start.
190
+ * @returns completion after transport shutdown, or rejection at the configured deadline.
195
191
  */
196
192
  async shutdown() {
197
193
  if (this.provider === void 0) return;
@@ -199,6 +195,7 @@ var OpenTelemetrySessionBackend = class extends SessionTelemetryBackend {
199
195
  let timer;
200
196
  const deadline = new Promise((_resolve, reject) => {
201
197
  timer = setTimeout(() => {
198
+ this.provider?.stopPending();
202
199
  reject(/* @__PURE__ */ new Error(`session-telemetry-otel: provider shutdown exceeded ${this.shutdownTimeoutMillis}ms`));
203
200
  }, this.shutdownTimeoutMillis);
204
201
  });
@@ -1,20 +1,16 @@
1
1
  /**
2
2
  * OpenTelemetry Service Provider for the DeepSeek Harness telemetry capability.
3
3
  *
4
- * Composes the OTel JS SDK as-is — a `LoggerProvider` with a
5
- * `BatchLogRecordProcessor` and an OTLP/HTTP log exporter — and maps each
6
- * record handed over by the capture coordinator onto `logger.emit()`. After that call,
7
- * batching, retry, queueing, and loss policy use the SDK's documented behavior, configured
8
- * verbatim through the `exporter`/`processor` passthroughs. This package owns
9
- * capture mode and an outer shutdown deadline: the SDK's export timeout does
10
- * not bound its preceding `forceFlush()` wait.
4
+ * Authorizes feedback-bounded capture and hands complete event strings to the
5
+ * Session-log reporter. This plugin owns resource identity and an outer
6
+ * shutdown deadline; the reporter owns byte-bounded SDK delivery.
11
7
  *
12
8
  * @module @deepseek-ai/dsh-session-telemetry-otel
13
9
  */
14
10
  import z from '@deepseek-ai/schemastery';
15
11
  import type { Context } from '@deepseek-ai/cordis';
16
12
  import { SessionTelemetryBackend, type SessionTelemetryRecord, type SessionTelemetrySharingStatus } from '@deepseek-ai/dsh-session-telemetry';
17
- import { type BatchLogRecordProcessorOptions } from '@opentelemetry/sdk-logs';
13
+ import type { BatchLogRecordProcessorOptions } from '@opentelemetry/sdk-logs';
18
14
  import type { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base';
19
15
  /** Session-sharing policy selected by {@link Config.mode}. */
20
16
  export declare enum SessionTelemetryMode {
@@ -24,37 +20,36 @@ export declare enum SessionTelemetryMode {
24
20
  /** Default session-sharing policy for schema and direct construction. */
25
21
  export declare const DEFAULT_TELEMETRY_MODE = SessionTelemetryMode.FEEDBACK_ONLY;
26
22
  /**
27
- * Plugin configuration: one sharing policy, two verbatim SDK option objects,
28
- * and one DSH-owned shutdown bound. Uploading modes validate their endpoint
23
+ * Plugin configuration: sharing policy, SDK transport options, byte/count queue
24
+ * settings, and an overall shutdown bound. Uploading modes validate their endpoint
29
25
  * and shutdown deadline at plugin load; `DISABLED` reads neither.
30
26
  */
31
27
  export interface Config {
32
28
  /** Defaults to `FEEDBACK_ONLY`: capture session history only when feedback is explicitly submitted. */
33
29
  mode?: SessionTelemetryMode;
34
30
  /**
35
- * Passed verbatim to the SDK's OTLP/HTTP log exporter — the complete
36
- * `OTLPExporterNodeConfigBase` shape (`headers`, `timeoutMillis`,
37
- * `compression`, `keepAlive`, …), owned and documented by the SDK. `url`
38
- * is the one field this package requires and validates itself.
31
+ * Explicit SDK HTTP transport settings, including optional routing headers.
32
+ * Ambient credentials are not inherited. URL is required while uploading.
39
33
  */
40
34
  exporter?: OTLPExporterNodeConfigBase & {
41
35
  /** Full logs endpoint (e.g. `https://collector.example.com/v1/logs`). Required outside `DISABLED`; validated at load. */
42
36
  url?: string;
43
37
  };
44
38
  /**
45
- * Passed verbatim to `BatchLogRecordProcessor` (minus the exporter slot,
46
- * which this plugin fills); the SDK owns and documents these knobs.
39
+ * Count, queue, cadence, and per-request watchdog settings for the byte-bounded
40
+ * processor. A watchdog warning never releases an unsettled transport slot.
47
41
  */
48
42
  processor?: Omit<BatchLogRecordProcessorOptions, 'exporter'>;
49
43
  /** Maximum time spent awaiting the SDK provider's complete shutdown path. */
50
44
  shutdownTimeoutMillis?: number;
45
+ /** Uncompressed OTLP request byte limit, at most 4,000,000. */
46
+ maxRequestBytes?: number;
51
47
  }
52
48
  /**
53
49
  * Schemastery validator for {@link Config}; cordis runs it before the plugin
54
- * starts. It checks only the top-level fields; value checks live in the constructor
55
- * so their errors name the fields. Both SDK option objects pass through unchanged:
56
- * the SDK defines and validates their fields. Re-declaring them here would
57
- * silently drop every field this plugin did not repeat.
50
+ * starts. The constructor validates endpoint and shutdown requirements; the
51
+ * reporter validates Session byte and queue limits. SDK transport and
52
+ * processor settings retain their upstream types.
58
53
  */
59
54
  export declare const Config: z<Config>;
60
55
  /** Default outer allowance for the SDK's complete shutdown sequence. */
@@ -79,13 +74,10 @@ export declare class OpenTelemetrySessionBackend extends SessionTelemetryBackend
79
74
  */
80
75
  emit(_record: SessionTelemetryRecord): void;
81
76
  /**
82
- * Ask the SDK to drain and quiesce, but reject after the backend-owned
83
- * deadline. OTel's processor export timeout wraps `exportCompleted` only;
84
- * shutdown awaits `exporter.forceFlush()` first, which can remain pending
85
- * when the transport never obtains a socket. The provider promise remains
86
- * observed after the deadline so a later rejection cannot become unhandled.
87
- * `DISABLED` has no provider and resolves immediately.
88
- * @returns resolves when the SDK pipeline quiesces or is disabled, or rejects at the configured deadline.
77
+ * Drain queued HTTP requests until the deployment deadline. The watchdog
78
+ * never releases an unsettled transport slot. At the outer deadline,
79
+ * queued records are abandoned and no further requests may start.
80
+ * @returns completion after transport shutdown, or rejection at the configured deadline.
89
81
  */
90
82
  shutdown(): Promise<void>;
91
83
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-session-telemetry-otel",
3
- "description": "OpenTelemetry backend for the DeepSeek Harness telemetry seam: hands captured session records to the OTel JS SDK's log pipeline",
4
- "version": "0.1.7-rc.2",
3
+ "description": "Feedback-authorized Session logs over byte-bounded OpenTelemetry HTTP requests",
4
+ "version": "0.2.0-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,39 +27,38 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "dependencies": {
30
- "@opentelemetry/api": "^1.9.1",
31
30
  "@opentelemetry/api-logs": "^0.220.0",
32
- "@opentelemetry/exporter-logs-otlp-http": "^0.220.0",
33
31
  "@opentelemetry/otlp-exporter-base": "^0.220.0",
34
- "@opentelemetry/resources": "^2.9.0",
35
32
  "@opentelemetry/sdk-logs": "^0.220.0",
36
33
  "@deepseek-ai/schemastery": "~3.18.4"
37
34
  },
38
35
  "peerDependencies": {
39
- "@deepseek-ai/dsh-command-feedback": "0.1.7-rc.2",
40
- "@deepseek-ai/dsh-message-feedback": "0.1.7-rc.2",
41
- "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
42
- "@deepseek-ai/dsh-session": "0.1.7-rc.2",
43
- "@deepseek-ai/dsh-session-telemetry": "0.1.7-rc.2",
44
- "@deepseek-ai/dsh-anonymous-user-id": "0.1.7-rc.2",
45
- "@deepseek-ai/cordis": "~4.0.4"
36
+ "@deepseek-ai/dsh-command-feedback": "0.2.0-rc.2",
37
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
38
+ "@deepseek-ai/dsh-message-feedback": "0.2.0-rc.2",
39
+ "@deepseek-ai/dsh-session": "0.2.0-rc.2",
40
+ "@deepseek-ai/dsh-anonymous-user-id": "0.2.0-rc.2",
41
+ "@deepseek-ai/dsh-otel": "0.2.0-rc.2",
42
+ "@deepseek-ai/cordis": "~4.0.4",
43
+ "@deepseek-ai/dsh-session-telemetry": "0.2.0-rc.2"
46
44
  },
47
45
  "devDependencies": {
48
46
  "@deepseek-ai/cordis-plugin-logger-console": "~1.0.4",
49
47
  "@deepseek-ai/cordis-plugin-loader": "~1.0.5",
50
- "@deepseek-ai/dsh-app-boot": "0.1.7-rc.2",
51
- "@deepseek-ai/dsh-bash-local": "0.1.7-rc.2",
52
- "@deepseek-ai/dsh-command-feedback": "0.1.7-rc.2",
53
- "@deepseek-ai/dsh-message-feedback": "0.1.7-rc.2",
54
- "@deepseek-ai/dsh-loader-smoke": "0.1.7-rc.2",
55
- "@deepseek-ai/dsh-session": "0.1.7-rc.2",
56
- "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
57
- "@deepseek-ai/dsh-session-checkpoint-policy": "0.1.7-rc.2",
58
- "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-rc.2",
59
- "@deepseek-ai/dsh-session-telemetry": "0.1.7-rc.2",
60
- "@deepseek-ai/dsh-anonymous-user-id": "0.1.7-rc.2",
48
+ "@deepseek-ai/dsh-app-boot": "0.2.0-rc.2",
49
+ "@deepseek-ai/dsh-bash-local": "0.2.0-rc.2",
50
+ "@deepseek-ai/dsh-command-feedback": "0.2.0-rc.2",
51
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
52
+ "@deepseek-ai/dsh-message-feedback": "0.2.0-rc.2",
53
+ "@deepseek-ai/dsh-session": "0.2.0-rc.2",
54
+ "@deepseek-ai/dsh-loader-smoke": "0.2.0-rc.2",
55
+ "@deepseek-ai/dsh-session-checkpoint-policy": "0.2.0-rc.2",
56
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.0-rc.2",
57
+ "@deepseek-ai/dsh-session-telemetry": "0.2.0-rc.2",
58
+ "@deepseek-ai/dsh-subprocess-local": "0.2.0-rc.2",
59
+ "@deepseek-ai/dsh-anonymous-user-id": "0.2.0-rc.2",
61
60
  "@deepseek-ai/cordis": "~4.0.4",
62
- "@deepseek-ai/dsh-http-proxy": "0.1.7-rc.2",
63
- "@deepseek-ai/dsh-subprocess-local": "0.1.7-rc.2"
61
+ "@deepseek-ai/dsh-http-proxy": "0.2.0-rc.2",
62
+ "@deepseek-ai/dsh-otel": "0.2.0-rc.2"
64
63
  }
65
64
  }