niceeval 0.6.1 → 0.6.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.
Files changed (296) hide show
  1. package/dist/agents/types.d.ts +67 -5
  2. package/dist/context/types.d.ts +32 -12
  3. package/dist/i18n/en.d.ts +54 -0
  4. package/dist/i18n/zh-CN.d.ts +55 -1
  5. package/dist/o11y/types.d.ts +16 -2
  6. package/dist/report/aggregate.d.ts +5 -3
  7. package/dist/report/aggregate.js +32 -5
  8. package/dist/report/built-ins/experiment-comparison.d.ts +39 -1
  9. package/dist/report/built-ins/experiment-comparison.js +116 -10
  10. package/dist/report/built-ins/index.d.ts +1 -0
  11. package/dist/report/built-ins/index.js +1 -1
  12. package/dist/report/components.d.ts +8 -2
  13. package/dist/report/components.js +3 -3
  14. package/dist/report/compute.d.ts +11 -18
  15. package/dist/report/compute.js +54 -34
  16. package/dist/report/flag.d.ts +16 -1
  17. package/dist/report/flag.js +19 -1
  18. package/dist/report/format.d.ts +16 -8
  19. package/dist/report/format.js +27 -12
  20. package/dist/report/index.d.ts +4 -3
  21. package/dist/report/index.js +5 -4
  22. package/dist/report/locale.d.ts +11 -2
  23. package/dist/report/locale.js +23 -5
  24. package/dist/report/metrics.d.ts +13 -1
  25. package/dist/report/metrics.js +65 -14
  26. package/dist/report/primitives.d.ts +6 -0
  27. package/dist/report/react/AttemptList.d.ts +2 -2
  28. package/dist/report/react/AttemptList.js +5 -6
  29. package/dist/report/react/EvalList.d.ts +1 -1
  30. package/dist/report/react/EvalList.js +0 -0
  31. package/dist/report/react/ExperimentComparison.d.ts +8 -0
  32. package/dist/report/react/ExperimentComparison.js +11 -0
  33. package/dist/report/react/ExperimentList.d.ts +2 -1
  34. package/dist/report/react/ExperimentList.js +8 -10
  35. package/dist/report/react/MetricScatter.js +5 -11
  36. package/dist/report/react/chart-math.d.ts +23 -6
  37. package/dist/report/react/chart-math.js +71 -19
  38. package/dist/report/react/fixtures.d.ts +3 -3
  39. package/dist/report/react/fixtures.js +21 -14
  40. package/dist/report/report.d.ts +5 -1
  41. package/dist/report/report.js +6 -2
  42. package/dist/report/text/faces.d.ts +1 -1
  43. package/dist/report/text/faces.js +42 -41
  44. package/dist/report/text/table.js +36 -5
  45. package/dist/report/types.d.ts +39 -21
  46. package/dist/results/types.d.ts +11 -0
  47. package/dist/runner/feedback/sink.d.ts +110 -0
  48. package/dist/runner/types.d.ts +513 -22
  49. package/dist/sandbox/docker.d.ts +23 -2
  50. package/dist/sandbox/e2b.d.ts +15 -1
  51. package/dist/sandbox/errors.d.ts +30 -3
  52. package/dist/sandbox/io-retry.d.ts +17 -0
  53. package/dist/sandbox/registry.d.ts +2 -0
  54. package/dist/sandbox/resolve.d.ts +18 -5
  55. package/dist/sandbox/retry.d.ts +11 -1
  56. package/dist/sandbox/types.d.ts +39 -5
  57. package/dist/sandbox/vercel.d.ts +7 -1
  58. package/dist/scoring/coverage.d.ts +30 -0
  59. package/dist/scoring/display.d.ts +21 -0
  60. package/dist/scoring/display.js +120 -0
  61. package/dist/scoring/types.d.ts +103 -20
  62. package/dist/shared/aggregate.d.ts +1 -0
  63. package/dist/shared/aggregate.js +3 -3
  64. package/dist/shared/types.d.ts +28 -0
  65. package/dist/tty-line.d.ts +0 -4
  66. package/dist/util.d.ts +23 -0
  67. package/docs-site/zh/concepts/adapter.mdx +22 -4
  68. package/docs-site/zh/concepts/experiment.mdx +1 -1
  69. package/docs-site/zh/concepts/overview.mdx +6 -6
  70. package/docs-site/zh/guides/agent-feedback-loop.mdx +28 -26
  71. package/docs-site/zh/guides/authoring.mdx +33 -0
  72. package/docs-site/zh/guides/ci-integration.mdx +23 -12
  73. package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
  74. package/docs-site/zh/guides/custom-reports.mdx +29 -34
  75. package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
  76. package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
  77. package/docs-site/zh/guides/debugging.mdx +210 -0
  78. package/docs-site/zh/guides/experiments.mdx +10 -3
  79. package/docs-site/zh/guides/official-adapters.mdx +26 -2
  80. package/docs-site/zh/guides/publish-report.mdx +30 -16
  81. package/docs-site/zh/guides/report-components.mdx +42 -30
  82. package/docs-site/zh/guides/reporters.mdx +2 -2
  83. package/docs-site/zh/guides/results-data.mdx +17 -9
  84. package/docs-site/zh/guides/runner.mdx +17 -7
  85. package/docs-site/zh/guides/sandbox-agent.mdx +56 -7
  86. package/docs-site/zh/guides/sandbox-providers.mdx +257 -9
  87. package/docs-site/zh/guides/scoring-guide.mdx +4 -4
  88. package/docs-site/zh/guides/viewing-results.mdx +79 -36
  89. package/docs-site/zh/guides/write-experiment.mdx +5 -3
  90. package/docs-site/zh/guides/write-send.mdx +17 -1
  91. package/docs-site/zh/index.mdx +1 -1
  92. package/docs-site/zh/reference/builtin-agents.mdx +27 -0
  93. package/docs-site/zh/reference/capabilities.mdx +2 -2
  94. package/docs-site/zh/reference/cli.mdx +33 -7
  95. package/docs-site/zh/reference/define-agent.mdx +57 -4
  96. package/docs-site/zh/reference/define-config.mdx +1 -1
  97. package/docs-site/zh/reference/define-eval.mdx +42 -9
  98. package/docs-site/zh/reference/expect.mdx +26 -1
  99. package/package.json +5 -1
  100. package/src/agents/ai-sdk-otel.test.ts +1 -0
  101. package/src/agents/ai-sdk.test.ts +3 -0
  102. package/src/agents/ai-sdk.ts +3 -0
  103. package/src/agents/bub-install-spec.test.ts +34 -0
  104. package/src/agents/bub-install-spec.ts +32 -0
  105. package/src/agents/bub.ts +31 -32
  106. package/src/agents/claude-code.test.ts +130 -9
  107. package/src/agents/claude-code.ts +76 -4
  108. package/src/agents/codex.test.ts +189 -40
  109. package/src/agents/codex.ts +155 -14
  110. package/src/agents/coding-cli-versions.test.ts +15 -0
  111. package/src/agents/coding-cli-versions.ts +3 -0
  112. package/src/agents/index.ts +11 -0
  113. package/src/agents/langgraph.test.ts +204 -0
  114. package/src/agents/langgraph.ts +495 -0
  115. package/src/agents/marketplace.ts +85 -0
  116. package/src/agents/native-config.test.ts +179 -0
  117. package/src/agents/native-config.ts +267 -0
  118. package/src/agents/openai-compat.test.ts +1 -0
  119. package/src/agents/openclaw.test.ts +31 -0
  120. package/src/agents/openclaw.ts +171 -0
  121. package/src/agents/plugin-config.test.ts +1 -0
  122. package/src/agents/sdk-streams.test.ts +79 -0
  123. package/src/agents/sdk-streams.ts +55 -10
  124. package/src/agents/skills.test.ts +1 -0
  125. package/src/agents/streaming.test.ts +3 -9
  126. package/src/agents/types.ts +68 -5
  127. package/src/agents/ui-message-stream.test.ts +3 -0
  128. package/src/cli.ts +411 -108
  129. package/src/context/context.test.ts +51 -12
  130. package/src/context/context.ts +161 -29
  131. package/src/context/session.test.ts +1 -0
  132. package/src/context/session.ts +114 -6
  133. package/src/context/types.ts +30 -12
  134. package/src/define.test.ts +13 -8
  135. package/src/define.ts +25 -4
  136. package/src/expect/index.ts +53 -23
  137. package/src/i18n/en.ts +64 -2
  138. package/src/i18n/zh-CN.ts +65 -3
  139. package/src/o11y/cost.test.ts +1 -0
  140. package/src/o11y/execution-tree.test.ts +1 -20
  141. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  142. package/src/o11y/otlp/parse.test.ts +1 -0
  143. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  144. package/src/o11y/parsers/bub.test.ts +1 -0
  145. package/src/o11y/parsers/claude-code.test.ts +1 -34
  146. package/src/o11y/parsers/openclaw.test.ts +154 -0
  147. package/src/o11y/parsers/openclaw.ts +310 -0
  148. package/src/o11y/prices.json +746 -311
  149. package/src/o11y/tool-names.test.ts +1 -0
  150. package/src/o11y/types.ts +16 -2
  151. package/src/report/aggregate.ts +34 -5
  152. package/src/report/built-in-user-parity.test.tsx +110 -153
  153. package/src/report/built-ins/experiment-comparison.tsx +173 -13
  154. package/src/report/built-ins/index.ts +6 -1
  155. package/src/report/components.tsx +9 -3
  156. package/src/report/compute.ts +70 -40
  157. package/src/report/dual-render.test.tsx +194 -67
  158. package/src/report/flag.ts +30 -2
  159. package/src/report/format.ts +35 -11
  160. package/src/report/index.ts +22 -4
  161. package/src/report/locale.ts +25 -5
  162. package/src/report/metrics.ts +67 -14
  163. package/src/report/primitives.tsx +6 -0
  164. package/src/report/react/AttemptList.tsx +6 -31
  165. package/src/report/react/EvalList.tsx +0 -0
  166. package/src/report/react/ExperimentComparison.tsx +68 -0
  167. package/src/report/react/ExperimentList.tsx +15 -9
  168. package/src/report/react/MetricScatter.tsx +12 -14
  169. package/src/report/react/chart-math.test.ts +85 -0
  170. package/src/report/react/chart-math.ts +101 -22
  171. package/src/report/react/enhance.js +33 -1
  172. package/src/report/react/fixtures.ts +24 -17
  173. package/src/report/react/render.test.tsx +9 -64
  174. package/src/report/react/styles.css +73 -2
  175. package/src/report/report.test.ts +306 -98
  176. package/src/report/report.ts +6 -2
  177. package/src/report/text/faces.ts +47 -43
  178. package/src/report/text/table.ts +42 -5
  179. package/src/report/types.ts +41 -21
  180. package/src/results/annotated-source.test.ts +62 -9
  181. package/src/results/annotated-source.ts +64 -6
  182. package/src/results/attempt-evidence.test.ts +9 -7
  183. package/src/results/attempt-evidence.ts +15 -8
  184. package/src/results/attempt-source.ts +6 -3
  185. package/src/results/copy.ts +145 -55
  186. package/src/results/host-equivalence.test.ts +8 -6
  187. package/src/results/index.ts +2 -0
  188. package/src/results/locator.test.ts +1 -22
  189. package/src/results/open.ts +7 -1
  190. package/src/results/publish.ts +149 -0
  191. package/src/results/results.test.ts +85 -51
  192. package/src/results/truncate.ts +90 -0
  193. package/src/results/types.ts +7 -0
  194. package/src/results/writer.ts +31 -13
  195. package/src/runner/attempt.test.ts +138 -7
  196. package/src/runner/attempt.ts +603 -104
  197. package/src/runner/discover.test.ts +47 -0
  198. package/src/runner/discover.ts +36 -2
  199. package/src/runner/eval-source.test.ts +1 -27
  200. package/src/runner/feedback/agent.test.ts +504 -0
  201. package/src/runner/feedback/agent.ts +409 -0
  202. package/src/runner/feedback/ci.test.ts +562 -0
  203. package/src/runner/feedback/ci.ts +401 -0
  204. package/src/runner/feedback/coordinator.test.ts +317 -0
  205. package/src/runner/feedback/coordinator.ts +397 -0
  206. package/src/runner/feedback/failure.ts +40 -0
  207. package/src/runner/feedback/human.test.ts +616 -0
  208. package/src/runner/feedback/human.ts +535 -0
  209. package/src/runner/feedback/index.ts +66 -0
  210. package/src/runner/feedback/io.ts +78 -0
  211. package/src/runner/feedback/profile.test.ts +50 -0
  212. package/src/runner/feedback/profile.ts +58 -0
  213. package/src/runner/feedback/reducer.test.ts +395 -0
  214. package/src/runner/feedback/reducer.ts +260 -0
  215. package/src/runner/feedback/renderer.ts +82 -0
  216. package/src/runner/feedback/sink.ts +203 -0
  217. package/src/runner/feedback/testing.ts +106 -0
  218. package/src/runner/ledger.test.ts +230 -0
  219. package/src/runner/ledger.ts +329 -0
  220. package/src/runner/report.test.ts +128 -3
  221. package/src/runner/report.ts +33 -9
  222. package/src/runner/reporters/artifacts.ts +8 -2
  223. package/src/runner/reporters/braintrust.test.ts +8 -7
  224. package/src/runner/reporters/braintrust.ts +9 -2
  225. package/src/runner/reporters/index.ts +2 -2
  226. package/src/runner/reporters/json.test.ts +162 -0
  227. package/src/runner/reporters/json.ts +35 -8
  228. package/src/runner/reporters/shared.ts +1 -5
  229. package/src/runner/run.test.ts +760 -3
  230. package/src/runner/run.ts +242 -36
  231. package/src/runner/sandbox-prep.ts +3 -42
  232. package/src/runner/timing.ts +158 -0
  233. package/src/runner/types.ts +518 -22
  234. package/src/sandbox/checkpoint.test.ts +55 -0
  235. package/src/sandbox/checkpoint.ts +29 -8
  236. package/src/sandbox/cli-commands.ts +407 -0
  237. package/src/sandbox/docker.ts +115 -16
  238. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  239. package/src/sandbox/e2b-agent-template.ts +94 -0
  240. package/src/sandbox/e2b.ts +74 -9
  241. package/src/sandbox/errors.ts +111 -4
  242. package/src/sandbox/index.ts +2 -0
  243. package/src/sandbox/io-retry.test.ts +58 -0
  244. package/src/sandbox/io-retry.ts +45 -0
  245. package/src/sandbox/keep-registry.test.ts +86 -0
  246. package/src/sandbox/keep-registry.ts +142 -0
  247. package/src/sandbox/keep.ts +178 -0
  248. package/src/sandbox/paths.test.ts +1 -0
  249. package/src/sandbox/paths.ts +19 -8
  250. package/src/sandbox/registry.ts +20 -3
  251. package/src/sandbox/resolve.ts +76 -11
  252. package/src/sandbox/retry.test.ts +70 -0
  253. package/src/sandbox/retry.ts +46 -4
  254. package/src/sandbox/types.ts +44 -6
  255. package/src/sandbox/vercel.ts +43 -20
  256. package/src/scoring/collector.ts +60 -17
  257. package/src/scoring/coverage.ts +95 -0
  258. package/src/scoring/diff.ts +81 -0
  259. package/src/scoring/display.test.ts +121 -0
  260. package/src/scoring/display.ts +133 -0
  261. package/src/scoring/evidence.test.ts +189 -0
  262. package/src/scoring/judge.test.ts +142 -0
  263. package/src/scoring/judge.ts +15 -18
  264. package/src/scoring/scoped.ts +217 -50
  265. package/src/scoring/types.ts +117 -20
  266. package/src/scoring/verdict.ts +16 -4
  267. package/src/shared/aggregate.ts +3 -2
  268. package/src/shared/types.ts +31 -0
  269. package/src/show/compose.ts +2 -2
  270. package/src/show/index.ts +21 -1
  271. package/src/show/render.ts +619 -104
  272. package/src/show/show.test.ts +235 -19
  273. package/src/tty-line.ts +8 -26
  274. package/src/util.test.ts +1 -0
  275. package/src/util.ts +41 -0
  276. package/src/view/app/components/AttemptModal.tsx +153 -2
  277. package/src/view/app/components/CodeView.tsx +32 -11
  278. package/src/view/app/components/CopyControls.tsx +2 -2
  279. package/src/view/app/i18n.ts +6 -0
  280. package/src/view/app/lib/attempt-route.test.ts +1 -0
  281. package/src/view/app/lib/verdict.ts +7 -9
  282. package/src/view/artifact-serving.test.ts +2 -1
  283. package/src/view/client-dist/app.css +1 -1
  284. package/src/view/client-dist/app.js +17 -17
  285. package/src/view/data.test.ts +1 -0
  286. package/src/view/data.ts +11 -1
  287. package/src/view/index.ts +11 -0
  288. package/src/view/server.ts +2 -0
  289. package/src/view/styles.css +3 -0
  290. package/src/view/view-report.test.ts +6 -5
  291. package/src/runner/reporters/console.ts +0 -70
  292. package/src/runner/reporters/live.test.ts +0 -56
  293. package/src/runner/reporters/live.ts +0 -247
  294. package/src/runner/reporters/quiet.test.ts +0 -66
  295. package/src/runner/reporters/quiet.ts +0 -49
  296. package/src/runner/reporters/table.ts +0 -277
