upstream-radar 0.43.5 → 0.45.0

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.
Files changed (40) hide show
  1. package/README-zh-CN.md +50 -24
  2. package/README.md +64 -33
  3. package/dist/src/cli.js +155 -1
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/dsh-compatibility-ledger.d.ts +2 -0
  6. package/dist/src/dsh-compatibility-ledger.d.ts.map +1 -1
  7. package/dist/src/dsh-compatibility-ledger.js +5 -0
  8. package/dist/src/dsh-compatibility-ledger.js.map +1 -1
  9. package/dist/src/dsh-directory-feed.d.ts +15 -6
  10. package/dist/src/dsh-directory-feed.d.ts.map +1 -1
  11. package/dist/src/dsh-directory-feed.js +104 -28
  12. package/dist/src/dsh-directory-feed.js.map +1 -1
  13. package/dist/src/dsh-install-observation.d.ts +6 -0
  14. package/dist/src/dsh-install-observation.d.ts.map +1 -1
  15. package/dist/src/dsh-install-observation.js +2 -2
  16. package/dist/src/dsh-install-observation.js.map +1 -1
  17. package/dist/src/dsh-surface-agent-plan.d.ts +64 -0
  18. package/dist/src/dsh-surface-agent-plan.d.ts.map +1 -0
  19. package/dist/src/dsh-surface-agent-plan.js +267 -0
  20. package/dist/src/dsh-surface-agent-plan.js.map +1 -0
  21. package/dist/src/dsh-surface-observation.d.ts +162 -0
  22. package/dist/src/dsh-surface-observation.d.ts.map +1 -0
  23. package/dist/src/dsh-surface-observation.js +1033 -0
  24. package/dist/src/dsh-surface-observation.js.map +1 -0
  25. package/dist/src/dsh-surface.d.ts +142 -0
  26. package/dist/src/dsh-surface.d.ts.map +1 -0
  27. package/dist/src/dsh-surface.js +805 -0
  28. package/dist/src/dsh-surface.js.map +1 -0
  29. package/dist/src/index.d.ts +3 -0
  30. package/dist/src/index.d.ts.map +1 -1
  31. package/dist/src/index.js +3 -0
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/upstream-observer.d.ts +4 -0
  34. package/dist/src/upstream-observer.d.ts.map +1 -1
  35. package/dist/src/upstream-observer.js +40 -4
  36. package/dist/src/upstream-observer.js.map +1 -1
  37. package/dist/src/version.d.ts +1 -1
  38. package/dist/src/version.js +1 -1
  39. package/docs/README.zh-CN.md +19 -19
  40. package/package.json +2 -2
package/README-zh-CN.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <h1 align="center">Upstream Radar</h1>
2
2
 
3
- <p align="center"><strong>在 DeepSeek Harness 生态变化后,找出哪些插件需要维护者关注。</strong></p>
3
+ <p align="center"><strong>持续验证 DeepSeek Harness 插件兼容性——覆盖 headless、Web 和 TUI。</strong></p>
4
4
 
5
5
  <p align="center">
6
6
  <a href="README.md">English</a> ·
@@ -10,9 +10,9 @@
10
10
  <a href="LICENSE"><img alt="Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
11
11
  </p>
12
12
 
13
- Upstream Radar 持续检查三者之间的真实关系:精确的 DSH 插件发布物、DSH 宿主和依赖图。
14
- DSH 或插件发布新版本改变这层关系时,Radar 告诉你变了什么、实际观测到了什么,以及维护者
15
- 可以修什么。
13
+ Upstream Radar 把精确的插件发布物、DSH 宿主和运行环境绑定在一起,让 Agent 根据仓库说明和
14
+ 已有失败证据推导受限环境,再放进一次性 GitHub VM 中实际验证。生态发生变化或证据过期后都会
15
+ 重跑,因此它检查的是**当前版本是否仍然可用**,而不只是比较一次 diff。
16
16
 
17
17
  它面向 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 插件生态。
18
18
  静态检查是关于发布包的证据;隔离运行检查是关于某个精确的
@@ -38,31 +38,38 @@ Upstream Radar 持续检查三者之间的真实关系:精确的 DSH 插件发
38
38
 
39
39
  ## Radar 做什么
40
40
 
