@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,240 @@
1
+ import { researchMapRecords, bindResearchReading, bindResearchIntent } from './research-map-records.mjs';
2
+ import { researchObjectRecords, reportedObservationRecords } from './research-object-records.mjs';
3
+ import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ import { attachCapAttestation, signCapArtifact } from "./attestation.mjs";
8
+ import { verifyCapArchive } from "./archive.mjs";
9
+ import { canonicalJsonBytes, parseCanonicalJson, sha256Bytes } from "./canonical-json.mjs";
10
+ import {
11
+ CAP_PROFILE,
12
+ assembleCapDirectory,
13
+ } from "./core.mjs";
14
+ import {
15
+ capActorRecord,
16
+ capClaimRecord,
17
+ capHypothesisRecords,
18
+ capRecordId,
19
+ capSourceWorkRecord,
20
+ } from "./research-records.mjs";
21
+ import { verifyCapDirectory } from "./verify.mjs";
22
+ import { captureProcess } from "../../process.mjs";
23
+ import { CiteArkError } from "../../util.mjs";
24
+
25
+ /**
26
+ * Turn one compiler's source-grounded inventory into a portable CAP Artifact.
27
+ * The compiler identity is supplied by the caller; CiteArk is not privileged.
28
+ */
29
+ export async function assembleResearchCompilationCap({
30
+ directory,
31
+ inventory,
32
+ compiler,
33
+ runId,
34
+ createdAt,
35
+ signingKeyPath,
36
+ processingBinding,
37
+ nativeOutput = inventory,
38
+ nativeOutputBytes,
39
+ nativeOutputMediaType,
40
+ }) {
41
+ if (inventory?.compilationStage !== "inventory") {
42
+ throw new CiteArkError("Research Compilation requires a source inventory, not an execution plan");
43
+ }
44
+ if (!Array.isArray(inventory.claims)) {
45
+ throw new CiteArkError("Research Compilation claims must be an array");
46
+ }
47
+ if (!compiler || typeof compiler !== "object" || Array.isArray(compiler)) {
48
+ throw new CiteArkError("Research Compilation requires compiler identity");
49
+ }
50
+ const compilerName = requiredText(compiler.name, "compiler.name");
51
+ const compilerId = requiredText(compiler.id, "compiler.id");
52
+ const compilationRunId = requiredText(runId, "runId");
53
+ const timestamp = isoDate(createdAt ?? inventory.provenance?.compiledAt);
54
+ const actorId = capRecordId(
55
+ "actor",
56
+ `research-compiler:${compilerId}:${compiler.version ?? "unversioned"}`,
57
+ );
58
+ const sourceWorkId = capRecordId("work", inventory.work.id);
59
+ const actor = {
60
+ ...capActorRecord(actorId, compilerName, compiler.kind ?? "agent", compiler.version),
61
+ ...(compiler.identity ? { identity: structuredClone(compiler.identity) } : {}),
62
+ compiler: {
63
+ id: compilerId,
64
+ ...(Array.isArray(compiler.capabilities)
65
+ ? { capabilities: [...new Set(compiler.capabilities)].sort() }
66
+ : {}),
67
+ },
68
+ citeark: { runId: compilationRunId },
69
+ };
70
+ const sourceWork = capSourceWorkRecord({
71
+ research: inventory,
72
+ id: sourceWorkId,
73
+ processingBinding,
74
+ });
75
+ const objectGraph = researchObjectRecords({ research: inventory, sourceWorkId, actorId });
76
+ const observations = reportedObservationRecords({ research: inventory, sourceWorkId, objectIds: objectGraph.objectIds });
77
+ const reading = bindResearchReading(inventory, objectGraph, sourceWorkId);
78
+ if (reading) sourceWork.citeark.reading = reading;
79
+ const claims = inventory.claims.map((claim) => capClaimRecord({
80
+ claim,
81
+ id: capRecordId("claim", `${inventory.work.id}:${claim.id}`),
82
+ sourceWorkId,
83
+ actorId,
84
+ reportedEvidenceIds: observations.idsByClaim.get(claim.id),
85
+ objectReferences: (claim.objectIds ?? []).map(id => ({ ref: objectGraph.objectIds.get(id) })),
86
+ }));
87
+ const inventoryBytes = canonicalJsonBytes(inventory);
88
+ const nativeBytes = nativeOutputBytes == null
89
+ ? canonicalJsonBytes(nativeOutput)
90
+ : Buffer.from(nativeOutputBytes);
91
+ const nativeMediaType = nativeOutputBytes == null
92
+ ? "application/json"
93
+ : requiredText(nativeOutputMediaType, "nativeOutputMediaType");
94
+ const sharedOutput = nativeMediaType === "application/json" && inventoryBytes.equals(nativeBytes);
95
+ const outputRights = {
96
+ statement: "Compiler-produced output; source materials retain their own rights.",
97
+ };
98
+ const built = await assembleCapDirectory({
99
+ directory,
100
+ artifact: {
101
+ id: capRecordId("artifact", `research-compilation:${compilerId}:${compilationRunId}`),
102
+ createdAt: timestamp,
103
+ createdBy: actorId,
104
+ },
105
+ profiles: [CAP_PROFILE.researchCompilation, CAP_PROFILE.publicBundle],
106
+ roots: [
107
+ { role: "primarySourceWork", ref: sourceWorkId },
108
+ ...claims.map((claim) => ({ role: "compiledClaim", ref: claim.id })),
109
+ ],
110
+ records: [...researchMapRecords({ research: inventory, objectGraph, sourceWorkId, actorId }), actor, sourceWork, ...objectGraph.records, ...claims, ...observations.records,
111
+ ...capHypothesisRecords({ research: inventory, sourceWorkId, actorId, objectIds: objectGraph.objectIds })],
112
+ blobs: sharedOutput
113
+ ? [{
114
+ id: capRecordId("compiler-output", `${compilerId}:${compilationRunId}`),
115
+ roles: ["compiler-output", "research-inventory"],
116
+ mediaType: nativeMediaType,
117
+ bytes: nativeBytes,
118
+ rights: outputRights,
119
+ }]
120
+ : [
121
+ {
122
+ id: capRecordId("compiler-output", `${compilerId}:${compilationRunId}`),
123
+ roles: ["compiler-output"],
124
+ mediaType: nativeMediaType,
125
+ bytes: nativeBytes,
126
+ rights: outputRights,
127
+ },
128
+ {
129
+ id: capRecordId("research-inventory", `${compilerId}:${compilationRunId}`),
130
+ roles: ["research-inventory"],
131
+ mediaType: "application/json",
132
+ bytes: inventoryBytes,
133
+ rights: outputRights,
134
+ },
135
+ ],
136
+ });
137
+ await writeAttachments({ directory, built, inventory, compilerName });
138
+ const attestation = await signCapArtifact({
139
+ artifactDigest: built.artifactDigest,
140
+ actor: { ref: actorId },
141
+ role: "researchCompiler",
142
+ createdAt: timestamp,
143
+ signingKeyPath,
144
+ processingBinding,
145
+ });
146
+ const attachedAttestation = await attachCapAttestation({ directory, attestation });
147
+ const verification = await verifyCapDirectory(directory);
148
+ if (!verification.valid) {
149
+ throw new CiteArkError(`Research Compilation CAP 2.0 failed self-verification:\n- ${verification.issues.join("\n- ")}`);
150
+ }
151
+ return {
152
+ ...built,
153
+ verification,
154
+ attestation: attachedAttestation,
155
+ recordIds: {
156
+ actor: actorId,
157
+ sourceWork: sourceWorkId,
158
+ claims: claims.map((claim) => claim.id),
159
+ researchObjects: Object.fromEntries(objectGraph.objectIds),
160
+ },
161
+ };
162
+ }
163
+
164
+ /** Read the normalized planner handoff from a verified Compilation CAP file. */
165
+ export async function loadResearchCompilationCap(archivePath) {
166
+ const source = path.resolve(archivePath);
167
+ const verification = await verifyCapArchive(source);
168
+ if (!verification.valid) {
169
+ throw new CiteArkError(`Research Compilation CAP verification failed:\n- ${verification.issues.join("\n- ")}`);
170
+ }
171
+ if (!verification.manifest.profiles.includes(CAP_PROFILE.researchCompilation)) {
172
+ throw new CiteArkError("CAP file does not declare the Research Compilation Profile");
173
+ }
174
+ const descriptor = verification.manifest.blobs.find((blob) =>
175
+ blob.roles.includes("research-inventory"),
176
+ );
177
+ if (!descriptor || descriptor.availability !== "embedded" || descriptor.mediaType !== "application/json") {
178
+ throw new CiteArkError("Research Compilation CAP requires an embedded JSON research-inventory Blob");
179
+ }
180
+ const temporary = await mkdtemp(path.join(os.tmpdir(), "citeark-compilation-input-"));
181
+ try {
182
+ const extraction = await captureProcess("tar", ["-xzf", source, "-C", temporary]);
183
+ if (extraction.code !== 0) throw new CiteArkError("Cannot extract verified Research Compilation CAP");
184
+ const bytes = await readFile(path.join(temporary, descriptor.path));
185
+ if (bytes.byteLength !== descriptor.size || sha256Bytes(bytes) !== descriptor.digest) {
186
+ throw new CiteArkError("Research Compilation inventory does not match its verified descriptor");
187
+ }
188
+ const inventory = parseCanonicalJson(bytes, "research-inventory Blob");
189
+ if (inventory?.compilationStage !== "inventory") {
190
+ throw new CiteArkError("Research Compilation inventory is not an inventory-stage handoff");
191
+ }
192
+ return {
193
+ inventory,
194
+ artifactDigest: verification.artifactDigest,
195
+ verification,
196
+ };
197
+ } finally {
198
+ await rm(temporary, { recursive: true, force: true });
199
+ }
200
+ }
201
+
202
+ async function writeAttachments({ directory, built, inventory, compilerName }) {
203
+ await mkdir(path.join(directory, "preview"), { recursive: true });
204
+ const crate = {
205
+ "@context": "https://w3id.org/ro/crate/1.3/context",
206
+ "@graph": [
207
+ { "@id": "ro-crate-metadata.json", "@type": "CreativeWork", about: { "@id": "./" } },
208
+ {
209
+ "@id": "./",
210
+ "@type": "Dataset",
211
+ name: inventory.work.title,
212
+ identifier: built.artifactDigest,
213
+ hasPart: [
214
+ ...built.manifest.records.map((record) => ({ "@id": record.path })),
215
+ ...built.manifest.blobs.filter((blob) => blob.path).map((blob) => ({ "@id": blob.path })),
216
+ ],
217
+ },
218
+ ],
219
+ };
220
+ const readme = `# ${inventory.work.title}\n\nCompiled by ${compilerName}.\n\n- Compiled claims: ${inventory.claims.length}\n- Artifact: ${built.artifactDigest}\n\nThis is a compiler interpretation of fixed sources. It is not execution evidence or a scientific assessment.\n`;
221
+ await Promise.all([
222
+ writeFile(path.join(directory, "ro-crate-metadata.json"), `${JSON.stringify(crate, null, 2)}\n`),
223
+ writeFile(path.join(directory, "preview", "README.md"), readme),
224
+ ]);
225
+ }
226
+
227
+ function requiredText(value, label) {
228
+ if (typeof value !== "string" || !value.trim()) {
229
+ throw new CiteArkError(`${label} must be a non-empty string`);
230
+ }
231
+ return value.trim();
232
+ }
233
+
234
+ function isoDate(value) {
235
+ const date = new Date(value ?? Date.now());
236
+ if (!Number.isFinite(date.getTime())) {
237
+ throw new CiteArkError(`CAP timestamp is invalid: ${String(value)}`);
238
+ }
239
+ return date.toISOString();
240
+ }
@@ -0,0 +1,282 @@
1
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import {
5
+ assertSha256Digest,
6
+ canonicalJson,
7
+ canonicalJsonBytes,
8
+ sha256Bytes,
9
+ } from "./canonical-json.mjs";
10
+
11
+ export const CAP_SPEC_VERSION = "2.0.0-alpha.2";
12
+ export const CAP_READABLE_SPEC_VERSIONS = new Set(["2.0.0-alpha.1", CAP_SPEC_VERSION]);
13
+ export const CAP_MANIFEST = "cap-manifest.json";
14
+ export const CAP_MANIFEST_MEDIA_TYPE = "application/vnd.citeark.cap.manifest.v2+json";
15
+ export const CAP_ARCHIVE_MEDIA_TYPE = "application/vnd.citeark.cap+tar+gzip;version=2";
16
+
17
+ export const CAP_PROFILE = Object.freeze({
18
+ core: "https://citeark.com/cap/profiles/core/2.0",
19
+ researchCompilation: "https://citeark.com/cap/profiles/research-compilation/2.0",
20
+ researchPlan: "https://citeark.com/cap/profiles/research-plan/2.0",
21
+ computationalRun: "https://citeark.com/cap/profiles/computational-run/2.0",
22
+ reproduction: "https://citeark.com/cap/profiles/reproduction/2.0",
23
+ agentTrace: "https://citeark.com/cap/profiles/agent-trace/2.0",
24
+ publicBundle: "https://citeark.com/cap/profiles/public-bundle/2.0",
25
+ restrictedEvidence: "https://citeark.com/cap/profiles/restricted-evidence/2.0",
26
+ });
27
+
28
+ export { CAP_RECORD_TYPE, CAP_RECORD_ROLE } from '../../graph/model.mjs';
29
+ import { CAP_RECORD_TYPE } from '../../graph/model.mjs';
30
+ import { researchRelations, bindRelation } from '../../graph/research-relations.mjs';
31
+
32
+ const CAP_RELATION = new Set([
33
+ "derivedFrom",
34
+ "revises",
35
+ "reproduces",
36
+ "extends",
37
+ "supersedes",
38
+ "refersTo",
39
+ ]);
40
+
41
+ export async function assembleCapDirectory({
42
+ directory,
43
+ artifact,
44
+ profiles = [],
45
+ roots,
46
+ records,
47
+ blobs = [],
48
+ relations = [],
49
+ }) {
50
+ await requireEmptyDirectory(directory);
51
+ const nodes = records.filter(entry => (entry.value ?? entry).type !== CAP_RECORD_TYPE.relation);
52
+ const normalizedRecords = await stageRecords(directory, nodes);
53
+ const recordById = new Map(normalizedRecords.map(item => [item.id, item]));
54
+ const creator = typeof artifact.createdBy === 'string' ? artifact.createdBy : artifact.createdBy?.ref;
55
+ const explicit = records.filter(entry => (entry.value ?? entry).type === CAP_RECORD_TYPE.relation).map(entry => entry.value ?? entry);
56
+ const generated = researchRelations(nodes.map(entry => entry.value ?? entry), creator);
57
+ const explicitIds = new Set(explicit.map(record => record.id));
58
+ const edges = [...explicit, ...generated.filter(record => !explicitIds.has(record.id))];
59
+ if (edges.length) {
60
+ const staged = await stageRecords(directory, edges.map(record => bindRelation(record, recordById)));
61
+ for (const edge of staged) {
62
+ if (recordById.has(edge.id)) throw Error(`Duplicate CAP Record id: ${edge.id}`);
63
+ recordById.set(edge.id, edge);
64
+ }
65
+ normalizedRecords.push(...staged);
66
+ normalizedRecords.sort((a, b) => compareText(a.id, b.id));
67
+ }
68
+ const normalizedBlobs = await stageBlobs(directory, blobs);
69
+ const allProfiles = [...new Set([CAP_PROFILE.core, ...profiles])].sort();
70
+ const manifest = {
71
+ $schema: "https://citeark.com/schemas/cap/v2/manifest.schema.json",
72
+ mediaType: CAP_MANIFEST_MEDIA_TYPE,
73
+ specVersion: CAP_SPEC_VERSION,
74
+ artifact: normalizeArtifact(artifact, recordById),
75
+ profiles: allProfiles,
76
+ roots: normalizeRoots(roots, recordById),
77
+ records: normalizedRecords,
78
+ blobs: normalizedBlobs,
79
+ relations: normalizeRelations(relations),
80
+ };
81
+ const manifestBytes = canonicalJsonBytes(manifest);
82
+ await writeFile(path.join(directory, CAP_MANIFEST), manifestBytes);
83
+ return {
84
+ directory,
85
+ manifest,
86
+ artifactDigest: sha256Bytes(manifestBytes),
87
+ };
88
+ }
89
+
90
+ export function capRecordReference(descriptor) {
91
+ return { ref: descriptor.id, digest: descriptor.digest };
92
+ }
93
+
94
+ export function capPolicyCommitment({ nonce, policy }) {
95
+ if (typeof nonce !== "string" || nonce.length < 22) {
96
+ throw new Error("CAP verification policy commitment requires a high-entropy nonce");
97
+ }
98
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
99
+ throw new Error("CAP verification policy must be a JSON object");
100
+ }
101
+ return sha256Bytes(canonicalJsonBytes({
102
+ algorithm: "citeark-policy-commitment-v1",
103
+ nonce,
104
+ policy,
105
+ }));
106
+ }
107
+
108
+ export function safeCapPath(value) {
109
+ if (typeof value !== "string" || !value || value.includes("\\") || value.includes("\0")) return false;
110
+ if (value.startsWith("/") || value.endsWith("/") || value.includes("//")) return false;
111
+ const segments = value.split("/");
112
+ return segments.every((segment) => segment && segment !== "." && segment !== "..");
113
+ }
114
+
115
+ async function stageRecords(directory, records) {
116
+ if (!Array.isArray(records) || records.length === 0) {
117
+ throw new Error("CAP 2.0 requires at least one Record");
118
+ }
119
+ const descriptors = [];
120
+ const ids = new Set();
121
+ for (const entry of records) {
122
+ const value = entry?.value ?? entry;
123
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
124
+ throw new Error("Each CAP Record must be a JSON object");
125
+ }
126
+ if (typeof value.id !== "string" || !value.id) throw new Error("Each CAP Record requires an id");
127
+ if (typeof value.type !== "string" || !value.type) throw new Error(`Record ${value.id} requires a type`);
128
+ if (ids.has(value.id)) throw new Error(`Duplicate CAP Record id: ${value.id}`);
129
+ ids.add(value.id);
130
+ const bytes = canonicalJsonBytes(value);
131
+ const digest = sha256Bytes(bytes);
132
+ const hex = digest.slice(7);
133
+ const recordPath = entry?.path ?? `records/sha256/${hex.slice(0, 2)}/${hex}.json`;
134
+ if (!safeCapPath(recordPath) || !recordPath.startsWith("records/")) {
135
+ throw new Error(`Unsafe CAP Record path: ${recordPath}`);
136
+ }
137
+ await mkdir(path.dirname(path.join(directory, recordPath)), { recursive: true });
138
+ await writeFile(path.join(directory, recordPath), bytes);
139
+ descriptors.push({
140
+ id: value.id,
141
+ recordType: value.type,
142
+ role: value.role,
143
+ mediaType: entry?.mediaType ?? "application/vnd.citeark.cap.record+json",
144
+ schema: entry?.schema ?? value.$schema ?? schemaForRecordType(value.type),
145
+ digest,
146
+ size: bytes.byteLength,
147
+ path: recordPath,
148
+ });
149
+ }
150
+ return descriptors.sort((left, right) => compareText(left.id, right.id));
151
+ }
152
+
153
+ async function stageBlobs(directory, blobs) {
154
+ const descriptors = [];
155
+ const ids = new Set();
156
+ const digests = new Set();
157
+ for (const entry of blobs) {
158
+ if (typeof entry?.id !== "string" || !entry.id) throw new Error("Each CAP Blob requires an id");
159
+ if (ids.has(entry.id)) throw new Error(`Duplicate CAP Blob id: ${entry.id}`);
160
+ ids.add(entry.id);
161
+ const availability = entry.availability ?? "embedded";
162
+ if (!new Set(["embedded", "external", "withheld"]).has(availability)) {
163
+ throw new Error(`Unsupported Blob availability: ${availability}`);
164
+ }
165
+ let digest;
166
+ let size;
167
+ let blobPath;
168
+ if (availability === "embedded") {
169
+ if (entry.bytes == null) throw new Error(`Embedded Blob ${entry.id} requires bytes`);
170
+ const bytes = Buffer.isBuffer(entry.bytes) ? entry.bytes : Buffer.from(entry.bytes);
171
+ digest = sha256Bytes(bytes);
172
+ size = bytes.byteLength;
173
+ if (entry.digest && entry.digest !== digest) throw new Error(`Blob ${entry.id} digest does not match its bytes`);
174
+ if (entry.size != null && entry.size !== size) throw new Error(`Blob ${entry.id} size does not match its bytes`);
175
+ const hex = digest.slice(7);
176
+ blobPath = entry.path ?? `blobs/sha256/${hex.slice(0, 2)}/${hex}`;
177
+ if (!safeCapPath(blobPath) || !blobPath.startsWith("blobs/")) {
178
+ throw new Error(`Unsafe CAP Blob path: ${blobPath}`);
179
+ }
180
+ await mkdir(path.dirname(path.join(directory, blobPath)), { recursive: true });
181
+ await writeFile(path.join(directory, blobPath), bytes);
182
+ } else {
183
+ digest = assertSha256Digest(entry.digest, `Blob ${entry.id} digest`);
184
+ if (!Number.isSafeInteger(entry.size) || entry.size < 0) {
185
+ throw new Error(`Blob ${entry.id} requires a non-negative integer size`);
186
+ }
187
+ size = entry.size;
188
+ if (entry.path != null) throw new Error(`${availability} Blob ${entry.id} must not declare a bundle path`);
189
+ }
190
+ if (digests.has(digest)) throw new Error(`Duplicate CAP Blob digest: ${digest}`);
191
+ digests.add(digest);
192
+ descriptors.push({
193
+ id: entry.id,
194
+ roles: [...new Set(entry.roles ?? [])].sort(),
195
+ mediaType: entry.mediaType ?? "application/octet-stream",
196
+ digest,
197
+ size,
198
+ availability,
199
+ ...(blobPath ? { path: blobPath } : {}),
200
+ ...(entry.rights ? { rights: entry.rights } : {}),
201
+ ...(entry.accessPolicy ? { accessPolicy: entry.accessPolicy } : {}),
202
+ });
203
+ }
204
+ return descriptors.sort((left, right) => compareText(left.digest, right.digest));
205
+ }
206
+
207
+ function normalizeArtifact(artifact, records) {
208
+ if (!artifact || typeof artifact !== "object") throw new Error("CAP Artifact metadata is required");
209
+ if (typeof artifact.createdAt !== "string" || !Number.isFinite(Date.parse(artifact.createdAt))) {
210
+ throw new Error("CAP Artifact createdAt must be an ISO date-time");
211
+ }
212
+ return {
213
+ ...(artifact.id ? { id: artifact.id } : {}),
214
+ createdAt: artifact.createdAt,
215
+ createdBy: resolveRecordReference(artifact.createdBy, records, "artifact.createdBy"),
216
+ };
217
+ }
218
+
219
+ function normalizeRoots(roots, records) {
220
+ if (!Array.isArray(roots) || roots.length === 0) throw new Error("CAP Artifact requires at least one root Record");
221
+ const output = roots.map((root, index) => ({
222
+ role: requiredText(root?.role, `roots[${index}].role`),
223
+ ...resolveRecordReference(root?.ref ?? root, records, `roots[${index}]`),
224
+ }));
225
+ return output.sort((left, right) => compareText(canonicalJson(left), canonicalJson(right)));
226
+ }
227
+
228
+ function normalizeRelations(relations) {
229
+ if (!Array.isArray(relations)) throw new Error("CAP Artifact relations must be an array");
230
+ return relations
231
+ .map((relation, index) => {
232
+ const artifactDigest = assertSha256Digest(
233
+ relation?.artifactDigest,
234
+ `relations[${index}].artifactDigest`,
235
+ );
236
+ const relationship = requiredText(relation.relationship, `relations[${index}].relationship`);
237
+ if (!CAP_RELATION.has(relationship)) throw new Error(`relations[${index}].relationship is unsupported`);
238
+ return {
239
+ relationship,
240
+ artifactDigest,
241
+ ...(relation.recordDigest
242
+ ? { recordDigest: assertSha256Digest(relation.recordDigest, `relations[${index}].recordDigest`) }
243
+ : {}),
244
+ ...(relation.summary ? { summary: relation.summary } : {}),
245
+ };
246
+ })
247
+ .sort((left, right) => compareText(canonicalJson(left), canonicalJson(right)));
248
+ }
249
+
250
+ function resolveRecordReference(input, records, label) {
251
+ const id = typeof input === "string" ? input : input?.ref;
252
+ if (typeof id !== "string" || !id) throw new Error(`${label} must reference a Record id`);
253
+ const descriptor = records.get(id);
254
+ if (!descriptor) throw new Error(`${label} references an unknown Record: ${id}`);
255
+ if (input?.digest && input.digest !== descriptor.digest) {
256
+ throw new Error(`${label} digest does not match Record ${id}`);
257
+ }
258
+ return capRecordReference(descriptor);
259
+ }
260
+
261
+ function schemaForRecordType(type) {
262
+ const name = Object.entries(CAP_RECORD_TYPE).find(([, value]) => value === type)?.[0];
263
+ if (!name) return "https://citeark.com/schemas/cap/v2/descriptor.schema.json";
264
+ const filename = name.replace(/[A-Z]/g, (character) => `-${character.toLowerCase()}`);
265
+ return `https://citeark.com/schemas/cap/v2/${filename}.schema.json`;
266
+ }
267
+
268
+ async function requireEmptyDirectory(directory) {
269
+ await mkdir(directory, { recursive: true });
270
+ if ((await readdir(directory)).length > 0) throw new Error(`CAP output directory is not empty: ${directory}`);
271
+ }
272
+
273
+ function requiredText(value, label) {
274
+ if (typeof value !== "string" || !value) throw new Error(`${label} must be a non-empty string`);
275
+ return value;
276
+ }
277
+
278
+ function compareText(left, right) {
279
+ if (left < right) return -1;
280
+ if (left > right) return 1;
281
+ return 0;
282
+ }
@@ -0,0 +1,23 @@
1
+ import { CAP_RECORD_TYPE } from './core.mjs';
2
+ import { capRecordId, CAP_SCHEMA } from './research-records.mjs';
3
+
4
+ /** Narrow judgments never replace the compound claim's verdict. */
5
+ export function measurementAssessmentRecords({ assessment, parent, identity, evidenceByMeasurement }) {
6
+ return (assessment.measurementAssessments ?? []).map(item => {
7
+ const evidence = evidenceByMeasurement.get(item.measurementId);
8
+ const comparison = parent.method.comparisons?.find(value => value.measurementId === item.measurementId)?.comparison;
9
+ return { $schema: CAP_SCHEMA.assessment,
10
+ id: capRecordId('measurement-assessment', `${identity}:${item.measurementId}`),
11
+ type: CAP_RECORD_TYPE.assertion, role: 'measurement-assessment',
12
+ title: `Assessment: ${item.measurementId}`, statement: { value: item.reason ?? 'No assessment explanation provided.', language: 'en' },
13
+ subject: parent.claim ?? parent.subject, execution: parent.execution,
14
+ evidence: evidence ? [{ ref: evidence }] : [],
15
+ method: { type: parent.method.type, measurementId: item.measurementId, ...(comparison ? { comparison } : {}) },
16
+ conclusion: ['supports', 'challenges', 'contradicts'].includes(item.verdict) ? item.verdict : 'inconclusive',
17
+ scope: { ...parent.scope, measurementIds: [item.measurementId] },
18
+ limitations: [...(parent.limitations ?? []), ...(!evidence ? ['No available measurement observation is attached.'] : [])],
19
+ performedBy: parent.performedBy, createdAt: parent.createdAt,
20
+ citeark: { measurementId: item.measurementId, reason: item.reason ?? null, verificationStatus: item.verificationStatus ?? null },
21
+ };
22
+ });
23
+ }