DSH / Atlas
2026-08-07implementedfeature

Feedback acknowledgement sharing disclosure

反馈确认中的会话共享披露

The `/feedback` command records a log-only `feedback/record` event and acknowledges the user, but the acknowledgement carried no durable context about what happened to the session: deployments that mount session telemetry (`FULL`, `FEEDBACK_ONLY`, or `DISABLED`) had no way to tell the user whether their feedback and session left the process, and the receiving session id was not echoed. The command plugin could not re

English

Problem

The /feedback command records a log-only feedback/record event and acknowledges the user, but the acknowledgement carried no durable context about what happened to the session: deployments that mount session telemetry (FULL, FEEDBACK_ONLY, or DISABLED) had no way to tell the user whether their feedback and session left the process, and the receiving session id was not echoed. The command plugin could not read the sharing policy because the telemetry seam exposed capture only, and the OTel mode enum lived in the optional backend package.

Decision

The telemetry seam (@deepseek-ai/dsh-session-telemetry) now owns a backend-independent sharing vocabulary: SessionTelemetrySharingStatus (full | feedback-only | disabled) plus a required abstract sharing member on the SessionTelemetryBackend service class — every backend must disclose its policy, so a consumer renders "not configured" only when no telemetry service is mounted. @deepseek-ai/dsh-session-telemetry-otel maps its serialized SessionTelemetryMode (the feedback-gated delivery decision owns the mode semantics) onto that status in the constructor and discloses it, including in DISABLED. The /feedback handler reads the mounted service through the plugin context (ctx.get('telemetry'), never a declared injection, so the command loads and runs without telemetry) and appends one sharing sentence to the acknowledgement: Feedback recorded for session {id}. <sentence>. No service → Session sharing is not configured.; disabledSession sharing is disabled.; feedback-onlySession sharing is feedback-gated; recording feedback releases the session prefix for sharing.; fullSession sharing is enabled.

The disclosure states the current sharing policy only; it never promises delivery or retention. Handoff is the backend's non-blocking enqueue and batching, retry, and loss policy stay the backend SDK's, and a later reconfiguration can change what was shared, so the sentences claim nothing about what reached a collector or about future retention. The disclosure adds no session event and never reaches the model surface; the web client renders it through the existing command row (CommandNode outcome text) with no client change.

Alternatives considered

A client-side status RPC and badge. Rejected because the acknowledgement is host-produced and the web client already renders the command result text verbatim in the command row; a separate RPC would duplicate the status in a second surface and add a wire contract for a sentence.

Declared telemetry injection in command-feedback. Rejected because telemetry is optional: a declared injection fails plugin load when the service is absent, while the command must work without it. The plugin reads the service with ctx.get('telemetry') at handler time instead.

OTel package owns the vocabulary. Rejected because command-feedback must not depend on the optional OTel backend package. The seam owns SessionTelemetrySharingStatus so any backend can disclose a policy.

Consequences

The acknowledgement is user-visible: it names the receiving session and reports the current sharing policy, honest about the fire-and-forget handoff. Package tests pin the sentence for each status and for the absent-service case; the assembled-browser e2e mounts the shipped telemetry row in FULL mode against a local dead endpoint and pins the shipped default sentence (Session sharing is enabled.) as a golden. The seam member is required, so a mounted backend always discloses a policy and the "not configured" sentence truthfully means no telemetry service; the /feedback command keeps working with no telemetry mounted. A still-blank web session renders no command row, so feedback recorded before the first message gets no visible acknowledgement (documented under the package README's limitations).

中文

问题

/feedback 命令会记录一个仅写入日志的 feedback/record 事件并确认用户,但确认文本没有携带关于会话去向的持久信息:挂载了会话遥测(FULLFEEDBACK_ONLYDISABLED)的部署无法告知用户其反馈和会话是否离开了进程,确认文本也没有回显接收会话的 id。命令插件无法读取共享策略,因为遥测 seam 只暴露采集能力,而 OTel 模式枚举位于可选的后端包中。

决策

遥测 seam(@deepseek-ai/dsh-session-telemetry)现在拥有与后端无关的共享词汇:SessionTelemetrySharingStatusfull | feedback-only | disabled),并在 SessionTelemetryBackend 服务类上增加一个必需的抽象 sharing 成员——每个后端都必须披露其策略,因此消费方只有在未挂载任何遥测服务时才渲染「未配置」。@deepseek-ai/dsh-session-telemetry-otel 在构造函数中把序列化的 SessionTelemetryMode(模式语义由反馈门控投递决策负责)映射到该状态并披露,包括 DISABLED 模式。/feedback 处理器通过插件上下文读取已挂载的服务(ctx.get('telemetry'),绝不是声明的注入,因此命令在无遥测时也能加载和运行),并在确认文本后追加一句共享披露:Feedback recorded for session {id}. <句子>。无服务 → Session sharing is not configured.disabledSession sharing is disabled.feedback-onlySession sharing is feedback-gated; recording feedback releases the session prefix for sharing.fullSession sharing is enabled.

披露只陈述当前的共享策略,绝不承诺投递或留存:交接是后端的非阻塞入队,批处理、重试与丢失策略仍归后端 SDK,且后续重新配置可能改变已共享的内容,因此句子不声称任何内容已到达采集端,也不声称未来的留存。披露不新增任何会话事件,也绝不会进入模型 surface;Web 客户端通过现有的命令行(CommandNode 的结果文本)原样渲染,无需客户端改动。

备选方案

客户端新增状态 RPC 与徽标。 拒绝,因为确认文本由宿主生成,Web 客户端已经在命令行中原样渲染命令结果文本;单独的 RPC 会在第二个 surface 重复该状态,并为一句文案新增线上契约。

command-feedback 中声明 telemetry 注入。 拒绝,因为遥测是可选的:服务缺失时声明注入会导致插件加载失败,而命令必须在无遥测时可用。插件改为在处理器执行时用 ctx.get('telemetry') 读取服务。

由 OTel 包拥有词汇。 拒绝,因为 command-feedback 不能依赖可选的 OTel 后端包。seam 拥有 SessionTelemetrySharingStatus,任何后端都能披露策略。

后果

确认文本对用户可见:它点名接收会话并报告当前的共享策略,如实说明 fire-and-forget 交接。包级测试为每种状态以及无服务场景固定句子;组装浏览器 e2e 以 FULL 模式挂载随附的遥测行(指向本地 dead 端点),并以 golden 固定随附默认句子(Session sharing is enabled.)。seam 成员是必需的,因此已挂载的后端总会披露策略,「未配置」句子如实地表示没有遥测服务;/feedback 命令在未挂载遥测时仍能正常工作。仍为空白的新 Web 会话不渲染命令行,因此首条消息之前记录的反馈没有可见确认(已在包 README 的限制中记录)。