Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,48 @@
# Zero-content privacy contract
# Task-agnostic feedback privacy contract

Plugin Lab v2 的隐私目标不是“先收集再脱敏”,而是让私密内容在能力层面无法进入反馈数据流
Plugin Lab v3 允许上传一个经过用户确认的体验大类,但不允许上传模型生成的自由文本摘要。目标是让开发者获得可聚合、可行动的方向,同时让当前任务、会话和日志在协议层面没有入口

## 不变量

1. **内容非干涉**:在插件、健康状态、用户选择和单次事件 ID 相同时,替换任意 Prompt、回复、日志、异常、文件、路径或 Secret,序列化反馈字节必须保持不变。
2. **有限字母表**:网络包只允许协议中列出的标量和公开插件坐标;未知字段直接拒绝。
3. **宿主真源**:探活只读取 DSH Host 的 Loader/Fiber 状态,不调用目标插件,也不进行文件、网络或数据库 I/O。
4. **Agent 零内容**:Agent 工具输入为空,输出只含 `health`、固定 `experience=unknown` 和确认布尔值。
5. **主观状态不臆测**:只有用户点击产生 `good/mixed/bad`;健康状态不能自动转换为体验评价。
6. **本地与上传解耦**:探活和确认体验都不产生网络请求;`/omdsh-join` 是独立同意动作。
7. **无稳定身份**:没有用户、账号、设备、安装、Session 或跨报告匿名 ID。
8. **无日志派生**:不接受日志摘要、错误类型、错误码、堆栈、指纹或哈希。
9. **固定渲染**:Agent 状态和反馈字段使用有限枚举;服务端错误不回显原始输入。
10. **聚合后通知**:GitHub 只接收达到阈值后的聚合枚举,不接收单条报告。
1. **封闭影响范围**:私密内容最多影响 Agent 建议的一个大类;一旦用户确认大类,反馈序列化只由公开插件坐标、有限枚举和随机单次 ID 决定。
2. **有限字母表**:网络包只允许协议列出的字段;未知字段直接拒绝,不做“接收后脱敏”。
3. **没有自由摘要字段**:`summary`、`note`、`reason`、`message` 等字段全部拒绝。可读摘要只由固定模板渲染。
4. **宿主真源**:探活只读取 DSH Host 的 Loader/Fiber 状态,不调用目标插件,也不进行文件、网络或数据库 I/O。
5. **Agent 窄工具**:探活工具输入为空;预览工具只接受 `experience` 和 `category` 两个枚举,且没有存储或网络副作用。
6. **主观状态不臆测**:只有用户确认才能产生 `source=user_confirmed` 的本地记录;健康状态不能自动转换为体验评价。
7. **预览后提交**:体验和大类先写入本地草稿并显示完整预览;`/omdsh-join` 是独立的逐条发送动作。
8. **无稳定身份**:没有用户、账号、设备、安装、Session 或跨报告匿名 ID。
9. **无日志派生详情**:不接受日志摘要、错误类型、错误码、堆栈、指纹或哈希;崩溃只能成为 `health=error`。
10. **聚合后通知**:GitHub 只接收达到阈值后的大类、状态、体验和报告数,不接收单条报告。

## 允许的大类

```text
installation | startup | invocation | compatibility
reliability | performance | result_quality | general
```

这些大类描述插件体验阶段,不描述用户正在执行什么任务。Agent 可以基于当前交互建议一个大类,但用户必须在预览中检查、修改或取消。

## 必须通过的攻击测试

- 把邮箱、Token、绝对路径、聊天文本分别放入会话和异常;反馈包不得变化或出现 canary
- 目标插件处于同一 Fiber 状态时,替换所有私密内容,反馈序列化结果必须一致
- 向 Agent 工具加入 `reason/log/prompt` 参数必须被拒绝
- 向上传包加入 `note/log/stack/signals/environment/participantId/occurredAt` 必须拒绝整个请求
- 向 Agent 预览工具加入 `summary/task/log/prompt` 参数必须拒绝整个调用
- 向上传包加入 `summary/note/log/stack/signals/environment/participantId/occurredAt` 必须拒绝整个请求
- 把邮箱、Token、绝对路径、聊天文本放入会话和异常;本地包与固定摘要不得出现 canary
- 相同公开插件、健康状态、体验和大类下,更换私密任务内容不能改变序列化包
- `verbose/debug/include=logs` 不能改变探活结果。
- 未配置 Loader、状态缺失或序列化失败只能得到 `unknown`,不能输出异常详情。
- 用户只点击探活或体验选择时,抓包必须没有反馈请求。
- 请求体超过 1 KiB 必须在 JSON 解析前拒绝。
- 未配置 Loader、状态缺失或内部异常只能得到 `unknown`,不能输出异常详情。
- 用户只点击探活、体验或大类时,抓包必须没有反馈请求。
- 用户必须能在发送前看到插件、版本、状态、体验、大类和单次报告 ID。
- 请求体超过 1 KiB 必须在接受字段前拒绝。
- 服务端 4xx/5xx 响应不得包含 canary、异常、路径或请求正文。
- 未达到聚合阈值时不能创建 GitHub Issue。

