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
@@ -0,0 +1,310 @@
1
+ // OpenClaw transcript / `agent --json` 解析器。OpenClaw 方言只住这里,不进 core
2
+ // (契约见 docs/feature/adapters/sdk/openclaw/README.md:字段事实以真实 CLI 与 transcript
3
+ // fixture 固定为准;fixture 未证明的形状这里只做防御式解析,认不出的行标 parseSuccess=false,
4
+ // 绝不从最终文本猜测工具轨迹)。
5
+ //
6
+ // 行为轨两个来源(collection.md 的通道优先级):
7
+ // 1. session transcript JSONL(~/.openclaw/agents/**/sessions/*.jsonl,pi-agent 系
8
+ // 消息格式:role/content parts,toolCall/toolResult 按 call ID 配对)→ parseOpenClawTranscript;
9
+ // 2. `openclaw agent --json` 的 stdout 结果封包(最终回复、session key、失败与 usage 摘要,
10
+ // 无完整工具轨迹)→ parseOpenClawRunJson,只在 transcript 拿不到时兜底。
11
+
12
+ import type { StreamEvent, Usage, ToolName, JsonValue } from "../../types.ts";
13
+ import type { ParsedTranscript } from "./index.ts";
14
+ import { GENERIC_VERB_ALIASES, normalizeToolName as normalizeShared } from "../tool-names.ts";
15
+
16
+ /**
17
+ * OpenClaw 特有工具别名(CLI 自己的 transcript 词汇,不会撞用户域名,裸动词可 opt-in)
18
+ * + 通用裸动词基表。
19
+ */
20
+ export const OPENCLAW_TOOL_ALIASES: Record<string, ToolName> = {
21
+ ...GENERIC_VERB_ALIASES,
22
+ read: "file_read",
23
+ write: "file_write",
24
+ edit: "file_edit",
25
+ process: "shell",
26
+ browser: "web_fetch",
27
+ };
28
+
29
+ function normalizeToolName(name: string): ToolName {
30
+ return normalizeShared(name, OPENCLAW_TOOL_ALIASES);
31
+ }
32
+
33
+ function get(obj: unknown, key: string): unknown {
34
+ return obj && typeof obj === "object" ? (obj as Record<string, unknown>)[key] : undefined;
35
+ }
36
+
37
+ function str(v: unknown): string | undefined {
38
+ return typeof v === "string" && v ? v : undefined;
39
+ }
40
+
41
+ function num(obj: unknown, ...keys: string[]): number {
42
+ for (const k of keys) {
43
+ const v = get(obj, k);
44
+ if (typeof v === "number" && Number.isFinite(v)) return v;
45
+ }
46
+ return 0;
47
+ }
48
+
49
+ function coerceArgs(value: unknown): JsonValue {
50
+ if (typeof value === "string") {
51
+ try {
52
+ return JSON.parse(value) as JsonValue;
53
+ } catch {
54
+ return value;
55
+ }
56
+ }
57
+ return (value ?? {}) as JsonValue;
58
+ }
59
+
60
+ /** content parts / 字符串 → 纯文本(text 类 part 拼接)。 */
61
+ function extractText(content: unknown): string {
62
+ if (typeof content === "string") return content;
63
+ if (Array.isArray(content)) {
64
+ const parts: string[] = [];
65
+ for (const b of content) {
66
+ if (typeof b === "string") parts.push(b);
67
+ else {
68
+ const t = get(b, "text") ?? get(b, "content");
69
+ if (typeof t === "string" && t) parts.push(t);
70
+ }
71
+ }
72
+ return parts.join("\n");
73
+ }
74
+ return "";
75
+ }
76
+
77
+ /**
78
+ * OpenClaw session transcript(JSONL,pi-agent 系消息格式)→ 标准事件流 + 用量 + 压缩计数。
79
+ * assistant 消息的 text / thinking / toolCall parts → message / thinking / action.called,
80
+ * toolResult 消息按 toolCallId 配对成 action.result(isError → failed),消息级 usage 逐条
81
+ * 累加,compaction 条目计入压缩次数。认不出 / 解析失败的行标 `parseSuccess: false` 并跳过,
82
+ * 不猜测——transcript 不完整时负断言不可信(契约见 sdk/openclaw/README.md)。
83
+ */
84
+ export function parseOpenClawTranscript(raw: string | undefined): ParsedTranscript {
85
+ const events: StreamEvent[] = [];
86
+ let inputTokens = 0;
87
+ let outputTokens = 0;
88
+ let cacheReadTokens = 0;
89
+ let cacheWriteTokens = 0;
90
+ let costUSD = 0;
91
+ let requests = 0;
92
+ let compactions = 0;
93
+ let parseSuccess = true;
94
+
95
+ if (!raw || !raw.trim()) {
96
+ return { events, usage: { inputTokens: 0, outputTokens: 0 }, compactions: 0, parseSuccess: true };
97
+ }
98
+
99
+ let synth = 0;
100
+ const nextSynthId = (): string => `oc_${++synth}`;
101
+
102
+ const addUsage = (usage: unknown): void => {
103
+ if (!usage || typeof usage !== "object") return;
104
+ // pi 系简写(input/output/cacheRead/cacheWrite/cost.total)与 snake_case 变体都认。
105
+ const input = num(usage, "input", "input_tokens", "inputTokens", "prompt_tokens");
106
+ const output = num(usage, "output", "output_tokens", "outputTokens", "completion_tokens");
107
+ const cacheRead = num(usage, "cacheRead", "cache_read_input_tokens", "cacheReadTokens");
108
+ const cacheWrite = num(usage, "cacheWrite", "cache_creation_input_tokens", "cacheWriteTokens");
109
+ const cost = num(usage, "cost") || num(get(usage, "cost"), "total");
110
+ if (input === 0 && output === 0 && cacheRead === 0 && cacheWrite === 0 && cost === 0) return;
111
+ inputTokens += input;
112
+ outputTokens += output;
113
+ cacheReadTokens += cacheRead;
114
+ cacheWriteTokens += cacheWrite;
115
+ costUSD += cost;
116
+ requests += 1;
117
+ };
118
+
119
+ for (const line of raw.split("\n")) {
120
+ const trimmed = line.trim();
121
+ if (!trimmed) continue;
122
+
123
+ let entry: unknown;
124
+ try {
125
+ entry = JSON.parse(trimmed);
126
+ } catch {
127
+ parseSuccess = false;
128
+ continue;
129
+ }
130
+
131
+ try {
132
+ // 会话条目可能带 { type: "message", message: {...} } 包装,也可能就是消息本体。
133
+ const wrappedMessage = get(entry, "message");
134
+ const entryType = get(entry, "type");
135
+ const msg =
136
+ wrappedMessage && typeof wrappedMessage === "object" ? wrappedMessage : entry;
137
+ const role = get(msg, "role");
138
+
139
+ if (entryType === "compaction" || entryType === "compact" || get(entry, "kind") === "compaction") {
140
+ compactions += 1;
141
+ events.push({ type: "compaction", reason: str(get(entry, "reason")) });
142
+ continue;
143
+ }
144
+ if (entryType === "error" && role === undefined) {
145
+ events.push({ type: "error", message: String(get(entry, "message") ?? get(entry, "error") ?? "error") });
146
+ continue;
147
+ }
148
+
149
+ if (role === "assistant") {
150
+ addUsage(get(msg, "usage"));
151
+ const content = get(msg, "content");
152
+ if (typeof content === "string") {
153
+ if (content) events.push({ type: "message", role: "assistant", text: content });
154
+ continue;
155
+ }
156
+ if (!Array.isArray(content)) continue;
157
+ for (const part of content) {
158
+ const partType = get(part, "type");
159
+ if (partType === "text") {
160
+ const text = str(get(part, "text"));
161
+ if (text) events.push({ type: "message", role: "assistant", text });
162
+ } else if (partType === "thinking" || partType === "reasoning") {
163
+ const text = str(get(part, "thinking")) ?? str(get(part, "reasoning")) ?? str(get(part, "text"));
164
+ if (text) events.push({ type: "thinking", text });
165
+ } else if (partType === "toolCall" || partType === "tool_call" || partType === "tool-call" || partType === "toolUse") {
166
+ const name = String(get(part, "name") ?? get(part, "toolName") ?? "unknown");
167
+ const callId = str(get(part, "id")) ?? str(get(part, "toolCallId")) ?? str(get(part, "tool_call_id")) ?? nextSynthId();
168
+ events.push({
169
+ type: "action.called",
170
+ callId,
171
+ name,
172
+ input: coerceArgs(get(part, "arguments") ?? get(part, "args") ?? get(part, "input")),
173
+ tool: normalizeToolName(name),
174
+ });
175
+ }
176
+ }
177
+ continue;
178
+ }
179
+
180
+ if (role === "user") {
181
+ const text = extractText(get(msg, "content") ?? get(msg, "text"));
182
+ if (text) events.push({ type: "message", role: "user", text });
183
+ continue;
184
+ }
185
+
186
+ if (role === "toolResult" || role === "tool") {
187
+ const callId =
188
+ str(get(msg, "toolCallId")) ?? str(get(msg, "tool_call_id")) ?? str(get(msg, "id")) ?? nextSynthId();
189
+ const isError = get(msg, "isError") === true || get(msg, "is_error") === true;
190
+ const output = get(msg, "output") ?? get(msg, "result") ?? get(msg, "content");
191
+ events.push({
192
+ type: "action.result",
193
+ callId,
194
+ output: (typeof output === "string" ? output : (extractText(output) || output)) as JsonValue,
195
+ status: isError ? "failed" : "completed",
196
+ });
197
+ continue;
198
+ }
199
+ // 其它条目(system prompt、订阅元数据等):无对应 StreamEvent。
200
+ } catch {
201
+ parseSuccess = false;
202
+ }
203
+ }
204
+
205
+ const usage: Usage = { inputTokens, outputTokens };
206
+ if (cacheReadTokens > 0) usage.cacheReadTokens = cacheReadTokens;
207
+ if (cacheWriteTokens > 0) usage.cacheWriteTokens = cacheWriteTokens;
208
+ if (requests > 0) usage.requests = requests;
209
+ if (costUSD > 0) usage.costUSD = costUSD;
210
+
211
+ return { events, usage, compactions, parseSuccess };
212
+ }
213
+
214
+ /** 便捷形态:只要 StreamEvent[]。 */
215
+ export function parseOpenClaw(raw: string | undefined): StreamEvent[] {
216
+ return parseOpenClawTranscript(raw).events;
217
+ }
218
+
219
+ /** `openclaw agent --json` 结果封包里抠出的摘要(无完整工具轨迹,只兜底)。 */
220
+ export interface OpenClawRunJson {
221
+ /** 最终回复文本(result.text / payloads[].text / reply 变体)。 */
222
+ text?: string;
223
+ /** 本次 run 的 session key(首轮 capture、后续 resume 用)。 */
224
+ sessionId?: string;
225
+ /** 封包报告的用量;没报就省略,不编造。 */
226
+ usage?: Usage;
227
+ /** 封包自报失败(error 字段 / status=error|failed / success=false)。 */
228
+ failed: boolean;
229
+ }
230
+
231
+ /**
232
+ * `openclaw agent --json` 的 stdout → 结果摘要。整段 stdout 先按单个 JSON 文档解析
233
+ * (含 pretty-print 多行),失败再逐行扫最后一个 JSON 对象(混了日志行的场景)。
234
+ * 完全解析不出时返回空摘要(`failed: false`)——失败判定交给调用方的 exitCode。
235
+ */
236
+ export function parseOpenClawRunJson(stdout: string | undefined): OpenClawRunJson {
237
+ const doc = parseJsonDocument(stdout);
238
+ if (!doc) return { failed: false };
239
+
240
+ const result = get(doc, "result");
241
+ const textFromPayloads = (payloads: unknown): string | undefined => {
242
+ if (!Array.isArray(payloads)) return undefined;
243
+ const texts = payloads.map((p) => str(get(p, "text"))).filter((t): t is string => !!t);
244
+ return texts.length ? texts.join("\n") : undefined;
245
+ };
246
+ const text =
247
+ str(get(result, "text")) ??
248
+ textFromPayloads(get(result, "payloads")) ??
249
+ textFromPayloads(get(doc, "payloads")) ??
250
+ str(get(doc, "text")) ??
251
+ str(get(doc, "reply")) ??
252
+ str(get(doc, "message"));
253
+
254
+ const sessionId =
255
+ str(get(doc, "sessionId")) ??
256
+ str(get(doc, "session_id")) ??
257
+ str(get(doc, "sessionKey")) ??
258
+ str(get(result, "sessionId")) ??
259
+ str(get(result, "session_id"));
260
+
261
+ const rawUsage = get(doc, "usage") ?? get(result, "usage") ?? get(get(doc, "meta"), "usage");
262
+ let usage: Usage | undefined;
263
+ if (rawUsage && typeof rawUsage === "object") {
264
+ const input = num(rawUsage, "input", "input_tokens", "inputTokens", "prompt_tokens");
265
+ const output = num(rawUsage, "output", "output_tokens", "outputTokens", "completion_tokens");
266
+ if (input > 0 || output > 0) {
267
+ usage = { inputTokens: input, outputTokens: output };
268
+ const cacheRead = num(rawUsage, "cacheRead", "cache_read_input_tokens", "cacheReadTokens");
269
+ if (cacheRead > 0) usage.cacheReadTokens = cacheRead;
270
+ const cost = num(rawUsage, "cost") || num(get(rawUsage, "cost"), "total");
271
+ if (cost > 0) usage.costUSD = cost;
272
+ }
273
+ }
274
+
275
+ const status = get(doc, "status");
276
+ const failed =
277
+ get(doc, "error") != null ||
278
+ get(doc, "success") === false ||
279
+ (typeof status === "string" && /^(error|failed)$/i.test(status));
280
+
281
+ return {
282
+ ...(text !== undefined ? { text } : {}),
283
+ ...(sessionId !== undefined ? { sessionId } : {}),
284
+ ...(usage !== undefined ? { usage } : {}),
285
+ failed,
286
+ };
287
+ }
288
+
289
+ function parseJsonDocument(stdout: string | undefined): Record<string, unknown> | undefined {
290
+ if (!stdout || !stdout.trim()) return undefined;
291
+ const trimmed = stdout.trim();
292
+ try {
293
+ const doc = JSON.parse(trimmed) as unknown;
294
+ if (doc && typeof doc === "object" && !Array.isArray(doc)) return doc as Record<string, unknown>;
295
+ } catch {
296
+ // 混日志行:逐行找最后一个完整 JSON 对象
297
+ }
298
+ const lines = trimmed.split("\n").reverse();
299
+ for (const line of lines) {
300
+ const l = line.trim();
301
+ if (!l.startsWith("{") || !l.endsWith("}")) continue;
302
+ try {
303
+ const doc = JSON.parse(l) as unknown;
304
+ if (doc && typeof doc === "object") return doc as Record<string, unknown>;
305
+ } catch {
306
+ // 继续往上找
307
+ }
308
+ }
309
+ return undefined;
310
+ }