@citeark/agent 0.3.20

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 (347) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +128 -0
  3. package/data/dataset-source-registry.v1.json +300 -0
  4. package/dist/arkgraph/boot.js +6 -0
  5. package/dist/arkgraph/index.html +1 -0
  6. package/dist/arkgraph/viewer.css +1 -0
  7. package/dist/arkgraph/viewer.en.css +1 -0
  8. package/dist/arkgraph/viewer.en.js +49 -0
  9. package/dist/arkgraph/viewer.en.js.LEGAL.txt +56 -0
  10. package/dist/arkgraph/viewer.js +49 -0
  11. package/dist/arkgraph/viewer.js.LEGAL.txt +56 -0
  12. package/docker/claude-code/Dockerfile +97 -0
  13. package/docker/claude-code/codex-pro-relay.mjs +466 -0
  14. package/docker/claude-code/runtime-contract-check.mjs +79 -0
  15. package/docs/arkgraph-reading.md +79 -0
  16. package/docs/configuration.md +100 -0
  17. package/docs/integration.md +92 -0
  18. package/docs/maturity-plan.md +27 -0
  19. package/docs/npm-release.md +44 -0
  20. package/docs/paper-reading.md +40 -0
  21. package/docs/research-plan-granularity.md +27 -0
  22. package/docs/terminal.md +49 -0
  23. package/examples/toy-evaluation/compile-task.json +27 -0
  24. package/examples/toy-evaluation/paper.md +5 -0
  25. package/examples/toy-evaluation/repository/README.md +9 -0
  26. package/examples/toy-evaluation/repository/checkpoint.json +4 -0
  27. package/examples/toy-evaluation/repository/evaluate.py +17 -0
  28. package/examples/toy-evaluation/task.json +81 -0
  29. package/package.json +59 -0
  30. package/prompts/compile-research.md +58 -0
  31. package/prompts/execute-contract.md +72 -0
  32. package/prompts/execute-workspace-simple.md +51 -0
  33. package/prompts/execute-workspace.md +34 -0
  34. package/prompts/prepare-reproduction.md +82 -0
  35. package/prompts/repair-research.md +45 -0
  36. package/protocol/CAP.md +129 -0
  37. package/protocol/LICENSE +12 -0
  38. package/protocol/MAPPINGS.md +72 -0
  39. package/protocol/README.md +38 -0
  40. package/protocol/conformance-v2.0-alpha.1.json +36 -0
  41. package/protocol/examples/arkgraph/checkpoint-evaluation.json +309 -0
  42. package/protocol/examples/arkgraph/fixtures.mjs +49 -0
  43. package/protocol/examples/arkgraph/paper-free.json +291 -0
  44. package/protocol/examples/arkgraph/partial-failure.json +344 -0
  45. package/protocol/examples/arkgraph/training-evaluation.json +443 -0
  46. package/protocol/profiles/agent-trace.md +16 -0
  47. package/protocol/profiles/computational-run.md +16 -0
  48. package/protocol/profiles/core.md +15 -0
  49. package/protocol/profiles/public-bundle.md +18 -0
  50. package/protocol/profiles/reproduction.md +29 -0
  51. package/protocol/profiles/research-compilation.md +44 -0
  52. package/protocol/profiles/research-plan.md +39 -0
  53. package/protocol/profiles/restricted-evidence.md +15 -0
  54. package/runtime/bootstrap-autodl-runtime.sh +314 -0
  55. package/runtime/create-runtime-venv.sh +41 -0
  56. package/runtime/install-local-cpu-runtime.sh +23 -0
  57. package/runtime/install-scientific-runtime.sh +153 -0
  58. package/runtime/mineru/parse.py +62 -0
  59. package/runtime/mineru/requirements.txt +4 -0
  60. package/runtime/requirements-baseline.txt +38 -0
  61. package/schemas/cap/v2/activity.schema.json +47 -0
  62. package/schemas/cap/v2/agent.schema.json +32 -0
  63. package/schemas/cap/v2/assertion.schema.json +110 -0
  64. package/schemas/cap/v2/descriptor.schema.json +243 -0
  65. package/schemas/cap/v2/entity.schema.json +64 -0
  66. package/schemas/cap/v2/manifest.schema.json +67 -0
  67. package/schemas/cap/v2/relation.schema.json +82 -0
  68. package/schemas/compute-catalog.schema.json +63 -0
  69. package/schemas/compute-decision.schema.json +27 -0
  70. package/schemas/execution-contract.schema.json +1024 -0
  71. package/schemas/research-card.schema.json +30 -0
  72. package/schemas/research-inventory-draft.schema.json +366 -0
  73. package/schemas/research.schema.json +1044 -0
  74. package/schemas/result.schema.json +173 -0
  75. package/schemas/verification-policy.schema.json +47 -0
  76. package/schemas/verified-conclusion.schema.json +58 -0
  77. package/schemas/workspace-summary.schema.json +24 -0
  78. package/scripts/build-arkgraph-view.mjs +12 -0
  79. package/scripts/check-execution-feasibility.mjs +24 -0
  80. package/scripts/check-syntax.mjs +15 -0
  81. package/scripts/deterministic-asset-preparation.py +438 -0
  82. package/scripts/package-cap.mjs +23 -0
  83. package/scripts/package-local-agent.mjs +23 -0
  84. package/scripts/preview-arkgraph.mjs +25 -0
  85. package/scripts/replay-research-compiler-candidate.mjs +134 -0
  86. package/scripts/review-compiler-sources.mjs +44 -0
  87. package/scripts/run-asset-preparation.sh +17 -0
  88. package/scripts/run-research-plan.mjs +98 -0
  89. package/scripts/validate-asset-preparation.py +290 -0
  90. package/scripts/verify-local-runtime.mjs +57 -0
  91. package/scripts/verify-npm-package.mjs +57 -0
  92. package/src/adapters/paper2agent.mjs +107 -0
  93. package/src/assets/cache.mjs +159 -0
  94. package/src/assets/compute.mjs +98 -0
  95. package/src/assets/executor.mjs +145 -0
  96. package/src/assets/lifecycle.mjs +213 -0
  97. package/src/assets/manifest.mjs +242 -0
  98. package/src/assets/opportunistic-preparation.mjs +81 -0
  99. package/src/assets/plan.mjs +411 -0
  100. package/src/assets/prompts.mjs +29 -0
  101. package/src/assets/public-asset-probe.mjs +525 -0
  102. package/src/assets/qualification.mjs +119 -0
  103. package/src/assets/readiness.mjs +130 -0
  104. package/src/assets/reproduction-admission.mjs +355 -0
  105. package/src/assets/requirements.mjs +152 -0
  106. package/src/assets/source-grounding.mjs +341 -0
  107. package/src/assets/source-policy.mjs +118 -0
  108. package/src/autodl/client.mjs +260 -0
  109. package/src/autodl/ssh.mjs +380 -0
  110. package/src/autodl/tools.mjs +129 -0
  111. package/src/cap/redaction.mjs +38 -0
  112. package/src/cap/v2/archive.mjs +152 -0
  113. package/src/cap/v2/attestation.mjs +204 -0
  114. package/src/cap/v2/canonical-json.mjs +114 -0
  115. package/src/cap/v2/compilation-artifact.mjs +240 -0
  116. package/src/cap/v2/core.mjs +282 -0
  117. package/src/cap/v2/measurement-assessment-records.mjs +23 -0
  118. package/src/cap/v2/pipeline-artifact.mjs +922 -0
  119. package/src/cap/v2/read.mjs +41 -0
  120. package/src/cap/v2/reassessment-artifact.mjs +383 -0
  121. package/src/cap/v2/research-artifact.mjs +231 -0
  122. package/src/cap/v2/research-map-records.mjs +46 -0
  123. package/src/cap/v2/research-object-records.mjs +163 -0
  124. package/src/cap/v2/research-records.mjs +187 -0
  125. package/src/cap/v2/verify.mjs +642 -0
  126. package/src/cli.mjs +1146 -0
  127. package/src/compute/autodl-pro-compiler.mjs +347 -0
  128. package/src/compute/autodl-pro-executor.mjs +459 -0
  129. package/src/compute/autodl-pro-job.mjs +843 -0
  130. package/src/compute/autodl-pro-network.mjs +295 -0
  131. package/src/compute/autodl-pro-remote.mjs +810 -0
  132. package/src/compute/autodl-pro-staging.mjs +117 -0
  133. package/src/compute/campaign.mjs +110 -0
  134. package/src/compute/catalog.mjs +123 -0
  135. package/src/compute/checkpoint-protocol.mjs +154 -0
  136. package/src/compute/codex-account-lock.mjs +111 -0
  137. package/src/compute/codex-account-session.mjs +107 -0
  138. package/src/compute/compiler-profile.mjs +38 -0
  139. package/src/compute/compiler-router.mjs +23 -0
  140. package/src/compute/coordinator-recovery.mjs +210 -0
  141. package/src/compute/executor-router.mjs +29 -0
  142. package/src/compute/gcp-batch-compiler.mjs +685 -0
  143. package/src/compute/gcp-batch-executor.mjs +1215 -0
  144. package/src/compute/gcp-batch-failure.mjs +92 -0
  145. package/src/compute/gcp-batch-job.mjs +527 -0
  146. package/src/compute/gcp-batch-lifecycle.mjs +81 -0
  147. package/src/compute/gcp-checkpoint-worker.mjs +1633 -0
  148. package/src/compute/local-codex-compiler.mjs +52 -0
  149. package/src/compute/measurement-hardware.mjs +128 -0
  150. package/src/compute/remote-attempt.mjs +226 -0
  151. package/src/compute/requirements.mjs +124 -0
  152. package/src/compute/research-phases.mjs +48 -0
  153. package/src/compute/scheduler.mjs +452 -0
  154. package/src/compute/shared-workloads.mjs +26 -0
  155. package/src/compute/stage-archive.mjs +79 -0
  156. package/src/contracts/campaign-contract.mjs +52 -0
  157. package/src/contracts/execution-contract.mjs +819 -0
  158. package/src/contracts/execution-mode.mjs +19 -0
  159. package/src/contracts/execution-timeouts.mjs +45 -0
  160. package/src/contracts/execution-workload.mjs +68 -0
  161. package/src/contracts/preflight-schema.mjs +25 -0
  162. package/src/contracts/public-contract.mjs +63 -0
  163. package/src/contracts/subject-tags.mjs +31 -0
  164. package/src/dashboard/data.mjs +898 -0
  165. package/src/dashboard/server.mjs +79 -0
  166. package/src/dashboard/static/dashboard.css +366 -0
  167. package/src/dashboard/static/dashboard.js +560 -0
  168. package/src/dashboard/static/index.html +85 -0
  169. package/src/deployment/community-policy.mjs +9 -0
  170. package/src/deployment/environment.mjs +112 -0
  171. package/src/deployment/guided.mjs +98 -0
  172. package/src/deployment/handoff.mjs +102 -0
  173. package/src/deployment/local-contract.mjs +31 -0
  174. package/src/deployment/local.mjs +100 -0
  175. package/src/deployment/prepare.mjs +46 -0
  176. package/src/deployment/recipe.mjs +108 -0
  177. package/src/deployment/supplement.mjs +51 -0
  178. package/src/deployment/terminal.mjs +43 -0
  179. package/src/diagnosis/renderer.mjs +75 -0
  180. package/src/diagnosis/target-failure.mjs +46 -0
  181. package/src/evidence/parser-registry.mjs +54 -0
  182. package/src/evidence/parsers/fasttext-classification.mjs +82 -0
  183. package/src/evidence/parsers/json-scalar.mjs +96 -0
  184. package/src/evidence/parsers/simcse-senteval.mjs +104 -0
  185. package/src/evidence/parsers/starspace-classification.mjs +78 -0
  186. package/src/evidence/registry.mjs +147 -0
  187. package/src/execution/runner-audit.mjs +473 -0
  188. package/src/gcp/auth.mjs +106 -0
  189. package/src/gcp/batch-client.mjs +120 -0
  190. package/src/gcp/resource-discovery.mjs +177 -0
  191. package/src/gcp/rest.mjs +82 -0
  192. package/src/gcp/secret-manager.mjs +34 -0
  193. package/src/gcp/signed-url.mjs +133 -0
  194. package/src/gcp/storage.mjs +220 -0
  195. package/src/graph/command.mjs +41 -0
  196. package/src/graph/execution.mjs +97 -0
  197. package/src/graph/model.mjs +37 -0
  198. package/src/graph/presentation.mjs +110 -0
  199. package/src/graph/query.mjs +159 -0
  200. package/src/graph/research-relations.mjs +69 -0
  201. package/src/graph/source-page.mjs +12 -0
  202. package/src/graph/source-preview.mjs +34 -0
  203. package/src/graph/validate.mjs +76 -0
  204. package/src/job.mjs +496 -0
  205. package/src/network/autodl-routing-proxy.mjs +462 -0
  206. package/src/network/egress-proxy.mjs +158 -0
  207. package/src/observability/event-contract.mjs +230 -0
  208. package/src/observability/pipeline-monitor.mjs +166 -0
  209. package/src/pipeline/orchestrator.mjs +1281 -0
  210. package/src/pipeline/recovery-error.mjs +11 -0
  211. package/src/pipeline/replay.mjs +304 -0
  212. package/src/pipeline/shared-execution.mjs +115 -0
  213. package/src/pipeline/stage-checkpoint.mjs +86 -0
  214. package/src/pipeline/stage-recovery.mjs +101 -0
  215. package/src/pipeline/targets.mjs +110 -0
  216. package/src/process.mjs +143 -0
  217. package/src/protocol.mjs +312 -0
  218. package/src/provider/codex-account.mjs +44 -0
  219. package/src/provider/codex-completion.mjs +49 -0
  220. package/src/provider/completion.mjs +292 -0
  221. package/src/provider/model-client.mjs +44 -0
  222. package/src/provider/model-route.mjs +29 -0
  223. package/src/provider/openrouter-readiness.mjs +189 -0
  224. package/src/provider/reader-bridge.mjs +35 -0
  225. package/src/provider/relay.mjs +263 -0
  226. package/src/provider/runtime-auth.mjs +40 -0
  227. package/src/public/cap.d.mts +90 -0
  228. package/src/public/cap.mjs +12 -0
  229. package/src/public/contracts.d.mts +2 -0
  230. package/src/public/host.mjs +171 -0
  231. package/src/public/operations.d.mts +11 -0
  232. package/src/public/presentation.d.mts +4 -0
  233. package/src/records/views.mjs +26 -0
  234. package/src/remote/command.mjs +178 -0
  235. package/src/remote/ssh.mjs +59 -0
  236. package/src/repository-origin.mjs +81 -0
  237. package/src/reproduction/evidence-feedback.mjs +96 -0
  238. package/src/reproduction/incomplete-initialization.mjs +25 -0
  239. package/src/reproduction/lifecycle.mjs +253 -0
  240. package/src/reproduction/plan.mjs +132 -0
  241. package/src/reproduction/prompts.mjs +70 -0
  242. package/src/reproduction/runner.mjs +188 -0
  243. package/src/reproduction/summary.mjs +130 -0
  244. package/src/reproduction/workspace-mode.mjs +7 -0
  245. package/src/research/automatic-admission.mjs +156 -0
  246. package/src/research/compiler-coverage.mjs +85 -0
  247. package/src/research/compiler-failure.mjs +24 -0
  248. package/src/research/compiler-normalization-guards.mjs +112 -0
  249. package/src/research/compiler-repair.mjs +3 -0
  250. package/src/research/compiler.mjs +853 -0
  251. package/src/research/continuation-selection.mjs +26 -0
  252. package/src/research/execution-graph-context.mjs +43 -0
  253. package/src/research/experiment-importance.mjs +15 -0
  254. package/src/research/inventory-handoff.mjs +104 -0
  255. package/src/research/inventory-revisions.mjs +32 -0
  256. package/src/research/mineru-local.mjs +73 -0
  257. package/src/research/paper-command.mjs +19 -0
  258. package/src/research/paper-markdown.mjs +180 -0
  259. package/src/research/paper-source-map.mjs +69 -0
  260. package/src/research/planning-policy.mjs +88 -0
  261. package/src/research/reference-materials.mjs +11 -0
  262. package/src/research/reproduction-scope.mjs +30 -0
  263. package/src/research/research-map.mjs +94 -0
  264. package/src/research/research-objects.mjs +88 -0
  265. package/src/research/source-discovery.mjs +646 -0
  266. package/src/research/source-observations.mjs +75 -0
  267. package/src/research/source-review-cli-mcp.mjs +26 -0
  268. package/src/research/source-review-input.mjs +209 -0
  269. package/src/research/source-review-local-codex.mjs +36 -0
  270. package/src/research/source-review-model.mjs +70 -0
  271. package/src/research/source-review.mjs +173 -0
  272. package/src/research/structure.mjs +3163 -0
  273. package/src/research-card/renderer.mjs +277 -0
  274. package/src/research-card/verified-conclusion.mjs +143 -0
  275. package/src/results/output-registry.mjs +183 -0
  276. package/src/runtime/claude-code.mjs +52 -0
  277. package/src/runtime/codex-capacity-retry.mjs +87 -0
  278. package/src/runtime/codex.mjs +64 -0
  279. package/src/runtime/config.mjs +157 -0
  280. package/src/runtime/final-output.mjs +40 -0
  281. package/src/runtime/index.mjs +21 -0
  282. package/src/runtime/local-codex.mjs +74 -0
  283. package/src/runtime/opencode.mjs +95 -0
  284. package/src/runtime/prompt.mjs +13 -0
  285. package/src/sandbox/docker.mjs +363 -0
  286. package/src/settings/command.mjs +297 -0
  287. package/src/settings/store.mjs +119 -0
  288. package/src/telemetry/pricing.mjs +68 -0
  289. package/src/telemetry/usage.mjs +265 -0
  290. package/src/terminal/events.mjs +97 -0
  291. package/src/terminal/input.mjs +40 -0
  292. package/src/terminal/plain.mjs +40 -0
  293. package/src/terminal/remote-stream.mjs +22 -0
  294. package/src/terminal/screen.mjs +214 -0
  295. package/src/terminal/transcript.mjs +69 -0
  296. package/src/util.mjs +107 -0
  297. package/src/verification/ai-assessor.mjs +534 -0
  298. package/src/verification/claim-evaluator.mjs +242 -0
  299. package/src/verification/evidence-context.mjs +165 -0
  300. package/src/verification/evidence-reader.mjs +95 -0
  301. package/src/verification/integrity.mjs +570 -0
  302. package/src/verification/tolerance.mjs +32 -0
  303. package/src/workloads/cpu-research-preparation.mjs +56 -0
  304. package/src/workloads/definition.mjs +74 -0
  305. package/src/workloads/phase-aware-reproduction.mjs +46 -0
  306. package/src/workloads/reproduction.mjs +85 -0
  307. package/src/workspace/command.mjs +242 -0
  308. package/src/workspace/control.mjs +49 -0
  309. package/src/workspace/entry.mjs +28 -0
  310. package/src/workspace/input.mjs +93 -0
  311. package/src/workspace/interactive.mjs +94 -0
  312. package/src/workspace/jobs.mjs +418 -0
  313. package/src/workspace/session.mjs +97 -0
  314. package/src/workspace/worker.mjs +137 -0
  315. package/ui/arkgraph/ambient-motion.mjs +10 -0
  316. package/ui/arkgraph/app.jsx +153 -0
  317. package/ui/arkgraph/boot.js +6 -0
  318. package/ui/arkgraph/camera-motion.mjs +20 -0
  319. package/ui/arkgraph/context-reveal.mjs +39 -0
  320. package/ui/arkgraph/details.css +3 -0
  321. package/ui/arkgraph/entry.jsx +28 -0
  322. package/ui/arkgraph/experiment-curves.mjs +17 -0
  323. package/ui/arkgraph/experiment-selection.mjs +15 -0
  324. package/ui/arkgraph/experiment-style.css +26 -0
  325. package/ui/arkgraph/experiment-ui.jsx +32 -0
  326. package/ui/arkgraph/frame.html +1 -0
  327. package/ui/arkgraph/graph-gestures.mjs +62 -0
  328. package/ui/arkgraph/label-layout.mjs +57 -0
  329. package/ui/arkgraph/locales/en.json +229 -0
  330. package/ui/arkgraph/locales/source-types.json +15 -0
  331. package/ui/arkgraph/localization-build.mjs +27 -0
  332. package/ui/arkgraph/material-build.mjs +23 -0
  333. package/ui/arkgraph/material-colors.mjs +39 -0
  334. package/ui/arkgraph/material-style.css +15 -0
  335. package/ui/arkgraph/open-graph.jsx +326 -0
  336. package/ui/arkgraph/outline.jsx +49 -0
  337. package/ui/arkgraph/package-lock.json +888 -0
  338. package/ui/arkgraph/package.json +17 -0
  339. package/ui/arkgraph/reading-layout.mjs +130 -0
  340. package/ui/arkgraph/reading-presentation.mjs +73 -0
  341. package/ui/arkgraph/record-detail.css +51 -0
  342. package/ui/arkgraph/record-details.jsx +29 -0
  343. package/ui/arkgraph/research-types.mjs +31 -0
  344. package/ui/arkgraph/selection-mark.jsx +6 -0
  345. package/ui/arkgraph/soft-spine.mjs +26 -0
  346. package/ui/arkgraph/steering-style.css +187 -0
  347. package/ui/arkgraph/style.css +272 -0