## 明确限制

- 受控大类仍是一条信息,例如 `result_quality` 会说明用户认为“结果质量”值得反馈;因此必须预览并由用户确认。
- Agent 已经处在正常会话链路时可能看得到当前任务;本协议保证的是这些内容不能进入反馈工具参数或上传载荷,而不是声称 Agent 从未见过会话。
- `health=ok` 只表示宿主认为插件可运行,不代表用户满意,也不证明业务结果正确。
- 没有稳定身份后,聚合只能统计报告数,不能准确统计独立用户。
- IP、连接时间等传输元数据仍会被网络基础设施瞬时观察;部署者必须通过日志配置和保留策略降低风险,不能据此宣称绝对匿名
- 旧版本可能已经创建本地崩溃或安装身份文件。v2 不读取、不补传,也不自动删除这些历史文件
- IP、连接时间等传输元数据仍会被网络基础设施瞬时观察;部署者必须通过日志配置和保留策略降低风险,不能宣称绝对匿名
- v3 不读取、不补传,也不自动删除历史 v1/v2 文件或表
106 changes: 56 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,107 +1,113 @@
# Oh My DSH Plugin Lab

Plugin Lab 0.3 是 DeepSeek Harness rc.6 的零内容插件探活与体验反馈闭环
Plugin Lab 0.4 是面向 DeepSeek Harness rc.6 的隐私优先插件反馈闭环:Agent 可以探活并准备脱敏大类摘要,用户在上传前看到完整预览并逐次确认,后端只把达到阈值的聚合信号提交到 GitHub

它把两件事严格分开:
## 现在的体验

- DSH Host 可以判断插件当前是否正常运行;
- “好用 / 一般 / 不好用”只能由用户确认,Agent 不得读取会话或日志代替用户推断。

## 体验流程

1. `/omdsh-start <plugin>#<version>` 选择本次试用的公开插件。
2. 用户随时点击 Composer 左侧的“插件探活”,本地查看 `OK / 暂不可用 / 异常 / 未知`。
3. Agent 可以调用零参数工具 `omdsh_analyze_plugin_experience`,但只会得到运行状态和 `experience=unknown`。
4. 最新回复旁出现体验卡,由用户选择“好用 / 一般 / 不好用”。这一步只保存在本机。
5. 用户再次点击加入跟进或运行 `/omdsh-join latest`,才会发送屏幕上已经说明的有限字段。
6. 后端按插件、版本和状态聚合;达到阈值后创建 GitHub Issue,并通过回执邀请用户复测。
1. `/omdsh-start <plugin>#<version>` 选择公开的插件坐标。
2. 用户或 Agent 运行探活,只得到 `ok / unavailable / error / unknown`。
3. Agent 可以建议“安装、启动、调用、兼容性、稳定性、性能、结果质量、整体体验”之一,但反馈工具不接受任务文本。
4. 用户选择“好用 / 一般 / 不好用”和一个大类。
5. Plugin Lab 在本地生成固定模板,并展示即将上传的全部可读信息;此时没有网络请求。
6. 用户点击“确认并提交”或运行 `/omdsh-join latest` 后,有限枚举包才会发送。
7. 后端按插件、版本、状态、体验和大类聚合;同类报告达到阈值后创建 GitHub Issue,并通过回执邀请复测。

```mermaid
flowchart LR
A["DSH Host Loader/Fiber"] --> B["本地状态枚举"]
B --> C["一键探活"]
B --> D["Agent 零参数工具"]
D --> E["experience = unknown"]
E --> F["用户确认好用 / 一般 / 不好用"]
F --> G["单独同意发送"]
G --> H["后端有限枚举聚合"]
H -->|"达到阈值"| I["GitHub 聚合 Issue"]
I --> J["修复与复测回执"]
A["插件试用"] --> B["Host 无日志探活"]
B --> C["Agent 建议有限大类"]
C --> D["用户选择体验和大类"]
D --> E["本地固定模板预览"]
E -->|"用户再次确认"| F["发送有限枚举"]
F --> G["后端聚合"]
G -->|"达到阈值"| H["GitHub 聚合 Issue"]
H --> I["修复与复测回执"]
```