41
- 1. **锁定真实输入。** 读取精确 npm 发布物、DSH 版本、Node 运行时、profile、lockfile 和依赖路径。
42
- 2. **比较真实关系。** 发现上游变化、发布/源码漂移、依赖图不完整和 DSH 契约不一致。
43
- 3. **需要执行时再观察。** 在全新的、无密钥的环境中安装、注册并加载精确发布物,记录结果和边界。
44
- 4. **完成闭环。** 生成有边界的证据,把重要变化交给可选的 DSH Agent,并在干净复测后更新或关闭一条面向维护者的 Issue。
41
+ 1. **建立统一兼容记录(IR)。** 对齐 npm 发布物、源码 commit、DSH 宿主、运行环境/profile、依赖路径和漏洞情报。
42
+ 2. **推导运行环境。** Agent 读取作者声明的安装说明和失败证据,只输出边界明确的安装计划。
43
+ 3. **验证不同执行平面。** 全新、无密钥的 runner 分别验证 headless 加载、Chromium Web 启动或真实 PTY TUI 交互。
44
+ 4. **让结果持续有效。** DSH/插件/依赖变化以及证据过期都会触发复测;确认的问题变成可修报告,干净复测后完成闭环。
45
45
 
46
46
  ```mermaid
47
47
  flowchart TB
48
- Change["定时运行 / DSH 或插件变化"] --> Agent["Agent 规划受限的 headless 重试"]
49
- Agent --> Runtime["一次性虚拟机:安装 → 注册 → 加载"]
50
- Runtime -->|"出现下一层门槛"| Agent
51
- Runtime -->|"兼容 / 超出 headless"| Evidence["发布精确证据"]
52
- Runtime -->|"复现真实失败"| Issue["创建一条可修复的 Issue"]
53
- Issue -->|"作者发布修复"| Change
48
+ Trigger["定时运行 / 上游变化 / 证据过期"] --> IR["精确 IR:插件字节 DSH ↔ 运行环境 ↔ 依赖"]
49
+ IR --> Agent["Agent 推导受限安装计划"]
50
+ Agent --> VM{"全新、无密钥的 GitHub VM"}
51
+ VM --> Headless["Headless:安装 注册 加载"]
52
+ VM --> Web["Web:Chromium → 启动交接 → 客户端包"]
53
+ VM --> TUI["TUI:PTY → 画面 → 输入 → 声明的退出方式"]
54
+ Headless --> Ledger["版本化证据账本 + 反向影响索引"]
55
+ Web --> Ledger
56
+ TUI --> Ledger
57
+ Ledger --> Decision{"能归责于插件吗?"}
58
+ Decision -->|"能"| Issue["生成一条可修复的维护者报告"]
59
+ Decision -->|"不能 / 检测器缺口"| Hold["暂扣报告并校准"]
60
+ Issue -->|"作者发布修复"| Trigger
61
+ Hold --> Trigger
54
62
  ```
55
63
 
56
- Agent 读取仓库说明和最新运行证据,决定 headless 是否重试、重试时允许哪些安装条件。
57
- 真正的结果由一次性虚拟机执行得出,而不是模型判断。模型不能凭空增加安装包、不能进入目标
58
- 虚拟机执行,也不能把缺失证据说成通过。
64
+ Agent 可以选择作者声明的构建包、profile 设置和下一次受限重试。精确指纹决定一份报告能填入
65
+ 哪个测试格子;真正的结果由一次性虚拟机执行得出,而不是模型判断。缺失证据永远不能变成通过。
59
66
 
60
67
  ## 试试一个真实检查
61
68
 
62
69
  第一次检查不需要本地 DSH profile,也不会执行插件代码:
63
70
 
64
71
  ```bash
65
- npx --yes upstream-radar@0.43.5 inspect \
72
+ npx --yes upstream-radar@0.45.0 inspect \
66
73
  @sanqi-normal/dsh-webui-market-plugin@0.5.4 \
67
74
  --deep --fail-on never
68
75
  ```
@@ -73,7 +80,7 @@ npx --yes upstream-radar@0.43.5 inspect \
73
80
  如果要检查自己的公开仓库,而不安装它:
74
81
 
75
82
  ```bash
76
- npx --yes upstream-radar@0.43.5 scan \
83
+ npx --yes upstream-radar@0.45.0 scan \
77
84
  https://github.com/owner/dsh-plugin \
78
85
  --fail-on never
79
86
  ```