@@ -0,0 +1,560 @@
1
+ const state = {
2
+ data: null,
3
+ selectedId: null,
4
+ filter: "all",
5
+ loading: false,
6
+ };
7
+
8
+ const statusLabels = {
9
+ reproduced: "已复现",
10
+ approximately_reproduced: "近似复现",
11
+ completed: "已完成",
12
+ running: "运行中",
13
+ prepared: "已准备",
14
+ failed: "失败",
15
+ cancelled: "已取消",
16
+ inconclusive: "未定论",
17
+ not_reproduced: "未复现",
18
+ pending: "等待中",
19
+ skipped: "已跳过",
20
+ };
21
+
22
+ const elements = {
23
+ connection: document.querySelector(".connection"),
24
+ connectionLabel: document.querySelector("#connection-label"),
25
+ refresh: document.querySelector("#refresh-button"),
26
+ updatedAt: document.querySelector("#updated-at"),
27
+ totalRuns: document.querySelector("#total-runs"),
28
+ runningRuns: document.querySelector("#running-runs"),
29
+ decisiveRate: document.querySelector("#decisive-rate"),
30
+ reproducedRuns: document.querySelector("#reproduced-runs"),
31
+ totalDuration: document.querySelector("#total-duration"),
32
+ totalTokens: document.querySelector("#total-tokens"),
33
+ totalCost: document.querySelector("#total-cost"),
34
+ runCount: document.querySelector("#run-count"),
35
+ filters: document.querySelector("#filters"),
36
+ runList: document.querySelector("#run-list"),
37
+ detail: document.querySelector("#detail-panel"),
38
+ };
39
+
40
+ async function refresh() {
41
+ if (state.loading) return;
42
+ state.loading = true;
43
+ elements.refresh.disabled = true;
44
+ try {
45
+ const response = await fetch("/api/pipelines", { cache: "no-store" });
46
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
47
+ state.data = await response.json();
48
+ if (!state.selectedId || !state.data.pipelines.some((item) => item.id === state.selectedId)) {
49
+ state.selectedId = state.data.pipelines[0]?.id ?? null;
50
+ }
51
+ setConnection(true);
52
+ render();
53
+ } catch (error) {
54
+ setConnection(false);
55
+ if (!state.data) {
56
+ elements.detail.innerHTML = `<div class="empty-state"><div class="empty-symbol">!</div><h2>无法读取流水线</h2><p>${escapeHtml(error.message)}。仪表盘会继续自动重试。</p></div>`;
57
+ }
58
+ } finally {
59
+ state.loading = false;
60
+ elements.refresh.disabled = false;
61
+ }
62
+ }
63
+
64
+ function render() {
65
+ renderSummary();
66
+ renderRunList();
67
+ renderDetail();
68
+ elements.updatedAt.textContent = `最近同步 ${formatDateTime(state.data.generatedAt)}`;
69
+ }
70
+
71
+ function renderSummary() {
72
+ const totals = state.data.totals;
73
+ elements.totalRuns.textContent = formatNumber(totals.runs);
74
+ elements.runningRuns.textContent = totals.running
75
+ ? `${totals.running} 个运行中 · ${totals.runningActive} 个活跃 · ${totals.attentionRequired} 个需关注`
76
+ : `${totals.completed} 个已结束 · ${totals.failed} 个失败`;
77
+ elements.decisiveRate.textContent = `${totals.decisiveRate}%`;
78
+ elements.reproducedRuns.textContent = `${totals.decisive}/${totals.assessed} 个已评估结果形成结论 · ${totals.reproduced} 个复现 · ${totals.approximatelyReproduced ?? 0} 个近似复现 · ${totals.inconclusive} 个无法判定`;
79
+ elements.totalDuration.textContent = formatDuration(totals.durationMs);
80
+ elements.totalTokens.textContent = usageTokenValue(totals.usage);
81
+ elements.totalCost.textContent = pricingLabel(totals.usage.pricing);
82
+ }
83
+
84
+ function renderRunList() {
85
+ const pipelines = filteredPipelines();
86
+ elements.runCount.textContent = String(pipelines.length);
87
+ if (!pipelines.length) {
88
+ elements.runList.innerHTML = '<div class="empty-list">当前筛选条件下没有运行记录</div>';
89
+ return;
90
+ }
91
+ elements.runList.innerHTML = pipelines.map((pipeline) => {
92
+ const status = displayStatus(pipeline);
93
+ const activity = pipeline.activity ?? {};
94
+ return `
95
+ <button class="run-item ${pipeline.id === state.selectedId ? "selected" : ""}" type="button" data-run-id="${escapeHtml(pipeline.id)}">
96
+ <div class="run-item-top">
97
+ <span class="status-badge status-${escapeHtml(status)}">${statusLabel(status)}</span>
98
+ <span class="run-id">${escapeHtml(shortId(pipeline.id))}</span>
99
+ </div>
100
+ <div class="run-title">${escapeHtml(pipeline.title)}</div>
101
+ ${pipeline.status === "running" ? `<div class="run-live health-${escapeHtml(activity.health ?? "quiet")}"><i></i><span>${escapeHtml(activity.healthLabel ?? "运行中")} · ${formatAge(activity.activityAgeMs)}有活动</span></div><div class="run-current">${escapeHtml(activity.currentAction ?? "等待执行事件")}</div>` : ""}
102
+ <div class="run-item-meta">
103
+ <span>${formatDuration(pipeline.durationMs)}</span>
104
+ <span>${escapeHtml(usageTokenLabel(pipeline.usage))}</span>
105
+ <span class="mini-progress"><i style="width:${safePercent(pipeline.progressPercent)}%"></i></span>
106
+ </div>
107
+ </button>`;
108
+ }).join("");
109
+ }
110
+
111
+ function renderDetail() {
112
+ const pipeline = state.data.pipelines.find((item) => item.id === state.selectedId);
113
+ if (!pipeline) {
114
+ elements.detail.innerHTML = '<div class="empty-state"><div class="empty-symbol">⌁</div><h2>还没有流水线记录</h2><p>启动一次 CiteArk Agent 全流水线后,阶段、Token、结果与产物会自动显示在这里。</p></div>';
115
+ return;
116
+ }
117
+
118
+ const status = displayStatus(pipeline);
119
+ const comparison = pipeline.comparison;
120
+ const usage = pipeline.usage;
121
+ const usageReported = usage.availability?.status !== "unavailable";
122
+ const tokenInput = Number(usage.inputTokens ?? 0) + Number(usage.cacheCreationInputTokens ?? 0);
123
+ const tokenCache = Number(usage.cacheReadInputTokens ?? 0);
124
+ const tokenOutput = Number(usage.outputTokens ?? 0);
125
+ const tokenTotal = Math.max(1, tokenInput + tokenCache + tokenOutput);
126
+ const completedChecks = pipeline.checks.filter((check) => check.status === "passed").length;
127
+
128
+ elements.detail.innerHTML = `
129
+ <div class="detail-header">
130
+ <div>
131
+ <div class="detail-title-row">
132
+ <span class="status-badge status-${escapeHtml(status)}">${statusLabel(status)}</span>
133
+ <span class="run-id">${escapeHtml(pipeline.id)}</span>
134
+ </div>
135
+ <h2>${escapeHtml(pipeline.title)}</h2>
136
+ <p class="detail-claim">${escapeHtml(pipeline.claim?.statement ?? pipeline.experiment?.title ?? "研究对象正在结构化")}</p>
137
+ </div>
138
+ <div class="progress-ring" style="--progress:${safePercent(pipeline.progressPercent)}" aria-label="完成度 ${safePercent(pipeline.progressPercent)}%">
139
+ <strong>${safePercent(pipeline.progressPercent)}%</strong><span>完成度</span>
140
+ </div>
141
+ </div>
142
+
143
+ ${renderLiveStatus(pipeline)}
144
+
145
+ <section class="pipeline-section">
146
+ <div class="section-heading">
147
+ <div><h3>全流程进度</h3><p>${formatDateTime(pipeline.createdAt)} 开始 · 累计 ${formatDuration(pipeline.durationMs)}</p></div>
148
+ <span class="run-id">当前阶段 ${escapeHtml(stageLabel(pipeline.phase))}</span>
149
+ </div>
150
+ <div class="pipeline-track">${pipeline.stages.map(renderStage).join("")}</div>
151
+ </section>
152
+
153
+ ${pipeline.error ? `<div class="error-box"><strong>失败原因:</strong>${escapeHtml(pipeline.error)}</div>` : ""}
154
+
155
+ <div class="detail-grid">
156
+ ${renderVerifiedConclusion(pipeline.verifiedConclusion)}
157
+
158
+ ${renderComparisonCard(comparison, pipeline.metric, pipeline.verificationStatus)}
159
+
160
+ <article class="data-card">
161
+ <div class="card-title"><h3>Token 消耗</h3><span>${usageReported ? `${usage.turns ?? 0} 个 Agent 回合 · ${pricingLabel(usage.pricing)}` : "Agent CLI 未返回用量"}</span></div>
162
+ <div class="token-total"><strong>${usageReported ? compactNumber(usage.totalTokens) : "—"}</strong><span>${usageReported ? "上下文处理量" : "暂不可统计"}</span></div>
163
+ <div class="token-bar" aria-label="Token 构成">
164
+ <i style="width:${(tokenInput / tokenTotal) * 100}%"></i>
165
+ <i style="width:${(tokenCache / tokenTotal) * 100}%"></i>
166
+ <i style="width:${(tokenOutput / tokenTotal) * 100}%"></i>
167
+ </div>
168
+ ${usageReported ? `<div class="token-legend">
169
+ <span>输入<strong>${compactNumber(tokenInput)}</strong></span>
170
+ <span>缓存读取<strong>${compactNumber(tokenCache)}</strong></span>
171
+ <span>输出<strong>${compactNumber(tokenOutput)}</strong></span>
172
+ </div>` : `<p class="muted">${escapeHtml(usage.availability?.reason ?? "未取得 Token 明细")}</p>`}
173
+ </article>
174
+
175
+ <article class="data-card">
176
+ <div class="card-title"><h3>Agent 与执行环境</h3><span>${escapeHtml(pipeline.environment.sandbox ?? "环境待分配")}</span></div>
177
+ <dl class="definition-grid">
178
+ <div><dt>运行时</dt><dd>${escapeHtml(pipeline.agent.runtime ?? "—")}</dd></div>
179
+ <div><dt>模型提供方</dt><dd>${escapeHtml(pipeline.agent.provider ?? "—")}</dd></div>
180
+ <div><dt>主模型</dt><dd>${escapeHtml(pipeline.agent.model ?? "—")}</dd></div>
181
+ <div><dt>子 Agent 模型</dt><dd>${escapeHtml(pipeline.agent.subagentModel ?? "—")}</dd></div>
182
+ <div><dt>资源</dt><dd>${resourceLabel(pipeline.environment)}</dd></div>
183
+ <div><dt>镜像</dt><dd class="hash" title="${escapeHtml(pipeline.environment.image ?? "")}">${escapeHtml(pipeline.environment.image ?? "—")}</dd></div>
184
+ ${pipeline.environment.cloud ? `<div><dt>云资源</dt><dd>${escapeHtml(cloudResourceLabel(pipeline.environment.cloud))}</dd></div>` : ""}
185
+ ${pipeline.environment.cloud ? `<div><dt>临时资源</dt><dd>${escapeHtml(cloudCleanupLabel(pipeline.environment.cloud.temporaryResources))}</dd></div>` : ""}
186
+ ${pipeline.environment.cloud ? `<div><dt>云计算用时</dt><dd>${formatDuration(Number(pipeline.environment.cloud.computeUsage?.jobRunSeconds ?? 0) * 1000)}</dd></div>` : ""}
187
+ ${pipeline.environment.cloud ? `<div><dt>云费用估算</dt><dd>${pipeline.environment.cloud.computeUsage?.estimatedCostUsd == null ? "等待账单/目录单价" : `$${Number(pipeline.environment.cloud.computeUsage.estimatedCostUsd).toFixed(4)}`}</dd></div>` : ""}
188
+ ${pipeline.resilience ? `<div><dt>断点续跑</dt><dd>${escapeHtml(resilienceLabel(pipeline.resilience))}</dd></div>` : ""}
189
+ ${pipeline.resilience?.latestCheckpoint ? `<div><dt>最近检查点</dt><dd>${escapeHtml(checkpointLabel(pipeline.resilience.latestCheckpoint))}</dd></div>` : ""}
190
+ </dl>
191
+ </article>
192
+
193
+ <article class="data-card">
194
+ <div class="card-title"><h3>计算调度决策</h3><span>${escapeHtml(computeClassLabel(pipeline.compute?.classification))}</span></div>
195
+ ${renderComputeDecision(pipeline.compute)}
196
+ </article>
197
+
198
+ <article class="data-card">
199
+ <div class="card-title"><h3>可验证成果</h3><span>${formatBytes(pipeline.artifacts.totalBytes)}</span></div>
200
+ <div class="artifact-summary">
201
+ <div><div class="artifact-number">${pipeline.artifacts.fileCount}</div><p>CAP 内固定文件</p></div>
202
+ <div class="artifact-flags">
203
+ ${pipeline.artifacts.attestation ? '<span class="artifact-flag">执行签名</span>' : ""}
204
+ ${pipeline.artifacts.attestation ? '<span class="artifact-flag">CAP 2.0 签名证明</span>' : ""}
205
+ ${pipeline.artifacts.researchCard ? '<span class="artifact-flag">结构化研究卡片</span>' : ""}
206
+ ${pipeline.artifacts.verifiedConclusion ? '<span class="artifact-flag">论文级验证结论</span>' : ""}
207
+ ${pipeline.artifacts.readme ? '<span class="artifact-flag">README 快照</span>' : ""}
208
+ ${pipeline.artifacts.archive ? '<span class="artifact-flag">.cap 已生成</span>' : ""}
209
+ ${pipeline.integrityStatus === "passed" ? '<span class="artifact-flag">完整性通过</span>' : ""}
210
+ </div>
211
+ </div>
212
+ <dl class="definition-grid">
213
+ <div><dt>Artifact Digest</dt><dd class="hash" title="${escapeHtml(pipeline.artifacts.artifactDigest ?? "")}">${escapeHtml(shortHash(pipeline.artifacts.artifactDigest))}</dd></div>
214
+ <div><dt>发布状态</dt><dd>${escapeHtml(publicationLabel(pipeline.publicationStatus))}</dd></div>
215
+ <div><dt>证据解析器</dt><dd>${escapeHtml(pipeline.parser ? `${pipeline.parser.id}@${pipeline.parser.version}` : "—")}</dd></div>
216
+ <div><dt>原始证据</dt><dd>${escapeHtml(pipeline.sourceEvidence?.path ?? "—")}</dd></div>
217
+ <div><dt>单文件大小</dt><dd>${pipeline.artifacts.archive ? formatBytes(pipeline.artifacts.archive.size) : "—"}</dd></div>
218
+ <div><dt>单文件 SHA-256</dt><dd class="hash" title="${escapeHtml(pipeline.artifacts.archive?.sha256 ?? "")}">${escapeHtml(shortHash(pipeline.artifacts.archive?.sha256))}</dd></div>
219
+ <div><dt>研究卡片摘要</dt><dd class="hash" title="${escapeHtml(pipeline.researchCard?.cardDigest ?? "")}">${escapeHtml(shortHash(pipeline.researchCard?.cardDigest))}</dd></div>
220
+ </dl>
221
+ </article>
222
+
223
+ <article class="data-card wide">
224
+ <div class="card-title"><h3>执行尝试</h3><span>${pipeline.attempts.length} 次记录</span></div>
225
+ ${renderAttempts(pipeline.attempts)}
226
+ </article>
227
+
228
+ <article class="data-card wide">
229
+ <div class="card-title"><h3>完整性检查</h3><span>${completedChecks}/${pipeline.checks.length} 项通过</span></div>
230
+ <div class="checks-row">${pipeline.checks.length ? pipeline.checks.map((check) => `<span class="check-chip ${escapeHtml(check.status)}" title="${escapeHtml(check.description ?? "")}">${escapeHtml(checkLabel(check.name))}</span>`).join("") : '<span class="check-chip">尚未进入验证阶段</span>'}</div>
231
+ </article>
232
+ </div>`;
233
+ }
234
+
235
+ function renderLiveStatus(pipeline) {
236
+ if (pipeline.status !== "running") return "";
237
+ const activity = pipeline.activity ?? {};
238
+ const events = activity.events ?? [];
239
+ return `<section class="live-status health-${escapeHtml(activity.health ?? "quiet")}">
240
+ <div class="live-status-heading">
241
+ <div>
242
+ <span class="live-eyebrow">实时运行状态</span>
243
+ <h3>${escapeHtml(activity.healthLabel ?? "运行中")}</h3>
244
+ </div>
245
+ <span class="live-refresh">每 ${Number(activity.refreshIntervalSeconds ?? 4)} 秒检查一次</span>
246
+ </div>
247
+ <div class="live-current">
248
+ <span>当前动作</span>
249
+ <strong>${escapeHtml(activity.currentAction ?? "等待 Agent 输出")}</strong>
250
+ <span class="live-reason">${escapeHtml(activity.reason ?? "正在等待新的可观测事件")}</span>
251
+ </div>
252
+ ${renderResilienceStatus(pipeline.resilience)}
253
+ <div class="live-metrics">
254
+ <div><span>控制器心跳</span><strong>${activity.lastHeartbeatAt ? `${formatAge(activity.heartbeatAgeMs)}前` : "缺少记录"}</strong></div>
255
+ <div><span>最近有效活动</span><strong>${activity.lastActivityAt ? `${formatAge(activity.activityAgeMs)}前` : "尚无记录"}</strong></div>
256
+ <div><span>最近输出或日志</span><strong>${activity.lastOutputAt ? `${formatAge(activity.outputAgeMs)}前` : "尚无输出"}</strong></div>
257
+ <div><span>当前阶段已运行</span><strong>${activity.currentStageStartedAt ? formatDuration(elapsedSince(activity.currentStageStartedAt)) : "—"}</strong></div>
258
+ </div>
259
+ <div class="activity-timeline">
260
+ <div class="activity-title"><strong>最近活动</strong><span>心跳只表示控制器存活;这里的事件表示 Agent、命令或日志实际发生了变化</span></div>
261
+ ${events.length ? `<ol>${events.slice(0, 8).map(renderActivityEvent).join("")}</ol>` : '<div class="empty-list">尚未捕获到 Agent 活动,等待下一次刷新</div>'}
262
+ </div>
263
+ </section>`;
264
+ }
265
+
266
+ function renderActivityEvent(event) {
267
+ const time = event.at
268
+ ? formatDateTime(event.at)
269
+ : event.order != null
270
+ ? `旧 trace #${Number(event.order) + 1}`
271
+ : event.observedAt
272
+ ? `文件更新 ${formatDateTime(event.observedAt)}`
273
+ : "未记录时间";
274
+ return `<li class="level-${escapeHtml(event.level ?? "info")}">
275
+ <i></i>
276
+ <div><strong>${escapeHtml(event.message)}</strong><span>${escapeHtml(time)} · ${activitySourceLabel(event.source)}</span></div>
277
+ </li>`;
278
+ }
279
+
280
+ function renderResilienceStatus(resilience) {
281
+ if (!resilience?.enabled) return "";
282
+ const attempt = resilience.infrastructureAttempt ?? 1;
283
+ const limit = resilience.infrastructureAttemptLimit ?? "—";
284
+ const checkpoint = resilience.latestCheckpoint;
285
+ return `<div class="resilience-status">
286
+ <div><span>基础设施执行</span><strong>第 ${attempt}/${limit} 次</strong></div>
287
+ <div><span>中断恢复</span><strong>${Number(resilience.resumeCount ?? 0)} 次</strong></div>
288
+ <div><span>关键检查点</span><strong>每 ${Number(resilience.criticalIntervalSeconds ?? 0)} 秒</strong></div>
289
+ <div><span>最近持久化</span><strong>${checkpoint?.createdAt ? formatDateTime(checkpoint.createdAt) : "等待首个检查点"}</strong></div>
290
+ </div>`;
291
+ }
292
+
293
+ function renderComputeDecision(compute) {
294
+ if (!compute) return '<div class="empty-list">等待计算调度记录</div>';
295
+ const profile = compute.selectedProfile ?? {};
296
+ const estimate = compute.estimate ?? {};
297
+ return `<dl class="definition-grid">
298
+ <div><dt>资源分类</dt><dd>${escapeHtml(computeClassLabel(compute.classification))}</dd></div>
299
+ <div><dt>选中规格</dt><dd>${escapeHtml(profile.label ?? profile.id ?? compute.profileId ?? "—")}</dd></div>
300
+ <div><dt>执行器</dt><dd>${escapeHtml(profile.executor ?? compute.executor ?? "—")}</dd></div>
301
+ <div><dt>加速器</dt><dd>${escapeHtml(acceleratorLabel(profile.accelerator ?? compute.accelerator))}</dd></div>
302
+ <div><dt>预计时长</dt><dd>${estimate.durationMinutes == null ? "未知" : `${escapeHtml(estimate.durationMinutes)} 分钟`}</dd></div>
303
+ <div><dt>预计计算费用</dt><dd>${estimate.costUsd == null && compute.estimatedCostUsd == null ? "未知" : `$${Number(estimate.costUsd ?? compute.estimatedCostUsd).toFixed(4)}`}</dd></div>
304
+ <div class="span-two"><dt>决策理由</dt><dd>${escapeHtml(compute.rationale ?? "未记录")}</dd></div>
305
+ <div class="span-two"><dt>决策摘要</dt><dd class="hash" title="${escapeHtml(compute.decisionDigest ?? "")}">${escapeHtml(shortHash(compute.decisionDigest))}</dd></div>
306
+ </dl>`;
307
+ }
308
+
309
+ function renderStage(stage) {
310
+ const marker = stage.status === "completed" || stage.status === "skipped"
311
+ ? "✓"
312
+ : stage.status === "failed"
313
+ ? "×"
314
+ : stage.status === "cancelled"
315
+ ? "−"
316
+ : "";
317
+ const timing = stage.durationMs != null
318
+ ? formatDuration(stage.durationMs)
319
+ : stage.status === "running"
320
+ ? "正在执行"
321
+ : statusLabel(stage.status);
322
+ return `<div class="stage ${escapeHtml(stage.status)}"><span class="stage-dot">${marker}</span><strong>${escapeHtml(stage.label)}</strong><small>${escapeHtml(timing)}</small></div>`;
323
+ }
324
+
325
+ function renderComparisonCard(comparison, metric, verificationStatus) {
326
+ if (!comparison) {
327
+ return `<article class="data-card"><div class="card-title"><h3>复现结果</h3><span>${statusLabel(verificationStatus ?? "pending")}</span></div><div class="empty-list">等待证据解析和 Claim 验证</div></article>`;
328
+ }
329
+ return `<article class="data-card">
330
+ <div class="card-title"><h3>论文结果 vs. 实际运行</h3><span>${escapeHtml(comparison.metric ?? metric?.metric ?? "指标")}</span></div>
331
+ <div class="comparison">
332
+ <div><div class="metric-value">${formatMetric(comparison.reportedValue, comparison.unit)}</div><span class="metric-label">论文报告</span></div>
333
+ <div class="comparison-arrow">→</div>
334
+ <div><div class="metric-value observed">${formatMetric(comparison.observedValue, comparison.unit)}</div><span class="metric-label">Agent 实测</span></div>
335
+ </div>
336
+ <p class="comparison-note">绝对差值 <strong>${formatMetric(comparison.absoluteDifference, comparison.unit)}</strong>,参考容差 <strong>±${formatMetric(comparison.tolerance, comparison.unit)}</strong>。科学状态由整组证据与协议条件的独立评估决定:${statusLabel(verificationStatus ?? "pending")}。</p>
337
+ </article>`;
338
+ }
339
+
340
+ function renderVerifiedConclusion(conclusion) {
341
+ if (!conclusion?.text) return "";
342
+ const coverage = conclusion.coverage ?? {};
343
+ return `<article class="data-card wide verified-conclusion-card">
344
+ <div class="card-title"><h3>论文级验证结论</h3><span>${escapeHtml(conclusionStatusLabel(conclusion.overallStatus))}</span></div>
345
+ <p class="verified-conclusion-text">${escapeHtml(conclusion.text)}</p>
346
+ <p class="comparison-note">覆盖 ${formatNumber(coverage.evaluatedClaims ?? 0)}/${formatNumber(coverage.structuredClaims ?? 0)} 项结构化主张 · ${formatNumber(coverage.reproduced ?? 0)} 项已复现 · ${formatNumber(coverage.approximatelyReproduced ?? 0)} 项近似复现 · ${formatNumber(coverage.notReproduced ?? 0)} 项未复现 · ${formatNumber(coverage.inconclusive ?? 0)} 项未定论</p>
347
+ </article>`;
348
+ }
349
+
350
+ function conclusionStatusLabel(value) {
351
+ return {
352
+ tested_claims_supported: "受测主张支持",
353
+ supported: "受测主张支持",
354
+ partially_supported: "部分支持",
355
+ not_supported: "未获支持",
356
+ inconclusive: "证据不足",
357
+ }[value] ?? "等待汇总";
358
+ }
359
+
360
+ function renderAttempts(attempts) {
361
+ if (!attempts.length) return '<div class="empty-list">Agent 尚未开始执行</div>';
362
+ return `<table class="attempt-table"><thead><tr><th>阶段</th><th>科学尝试</th><th>基础设施</th><th>执行位置</th><th>开始时间</th><th>耗时</th><th>退出状态</th></tr></thead><tbody>${attempts.map((attempt) => `<tr><td>${escapeHtml(attempt.stage)}</td><td>#${attempt.attempt}</td><td>${attempt.infrastructureAttempt ? `第 ${attempt.infrastructureAttempt}/${attempt.infrastructureAttemptLimit ?? "—"} 次${attempt.resumeCount ? ` · 恢复 ${attempt.resumeCount}` : ""}` : "—"}</td><td>${escapeHtml(attempt.batchState ? `GCP ${attempt.batchState}` : attempt.executor ?? "本机")}</td><td>${formatDateTime(attempt.startedAt)}</td><td>${formatDuration(attempt.durationMs)}</td><td class="${attempt.exitCode === 0 && !attempt.timedOut ? "exit-ok" : attempt.exitCode == null && !attempt.timedOut ? "" : "exit-fail"}">${attempt.timedOut ? "超时" : attempt.exitCode == null ? "运行中" : `code ${attempt.exitCode}`}</td></tr>`).join("")}</tbody></table>`;
363
+ }
364
+
365
+ function filteredPipelines() {
366
+ const pipelines = state.data?.pipelines ?? [];
367
+ if (state.filter === "all") return pipelines;
368
+ if (state.filter === "reproduced") return pipelines.filter((item) => new Set(["reproduced", "approximately_reproduced"]).has(item.verificationStatus));
369
+ if (state.filter === "failed") return pipelines.filter((item) => item.status === "failed" || item.verificationStatus === "not_reproduced");
370
+ return pipelines.filter((item) => item.status === state.filter);
371
+ }
372
+
373
+ function displayStatus(pipeline) {
374
+ if (["running", "prepared", "failed", "cancelled"].includes(pipeline.status)) return pipeline.status;
375
+ return pipeline.verificationStatus ?? pipeline.status;
376
+ }
377
+
378
+ function setConnection(online) {
379
+ elements.connection.classList.toggle("online", online);
380
+ elements.connection.classList.toggle("offline", !online);
381
+ elements.connectionLabel.textContent = online ? "实时同步中 · 每 4 秒刷新" : "连接中断 · 自动重试";
382
+ }
383
+
384
+ function stageLabel(value) {
385
+ const match = state.data?.pipelines.flatMap((pipeline) => pipeline.stages).find((stage) => stage.id === value);
386
+ return match?.label ?? (value === "completed" ? "全部完成" : value ?? "等待中");
387
+ }
388
+
389
+ function statusLabel(value) { return statusLabels[value] ?? value ?? "未知"; }
390
+ function publicationLabel(value) { return value === "publishable" ? "可发布" : value === "draft" ? "草稿" : value ?? "—"; }
391
+ function computeClassLabel(value) { return ({ "cpu-only": "仅 CPU", "gpu-optional": "GPU 可选", "gpu-required": "必须 GPU" })[value] ?? value ?? "尚未调度"; }
392
+ function acceleratorLabel(value) { return value === "gpu" ? "GPU" : value === "cpu" ? "CPU" : value ?? "—"; }
393
+ function pricingLabel(pricing) {
394
+ if (pricing?.currency === "USD" && Number(pricing.costUsd) > 0) {
395
+ return `Agent 报告 $${Number(pricing.costUsd).toFixed(2)}`;
396
+ }
397
+ if (!pricing || (!pricing.idleCny && !pricing.peakCny)) return "当前 API 未提供可核验费用";
398
+ return `官方价估算 ¥${Number(pricing.idleCny).toFixed(2)}–¥${Number(pricing.peakCny).toFixed(2)}`;
399
+ }
400
+ function shortId(value) { return value.length > 25 ? `${value.slice(0, 20)}…` : value; }
401
+ function shortHash(value) { return value ? `${value.slice(0, 13)}…${value.slice(-8)}` : "—"; }
402
+ function safePercent(value) { return Math.max(0, Math.min(100, Math.round(Number(value) || 0))); }
403
+
404
+ function compactNumber(value) {
405
+ const number = Number(value ?? 0);
406
+ return new Intl.NumberFormat("zh-CN", { notation: number >= 10_000 ? "compact" : "standard", maximumFractionDigits: 1 }).format(number);
407
+ }
408
+
409
+ function usageTokenValue(usage) {
410
+ if (usage?.availability?.status === "unavailable") return "未取得";
411
+ const suffix = usage?.availability?.status === "partial" ? "+" : "";
412
+ return `${compactNumber(usage?.totalTokens)}${suffix}`;
413
+ }
414
+
415
+ function usageTokenLabel(usage) {
416
+ return usage?.availability?.status === "unavailable"
417
+ ? "Token 未取得"
418
+ : `${usageTokenValue(usage)} tokens`;
419
+ }
420
+
421
+ function formatNumber(value) { return new Intl.NumberFormat("zh-CN").format(Number(value ?? 0)); }
422
+
423
+ function formatAge(value) {
424
+ if (value == null || !Number.isFinite(Number(value))) return "未知时间";
425
+ const seconds = Math.max(0, Math.round(Number(value) / 1000));
426
+ if (seconds < 5) return "刚刚";
427
+ if (seconds < 60) return `${seconds} 秒`;
428
+ const minutes = Math.floor(seconds / 60);
429
+ if (minutes < 60) return `${minutes} 分钟`;
430
+ const hours = Math.floor(minutes / 60);
431
+ if (hours < 24) return `${hours} 小时 ${minutes % 60} 分钟`;
432
+ return `${Math.floor(hours / 24)} 天 ${hours % 24} 小时`;
433
+ }
434
+
435
+ function elapsedSince(value) {
436
+ const start = Date.parse(value);
437
+ const end = Date.parse(state.data?.generatedAt ?? new Date().toISOString());
438
+ return Number.isFinite(start) && Number.isFinite(end) ? Math.max(0, end - start) : null;
439
+ }
440
+
441
+ function activitySourceLabel(value) {
442
+ return value === "pipeline-monitor" ? "流水线事件" : value === "agent-trace" ? "Agent trace" : value ?? "执行记录";
443
+ }
444
+
445
+ function formatDuration(value) {
446
+ if (value == null || !Number.isFinite(Number(value))) return "—";
447
+ const seconds = Math.max(0, Math.round(Number(value) / 1000));
448
+ if (seconds < 60) return `${seconds} 秒`;
449
+ const minutes = Math.floor(seconds / 60);
450
+ if (minutes < 60) return `${minutes} 分 ${seconds % 60} 秒`;
451
+ const hours = Math.floor(minutes / 60);
452
+ return `${hours} 小时 ${minutes % 60} 分`;
453
+ }
454
+
455
+ function formatDateTime(value) {
456
+ if (!value) return "—";
457
+ const date = new Date(value);
458
+ if (Number.isNaN(date.getTime())) return "—";
459
+ return new Intl.DateTimeFormat("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false }).format(date);
460
+ }
461
+
462
+ function formatMetric(value, unit) {
463
+ const number = Number(value);
464
+ if (!Number.isFinite(number)) return "—";
465
+ const formatted = new Intl.NumberFormat("zh-CN", { maximumFractionDigits: 3 }).format(number);
466
+ return unit === "percentage_points" ? `${formatted}%` : `${formatted} ${unit ?? ""}`.trim();
467
+ }
468
+
469
+ function formatBytes(value) {
470
+ const bytes = Number(value ?? 0);
471
+ if (bytes < 1024) return `${bytes} B`;
472
+ if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`;
473
+ return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
474
+ }
475
+
476
+ function resourceLabel(environment) {
477
+ const parts = [];
478
+ if (environment.cpus) parts.push(`${environment.cpus} CPU`);
479
+ if (environment.memoryGb) parts.push(`${environment.memoryGb} GB`);
480
+ if (environment.gpu && environment.gpu !== "none") parts.push(`GPU ${environment.gpu}`);
481
+ else parts.push("无 GPU");
482
+ return escapeHtml(parts.join(" · "));
483
+ }
484
+
485
+ function cloudResourceLabel(cloud) {
486
+ return [cloud.service === "batch" ? "GCP Batch" : cloud.service, cloud.region, cloud.machineType, cloud.provisioningModel]
487
+ .filter(Boolean)
488
+ .join(" · ");
489
+ }
490
+
491
+ function cloudCleanupLabel(resources) {
492
+ if (!resources?.cleanupPolicy?.deleteBatchJobs || !resources?.cleanupPolicy?.deleteStagingObjects) return "按调试参数保留";
493
+ if (resources.batchJobDeletionRequested === true && resources.stagingObjectsDeleted === true) return "清理请求已完成";
494
+ if (resources.batchJobDeletionRequested === false || resources.stagingObjectsDeleted === false) return "清理失败,需人工检查";
495
+ return "完成后自动清理";
496
+ }
497
+
498
+ function resilienceLabel(resilience) {
499
+ if (!resilience?.enabled) return "未启用";
500
+ return `已启用 · 关键 ${Number(resilience.criticalIntervalSeconds ?? 0)} 秒 · 完整 ${Number(resilience.fullIntervalSeconds ?? 0)} 秒`;
501
+ }
502
+
503
+ function checkpointLabel(checkpoint) {
504
+ const kind = checkpoint.kind === "full" ? "完整" : "关键";
505
+ return `${kind} · ${formatDateTime(checkpoint.createdAt)} · ${formatBytes(checkpoint.bytes)}`;
506
+ }
507
+
508
+ function checkLabel(value) {
509
+ const labels = {
510
+ run_record_present: "运行记录",
511
+ claim_binding: "Claim 绑定",
512
+ experiment_binding: "实验绑定",
513
+ contract_digest: "契约摘要",
514
+ evidence_digest: "证据摘要",
515
+ metric_binding: "指标绑定",
516
+ repository_commit: "仓库版本",
517
+ execution_succeeded: "执行成功",
518
+ no_scientific_change: "科学语义未改",
519
+ scientific_change_policy: "科学语义变更策略",
520
+ source_patch_accounted: "源码变更可追溯",
521
+ workspace_mutations_allowed: "工作区变更合规",
522
+ parser_evidence_declared: "解析证据声明",
523
+ };
524
+ if (labels[value]) return labels[value];
525
+ if (value?.startsWith("execution_record:")) return `记录 ${value.split(":")[1]}`;
526
+ if (value?.startsWith("command_fragment:")) return `命令 ${value.slice("command_fragment:".length)}`;
527
+ return value ?? "检查项";
528
+ }
529
+
530
+ function escapeHtml(value) {
531
+ return String(value ?? "")
532
+ .replaceAll("&", "&amp;")
533
+ .replaceAll("<", "&lt;")
534
+ .replaceAll(">", "&gt;")
535
+ .replaceAll('"', "&quot;")
536
+ .replaceAll("'", "&#039;");
537
+ }
538
+
539
+ elements.runList.addEventListener("click", (event) => {
540
+ const button = event.target.closest("[data-run-id]");
541
+ if (!button) return;
542
+ state.selectedId = button.dataset.runId;
543
+ renderRunList();
544
+ renderDetail();
545
+ });
546
+
547
+ elements.filters.addEventListener("click", (event) => {
548
+ const button = event.target.closest("[data-filter]");
549
+ if (!button) return;
550
+ state.filter = button.dataset.filter;
551
+ for (const item of elements.filters.querySelectorAll(".filter")) item.classList.toggle("active", item === button);
552
+ const visible = filteredPipelines();
553
+ if (!visible.some((item) => item.id === state.selectedId)) state.selectedId = visible[0]?.id ?? state.data.pipelines[0]?.id ?? null;
554
+ renderRunList();
555
+ renderDetail();
556
+ });
557
+
558
+ elements.refresh.addEventListener("click", refresh);
559
+ refresh();
560
+ setInterval(refresh, 4_000);
@@ -0,0 +1,85 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="color-scheme" content="light" />
7
+ <title>CiteArk Agent · 自主复现流水线</title>
8
+ <meta name="description" content="查看 CiteArk Agent 论文复现流水线的进度、心跳、当前动作、异常预警、耗时、Token、验证结果与 CAP 产物。" />
9
+ <link rel="stylesheet" href="/dashboard.css" />
10
+ <script src="/dashboard.js" defer></script>
11
+ </head>
12
+ <body>
13
+ <header class="topbar">
14
+ <div class="brand" aria-label="CiteArk Agent">
15
+ <span class="brand-mark" aria-hidden="true">C</span>
16
+ <span>CiteArk <strong>Agent</strong></span>
17
+ </div>
18
+ <div class="topbar-meta">
19
+ <span class="connection"><i></i><span id="connection-label">正在连接流水线</span></span>
20
+ <button class="icon-button" id="refresh-button" type="button" aria-label="立即刷新">↻</button>
21
+ </div>
22
+ </header>
23
+
24
+ <main class="page-shell">
25
+ <section class="page-heading">
26
+ <div>
27
+ <p class="eyebrow">AUTONOMOUS REPRODUCTION OBSERVATORY</p>
28
+ <h1>自主复现流水线</h1>
29
+ <p class="subtitle">从研究结构化到可验证产物,实时查看当前动作、心跳和异常预警。</p>
30
+ </div>
31
+ <div class="updated-at" id="updated-at">尚未同步</div>
32
+ </section>
33
+
34
+ <section class="summary-grid" aria-label="流水线总览">
35
+ <article class="summary-card">
36
+ <span class="summary-label">流水线</span>
37
+ <strong class="summary-value" id="total-runs">—</strong>
38
+ <span class="summary-note" id="running-runs">等待数据</span>
39
+ </article>
40
+ <article class="summary-card">
41
+ <span class="summary-label">严格判定率</span>
42
+ <strong class="summary-value mint" id="decisive-rate">—</strong>
43
+ <span class="summary-note" id="reproduced-runs">以已评估结果为分母</span>
44
+ </article>
45
+ <article class="summary-card">
46
+ <span class="summary-label">累计耗时</span>
47
+ <strong class="summary-value" id="total-duration">—</strong>
48
+ <span class="summary-note">所有可观测阶段</span>
49
+ </article>
50
+ <article class="summary-card accent-card">
51
+ <span class="summary-label">Token 总量</span>
52
+ <strong class="summary-value" id="total-tokens">—</strong>
53
+ <span class="summary-note" id="total-cost">模型费用待同步</span>
54
+ </article>
55
+ </section>
56
+
57
+ <section class="workspace">
58
+ <aside class="run-panel">
59
+ <div class="panel-heading">
60
+ <div>
61
+ <span class="section-kicker">运行队列</span>
62
+ <h2>复现任务</h2>
63
+ </div>
64
+ <span class="count-pill" id="run-count">0</span>
65
+ </div>
66
+ <div class="filters" id="filters" role="group" aria-label="筛选运行状态">
67
+ <button class="filter active" type="button" data-filter="all">全部</button>
68
+ <button class="filter" type="button" data-filter="running">运行中</button>
69
+ <button class="filter" type="button" data-filter="reproduced">已复现</button>
70
+ <button class="filter" type="button" data-filter="failed">失败</button>
71
+ </div>
72
+ <div class="run-list" id="run-list" aria-live="polite"></div>
73
+ </aside>
74
+
75
+ <section class="detail-panel" id="detail-panel" aria-live="polite">
76
+ <div class="empty-state">
77
+ <div class="empty-symbol">⌁</div>
78
+ <h2>正在读取流水线</h2>
79
+ <p>运行记录加载后,这里会显示当前动作、心跳、近期事件、阶段耗时与验证产物。</p>
80
+ </div>
81
+ </section>
82
+ </section>
83
+ </main>
84
+ </body>
85
+ </html>