Agent 可能已经拥有当前任务的正常会话上下文,但 Plugin Lab 的工具参数和上传协议都没有承载这些内容的字段。Agent 只能从封闭枚举中提出建议;最终分类和主观体验由用户确认。

## 安装开发版本

```sh
pnpm install
pnpm pack:release
dsh plugin --profile web add ./oh-my-dsh-plugin-lab-0.3.0.tgz
dsh plugin --profile web add ./oh-my-dsh-plugin-lab-0.4.0.tgz
dsh --profile web
```

Plugin Lab 是标准 DSH Bundle:`package.json` 通过 `dsh.bundle.patch` 声明 Host 插件,通过 `dsh.client` 提供 Web 探活、结果卡和收件箱。

版本 `0.3.0` 的 Peer 契约从 DSH `0.1.0-rc.6` 起。完整测试会执行真实的 rc.6 打包、安装、Host/Web 启动、Client Loader 注册和卸载。
版本 `0.4.0` 的 Peer 契约从 DSH `0.1.0-rc.6` 起。完整测试会执行真实的 rc.6 打包、安装、Host/Web 启动、Client Loader 注册和卸载。

## 命令与 Agent 工具

| 接口 | 作用 |
|---|---|
| `/omdsh-start <module>[#version]` | 开始单插件试用;不接受任务标签或备注 |
| `/omdsh-probe` | 本地读取当前目标的 Host 生命周期状态 |
| `/omdsh-result <good\|mixed\|bad>` | 用户确认体验,只保存本机 |
| `/omdsh-join <latest\|event-id>` | 单独同意发送有限字段 |
| `/omdsh-result <verdict> <category>` | 用户确认体验和大类,生成本地预览 |
| `/omdsh-join <latest\|event-id>` | 用户确认发送已经展示的有限字段 |
| `/omdsh-inbox [--peek]` | 查看聚合问题、修复版本与复测邀请 |
| `/omdsh-retest <receipt-id> <module>[#version]` | 从单条回执开始复测 |
| `/omdsh-status` | 查看本地试用、待发送和未读状态 |
| `/omdsh-privacy` | 显示完整隐私边界 |
| `omdsh_analyze_plugin_experience({})` | Agent 查询运行状态;输入必须为空 |
| `omdsh_analyze_plugin_experience({})` | Agent 查询状态和允许的大类;输入必须为空 |
| `omdsh_preview_plugin_feedback({experience, category})` | 生成无副作用固定模板预览;不保存、不上传 |

`verdict` 只能是 `good / mixed / bad`。`category` 只能是:

```text
installation | startup | invocation | compatibility
reliability | performance | result_quality | general
```

## 精确数据边界
## 精确上传协议

唯一允许上传的数据包为:

```json
{
"schemaVersion": 2,
"schemaVersion": 3,
"type": "feedback.signal",
"eventId": "随机单次 UUID",
"plugin": {
"moduleName": "marketplace-public-id",
"version": "1.2.3"
},
"health": "ok",
"experience": "good",
"health": "error",
"experience": "bad",
"category": "reliability",
"source": "user_confirmed"
}
```

复测时可以额外出现一个随机、单报告范围的 `retestOfReceiptId`。客户端和服务端都拒绝任何其他字段,而不是接收后脱敏
复测时可以额外出现一个随机、单报告范围的 `retestOfReceiptId`。客户端和服务端拒绝任何其他字段。协议没有 `summary` 自由文本字段;界面和 GitHub 中看到的中文摘要都由上述枚举通过固定模板生成

不会创建、读取或发送:

- stdout、stderr、访问日志、应用日志;
- 当前任务、任务标签、Prompt、Assistant 回复或 Agent memory;
- stdout、stderr、访问日志、应用日志或 Tool 参数/结果;
- exception、错误码、stack、frame、崩溃指纹;
- Prompt、Assistant 回复、Agent memory、Tool 参数和结果;
- 文件、代码、路径、URL、环境变量、配置;
- 用户、账号、设备、安装、Session 等稳定标识;
- 客户端时间、locale、OS、架构、DSH/Node 版本、任务标签、计数和时延
- 备注、理由或任何自由文本反馈
- 客户端时间、locale、OS、架构、计数或时延
- 备注、理由、模型自由摘要或其他自由文本

本地 v2 文件只有:
本地 v3 文件只有:

```text
$DSH_HOME/omdsh-plugin-lab/
feedback-v2.ndjson
share-requests-v2.ndjson
receipts-v2.ndjson
receipt-seen-v2.ndjson
feedback-v3.ndjson
share-requests-v3.ndjson
receipts-v3.ndjson
receipt-seen-v3.ndjson
```