@@ -88,14 +95,33 @@ npx --yes upstream-radar@0.43.5 scan \
88
95
  - [每天观察一个插件仓库](examples/github-actions/upstream-observer-minimal.yml):比较 commit、发布版本、manifest 和依赖图,只有发生重要变化才唤起 Agent。
89
96
  - [在 CI 中运行依赖门禁](examples/github-actions/upstream-radar.yml):在合并前检查 lockfile 或审查过的 Radar 配置。
90
97
 
91
- [隔离观察 workflow](.github/workflows/observe-dsh-plugin-install.yml) 会为需要执行代码的检查使用全新的 GitHub 托管 runner。
92
- 它不是你的电脑,也不会接收项目密钥。
98
+ 隔离的 [headless](.github/workflows/observe-dsh-plugin-install.yml)
99
+ [Web/TUI](.github/workflows/observe-dsh-plugin-surface.yml) workflow 都使用全新的 GitHub 托管 runner。
100
+ 它们不是你的电脑,也不会接收项目或模型密钥。
93
101
 
94
102
  ## 来自真实生态的结果
95
103
 
96
- 当前的[100 插件兼容性 feed](feeds/dsh-plugin-compatibility.md)记录了 74 个精确 headless
97
- 通过、22 个仅待复核结果、0 个已复现不兼容和 4 个仅仓库条目。在真正观测所需的
98
- Web/client 或仓库安装平面之前,待复核项不会被包装成插件故障。
104
+ 当前的[100 插件兼容性 feed](feeds/dsh-plugin-compatibility.md)记录了 **87 个已观测兼容、
105
+ 9 个待复核、0 个已复现不兼容和 4 个尚未观测**。执行平面账本包含 22 个精确 Web/TUI
106
+ 格子;目前 22 个都已在隔离 GitHub VM 中通过。
107
+
108
+ 剩余 9 个待复核格子不是被藏起来的失败:其中 7 个已有绿色 Web 证明,但仍保留独立的
109
+ headless 宿主/peer 契约证据;另 2 个仍声明旧 DSH 宿主包范围,并已有维护者 Issue 跟踪。
110
+ Radar 会保留这些事实,但不会把能正常运行的浏览器插件说成坏了。
111
+
112
+ 首批非 headless 测试已经在 GitHub 托管 VM 中跑通:
113
+
114
+ | 精确测试格子 | 实际证据 | 结果 |
115
+ | --- | --- | --- |
116
+ | [`dsh-univer-office@0.2.9 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205358) | HTTP 200、DSH 启动完成交接、客户端下载成功、浏览器和页面无错误 | **兼容** |
117
+ | [`@deepseek-harness-tui/dsh-tui@0.9.2 × DSH 0.1.1-rc.2 × TUI`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205289) | 真实 PTY 画面、键盘输入、按文档双击 Ctrl-C 后以 code 0 退出 | **兼容** |
118
+ | [`@linxin666/dsh-web-all@0.3.3 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32828788296/job/97742850608) | Agent 批准 4 个依赖构建;聚合客户端包返回 200;启动清单、应用挂载和插件实体相互吻合 | **兼容** |
119
+ | [`dsh-better-sidebar@0.16.1 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32835449819/job/97763410354) | VM 发现 `node-pty` 构建门槛;DeepSeek 只批准这个精确依赖;无密钥重试通过安装、宿主、浏览器交互和关闭阶段 | **兼容** |
120
+
121
+ `better-sidebar` 展示了完整闭环:动态证据发现 headless 计划没遇到的构建要求;DeepSeek 核对
122
+ 精确 manifest、README 和 VM 日志;与指纹绑定的策略只批准 `node-pty`;随后另一台不带模型密钥
123
+ 的 runner 给出通过结论。这是 Radar 的环境缺口,因此没有向插件作者提交 Issue。此前 TUI 与 Web
124
+ 检测器自身的错误也按同样方式处理:先暂扣、修正并复测,而不是发给作者。
99
125
 
100
126
  截至 2026-08-25,Radar 共向维护者提交了 13 条报告。比数量更重要的是处理结果:
101
127
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <h1 align="center">Upstream Radar</h1>
2
2
 
3
- <p align="center"><strong>Find the DeepSeek Harness plugins that need attention when the ecosystem moves.</strong></p>
3
+ <p align="center"><strong>Always-on compatibility testing for DeepSeek Harness plugins—across headless, Web, and TUI.</strong></p>
4
4
 
5
5
  <p align="center">
6
6
  <a href="README-zh-CN.md">简体中文</a> ·
@@ -10,10 +10,11 @@
10
10
  <a href="LICENSE"><img alt="Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
11
11
  </p>
12
12
 
13
- Upstream Radar continuously checks the relationship between an exact published
14
- DSH plugin, its DSH host, and its dependency graph. When a DSH or plugin release
15
- changes that relationship, Radar shows what changed, what was actually observed,
16
- and what a maintainer can fix.
13
+ Upstream Radar binds exact plugin bytes to an exact DSH host and runtime, lets an
14
+ Agent derive a bounded environment from repository instructions and prior
15
+ evidence, then proves the relationship in disposable GitHub VMs. It runs again
16
+ when the ecosystem changes **or evidence expires**, so it tests the current
17
+ version—not only the diff.
17
18
 
18
19
  It is built for the [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness)
19
20
  plugin ecosystem. A static review is evidence about a package; an isolated
@@ -41,30 +42,38 @@ repositories are checked separately.
41
42
 
42
43
  ## What Radar does
43
44
 
44
- 1. **Pin the real inputs.** Read the exact npm artifact, DSH version, Node
45
- runtime, profile, lockfile, and dependency paths.
46
- 2. **Compare the relationship.** Detect upstream changes, package/release drift,
47
- incomplete graphs, and DSH contract mismatches.
48
- 3. **Observe when execution matters.** In a fresh, secret-free runner, install,
49
- register, and load the exact artifact; record the result and its boundary.
50
- 4. **Close the loop.** Produce bounded evidence, route meaningful changes to an
51
- optional DSH Agent, and update or close one maintainer-facing issue after a
52
- clean retest.
45
+ 1. **Build one exact compatibility record (IR).** Align the npm artifact,
46
+ source commit, DSH host, runtime/profile, dependency paths, and advisories.
47
+ 2. **Derive the environment.** An Agent reads declared installation guidance
48
+ and failed evidence, then emits only a bounded install plan.
49
+ 3. **Prove each execution plane.** Fresh, secret-free runners exercise headless
50
+ load, Chromium Web boot, or a real PTY TUI interaction.
51
+ 4. **Keep the result alive.** DSH/plugin/dependency changes and evidence expiry
52
+ trigger retests; confirmed failures become fixable reports and clean retests
53
+ close the loop.
53
54
 
54
55
  ```mermaid
55
56
  flowchart TB
56
- Change["Schedule / DSH or plugin change"] --> Agent["Agent plans a bounded headless retry"]
57
- Agent --> Runtime["Disposable VM: install register → load"]
58
- Runtime -->|"next observed gate"| Agent
59
- Runtime -->|"compatible / outside headless"| Evidence["Publish exact evidence"]
60
- Runtime -->|"reproduced failure"| Issue["Open one fixable issue"]
61
- Issue -->|"author ships a fix"| Change
57
+ Trigger["Schedule / upstream change / evidence expiry"] --> IR["Exact IR: plugin bytes DSH ↔ runtime ↔ dependencies"]
58
+ IR --> Agent["Agent derives a bounded install plan"]
59
+ Agent --> VM{"Fresh secret-free GitHub VM"}
60
+ VM --> Headless["Headless: install register load"]
61
+ VM --> Web["Web: Chromium boot handoff → client bundle"]
62
+ VM --> TUI["TUI: PTY frame → input → declared shutdown"]
63
+ Headless --> Ledger["Versioned evidence ledger + reverse impact index"]
64
+ Web --> Ledger
65
+ TUI --> Ledger
66
+ Ledger --> Decision{"Plugin-attributable failure?"}
67
+ Decision -->|"yes"| Issue["Produce one fixable maintainer report"]
68
+ Decision -->|"no / detector gap"| Hold["Hold the report and calibrate"]
69
+ Issue -->|"author ships a fix"| Trigger
70
+ Hold --> Trigger
62
71
  ```
63
72
 
64
- The Agent interprets repository instructions and the latest runtime evidence,
65
- then chooses whether and how headless should retry. The disposable runner—not
66
- the model—establishes the result. A model cannot invent a build package, execute
67
- inside the target VM, or turn missing evidence into a pass.
73
+ The Agent may choose declared build packages, profile setup, and the next bounded
74
+ retry. Exact fingerprints decide which cell a report can satisfy, and the
75
+ disposable runner—not the model—establishes the result. Missing evidence can
76
+ never become a pass.
68
77
 
69
78
  ## Try a real check
70
79
 
@@ -72,7 +81,7 @@ No local DSH profile is needed for this first check. It reviews one exact
72
81
  published artifact without executing plugin code:
73
82
 
74
83
  ```bash
75
- npx --yes upstream-radar@0.43.5 inspect \
84
+ npx --yes upstream-radar@0.45.0 inspect \
76
85
  @sanqi-normal/dsh-webui-market-plugin@0.5.4 \
77
86
  --deep --fail-on never
78
87
  ```
@@ -85,7 +94,7 @@ behavior. See the [full evidence report](examples/dsh/reports/sanqi-market-plugi
85
94
  To review your own public repository without installing it:
86
95
 
87
96
  ```bash
88
- npx --yes upstream-radar@0.43.5 scan \
97
+ npx --yes upstream-radar@0.45.0 scan \
89
98
  https://github.com/owner/dsh-plugin \
90
99
  --fail-on never
91
100
  ```
@@ -106,17 +115,39 @@ Copy one of the maintained workflows into your repository:
106
115
  - [Run the dependency gate in CI](examples/github-actions/upstream-radar.yml)
107
116
  — checks the lockfile or reviewed Radar configuration before merge.
108
117
 
109
- The [isolated observer workflow](.github/workflows/observe-dsh-plugin-install.yml)
110
- uses a fresh GitHub-hosted runner for code-executing checks. The runner is not
111
- your workstation and does not receive project secrets.
118
+ The isolated [headless](.github/workflows/observe-dsh-plugin-install.yml) and
119
+ [Web/TUI](.github/workflows/observe-dsh-plugin-surface.yml) observers use fresh
120
+ GitHub-hosted runners. They are not your workstation and receive no project or
121
+ model secrets.
112
122
 
113
123
  ## Evidence from the ecosystem
114
124
 
115
125
  The current [100-plugin compatibility feed](feeds/dsh-plugin-compatibility.md)
116
- records 74 exact headless passes, 22 review-only results, zero reproduced
117
- incompatibilities, and four repository-only entries. The review-only group is
118
- kept separate from failures until the required Web/client or repository install
119
- plane is actually observed.
126
+ records **87 observed compatible, 9 needs review, 0 reproduced incompatible,
127
+ and 4 not observed**. Its execution-plane ledger contains 22 exact Web/TUI
128
+ cells; all 22 now pass in isolated GitHub VMs.
129
+
130
+ The nine review cells are not hidden failures. Seven have a green Web proof but
131
+ retain separate headless host/peer-contract evidence; two retain known old DSH
132
+ host-package ranges tracked by existing maintainer issues. Radar keeps those
133
+ facts visible without calling a working browser plugin broken.
134
+
135
+ The first non-headless cells now run in GitHub-hosted VMs:
136
+
137
+ | Exact cell | Observed proof | Result |
138
+ | --- | --- | --- |
139
+ | [`dsh-univer-office@0.2.9 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205358) | HTTP 200, DSH boot handoff, declared client bundle fetched, no browser/page errors | **Compatible** |
140
+ | [`@deepseek-harness-tui/dsh-tui@0.9.2 × DSH 0.1.1-rc.2 × TUI`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205289) | Real PTY frame, keyboard input, documented double-Ctrl-C exit, code 0 | **Compatible** |
141
+ | [`@linxin666/dsh-web-all@0.3.3 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32828788296/job/97742850608) | Agent-approved four dependency builds; aggregate client bundle returned 200; boot manifest, app mount, and plugin materialization matched | **Compatible** |
142
+ | [`dsh-better-sidebar@0.16.1 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32835449819/job/97763410354) | VM observed a `node-pty` build gate; DeepSeek approved only that exact dependency; the secret-free retry passed install, host, browser interaction, and shutdown | **Compatible** |
143
+
144
+ The `better-sidebar` run demonstrates the closed loop: dynamic evidence found a
145
+ build requirement absent from the headless plan; DeepSeek checked the exact
146
+ manifest, README, and VM log; a fingerprint-bound policy approved only
147
+ `node-pty`; then a separate runner with no model secrets established the pass.
148
+ This was Radar's environment gap, so no plugin issue was filed. Earlier TUI and
149
+ Web detector mistakes were handled the same way: held, corrected, and rerun
150
+ instead of being sent to authors.
120
151
 