@@ -1,3 +1,4 @@
1
+ // cases: docs/engineering/unit-tests/adapters/cases.md
1
2
  import { describe, expect, it } from "vitest";
2
3
 
3
4
  import { fromClaudeSdkMessages, fromCodexThreadEvents, fromPiAgentEvents, sseJsonFrames } from "./sdk-streams.ts";
@@ -50,6 +51,26 @@ describe("fromClaudeSdkMessages", () => {
50
51
  expect(s.add({ type: "user", message: { content: [{ type: "tool_result", tool_use_id: "tu1", content: "denied" }] } })).toEqual([]);
51
52
  });
52
53
 
54
+ it("assistant 的 thinking 块 → thinking 事件,与 text/tool_use 保持原始顺序", () => {
55
+ const s = fromClaudeSdkMessages();
56
+ expect(
57
+ s.add({
58
+ type: "assistant",
59
+ message: {
60
+ content: [
61
+ { type: "thinking", thinking: "先查天气", signature: "sig-1" },
62
+ { type: "text", text: "我来查" },
63
+ { type: "tool_use", id: "tu1", name: "get_weather", input: { city: "北京" } },
64
+ ],
65
+ },
66
+ }),
67
+ ).toEqual([
68
+ { type: "thinking", text: "先查天气" },
69
+ { type: "message", role: "assistant", text: "我来查" },
70
+ { type: "action.called", callId: "tu1", name: "get_weather", input: { city: "北京" } },
71
+ ]);
72
+ });
73
+
53
74
  it("stream_event 等未知帧返回 []", () => {
54
75
  expect(fromClaudeSdkMessages().add({ type: "stream_event" })).toEqual([]);
55
76
  });