目录权限为 `0700`,文件权限为 `0600`。升级不会读取或补传旧版 `.install-id`、`events.ndjson``crashes.ndjson`;历史文件不会被自动删除。
目录权限为 `0700`,文件权限为 `0600`。v3 不读取或补传旧版 `.install-id`、`events.ndjson``crashes.ndjson` 或 v2 队列;历史文件不会被自动删除。

网络传输天然会让服务器或中间层观察 IP、时间等元数据,因此项目只承诺“载荷零内容、无身份字段”,不宣称绝对匿名。生产部署必须关闭代理、网关、WAF、应用和数据库的请求体日志,并不得把 IP/User-Agent 写入业务数据。
网络传输仍会让服务器或中间层观察 IP、请求时间等元数据,因此项目不宣称绝对匿名。生产部署必须关闭代理、网关、WAF、应用和数据库的请求体日志,并不得把 IP/User-Agent 写入业务数据。

更完整的可验证不变量和攻击测试见 [PRIVACY.md](./PRIVACY.md)。
完整不变量和攻击测试见 [PRIVACY.md](./PRIVACY.md)。

## 启用中央反馈

Expand All @@ -117,19 +123,19 @@ Bundle 默认关闭网络发送:
retryIntervalMs: 30000
```

部署开启发送能力不等于用户同意。每条记录仍要由用户单独运行 `/omdsh-join`;拒绝发送不影响插件功能。
部署开启发送能力不等于用户同意。每条记录仍要由用户单独确认 `/omdsh-join`;拒绝发送不影响插件功能。

## 后端与 GitHub 飞轮

`server/` 提供 Node.js + PostgreSQL 接收器:

- 只接受 schema v2,未知字段 fail closed;
- 只接受 schema v3,未知字段 fail closed;
- 请求体上限 1 KiB,错误响应不回显输入或异常;
- 不存 IP、User-Agent、原始请求体或客户端时间;
- 不使用用户、安装或稳定匿名 ID,因此统计口径是“报告数”,不是“独立用户数”;
- 按公开插件、版本、health 和 experience 聚合;
- 不使用稳定用户 ID,统计口径是“报告数”而非“独立用户数”;
- 按公开插件、版本、health、experiencecategory 聚合;
- 默认同类报告达到 5 条后才创建 GitHub 聚合 Issue;
- GitHub 不接收单条反馈或回执 ID
- GitHub 只接收固定模板和聚合计数,不接收单条反馈、回执 ID或任务信息
- Follow Token 只关联一条报告,用于返回修复与复测状态。

生产环境需要:
Expand All @@ -155,4 +161,4 @@ GITHUB_REPORT_THRESHOLD=5
pnpm test:all
```

验证覆盖:闭合输入/输出 Schema、Loader 状态映射、Agent 零参数工具、内容非干涉金丝雀、无崩溃监听器、两阶段同意、Client 真实点击、后端未知字段拒绝、PostgreSQL v2 列审计、聚合阈值、回执/复测,以及真实 DSH rc.6 安装与启动生命周期。
验证覆盖闭合 Schema、Agent 探活/预览工具、任务内容 canary、两阶段确认、Client 真实点击、服务端未知字段拒绝、PostgreSQL v3 列审计、GitHub 固定模板、聚合阈值、回执/复测,以及真实 DSH rc.6 安装与启动生命周期。
8 changes: 7 additions & 1 deletion dist/agent-tool.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { Agent } from '@deepseek-ai/dsh-agent';
import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
import type { SafeExperienceAssessment } from './protocol.js';
import { type ExperienceVerdict, type FeedbackCategory, type FeedbackPreview, type SafeExperienceAssessment } from './protocol.js';
export declare const AGENT_ASSESSMENT_TOOL = "omdsh_analyze_plugin_experience";
export declare const AGENT_PREVIEW_TOOL = "omdsh_preview_plugin_feedback";
/** A raw ToolDefinition is used so the rc.6 input schema is closed as well as the output. */
export declare function createAgentAssessmentTool(assess: (agent: Agent | undefined) => SafeExperienceAssessment): ToolDefinition;
/**
* Pure preview tool: finite enum input, fixed-template output, and no storage or network side effect.
* The Agent can prepare the card, but only the user's separate confirmation can publish it.
*/
export declare function createAgentPreviewTool(preview: (agent: Agent | undefined, experience: ExperienceVerdict, category: FeedbackCategory) => FeedbackPreview): ToolDefinition;
Loading