121
152
  As of 2026-08-25, Radar has filed 13 maintainer-facing reports. The outcome is
122
153
  more useful than the raw count:
package/dist/src/cli.js CHANGED
@@ -7,6 +7,7 @@ import { renderCompatibilityBenchmark, runCompatibilityBenchmark } from './compa
7
7
  import { assessCompatibilityChange } from './compatibility.js';
8
8
  import { probeDshLoad, probeDshLoadMatrix, renderDshLoadMatrix, renderDshLoadProbe } from './dsh-probe.js';
9
9
  import { observeDshPluginInstall, renderDshInstallObservation, } from './dsh-install-observation.js';
10
+ import { observeDshPluginSurface, renderDshSurfaceObservation, } from './dsh-surface-observation.js';
10
11
  import { renderDshPluginReview, reviewDshPlugin } from './dsh-review.js';
11
12
  import { createAnalysisTask, renderAgentAnalysisPrompt } from './dsh-analysis.js';
12
13
  import { createDshCaseReport, renderDshCase } from './dsh-case.js';
@@ -281,6 +282,14 @@ Usage:
281
282
  upstream-radar probe dsh-install [npm:]<package>@<exact-version>
282
283
  --dsh-version <exact-version> --isolation-provider <provider> --execute
283
284
  [--allow-build <package>]... [--timeout <seconds>] [--report <report.json>] [--json]