@@ -86,6 +107,64 @@ describe("fromPiAgentEvents", () => {
86
107
  { type: "action.result", callId: "c9", output: undefined, status: "rejected" },
87
108
  ]);
88
109
  });
110
+
111
+ it("三段式聚合成一条:start/delta 不产事件,end 帧的完整 content 只落一条", () => {
112
+ const s = fromPiAgentEvents();
113
+ expect(s.add({ type: "message_start", message: { role: "assistant", content: [] } })).toEqual([]);
114
+ expect(s.add({ type: "message_update", assistantMessageEvent: { type: "text_delta", contentIndex: 0, delta: "18" } })).toEqual([]);
115
+ expect(s.add({ type: "message_update", assistantMessageEvent: { type: "text_delta", contentIndex: 0, delta: " 度" } })).toEqual([]);
116
+ // end 帧带完整 content:以它为准,增量不重复落地
117
+ expect(s.add({ type: "message_end", message: { role: "assistant", content: [{ type: "text", text: "18 度" }] } })).toEqual([
118
+ { type: "message", role: "assistant", text: "18 度" },
119
+ ]);
120
+ });
121
+
122
+ it("end 帧缺 content 时用累加的 text/thinking 增量兜底", () => {
123
+ const s = fromPiAgentEvents();
124
+ s.add({ type: "message_start", message: { role: "assistant" } });
125
+ s.add({ type: "message_update", assistantMessageEvent: { type: "thinking_delta", contentIndex: 0, delta: "想" } });
126
+ s.add({ type: "message_update", assistantMessageEvent: { type: "thinking_delta", contentIndex: 0, delta: "想" } });
127
+ s.add({ type: "message_update", assistantMessageEvent: { type: "text_delta", contentIndex: 1, delta: "18 度" } });
128
+ expect(s.add({ type: "message_end", message: { role: "assistant" } })).toEqual([
129
+ { type: "message", role: "assistant", text: "18 度" },
130
+ { type: "thinking", text: "想想" },
131
+ ]);
132
+ });
133
+
134
+ it("只有 start/delta 的截断流不产半条消息,下一条消息不串入旧增量", () => {
135
+ const s = fromPiAgentEvents();
136
+ expect(s.add({ type: "message_start", message: { role: "assistant" } })).toEqual([]);
137
+ expect(s.add({ type: "message_update", assistantMessageEvent: { type: "text_delta", contentIndex: 0, delta: "半截" } })).toEqual([]);
138
+ // 流在这里断掉:没有 message_end,什么都不落地。新消息开始后旧增量作废。
139
+ expect(s.add({ type: "message_start", message: { role: "assistant" } })).toEqual([]);
140
+ expect(s.add({ type: "message_end", message: { role: "assistant", content: [{ type: "text", text: "新消息" }] } })).toEqual([
141
+ { type: "message", role: "assistant", text: "新消息" },
142
+ ]);
143
+ });
144
+
145
+ it("失败状态帧:stopReason error/aborted → error 事件 + failed", () => {
146
+ const s = fromPiAgentEvents();
147
+ expect(s.failed).toBe(false);
148
+ expect(
149
+ s.add({ type: "message_end", message: { role: "assistant", content: [{ type: "text", text: "试到一半" }], stopReason: "error", errorMessage: "boom" } }),
150
+ ).toEqual([
151
+ { type: "message", role: "assistant", text: "试到一半" },
152
+ { type: "error", message: "boom" },
153
+ ]);
154
+ expect(s.failed).toBe(true);
155
+
156
+ const aborted = fromPiAgentEvents();
157
+ expect(aborted.add({ type: "message_end", message: { role: "assistant", stopReason: "aborted" } })).toEqual([
158
+ { type: "error", message: "pi agent stopReason=aborted" },
159
+ ]);
160
+ expect(aborted.failed).toBe(true);
161
+ });
162
+
163
+ it("正常收尾(stopReason stop/toolUse)不置 failed", () => {
164
+ const s = fromPiAgentEvents();
165
+ s.add({ type: "message_end", message: { role: "assistant", content: [{ type: "text", text: "好了" }], stopReason: "stop" } });
166
+ expect(s.failed).toBe(false);
167
+ });
89
168
  });
