Feedback-gated session telemetry
反馈门控的会话遥测
Session telemetry originally has one mounted behavior: every accepted record enters the reporting backend immediately. Deployments need two stricter policies without replacing the plugin: hold a session's telemetry unless its user records feedback, or disable reporting while still explaining what happens to feedback. The policy must preserve the telemetry seam's redaction-before-backend boundary.
English
Problem
Session telemetry originally has one mounted behavior: every accepted record enters the reporting backend immediately. Deployments need two stricter policies without replacing the plugin: hold a session's telemetry unless its user records feedback, or disable reporting while still explaining what happens to feedback. The policy must preserve the telemetry seam's redaction-before-backend boundary.
Decision
@deepseek-ai/dsh-session-telemetry-otel exposes the string-valued SessionTelemetryMode enum to TypeScript callers and accepts the same three uppercase mode values in serialized configuration:
FULLexplicitly selects immediate delivery to the configured OTel pipeline.FEEDBACK_ONLYreads the canonical session log whenfeedback/recordis appended and hands over the unreleased prefix through that exact event. Records appended after that boundary remain local until another feedback event.DISABLEDis the default, constructs no exporter, processor, or logger provider, and prints that nothing is shared and the feedback remains local when it observesfeedback/record.
The generic telemetry coordinator owns live and on-demand capture. Live capture projects, clones, redacts, and hands each event to the backend on the session firehose. On-demand capture registers no continuous capture listeners; captureSession(session, throughSeq) reads the canonical log from the handoff cursor through an inclusive boundary, then projects, clones, redacts, and hands over that prefix. The cursor advances only for handed-over records. The buffer-free replay decision owns why the on-demand path uses the canonical log instead of copied records.
Mode resolution is a closed, fail-before-setup check: an unknown direct-construction value fails before transport configuration is read. Only FULL exposes the public service's emit() path to the SDK pipeline. FEEDBACK_ONLY gives its on-demand coordinator a private backend capability; its listener passes an event to captureSession() only when the exact feedback/record object is already stored at session.events[event.seq]. Session.append commits that object before publishing session/event, so replay includes the feedback but cannot extend past its boundary. DISABLED creates neither the capability nor the SDK pipeline and does not inspect exporter configuration.
Alternatives considered
Open a session permanently after its first feedback. Rejected because later work would be shared without another feedback act and the plugin would need additional open-session state. Releasing one pending prefix per feedback has the smaller state machine and the narrower sharing boundary.
Retain capture-time redacted records until feedback. Rejected because it duplicates an unbounded session prefix even though the canonical log already owns the events. It preserves capture-time redaction policy and operational records, but those properties do not justify the memory cost for a mode defined as uploading the session log after feedback.
Temporarily allow public emit() calls during feedback replay. Rejected because a redaction listener or another reentrant caller could enqueue an unrelated record while the flag was open. A private backend capability makes authorization structural and keeps the public service closed throughout replay.
Use an unmounted plugin as the disabled state. That remains the silent opt-out, but it cannot warn when feedback is recorded. The explicit disabled mode lets a deployment keep one configuration shape and communicate that the local feedback did not leave the process.
Consequences
FULL retains the original source and wire behavior as an explicit opt-in. FEEDBACK_ONLY adds no telemetry-owned per-event buffer before feedback; direct service calls and non-canonical feedback events upload nothing, and a crash before feedback uploads nothing from that prefix. Replay applies the redaction policy mounted when feedback is recorded and excludes operational records that do not exist in the canonical log. Feedback-only streams therefore carry neither agent-error nor shutdown records, and shutdown absence is not a crash signal. Each later feedback captures the suffix accumulated since the previous boundary. DISABLED can omit exporter.url, does no reporting work, and keeps feedback only in the canonical session log.
中文
问题
会话遥测原本只有一种已挂载行为:每条已接受记录都立即进入上报后端。部署方需要两种更严格的策略,且不替换插件:只有用户记录反馈时才释放该会话的遥测,或禁用上报并仍向用户说明反馈的去向。该策略必须保留遥测 seam 在记录抵达后端之前脱敏的边界。
决策
@deepseek-ai/dsh-session-telemetry-otel 向 TypeScript 调用方公开以字符串为值的 SessionTelemetryMode 枚举,并在序列化配置中接受相同的三个大写 mode 值:
FULL显式选择向已配置 OTel 流水线即时投递。FEEDBACK_ONLY在追加feedback/record时读取权威会话日志,并交接截至该事件的未释放前缀。该边界后追加的记录会留在本地,直到另一个反馈事件。DISABLED是默认值,不构造导出器、处理器或日志提供方,并在观察到feedback/record时输出警告,说明什么都不会共享,且反馈仍留在本地。
通用遥测协调器拥有 live 与 on-demand 捕获。实时捕获在会话 firehose 上投影、深拷贝、脱敏每个事件,并将其交给后端。按需捕获不注册持续捕获监听器;captureSession(session, throughSeq) 从 handoff 游标起读取权威日志,直至含边界的指定序列号,然后投影、深拷贝、脱敏并交接该前缀。游标只为已交接记录推进。无缓冲回放决策说明了按需路径为何使用权威日志而非记录副本。
模式解析采用封闭式检查,并在设置前失败:通过直接构造传入未知值时,会在读取传输配置前失败。只有 FULL 向 SDK 流水线开放公共服务的 emit() 路径。FEEDBACK_ONLY 向其按需协调器提供私有后端能力;其监听器向 captureSession() 传递事件的唯一条件,是该事件与那个 feedback/record 对象身份完全相同,且该对象已存储于 session.events[event.seq]。Session.append 在发布 session/event 前已提交该对象,因此回放包含该反馈,但不会越过其边界。DISABLED 既不创建该能力,也不创建 SDK 流水线,并且不检查导出器配置。
考虑过的替代方案
会话在首次反馈后永久开放。 已否决,因为后续工作会在用户未再次提交反馈的情况下被共享,而且插件需要额外的会话开放状态。每次反馈只释放一个待处理前缀,状态机更小,共享边界也更窄。
反馈前保留捕获时已脱敏记录。 已否决,因为权威日志已拥有这些事件,该方案仍会复制无上限的会话前缀。它能保留捕获时的脱敏策略与运维记录,但对于一个定义为「反馈后上传会话日志」的模式,这些性质不足以证明该内存成本合理。
在反馈回放期间临时允许公开 emit() 调用。 已否决,因为在标志开启期间,脱敏监听器或另一个可重入调用方可能将无关记录入队。私有后端能力使授权成为结构性保证,并确保公共服务在整个回放过程中保持关闭。
以不挂载插件表示禁用状态。 这仍然是静默退出方式,但无法在记录反馈时输出警告。显式禁用模式让部署方可以保持同一种配置形态,并说明本地反馈未离开进程。
后果
FULL 作为显式启用模式保留原有的源码与协议行为。FEEDBACK_ONLY 在反馈前不增加遥测自有的逐事件缓冲;直接服务调用与非权威反馈事件均不上传任何内容,且反馈前发生崩溃时,该前缀也不上传任何内容。回放使用记录反馈时挂载的脱敏策略,并排除权威日志中不存在的运维记录。因此,仅反馈的流既不携带 agent-error 记录,也不携带 shutdown 记录,而缺少 shutdown 不是崩溃信号。每个后续反馈都会捕获从上一个边界起累积的后缀。DISABLED 可省略 exporter.url,不执行任何上报工作,并仅在权威会话日志中保留反馈。