285
+ upstream-radar probe dsh-surface [npm:]<package>@<exact-version>
286
+ --dsh-version <exact-version> --case-id <id> --source-case-id <id>
287
+ --source-fingerprint <sha256:...> --contract-fingerprint <sha256:...>
288
+ --plane <web|tui> --profile <name> --runtime-id <id>
289
+ --artifact-sha256 <hex> --isolation-provider <provider> --execute
290
+ [--allow-build <package>]...
291
+ [--driver-root <path>] [--chromium-executable <path>]
292
+ [--artifacts <directory>] [--timeout <seconds>] [--report <report.json>] [--json]
284
293
 
285
294
  The load probes disable lifecycle scripts and check bundle registration/load.
286
295
  The dsh-install probe deliberately executes the exact plugin's lifecycle scripts
@@ -288,6 +297,10 @@ and loads the bundle while recording Linux process, network, and file-change
288
297
  evidence. Run it only inside a disposable, secret-free Linux environment. It
289
298
  requires both --execute and UPSTREAM_RADAR_ISOLATED_RUNNER=1.
290
299
  Radar records the caller's isolation-provider claim but cannot verify it.
300
+ The dsh-surface probe adds plane-specific proof: Chromium must observe DSH hand
301
+ off from its boot page after activating the client graph, or a real PTY must observe a
302
+ TUI frame, bounded input, and controlled shutdown. It has the same disposable,
303
+ secret-free execution requirement.
291
304
  `,
292
305
  review: `Upstream Radar — review one exact published DSH plugin in one command
