@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,277 @@
1
+ import { writeFile } from "node:fs/promises";
2
+
3
+ import { sha256Value, writeJson } from "../util.mjs";
4
+ import { buildVerifiedConclusion } from "./verified-conclusion.mjs";
5
+ import { primaryComparison, primaryMetric } from "../records/views.mjs";
6
+
7
+ export async function renderResearchCard({
8
+ research,
9
+ contract,
10
+ run,
11
+ result,
12
+ metrics,
13
+ outputs = null,
14
+ integrity,
15
+ assessment,
16
+ runnerAudit,
17
+ verifiedConclusion,
18
+ computeDecision = run?.computeDecision ?? null,
19
+ generatedAt = assessment?.assessedAt ?? run?.finishedAt ?? new Date().toISOString(),
20
+ jsonPath,
21
+ markdownPath,
22
+ }) {
23
+ const selectedClaim = research.claims.find((item) => item.versionId === contract.research.claimVersionId)
24
+ ?? research.claims.find((item) => item.id === contract.research.claimId);
25
+ const selectedExperiment = research.experiments.find((item) => item.versionId === contract.research.experimentVersionId)
26
+ ?? research.experiments.find((item) => item.id === contract.research.experimentId);
27
+ const conclusion = verifiedConclusion ?? buildVerifiedConclusion({
28
+ research,
29
+ verifications: [{ contract, assessment, metrics, integrity }],
30
+ generatedAt,
31
+ });
32
+ const body = {
33
+ schemaVersion: "1.0",
34
+ kind: "citeark.research-card",
35
+ generatedAt,
36
+ work: research.work,
37
+ sources: research.sources,
38
+ claims: research.claims.map((claim) => ({
39
+ id: claim.id,
40
+ versionId: claim.versionId,
41
+ statement: claim.statement,
42
+ type: claim.type,
43
+ sourceLocator: claim.sourceLocator,
44
+ reportedMeasurements: claim.reportedMeasurements,
45
+ reproduction: claim.reproduction,
46
+ selected: claim.id === selectedClaim?.id,
47
+ })),
48
+ experiments: research.experiments.map((experiment) => ({
49
+ id: experiment.id,
50
+ versionId: experiment.versionId,
51
+ title: experiment.title,
52
+ claimIds: experiment.claimIds,
53
+ reproductionLevel: experiment.reproductionLevel,
54
+ reconstructionFidelity: experiment.reconstructionFidelity
55
+ ?? (experiment.implementationOrigin === "citeark_reconstruction" ? "approximate" : "faithful"),
56
+ implementationOrigin: experiment.implementationOrigin ?? experiment.repository?.implementationOrigin ?? "official",
57
+ repository: experiment.repository,
58
+ selected: experiment.id === selectedExperiment?.id,
59
+ })),
60
+ latestExecution: {
61
+ runId: run.runId,
62
+ status: run.status,
63
+ outcome: {
64
+ status: result.execution?.status ?? "unknown",
65
+ summary: result.execution?.summary ?? null,
66
+ failure: result.execution?.failure ?? null,
67
+ },
68
+ selectedClaimId: selectedClaim?.id ?? contract.research.claimId,
69
+ selectedExperimentId: selectedExperiment?.id ?? contract.research.experimentId,
70
+ agent: run.agent ?? { runtime: run.runtime ?? null },
71
+ computeDecision,
72
+ metric: primaryMetric(metrics),
73
+ measurements: metrics.measurements ?? null,
74
+ outputs: outputs?.outputs ?? [],
75
+ integrityStatus: integrity.status,
76
+ verificationStatus: assessment.verificationStatus,
77
+ verdict: assessment.verdict,
78
+ comparison: primaryComparison(assessment),
79
+ measurementAssessments: assessment.measurementAssessments ?? null,
80
+ limitations: result.limitations ?? [],
81
+ executionAudit: runnerAudit?.audit ? {
82
+ status: runnerAudit.audit.status,
83
+ auditDigest: runnerAudit.audit.auditDigest,
84
+ captureBoundary: runnerAudit.audit.captureBoundary,
85
+ commandRecordCount: runnerAudit.audit.commandRecordCount,
86
+ } : null,
87
+ finishedAt: run.finishedAt ?? null,
88
+ },
89
+ verifiedConclusion: conclusion,
90
+ license: research.license,
91
+ provenance: research.provenance,
92
+ canonicalRecords: {
93
+ sourceWork: "https://citeark.com/cap/record-types/entity/2.0",
94
+ claim: "https://citeark.com/cap/record-types/assertion/2.0",
95
+ experiment: "https://citeark.com/cap/record-types/entity/2.0",
96
+ execution: "https://citeark.com/cap/record-types/activity/2.0",
97
+ evidence: "https://citeark.com/cap/record-types/entity/2.0",
98
+ assessment: "https://citeark.com/cap/record-types/assertion/2.0",
99
+ traceBlobRole: "execution-trace",
100
+ },
101
+ renderingPolicy: {
102
+ canonical: false,
103
+ verificationAuthority: "primaryAssessment CAP Record",
104
+ metricExtraction: "deterministic-external-parser",
105
+ commandProvenance: "runner-captured-runtime-json-stream",
106
+ description: "This card is derived from structured research, runner-captured runtime command records, and deterministic verification records. Private reasoning is excluded; command and output records are retained verbatim unless likely credentials require redaction, with hashes preserving the original byte identity.",
107
+ },
108
+ };
109
+ const card = { ...body, cardDigest: `sha256:${sha256Value(body)}` };
110
+ const markdown = renderResearchCardMarkdown(card);
111
+ if (jsonPath) await writeJson(jsonPath, card);
112
+ if (markdownPath) await writeFile(markdownPath, markdown, "utf8");
113
+ return { card, markdown };
114
+ }
115
+
116
+ export function renderResearchCardMarkdown(card) {
117
+ const execution = card.latestExecution;
118
+ const selectedClaim = card.claims.find((item) => item.selected);
119
+ const selectedExperiment = card.experiments.find((item) => item.selected);
120
+ const comparison = execution.comparison;
121
+ const compute = execution.computeDecision;
122
+ const lines = [
123
+ `# ${markdownText(card.work.title)}`,
124
+ "",
125
+ "> This is a research card snapshot automatically generated from CiteArk structured research, real execution records, and the verification engine. The machine-readable files are the source of truth; this page does not replace the verification conclusion.",
126
+ "",
127
+ markdownText(card.work.abstract),
128
+ "",
129
+ "## Paper-Level Verification Conclusion",
130
+ "",
131
+ markdownText(card.verifiedConclusion.text),
132
+ "",
133
+ `- Conclusion coverage: ${card.verifiedConclusion.coverage.evaluatedClaims}/${card.verifiedConclusion.coverage.structuredClaims} structured claims verified`,
134
+ `- Evidence digest: ${markdownCode(card.verifiedConclusion.conclusionDigest)}`,
135
+ "",
136
+ "## Current Reproduction Conclusion",
137
+ "",
138
+ `- Verification status: ${verificationLabel(execution.verificationStatus)}`,
139
+ `- Integrity status: ${execution.integrityStatus === "passed" ? "passed" : "failed"}`,
140
+ `- Run: \`${markdownCode(execution.runId)}\``,
141
+ `- Execution outcome: ${markdownText(execution.outcome?.status ?? "unknown")}`,
142
+ `- Runner audit: ${markdownText(execution.executionAudit?.status ?? "unavailable")}; ${execution.executionAudit?.commandRecordCount ?? 0} command records`,
143
+ `- Claim: ${markdownText(selectedClaim?.statement ?? execution.selectedClaimId)}`,
144
+ `- Experiment: ${markdownText(selectedExperiment?.title ?? execution.selectedExperimentId)}`,
145
+ `- Implementation origin: ${markdownText(selectedExperiment?.implementationOrigin ?? "unknown")}`,
146
+ `- Reconstruction fidelity: ${markdownText(selectedExperiment?.reconstructionFidelity ?? "not recorded")}`,
147
+ ];
148
+ if (execution.outcome?.summary) {
149
+ lines.push(`- Execution summary: ${markdownText(execution.outcome.summary)}`);
150
+ }
151
+ if (execution.outcome?.failure) {
152
+ const failure = execution.outcome.failure;
153
+ lines.push(
154
+ `- Failure: ${markdownText(failure.category)} during ${markdownText(failure.stage)}; ${failure.retryable ? "potentially retryable" : "not retryable without changed inputs or conditions"}`,
155
+ `- Failure reason: ${markdownText(failure.reason)}`,
156
+ );
157
+ }
158
+ if (comparison && typeof comparison.observedValue === "number" && Number.isFinite(comparison.observedValue)) {
159
+ lines.push(
160
+ `- Metric: ${markdownText(comparison.metric)}, reported ${formatMetric(comparison.reportedValue, comparison.unit)}, observed ${formatMetric(comparison.observedValue, comparison.unit)}`,
161
+ `- Absolute difference: ${formatMetric(comparison.absoluteDifference, comparison.unit)}; declared tolerance: ±${formatMetric(comparison.tolerance, comparison.unit)}`,
162
+ );
163
+ } else if (comparison) {
164
+ lines.push(`- Comparable metric: unavailable; claim assessment remains ${verificationLabel(execution.verificationStatus)}`);
165
+ }
166
+ if (Array.isArray(execution.measurementAssessments) && execution.measurementAssessments.length > 1) {
167
+ lines.push("- Measurement coverage:");
168
+ for (const item of execution.measurementAssessments) {
169
+ const detail = item.comparison;
170
+ lines.push(
171
+ ` - ${markdownCode(item.measurementId)}: ${verificationLabel(item.verificationStatus)}${
172
+ detail && typeof detail.observedValue === "number"
173
+ ? `; reported ${formatMetric(detail.reportedValue, detail.unit)}, observed ${formatMetric(detail.observedValue, detail.unit)}`
174
+ : ""
175
+ }`,
176
+ );
177
+ }
178
+ }
179
+ if (Array.isArray(execution.outputs) && execution.outputs.length) {
180
+ lines.push("- Scientific outputs:");
181
+ for (const output of execution.outputs) {
182
+ lines.push(
183
+ ` - ${markdownCode(output.id)}: ${markdownText(output.kind)} · ${markdownText(output.role)} · ${markdownText(output.mediaType)} · ${markdownText(output.description)} · ${markdownCode(output.digest)}`,
184
+ );
185
+ }
186
+ }
187
+ lines.push("", "## Research Claims", "");
188
+ if (card.claims.length) {
189
+ lines.push("| Claim | Type | Reported Metrics | Source |", "| --- | --- | --- | --- |");
190
+ for (const claim of card.claims) {
191
+ const measurements = claim.reportedMeasurements.map((item) => `${item.metric}=${formatMetric(item.value, item.unit)}`).join("; ") || "—";
192
+ lines.push(`| ${tableCell(claim.statement)} | ${tableCell(claim.type)} | ${tableCell(measurements)} | ${tableCell(`${claim.sourceLocator.sourceId}:${claim.sourceLocator.locator}`)} |`);
193
+ }
194
+ } else {
195
+ lines.push("No structured claims yet.");
196
+ }
197
+ lines.push("", "## Experiments", "", "| Experiment | Implementation Origin | Fidelity | Reproduction Level | Pinned Base |", "| --- | --- | --- | --- | --- |");
198
+ for (const experiment of card.experiments) {
199
+ lines.push(`| ${tableCell(experiment.title)} | ${tableCell(experiment.implementationOrigin)} | ${tableCell(experiment.reconstructionFidelity)} | ${tableCell(experiment.reproductionLevel)} | ${tableCell(shortDigest(experiment.repository.commit))} |`);
200
+ }
201
+ lines.push("", "## Compute Scheduling and Execution Origin", "");
202
+ if (compute) {
203
+ lines.push(
204
+ `- Resource classification: ${computeClassLabel(compute.requirement?.classification)}`,
205
+ `- Selected profile: ${markdownText(compute.selectedProfile?.label ?? compute.selectedProfile?.id ?? "unknown")}`,
206
+ `- Executor: ${markdownText(compute.selectedProfile?.executor ?? "unknown")}`,
207
+ `- Resources: ${resourceLabel(compute.environment)}`,
208
+ `- Estimated cost: ${compute.estimate?.costUsd === null || compute.estimate?.costUsd === undefined ? "unknown" : `${compute.estimate.costUsd} USD`}`,
209
+ `- Scheduling rationale: ${markdownText(compute.rationale)}`,
210
+ );
211
+ } else {
212
+ lines.push("- No compute scheduling record is available; this run cannot be presented as a complete CAP 2.0 workflow.");
213
+ }
214
+ lines.push(
215
+ `- Agent: ${markdownText(execution.agent?.runtime ?? "unknown")} / ${markdownText(execution.agent?.model ?? "not recorded")}`,
216
+ `- Execution finished: ${markdownText(execution.finishedAt ?? "not recorded")}`,
217
+ "",
218
+ "## Sources and Licenses",
219
+ "",
220
+ );
221
+ for (const source of card.sources) lines.push(`- ${markdownText(source.kind)}: ${markdownText(source.uri)}${source.version ? ` (${markdownText(source.version)})` : ""}`);
222
+ lines.push(
223
+ `- Paper license: ${markdownText(card.license.paper)}`,
224
+ `- Code license: ${markdownText(card.license.code)}`,
225
+ `- Execution allowed: ${card.license.executionAllowed ? "yes" : "no"}`,
226
+ "",
227
+ "## Limitations",
228
+ "",
229
+ );
230
+ if (execution.limitations.length) lines.push(...execution.limitations.map((item) => `- ${markdownText(item)}`));
231
+ else lines.push("- This execution reported no additional limitations; review together with the original paper, protocol, and underlying evidence.");
232
+ lines.push(
233
+ "",
234
+ "## Verifiable Records",
235
+ "",
236
+ ...Object.entries(card.canonicalRecords).map(([name, target]) => `- ${name}: \`${target}\``),
237
+ `- Evidence boundary: metrics are parsed outside the execution Agent; typed scientific outputs are independently hashed by the runner; command requests and runtime output come from the runner-captured stream and are bound to the signed CAP.`,
238
+ "",
239
+ `Card digest: \`${card.cardDigest}\``,
240
+ "",
241
+ );
242
+ return lines.join("\n");
243
+ }
244
+
245
+ function verificationLabel(value) {
246
+ return { reproduced: "reproduced", approximately_reproduced: "approximately reproduced", not_reproduced: "not reproduced", inconclusive: "inconclusive" }[value] ?? value ?? "unknown";
247
+ }
248
+
249
+ function computeClassLabel(value) {
250
+ return { "cpu-only": "CPU only", "gpu-optional": "GPU optional", "gpu-required": "GPU required" }[value] ?? value ?? "unknown";
251
+ }
252
+
253
+ function resourceLabel(environment = {}) {
254
+ return `${environment.cpus ?? "?"} CPU · ${environment.memoryGb ?? "?"} GB memory · ${environment.gpu === "all" ? "GPU" : "no GPU"}`;
255
+ }
256
+
257
+ function formatMetric(value, unit) {
258
+ if (typeof value !== "number" || !Number.isFinite(value)) return "—";
259
+ const formatted = new Intl.NumberFormat("en-US", { maximumFractionDigits: 6 }).format(value);
260
+ return unit === "percentage_points" ? `${formatted}%` : `${formatted} ${unit ?? ""}`.trim();
261
+ }
262
+
263
+ function shortDigest(value) {
264
+ return value?.length > 16 ? `${value.slice(0, 12)}…` : value ?? "—";
265
+ }
266
+
267
+ function tableCell(value) {
268
+ return markdownText(value).replaceAll("|", "\\|").replaceAll(/\r?\n/g, " ");
269
+ }
270
+
271
+ function markdownText(value) {
272
+ return String(value ?? "").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
273
+ }
274
+
275
+ function markdownCode(value) {
276
+ return String(value ?? "").replaceAll("`", "ˋ");
277
+ }
@@ -0,0 +1,143 @@
1
+ import { sha256Value } from "../util.mjs";
2
+ import { primaryComparison, primaryMetric } from "../records/views.mjs";
3
+
4
+ const MAX_STATEMENT_CHARACTERS = 72;
5
+
6
+ export function buildVerifiedConclusion({
7
+ research,
8
+ verifications = [],
9
+ generatedAt = new Date().toISOString(),
10
+ }) {
11
+ const claimsById = new Map(research.claims.map((claim) => [claim.id, claim]));
12
+ const claimsByVersion = new Map(
13
+ research.claims
14
+ .filter((claim) => claim.versionId)
15
+ .map((claim) => [claim.versionId, claim]),
16
+ );
17
+ const records = verifications
18
+ .map((verification) => normalizeVerification(verification, claimsById, claimsByVersion))
19
+ .filter(Boolean);
20
+ const evaluatedClaimIds = new Set(records.map((record) => record.claimId));
21
+ const counts = {
22
+ structuredClaims: research.claims.length,
23
+ evaluatedClaims: evaluatedClaimIds.size,
24
+ verificationRecords: records.length,
25
+ reproduced: records.filter((record) => record.verificationStatus === "reproduced").length,
26
+ approximatelyReproduced: records.filter((record) => record.verificationStatus === "approximately_reproduced").length,
27
+ notReproduced: records.filter((record) => record.verificationStatus === "not_reproduced").length,
28
+ inconclusive: records.filter((record) => record.verificationStatus === "inconclusive").length,
29
+ };
30
+ counts.unevaluatedClaims = Math.max(0, counts.structuredClaims - counts.evaluatedClaims);
31
+ const overallStatus = conclusionStatus(counts);
32
+ const body = {
33
+ schemaVersion: "0.1",
34
+ kind: "citeark.verified-conclusion",
35
+ workId: research.work.id,
36
+ generatedAt,
37
+ overallStatus,
38
+ text: conclusionParagraph({ title: research.work.title, counts, records, overallStatus }),
39
+ coverage: counts,
40
+ basis: records.map(({ statement, ...record }) => record),
41
+ policy: {
42
+ evidenceGrounded: true,
43
+ claimUniverse: "compiler-structured",
44
+ paperWideCoverageEstablished: false,
45
+ maximumDisplayedClaimsPerStatus: 1,
46
+ description: "This paragraph summarizes only compiler-structured claims and signed scientific assessment records. It is never a paper-wide validity judgment, and claims without executions are not counted as supported.",
47
+ },
48
+ };
49
+ return { ...body, conclusionDigest: `sha256:${sha256Value(body)}` };
50
+ }
51
+
52
+ function normalizeVerification(verification, claimsById, claimsByVersion) {
53
+ const assessment = verification?.assessment ?? verification;
54
+ if (!assessment || typeof assessment !== "object") return null;
55
+ const contract = verification?.contract ?? {};
56
+ const claim = claimsByVersion.get(assessment.claimVersionId ?? contract.research?.claimVersionId)
57
+ ?? claimsById.get(verification?.claimId ?? contract.research?.claimId);
58
+ if (!claim) return null;
59
+ const comparison = primaryComparison(assessment) ?? primaryMetric(verification?.metrics);
60
+ return {
61
+ claimId: claim.id,
62
+ claimVersionId: claim.versionId ?? assessment.claimVersionId ?? null,
63
+ statement: claim.statement,
64
+ verificationStatus: normalizedStatus(assessment.verificationStatus),
65
+ assessmentDigest: assessment.assessmentDigest ?? null,
66
+ integrityStatus: assessment.integrityStatus ?? verification?.integrity?.status ?? null,
67
+ comparison: comparison ? {
68
+ metric: comparison.metric ?? null,
69
+ unit: comparison.unit ?? null,
70
+ reportedValue: finiteOrNull(comparison.reportedValue),
71
+ observedValue: finiteOrNull(comparison.observedValue ?? comparison.value),
72
+ tolerance: finiteOrNull(comparison.tolerance),
73
+ absoluteDifference: finiteOrNull(comparison.absoluteDifference),
74
+ } : null,
75
+ };
76
+ }
77
+
78
+ function conclusionStatus(counts) {
79
+ const supported = counts.reproduced + counts.approximatelyReproduced;
80
+ if (supported > 0 && counts.notReproduced === 0 && counts.inconclusive === 0 && counts.unevaluatedClaims === 0) {
81
+ return "tested_claims_supported";
82
+ }
83
+ if (supported > 0) return "partially_supported";
84
+ if (counts.notReproduced > 0) return "not_supported";
85
+ return "inconclusive";
86
+ }
87
+
88
+ function conclusionParagraph({ title, counts, records, overallStatus }) {
89
+ if (!records.length) {
90
+ return `"${clean(title)}" has no verifiable claim execution records yet, so the paper's conclusions can neither be confirmed nor refuted at this stage.`;
91
+ }
92
+ const coverage = `CiteArk evaluated ${counts.evaluatedClaims} of ${counts.structuredClaims} structured claims`;
93
+ const tally = `with ${counts.reproduced} reproduced, ${counts.approximatelyReproduced} approximately reproduced, ${counts.notReproduced} not reproduced, and ${counts.inconclusive} inconclusive`;
94
+ const evidence = [
95
+ evidenceSentence(records, "reproduced", "was reproduced"),
96
+ evidenceSentence(records, "approximately_reproduced", "was approximately reproduced"),
97
+ evidenceSentence(records, "not_reproduced", "was not reproduced"),
98
+ evidenceSentence(records, "inconclusive", "lacks sufficient evidence for a conclusion"),
99
+ ].filter(Boolean).join("; ");
100
+ const ending = {
101
+ tested_claims_supported: "Therefore, within the pinned code, data, and execution environment, all claims structured and evaluated in this artifact were reproduced. This supports only those tested claims and does not establish that every material claim in the paper was covered.",
102
+ partially_supported: "Therefore, the execution evidence supports some tested structured claims, while untested, inconclusive, or inconsistent claims remain unsupported by this artifact. This is not a paper-wide validity judgment.",
103
+ not_supported: "Therefore, this execution did not support at least one tested structured claim. This finding applies to the recorded protocol and conditions, rather than serving as a paper-wide verdict.",
104
+ inconclusive: "Therefore, the available execution evidence is insufficient to confirm or refute the tested structured claims, and no paper-wide conclusion is warranted.",
105
+ }[overallStatus];
106
+ return `"${clean(title)}": ${coverage}, ${tally}: ${evidence}. ${ending}`;
107
+ }
108
+
109
+ function evidenceSentence(records, status, suffix) {
110
+ const selected = records.filter((record) => record.verificationStatus === status).slice(0, 1);
111
+ if (!selected.length) return null;
112
+ return selected.map((record) => {
113
+ const metric = metricComparison(record.comparison);
114
+ return `"${shortStatement(record.statement)}"${metric} ${suffix}`;
115
+ }).join("; ");
116
+ }
117
+
118
+ function metricComparison(comparison) {
119
+ if (!comparison || comparison.reportedValue === null || comparison.observedValue === null) return "";
120
+ return ` (reported ${formatMetric(comparison.reportedValue, comparison.unit)}, observed ${formatMetric(comparison.observedValue, comparison.unit)})`;
121
+ }
122
+
123
+ function formatMetric(value, unit) {
124
+ const formatted = new Intl.NumberFormat("en-US", { maximumFractionDigits: 6 }).format(value);
125
+ return unit === "percentage_points" ? `${formatted}%` : `${formatted}${unit ? ` ${unit}` : ""}`;
126
+ }
127
+
128
+ function shortStatement(value) {
129
+ const text = clean(value).replace(/[。.!!]+$/u, "");
130
+ return text.length > MAX_STATEMENT_CHARACTERS ? `${text.slice(0, MAX_STATEMENT_CHARACTERS - 1)}…` : text;
131
+ }
132
+
133
+ function clean(value) {
134
+ return String(value ?? "").replaceAll(/\s+/g, " ").trim();
135
+ }
136
+
137
+ function normalizedStatus(value) {
138
+ return new Set(["reproduced", "approximately_reproduced", "not_reproduced", "inconclusive"]).has(value) ? value : "inconclusive";
139
+ }
140
+
141
+ function finiteOrNull(value) {
142
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
143
+ }
@@ -0,0 +1,183 @@
1
+ import { lstat } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import {
5
+ CiteArkError,
6
+ pathExists,
7
+ safeRelativePath,
8
+ sha256File,
9
+ } from "../util.mjs";
10
+
11
+ export const INLINE_OUTPUT_MAX_BYTES = 64 * 1024 * 1024;
12
+
13
+ const OUTPUT_KINDS = new Set([
14
+ "table",
15
+ "figure",
16
+ "dataset",
17
+ "model",
18
+ "checkpoint",
19
+ "text",
20
+ "archive",
21
+ "audio",
22
+ "video",
23
+ "other",
24
+ ]);
25
+ const OUTPUT_ROLES = new Set(["primary", "supporting"]);
26
+ const WITHHELD_OUTPUT_KINDS = new Set(["model", "checkpoint", "dataset"]);
27
+
28
+ export async function buildOutputRegistry({
29
+ result,
30
+ outputDirectory,
31
+ runId,
32
+ contractDigest,
33
+ capturedAt,
34
+ inlineMaximumBytes = INLINE_OUTPUT_MAX_BYTES,
35
+ }) {
36
+ const declaredOutputs = Array.isArray(result?.outputs) ? result.outputs : [];
37
+ const evidencePaths = new Set(
38
+ (Array.isArray(result?.evidence) ? result.evidence : [])
39
+ .map((item) => item?.path)
40
+ .filter((value) => typeof value === "string"),
41
+ );
42
+ const outputs = await Promise.all(declaredOutputs.map(async (item) => {
43
+ const sourcePath = item.path;
44
+ const source = resolveOutputPath(outputDirectory, sourcePath);
45
+ const [stat, sha256] = await Promise.all([lstat(source), sha256File(source)]);
46
+ if (!stat.isFile()) throw new CiteArkError(`科学输出不是常规文件:${sourcePath}`);
47
+ const withheld = WITHHELD_OUTPUT_KINDS.has(item.kind);
48
+ const external = !withheld && stat.size > inlineMaximumBytes;
49
+ return {
50
+ id: item.id,
51
+ ...(item.scientificObjectId ? { scientificObjectId: item.scientificObjectId } : {}),
52
+ kind: item.kind,
53
+ role: item.role,
54
+ sourcePath,
55
+ artifactPath: withheld || external
56
+ ? null
57
+ : evidencePaths.has(sourcePath)
58
+ ? `evidence/${sourcePath}`
59
+ : `outputs/${sourcePath}`,
60
+ description: item.description,
61
+ digest: `sha256:${sha256}`,
62
+ byteSize: stat.size,
63
+ mediaType: mediaTypeForPath(sourcePath),
64
+ storage: withheld ? "withheld" : external ? "external" : "embedded",
65
+ ...(Array.isArray(item.relatedEvidencePaths) && item.relatedEvidencePaths.length
66
+ ? { relatedEvidencePaths: [...item.relatedEvidencePaths] }
67
+ : {}),
68
+ };
69
+ }));
70
+ return {
71
+ schemaVersion: "0.1",
72
+ kind: "citeark.output-registry",
73
+ runId,
74
+ contractDigest,
75
+ outputs,
76
+ checks: outputs.map((output) => ({
77
+ outputId: output.id,
78
+ type: "content-identity",
79
+ status: "passed",
80
+ description: "The runner resolved a regular file and recorded its exact byte size and SHA-256 digest",
81
+ })),
82
+ capturedAt: capturedAt ?? new Date().toISOString(),
83
+ };
84
+ }
85
+
86
+ export async function validateDeclaredOutputs(result, outputDirectory) {
87
+ if (result?.outputs === undefined) return [];
88
+ if (!Array.isArray(result.outputs)) return ["outputs 必须是数组"];
89
+ const issues = [];
90
+ const ids = new Set();
91
+ const paths = new Set();
92
+ const evidencePaths = new Set(
93
+ (Array.isArray(result?.evidence) ? result.evidence : [])
94
+ .map((item) => item?.path)
95
+ .filter((value) => typeof value === "string"),
96
+ );
97
+ for (const [index, item] of result.outputs.entries()) {
98
+ const label = `outputs[${index}]`;
99
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
100
+ issues.push(`${label} 必须是对象`);
101
+ continue;
102
+ }
103
+ if (typeof item.id !== "string" || !item.id.trim()) issues.push(`${label}.id 必须是非空字符串`);
104
+ else if (ids.has(item.id)) issues.push(`output id 重复:${item.id}`);
105
+ else ids.add(item.id);
106
+ if (item.scientificObjectId !== undefined && (typeof item.scientificObjectId !== "string" || !item.scientificObjectId.trim())) issues.push(`${label}.scientificObjectId must be a nonempty research object identity`);
107
+ if (!OUTPUT_KINDS.has(item.kind)) issues.push(`${label}.kind 无效`);
108
+ if (!OUTPUT_ROLES.has(item.role)) issues.push(`${label}.role 无效`);
109
+ if (typeof item.description !== "string" || !item.description.trim()) {
110
+ issues.push(`${label}.description 必须是非空字符串`);
111
+ }
112
+ if (!safeRelativePath(item.path)) {
113
+ issues.push(`${label}.path 必须是 output 内的安全相对路径`);
114
+ continue;
115
+ }
116
+ if (paths.has(item.path)) issues.push(`output path 重复:${item.path}`);
117
+ else paths.add(item.path);
118
+ const target = path.resolve(outputDirectory, item.path);
119
+ if (!(await pathExists(target))) {
120
+ issues.push(`科学输出文件不存在:${item.path}`);
121
+ } else if (!(await lstat(target)).isFile()) {
122
+ issues.push(`科学输出必须是常规文件:${item.path}`);
123
+ }
124
+ if (item.relatedEvidencePaths !== undefined) {
125
+ if (!Array.isArray(item.relatedEvidencePaths)) {
126
+ issues.push(`${label}.relatedEvidencePaths 必须是数组`);
127
+ } else {
128
+ const related = new Set();
129
+ for (const [relatedIndex, relatedPath] of item.relatedEvidencePaths.entries()) {
130
+ if (!safeRelativePath(relatedPath)) {
131
+ issues.push(`${label}.relatedEvidencePaths[${relatedIndex}] 必须是安全相对路径`);
132
+ } else if (!evidencePaths.has(relatedPath)) {
133
+ issues.push(`${label}.relatedEvidencePaths[${relatedIndex}] 没有对应的 evidence:${relatedPath}`);
134
+ } else if (related.has(relatedPath)) {
135
+ issues.push(`${label}.relatedEvidencePaths 包含重复路径:${relatedPath}`);
136
+ }
137
+ related.add(relatedPath);
138
+ }
139
+ }
140
+ }
141
+ }
142
+ return issues;
143
+ }
144
+
145
+ function resolveOutputPath(outputDirectory, relativePath) {
146
+ if (!safeRelativePath(relativePath)) throw new CiteArkError(`科学输出路径无效:${String(relativePath)}`);
147
+ const root = path.resolve(outputDirectory);
148
+ const target = path.resolve(root, relativePath);
149
+ if (!target.startsWith(`${root}${path.sep}`)) throw new CiteArkError(`科学输出路径逃逸:${relativePath}`);
150
+ return target;
151
+ }
152
+
153
+ export function mediaTypeForPath(value) {
154
+ const extension = path.extname(value).toLowerCase();
155
+ return ({
156
+ ".json": "application/json",
157
+ ".jsonl": "application/x-ndjson",
158
+ ".csv": "text/csv; charset=utf-8",
159
+ ".tsv": "text/tab-separated-values; charset=utf-8",
160
+ ".parquet": "application/vnd.apache.parquet",
161
+ ".arrow": "application/vnd.apache.arrow.file",
162
+ ".txt": "text/plain; charset=utf-8",
163
+ ".md": "text/markdown; charset=utf-8",
164
+ ".log": "text/plain; charset=utf-8",
165
+ ".png": "image/png",
166
+ ".jpg": "image/jpeg",
167
+ ".jpeg": "image/jpeg",
168
+ ".svg": "image/svg+xml",
169
+ ".pdf": "application/pdf",
170
+ ".safetensors": "application/octet-stream",
171
+ ".pt": "application/octet-stream",
172
+ ".pth": "application/octet-stream",
173
+ ".ckpt": "application/octet-stream",
174
+ ".zip": "application/zip",
175
+ ".tar": "application/x-tar",
176
+ ".gz": "application/gzip",
177
+ ".mp3": "audio/mpeg",
178
+ ".wav": "audio/wav",
179
+ ".flac": "audio/flac",
180
+ ".mp4": "video/mp4",
181
+ ".webm": "video/webm",
182
+ })[extension] ?? "application/octet-stream";
183
+ }
@@ -0,0 +1,52 @@
1
+ import { hasSessionEvents } from '../terminal/events.mjs';
2
+ import { CLI_COMMUNICATION } from './prompt.mjs';
3
+
4
+ export async function buildClaudeCodeInvocation({
5
+ bundle,
6
+ attempt,
7
+ prompt,
8
+ instructions,
9
+ }) {
10
+ const agent = bundle.runtimeAgent;
11
+ const attemptBudget = agent.maxBudgetUsd / (1 + agent.validationRetries);
12
+ const common = [
13
+ "--print",
14
+ "--bare",
15
+ "--safe-mode",
16
+ "--output-format",
17
+ "stream-json",
18
+ "--verbose",
19
+ ...(hasSessionEvents() ? ["--include-partial-messages"] : []),
20
+ "--model",
21
+ agent.model,
22
+ "--effort",
23
+ agent.effort,
24
+ "--max-turns",
25
+ String(agent.maxTurns),
26
+ "--max-budget-usd",
27
+ String(attemptBudget),
28
+ "--dangerously-skip-permissions",
29
+ "--append-system-prompt",
30
+ hasSessionEvents() ? `${instructions}\n\n${CLI_COMMUNICATION}` : instructions,
31
+ ];
32
+
33
+ if (attempt === 1) {
34
+ return {
35
+ command: "claude",
36
+ args: [
37
+ ...common,
38
+ "--session-id",
39
+ bundle.sessionId,
40
+ prompt,
41
+ ],
42
+ prompt,
43
+ promptArgument: prompt,
44
+ };
45
+ }
46
+ return {
47
+ command: "claude",
48
+ args: [...common, "--resume", bundle.sessionId, prompt],
49
+ prompt,
50
+ promptArgument: prompt,
51
+ };
52
+ }