90
169
 
91
170
  describe("fromCodexThreadEvents", () => {
@@ -96,10 +96,10 @@ export interface ClaudeSdkStream {
96
96
 
97
97
  /**
98
98
  * Claude Agent SDK 消息流(`system` / `assistant` / `user` / `result`)→ 标准事件。
99
- * `assistant` 的 text/tool_use 块 → message / action.called;`user` 的 tool_result 块 →
100
- * action.result;`system`/`permission_denied` → rejected;`stream_event`(逐 token 渲染)
101
- * 整个忽略。HITL 的停轮判定(哪个工具被门控)是应用侧的知识,不在这里——扫描 add()
102
- * 返回的 action.called 自行决定。
99
+ * `assistant` 的 text/thinking/tool_use 块 → message / thinking / action.called;`user` 的
100
+ * tool_result 块 → action.result;`system`/`permission_denied` → rejected;`stream_event`
101
+ * (逐 token 渲染)整个忽略。HITL 的停轮判定(哪个工具被门控)是应用侧的知识,不在这里——
102
+ * 扫描 add() 返回的 action.called 自行决定。
103
103
  */
104
104
  export function fromClaudeSdkMessages(): ClaudeSdkStream {
105
105
  let sessionId: string | undefined;
@@ -152,6 +152,8 @@ export function fromClaudeSdkMessages(): ClaudeSdkStream {
152
152
  if (!isRecord(block)) continue;
153
153
  if (block.type === "text" && typeof block.text === "string" && block.text) {
154
154
  events.push({ type: "message", role: "assistant", text: block.text });
155
+ } else if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking) {
156
+ events.push({ type: "thinking", text: block.thinking });
155
157
  } else if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
156
158
  events.push({ type: "action.called", callId: block.id, name: block.name, input: block.input as JsonValue });
157
159
  }
@@ -209,8 +211,14 @@ export interface PiAgentEventLike {
209
211
  message?: {
210
212
  role?: string;
211
213
  content?: unknown;
214
+ /** assistant 消息的收尾原因;"error" / "aborted" 表示这条消息以失败告终。 */
215
+ stopReason?: string;
216
+ /** stopReason 为 "error" 时的错误说明。 */
217
+ errorMessage?: string;
212
218
  usage?: { input: number; output: number; cacheRead: number; cacheWrite: number; cost: { total: number } };
213
219
  };
220
+ /** `message_update` 帧携带的增量事件(text_delta / thinking_delta 等)——只读 type/delta,防御式解析。 */
221
+ assistantMessageEvent?: { type?: string; delta?: unknown; [key: string]: unknown };
214
222
  [key: string]: unknown;
215
223
  }
216
224
 
@@ -219,17 +227,27 @@ export interface PiAgentStream {
219
227
  add(event: PiAgentEventLike): StreamEvent[];
220
228
  /** assistant `message_end` 逐条累加的用量。 */
221
229
  readonly usage: Usage | undefined;
230
+ /** assistant 消息以 stopReason "error" / "aborted" 收尾过。 */
231
+ readonly failed: boolean;
222
232
  /** 拒绝审批后续读前登记:该调用 `tool_execution_end` 的 isError 判成 "rejected" 而非 "failed"。 */
223
233
  markRejected(toolCallId: string): void;
224
234
  }
225
235
 
226
236
  /**
227
- * pi-agent-core 事件流(`message_end` / `tool_execution_start` / `tool_execution_end`)→
228
- * 标准事件。message_end assistant 文本与 thinking、累加 usage;
237
+ * pi-agent-core 事件流(`message_start` / `message_update` / `message_end` /
238
+ * `tool_execution_start` / `tool_execution_end`)→ 标准事件。消息三段式聚合成一条:
239
+ * message_start 清空增量缓冲、message_update 的 text_delta / thinking_delta 逐段累加、
240
+ * message_end 落地成 message / thinking(优先取 end 帧的完整 content,缺了用累加的增量兜底);
241
+ * 只有 start/delta 的截断流不产出半条消息。message_end 同时累加 usage;assistant 消息以
242
+ * stopReason "error" / "aborted" 收尾时记失败(`failed`)并发一条 error 事件。
229
243
  * tool_execution_start/end → action.called / action.result。
230
244
  */
231
245
  export function fromPiAgentEvents(): PiAgentStream {
232
246
  let usage: Usage | undefined;
247
+ let failed = false;
248
+ // message_start/update 累加的增量缓冲;message_end 落地后清空,截断流(无 end)不落地。
249
+ let deltaText = "";
250
+ let deltaThinking = "";
233
251
  const rejected = new Set<string>();
234
252
 
235
253
  const partText = (event: PiAgentEventLike, type: "text" | "thinking"): string => {
@@ -247,17 +265,44 @@ export function fromPiAgentEvents(): PiAgentStream {
247
265
  get usage() {
248
266
  return usage;
249
267
  },
268
+ get failed() {
269
+ return failed;
270
+ },
250
271
  markRejected(toolCallId) {
251
272
  rejected.add(toolCallId);
252
273
  },
253
274
  add(event) {
254
275
  const events: StreamEvent[] = [];
255
276
  switch (event.type) {
277
+ case "message_start": {
278
+ // 新消息开始:上一条没等到 message_end 的增量作废(不落地半条消息)。
279
+ deltaText = "";
280
+ deltaThinking = "";
281
+ break;
282
+ }
283
+ case "message_update": {
284
+ const e = event.assistantMessageEvent;
285
+ if (isRecord(e) && typeof e.delta === "string") {
286
+ if (e.type === "text_delta") deltaText += e.delta;
287
+ else if (e.type === "thinking_delta") deltaThinking += e.delta;
288
+ }
289
+ break;
290
+ }
256
291
  case "message_end": {
257
- const text = partText(event, "text");
292
+ // 优先取 end 帧的完整 content;缺了(部分传输只带增量)用累加的 delta 兜底。
293
+ const text = partText(event, "text") || (event.message?.role === "assistant" ? deltaText : "");
258
294
  if (text) events.push({ type: "message", role: "assistant", text });
259
- const thinking = partText(event, "thinking");
295
+ const thinking = partText(event, "thinking") || (event.message?.role === "assistant" ? deltaThinking : "");
260
296
  if (thinking) events.push({ type: "thinking", text: thinking });
297
+ deltaText = "";
298
+ deltaThinking = "";
299
+ if (event.message?.role === "assistant") {
300
+ const stopReason = event.message.stopReason;
301
+ if (stopReason === "error" || stopReason === "aborted") {
302
+ failed = true;
303
+ events.push({ type: "error", message: event.message.errorMessage ?? `pi agent stopReason=${stopReason}` });
304
+ }
305
+ }
261
306
  const u = event.message?.role === "assistant" ? event.message.usage : undefined;
262
307
  if (u) {
263
308
  usage = {
@@ -284,8 +329,8 @@ export function fromPiAgentEvents(): PiAgentStream {
284
329
  }
285
330
  break;
286
331
  }
287
- // agent_start / turn_start / turn_end / message_start / message_update /
288
- // tool_execution_update / agent_end:无对应 StreamEvent(message_end 已带完整文本)。
332
+ // agent_start / turn_start / turn_end / tool_execution_update / agent_end:
333
+ // 无对应 StreamEvent(消息文本统一在 message_end 落地)。
289
334
  default:
290
335
  break;
291
336
  }
@@ -1,3 +1,4 @@
1
+ // cases: docs/engineering/unit-tests/adapters/cases.md
1
2
  // SkillSpec 安装的单测:本地形状(目录 / 单文件 / 不支持)、repo 的 ref 钉定与选择规则、
2
3
  // manifest 记录形状、以及「setup 装进 workspace 的东西不算 agent diff」这条护栏。
3
4
  // 沙箱是内存 fake(记命令 + 记文件),不起容器:这里要验的是 adapter 侧的翻译规则,
@@ -1,3 +1,4 @@
1
+ // cases: docs/engineering/unit-tests/adapters/cases.md
1
2
  import { describe, expect, it } from "vitest";
2
3
 
3
4
  import { deltaStream, driveFrameStream } from "./streaming.ts";
@@ -12,6 +13,8 @@ function lineCtx(): AgentContext {
12
13
  flags: {},
13
14
  session: createAgentSession(),
14
15
  sandbox: undefined as never,
16
+ progress() {},
17
+ diagnostic() {},
15
18
  log() {},
16
19
  };
17
20
  }
@@ -73,15 +76,6 @@ describe("deltaStream", () => {
73
76
  expect(s.add({ finish_reason: "tool_calls" })).toEqual([{ type: "action.called", callId: "call_2", name: "broken", input: "not json" }]);
74
77
  });
75
78
 
76
- it("tool-result 独立到达,配对靠 core 的 deriveRunFacts,这里只管落地成 action.result", () => {
77
- const s = deltaStream<{ result?: { callId: string; output: string } }>({
78
- toOps: (f) => (f.result ? [{ kind: "tool-result", callId: f.result.callId, output: f.result.output, status: "completed" }] : []),
79
- });
80
- expect(s.add({ result: { callId: "call_1", output: "22C" } })).toEqual([
81
- { type: "action.result", callId: "call_1", output: "22C", status: "completed" },
82
- ]);
83
- });
84
-
85
79
  it("error 操作:落地未完成的文本、置 failed、附一条 error 事件", () => {
86
80
  const s = deltaStream({ toOps });
87
81
  s.add({ delta: { content: "还没说完" } });
@@ -2,7 +2,7 @@
2
2
  // 「连到哪个被测对象、协议怎么说」的全部契约在这里(见 docs-site/zh/concepts/adapter.mdx)。
3
3
  // 能力不再是问卷式声明:t 上解锁什么完全由构造证据决定(见 docs-site 「能力从哪来」一节)。
4
4
 
5
- import type { Cleanup } from "../shared/types.ts";
5
+ import type { Cleanup, DiagnosticInput, ProgressUpdate } from "../shared/types.ts";
6
6
  import type { StreamEvent, TraceSpan, Usage } from "../o11y/types.ts";
7
7
  import type { Sandbox } from "../sandbox/types.ts";
8
8
 
@@ -69,10 +69,51 @@ export interface AgentSetupManifest {
69
69
  }>;
70
70
  /** 挂上的 MCP server(不含 env:secret 不进 manifest)。 */
71
71
  mcpServers?: Array<{ name: string; command: string; args?: string[] }>;
72
+ /**
73
+ * 官方原生配置文件(Claude Code `settings.json` / Codex `config.toml`):只记 Agent 名、
74
+ * 项目相对来源路径与原始字节的 SHA-256,不落正文 —— 任意官方配置都可能携带敏感字符串,
75
+ * 不能靠字段白名单证明适合原样落盘。
76
+ */
77
+ nativeConfigFile?: { agent: "claude-code" | "codex"; path: string; sha256: string };
72
78
  /** Bub 的 Python Plugin(规范化后的 package 串)。 */
73
79
  pythonPlugins?: Array<{ package: string }>;
74
80
  }
75
81
 
82
+ // ───────────────────────── 证据覆盖声明 ─────────────────────────
83
+
84
+ /** 证据通道的覆盖状态。省略(不声明)= unknown,不是 complete——消费侧与 unavailable 同样保守。 */
85
+ export type CoverageStatus = "complete" | "partial" | "unavailable";
86
+
87
+ /** 单个证据通道的覆盖声明:状态 + 可选的人可读原因(如 "stream reconnected mid-turn")。 */
88
+ export interface CoverageDeclaration {
89
+ status: CoverageStatus;
90
+ reason?: string;
91
+ }
92
+
93
+ /**
94
+ * 覆盖声明(EvidenceCoverage):完整性不是口头承诺,是随数据走的声明
95
+ * (见 docs/feature/adapters/architecture/evidence.md)。两层:
96
+ * - Agent 级默认(`defineAgent` / `defineSandboxAgent` 的 `coverage`)声明该 Adapter 的常态覆盖;
97
+ * 官方 SDK 适配器显式声明全通道 complete(用 `completeCoverage` 常量)。
98
+ * - Turn 级降级(`Turn.coverage`)只用于相对 Agent 默认值降级(这一轮流断了 / 拿不到 usage),
99
+ * 不能把 Agent 未声明的通道升格成 complete。
100
+ * 整个 Agent 不声明时全部通道视为 unknown。
101
+ */
102
+ export interface EvidenceCoverage {
103
+ /** 完整事件流(event / notEvent / order 的依据)。 */
104
+ events?: CoverageDeclaration;
105
+ /** action 生命周期(工具正负断言、顺序、失败的依据)。 */
106
+ actions?: CoverageDeclaration;
107
+ /** assistant / user message(reply、messageIncludes 的依据)。 */
108
+ messages?: CoverageDeclaration;
109
+ /** usage(token / cost 上限断言的依据)。 */
110
+ usage?: CoverageDeclaration;
111
+ /** Turn status 的真实性(succeeded / parked 的依据)——恒 completed 的映射必须声明非 complete。 */
112
+ status?: CoverageDeclaration;
113
+ /** Turn.data(outputEquals / outputMatches 的依据)。 */
114
+ data?: CoverageDeclaration;
115
+ }
116
+
76
117
  /** 随一轮消息附带的文件(图片等多模态输入)。 */
77
118
  export interface InputFile {
78
119
  /** 文件名(可选,供 adapter / 模型参考)。 */
@@ -111,6 +152,11 @@ export interface Turn {
111
152
  readonly data?: unknown;
112
153
  readonly status: "completed" | "failed" | "waiting";
113
154
  readonly usage?: Usage;
155
+ /**
156
+ * 本轮相对 Agent 默认覆盖的降级声明(这一轮流断了、拿不到 usage 等);
157
+ * 只能降级,不能把 Agent 未声明的通道升格成 complete(见 EvidenceCoverage)。
158
+ */
159
+ readonly coverage?: EvidenceCoverage;
114
160
  }
115
161
 
116
162
  /**
@@ -238,10 +284,21 @@ export interface AgentContext {
238
284
  */
239
285
  readonly telemetry?: Telemetry;
240
286
  /**
241
- * 写一行进运行器自身的进度日志( stderr,或调用方传入的 `onProgress` 回调)
242
- * 超时失败时,最近若干行会并入结果的 error 信息,方便定位卡在哪一步。
243
- * `Sandbox.appendLog` 是两回事:那个是把一行写进容器自己的原生日志
244
- * (`docker logs` 能看到),这里只是运行器的观测通道。
287
+ * 作用域反馈:报告此刻正在做什么(turn / tool / 安装进度)。短命状态——Human profile
288
+ * 更新 active 行,`agent`/`ci` 不逐条打印,也不进最终结果;不要每个 token/delta 都调用。
289
+ * runner 按当前回调所处的生命周期阶段(agent.setup / agent.run / agent.teardown)归因,
290
+ * 调用方不能冒充其它阶段( docs/feature/experiments/library.md)
291
+ */
292
+ progress(update: ProgressUpdate): void;
293
+ /**
294
+ * 作用域反馈:报告运行结束后仍应保留的问题(协议降级、数据不完整、cleanup 问题)。
295
+ * 永久事件,落进 attempt 的 diagnostics 并进各 profile 的永久输出;`dedupeKey` 去重。
296
+ * 即使 level 为 "error" 也不改变 Turn.status / verdict——无法继续时抛异常。
297
+ */
298
+ diagnostic(input: DiagnosticInput): void;
299
+ /**
300
+ * `progress({ message: msg })` 的别名,不是第二条通道(见 docs/feature/experiments/cli.md
301
+ * 「Attempt 阶段」)。超时失败时最近若干行会并入结果的 error 信息,方便定位卡在哪一步。
245
302
  */
246
303
  log(msg: string): void;
247
304
  }
@@ -272,6 +329,8 @@ export interface Agent {
272
329
  * `t.sandbox`/`t.sandbox.fileChanged()` 等文件系统断言,见 docs-site「能力从哪来」一节。
273
330
  */
274
331
  readonly kind: "sandbox" | "remote";
332
+ /** 该 Adapter 的常态证据覆盖声明;省略 = 全通道 unknown(见 EvidenceCoverage)。 */
333
+ coverage?: EvidenceCoverage;
275
334
  setup?: AgentSetup;
276
335
  /** OTLP 导出配置(仅当此字段存在时运行器才为该 agent 开 OTLP 接收);与 setup 分开,见 AgentTracing。 */
277
336
  tracing?: AgentTracing;
@@ -285,6 +344,8 @@ export interface Agent {
285
344
  export interface SandboxAgentDef {
286
345
  /** agent 的显示名/标识,原样进入 `Agent.name`——不是注册表查找 key,只用于展示、结果归属与去重指纹。 */
287
346
  name: string;
347
+ /** 该 Adapter 的常态证据覆盖声明(完整采集的用 `completeCoverage` 常量);省略 = 全通道 unknown。 */
348
+ coverage?: EvidenceCoverage;
288
349
  /**
289
350
  * 每个沙箱一次(不是每轮一次):装 CLI、写 config.toml / 鉴权配置(model/base/auth 等
290
351
  * 本轮内不变的东西)。运行器在沙箱备好(上传/基线/eval.setup 之后)、第一次 send 前
@@ -305,6 +366,8 @@ export interface SandboxAgentDef {
305
366
  export interface RemoteAgentDef {
306
367
  /** agent 的显示名/标识,原样进入 `Agent.name`——不是注册表查找 key,只用于展示、结果归属与去重指纹。 */
307
368
  name: string;
369
+ /** 该 Adapter 的常态证据覆盖声明(完整采集的用 `completeCoverage` 常量);省略 = 全通道 unknown。 */
370
+ coverage?: EvidenceCoverage;
308
371
  /**
309
372
  * 每个 attempt 一次(remote agent 没有真实沙箱,运行器会传入一个仅含 `workdir`/`sandboxId`/
310
373
  * `otlpHost`/`stop` 等元信息的 stub `Sandbox`,其余方法调用即抛错——不要在这里调用
@@ -1,3 +1,4 @@
1
+ // cases: docs/engineering/unit-tests/adapters/cases.md
1
2
  import { afterEach, describe, expect, it, vi } from "vitest";
2
3
 
3
4
  import type { AgentContext } from "../types.ts";
@@ -77,6 +78,8 @@ function ctx(overrides: Partial<{ model: string }> = {}): AgentContext {
77
78
  sandbox: undefined as never,
78
79
  // 同一个 ctx 重复用 = 同一条会话线(续接,同一个 ctx.session);新造 = 新线。
79
80
  session: createAgentSession(),
81
+ progress: () => {},
82
+ diagnostic: () => {},
80
83
  log: () => {},
81
84
  } as AgentContext;
82
85
  }