293
306
 
@@ -484,6 +497,7 @@ Usage:
484
497
  upstream-radar probe dsh-load <package.tgz> [--dsh-version <exact-version>] [--timeout <seconds>] [--keep-profile] [--json]
485
498
  upstream-radar probe dsh-matrix <package.tgz> --dsh-version <v1>[,<v2>,...] [--timeout <seconds>] [--keep-profile] [--json]
486
499
  upstream-radar probe dsh-install [npm:]<package>@<exact-version> --dsh-version <exact-version> [--case-id <stable-label>] --isolation-provider <github-actions-hosted-runner|firecracker|other> --execute [--allow-build <package>]... [--timeout <seconds>] [--report <report.json>] [--json]
500
+ upstream-radar probe dsh-surface [npm:]<package>@<exact-version> --dsh-version <exact-version> --case-id <stable-label> --source-case-id <stable-label> --source-fingerprint <sha256:...> --contract-fingerprint <sha256:...> --plane <web|tui> --profile <name> --runtime-id <id> --artifact-sha256 <hex> --isolation-provider <github-actions-hosted-runner|firecracker|other> --execute [--allow-build <package>]... [--driver-root <path>] [--chromium-executable <path>] [--artifacts <directory>] [--timeout <seconds>] [--report <report.json>] [--json]
487
501
  upstream-radar review dsh-plugin [npm:]<package>@<exact-version> --dsh-version <v1>,<v2>,... [--json]
488
502
  upstream-radar demo [--json]
489
503
  upstream-radar case dsh-web-ui [--json]
@@ -1220,8 +1234,10 @@ async function runProbe(args) {
1220
1234
  const mode = args[0];
1221
1235
  if (mode === 'dsh-install')
1222
1236
  return runDshInstallObservation(args.slice(1));
1237
+ if (mode === 'dsh-surface')
1238
+ return runDshSurfaceObservation(args.slice(1));
1223
1239
  if (mode !== 'dsh-load' && mode !== 'dsh-matrix')
1224
- throw new Error('probe requires dsh-load or dsh-matrix');
1240
+ throw new Error('probe requires dsh-load, dsh-matrix, dsh-install or dsh-surface');
1225
1241
  const packagePath = args[1];
1226
1242
  if (packagePath === undefined || packagePath.startsWith('-'))
1227
1243
  throw new Error(`probe ${mode} requires a package.tgz file`);
@@ -1372,6 +1388,144 @@ async function runDshInstallObservation(args) {
1372
1388
  process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : renderDshInstallObservation(report));
1373
1389
  return report.result === 'compatible' ? 0 : report.result === 'unknown' ? 1 : 2;
1374
1390
  }
1391
+ async function runDshSurfaceObservation(args) {
1392
+ const packageSpec = args[0];
1393
+ if (packageSpec === undefined || packageSpec.startsWith('-')) {
1394
+ throw new Error('probe dsh-surface requires an exact npm package');
1395
+ }
1396
+ let dshVersion;
1397
+ let caseId;
1398
+ let sourceCaseId;
1399
+ let sourceFingerprint;
1400
+ let contractFingerprint;
1401
+ let plane;
1402
+ let profile;
1403
+ let runtimeId;
1404
+ let artifactSha256;
1405
+ let isolationProvider;
1406
+ let timeoutSeconds = 300;
1407
+ let reportPath;
1408
+ let artifactsDirectory;
1409
+ let driverRoot;
1410
+ let chromiumExecutable;
1411
+ const allowedBuilds = [];
1412
+ let execute = false;
1413
+ let json = false;
1414
+ for (let index = 1; index < args.length; index += 1) {
1415
+ const argument = args[index];
1416
+ if (argument === '--execute')
1417
+ execute = true;
1418
+ else if (argument === '--json')
1419
+ json = true;
1420
+ else if (argument === '--dsh-version' || argument === '--case-id' || argument === '--source-case-id'
1421
+ || argument === '--source-fingerprint' || argument === '--contract-fingerprint' || argument === '--plane'
1422
+ || argument === '--profile' || argument === '--runtime-id' || argument === '--artifact-sha256'
1423
+ || argument === '--isolation-provider' || argument === '--timeout' || argument === '--report'
1424
+ || argument === '--artifacts' || argument === '--driver-root' || argument === '--chromium-executable'
1425
+ || argument === '--allow-build') {
1426
+ const value = args[index + 1];
1427
+ if (value === undefined || value.startsWith('-'))
1428
+ throw new Error(`${argument} requires a value`);
1429
+ if (argument === '--dsh-version')
1430
+ dshVersion = value;
1431
+ else if (argument === '--case-id')
1432
+ caseId = value;
1433
+ else if (argument === '--source-case-id')
1434
+ sourceCaseId = value;
1435
+ else if (argument === '--source-fingerprint')
1436
+ sourceFingerprint = value;
1437
+ else if (argument === '--contract-fingerprint')
1438
+ contractFingerprint = value;
1439
+ else if (argument === '--plane') {
1440
+ if (value !== 'web' && value !== 'tui')
1441
+ throw new Error('--plane must be web or tui');
1442
+ plane = value;
1443
+ }
1444
+ else if (argument === '--profile')
1445
+ profile = value;
1446
+ else if (argument === '--runtime-id')
1447
+ runtimeId = value;
1448
+ else if (argument === '--artifact-sha256')
1449
+ artifactSha256 = value;
1450
+ else if (argument === '--isolation-provider') {
1451
+ if (value !== 'github-actions-hosted-runner' && value !== 'firecracker' && value !== 'other') {
1452
+ throw new Error('--isolation-provider must be github-actions-hosted-runner, firecracker or other');
1453
+ }
1454
+ isolationProvider = value;
1455
+ }
1456
+ else if (argument === '--timeout') {
1457
+ const parsed = Number(value);
1458
+ if (!Number.isSafeInteger(parsed) || parsed < 30 || parsed > 600)
1459
+ throw new Error('--timeout must be an integer between 30 and 600 seconds');
1460
+ timeoutSeconds = parsed;
1461
+ }
1462
+ else if (argument === '--report')
1463
+ reportPath = value;
1464
+ else if (argument === '--artifacts')
1465
+ artifactsDirectory = value;
1466
+ else if (argument === '--driver-root')
1467
+ driverRoot = value;
1468
+ else if (argument === '--chromium-executable')
1469
+ chromiumExecutable = value;
1470
+ else
1471
+ allowedBuilds.push(value);
1472
+ index += 1;
1473
+ }
1474
+ else {
1475
+ throw new Error(`unknown option for probe dsh-surface: ${argument}`);
1476
+ }
1477
+ }
1478
+ const required = [
1479
+ ['--dsh-version', dshVersion],
1480
+ ['--case-id', caseId],
1481
+ ['--source-case-id', sourceCaseId],
1482
+ ['--source-fingerprint', sourceFingerprint],
1483
+ ['--contract-fingerprint', contractFingerprint],
1484
+ ['--plane', plane],
1485
+ ['--profile', profile],
1486
+ ['--runtime-id', runtimeId],
1487
+ ['--artifact-sha256', artifactSha256],
1488
+ ['--isolation-provider', isolationProvider],
1489
+ ];
1490
+ const missing = required.find(([, value]) => value === undefined);
1491
+ if (missing !== undefined)
1492
+ throw new Error(`probe dsh-surface requires ${missing[0]}`);
1493
+ if (!execute)
1494
+ throw new Error('probe dsh-surface requires --execute because third-party code may run');
1495
+ if (process.env.UPSTREAM_RADAR_ISOLATED_RUNNER !== '1') {
1496
+ throw new Error('probe dsh-surface requires UPSTREAM_RADAR_ISOLATED_RUNNER=1 in the disposable environment');
1497
+ }
1498
+ const report = await observeDshPluginSurface({
1499
+ packageSpec,
1500
+ dshVersion: dshVersion,
1501
+ caseId: caseId,
1502
+ sourceCaseId: sourceCaseId,
1503
+ sourceFingerprint: sourceFingerprint,
1504
+ contractFingerprint: contractFingerprint,
1505
+ plane: plane,
1506
+ profile: profile,
1507
+ runtimeId: runtimeId,
1508
+ expectedArtifactSha256: artifactSha256,
1509
+ allowedBuilds,
1510
+ allowExecution: true,
1511
+ isolationProvider: isolationProvider,
1512
+ timeoutMs: timeoutSeconds * 1_000,
1513
+ ...(artifactsDirectory === undefined ? {} : { artifactsDirectory }),
1514
+ ...(driverRoot === undefined ? {} : { driverRoot }),
1515
+ ...(chromiumExecutable === undefined ? {} : { chromiumExecutable }),
1516
+ });
1517
+ if (reportPath !== undefined) {
1518
+ const absoluteReportPath = resolve(reportPath);
1519
+ await mkdir(dirname(absoluteReportPath), { recursive: true });
1520
+ // Surface reports are an explicit hand-off from the unprivileged observer
1521
+ // container to the GitHub runner. They contain bounded compatibility
1522
+ // evidence and no inherited secrets, so the runner must be able to read
1523
+ // and upload them even when the container uses a different uid.
1524
+ await writeFile(absoluteReportPath, `${JSON.stringify(report, null, 2)}\n`, { mode: 0o644 });
1525
+ }
1526
+ process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : renderDshSurfaceObservation(report));
1527
+ return report.result === 'compatible' ? 0 : report.result === 'surface-incompatible' ? 2 : 1;
1528
+ }
1375
1529
  async function runDshPluginReview(args) {
1376
1530
  if (args[0] !== 'dsh-plugin')
1377
1531
  throw new Error('review requires dsh-plugin');