@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.
- package/LICENSE +202 -0
- package/README.md +128 -0
- package/data/dataset-source-registry.v1.json +300 -0
- package/dist/arkgraph/boot.js +6 -0
- package/dist/arkgraph/index.html +1 -0
- package/dist/arkgraph/viewer.css +1 -0
- package/dist/arkgraph/viewer.en.css +1 -0
- package/dist/arkgraph/viewer.en.js +49 -0
- package/dist/arkgraph/viewer.en.js.LEGAL.txt +56 -0
- package/dist/arkgraph/viewer.js +49 -0
- package/dist/arkgraph/viewer.js.LEGAL.txt +56 -0
- package/docker/claude-code/Dockerfile +97 -0
- package/docker/claude-code/codex-pro-relay.mjs +466 -0
- package/docker/claude-code/runtime-contract-check.mjs +79 -0
- package/docs/arkgraph-reading.md +79 -0
- package/docs/configuration.md +100 -0
- package/docs/integration.md +92 -0
- package/docs/maturity-plan.md +27 -0
- package/docs/npm-release.md +44 -0
- package/docs/paper-reading.md +40 -0
- package/docs/research-plan-granularity.md +27 -0
- package/docs/terminal.md +49 -0
- package/examples/toy-evaluation/compile-task.json +27 -0
- package/examples/toy-evaluation/paper.md +5 -0
- package/examples/toy-evaluation/repository/README.md +9 -0
- package/examples/toy-evaluation/repository/checkpoint.json +4 -0
- package/examples/toy-evaluation/repository/evaluate.py +17 -0
- package/examples/toy-evaluation/task.json +81 -0
- package/package.json +59 -0
- package/prompts/compile-research.md +58 -0
- package/prompts/execute-contract.md +72 -0
- package/prompts/execute-workspace-simple.md +51 -0
- package/prompts/execute-workspace.md +34 -0
- package/prompts/prepare-reproduction.md +82 -0
- package/prompts/repair-research.md +45 -0
- package/protocol/CAP.md +129 -0
- package/protocol/LICENSE +12 -0
- package/protocol/MAPPINGS.md +72 -0
- package/protocol/README.md +38 -0
- package/protocol/conformance-v2.0-alpha.1.json +36 -0
- package/protocol/examples/arkgraph/checkpoint-evaluation.json +309 -0
- package/protocol/examples/arkgraph/fixtures.mjs +49 -0
- package/protocol/examples/arkgraph/paper-free.json +291 -0
- package/protocol/examples/arkgraph/partial-failure.json +344 -0
- package/protocol/examples/arkgraph/training-evaluation.json +443 -0
- package/protocol/profiles/agent-trace.md +16 -0
- package/protocol/profiles/computational-run.md +16 -0
- package/protocol/profiles/core.md +15 -0
- package/protocol/profiles/public-bundle.md +18 -0
- package/protocol/profiles/reproduction.md +29 -0
- package/protocol/profiles/research-compilation.md +44 -0
- package/protocol/profiles/research-plan.md +39 -0
- package/protocol/profiles/restricted-evidence.md +15 -0
- package/runtime/bootstrap-autodl-runtime.sh +314 -0
- package/runtime/create-runtime-venv.sh +41 -0
- package/runtime/install-local-cpu-runtime.sh +23 -0
- package/runtime/install-scientific-runtime.sh +153 -0
- package/runtime/mineru/parse.py +62 -0
- package/runtime/mineru/requirements.txt +4 -0
- package/runtime/requirements-baseline.txt +38 -0
- package/schemas/cap/v2/activity.schema.json +47 -0
- package/schemas/cap/v2/agent.schema.json +32 -0
- package/schemas/cap/v2/assertion.schema.json +110 -0
- package/schemas/cap/v2/descriptor.schema.json +243 -0
- package/schemas/cap/v2/entity.schema.json +64 -0
- package/schemas/cap/v2/manifest.schema.json +67 -0
- package/schemas/cap/v2/relation.schema.json +82 -0
- package/schemas/compute-catalog.schema.json +63 -0
- package/schemas/compute-decision.schema.json +27 -0
- package/schemas/execution-contract.schema.json +1024 -0
- package/schemas/research-card.schema.json +30 -0
- package/schemas/research-inventory-draft.schema.json +366 -0
- package/schemas/research.schema.json +1044 -0
- package/schemas/result.schema.json +173 -0
- package/schemas/verification-policy.schema.json +47 -0
- package/schemas/verified-conclusion.schema.json +58 -0
- package/schemas/workspace-summary.schema.json +24 -0
- package/scripts/build-arkgraph-view.mjs +12 -0
- package/scripts/check-execution-feasibility.mjs +24 -0
- package/scripts/check-syntax.mjs +15 -0
- package/scripts/deterministic-asset-preparation.py +438 -0
- package/scripts/package-cap.mjs +23 -0
- package/scripts/package-local-agent.mjs +23 -0
- package/scripts/preview-arkgraph.mjs +25 -0
- package/scripts/replay-research-compiler-candidate.mjs +134 -0
- package/scripts/review-compiler-sources.mjs +44 -0
- package/scripts/run-asset-preparation.sh +17 -0
- package/scripts/run-research-plan.mjs +98 -0
- package/scripts/validate-asset-preparation.py +290 -0
- package/scripts/verify-local-runtime.mjs +57 -0
- package/scripts/verify-npm-package.mjs +57 -0
- package/src/adapters/paper2agent.mjs +107 -0
- package/src/assets/cache.mjs +159 -0
- package/src/assets/compute.mjs +98 -0
- package/src/assets/executor.mjs +145 -0
- package/src/assets/lifecycle.mjs +213 -0
- package/src/assets/manifest.mjs +242 -0
- package/src/assets/opportunistic-preparation.mjs +81 -0
- package/src/assets/plan.mjs +411 -0
- package/src/assets/prompts.mjs +29 -0
- package/src/assets/public-asset-probe.mjs +525 -0
- package/src/assets/qualification.mjs +119 -0
- package/src/assets/readiness.mjs +130 -0
- package/src/assets/reproduction-admission.mjs +355 -0
- package/src/assets/requirements.mjs +152 -0
- package/src/assets/source-grounding.mjs +341 -0
- package/src/assets/source-policy.mjs +118 -0
- package/src/autodl/client.mjs +260 -0
- package/src/autodl/ssh.mjs +380 -0
- package/src/autodl/tools.mjs +129 -0
- package/src/cap/redaction.mjs +38 -0
- package/src/cap/v2/archive.mjs +152 -0
- package/src/cap/v2/attestation.mjs +204 -0
- package/src/cap/v2/canonical-json.mjs +114 -0
- package/src/cap/v2/compilation-artifact.mjs +240 -0
- package/src/cap/v2/core.mjs +282 -0
- package/src/cap/v2/measurement-assessment-records.mjs +23 -0
- package/src/cap/v2/pipeline-artifact.mjs +922 -0
- package/src/cap/v2/read.mjs +41 -0
- package/src/cap/v2/reassessment-artifact.mjs +383 -0
- package/src/cap/v2/research-artifact.mjs +231 -0
- package/src/cap/v2/research-map-records.mjs +46 -0
- package/src/cap/v2/research-object-records.mjs +163 -0
- package/src/cap/v2/research-records.mjs +187 -0
- package/src/cap/v2/verify.mjs +642 -0
- package/src/cli.mjs +1146 -0
- package/src/compute/autodl-pro-compiler.mjs +347 -0
- package/src/compute/autodl-pro-executor.mjs +459 -0
- package/src/compute/autodl-pro-job.mjs +843 -0
- package/src/compute/autodl-pro-network.mjs +295 -0
- package/src/compute/autodl-pro-remote.mjs +810 -0
- package/src/compute/autodl-pro-staging.mjs +117 -0
- package/src/compute/campaign.mjs +110 -0
- package/src/compute/catalog.mjs +123 -0
- package/src/compute/checkpoint-protocol.mjs +154 -0
- package/src/compute/codex-account-lock.mjs +111 -0
- package/src/compute/codex-account-session.mjs +107 -0
- package/src/compute/compiler-profile.mjs +38 -0
- package/src/compute/compiler-router.mjs +23 -0
- package/src/compute/coordinator-recovery.mjs +210 -0
- package/src/compute/executor-router.mjs +29 -0
- package/src/compute/gcp-batch-compiler.mjs +685 -0
- package/src/compute/gcp-batch-executor.mjs +1215 -0
- package/src/compute/gcp-batch-failure.mjs +92 -0
- package/src/compute/gcp-batch-job.mjs +527 -0
- package/src/compute/gcp-batch-lifecycle.mjs +81 -0
- package/src/compute/gcp-checkpoint-worker.mjs +1633 -0
- package/src/compute/local-codex-compiler.mjs +52 -0
- package/src/compute/measurement-hardware.mjs +128 -0
- package/src/compute/remote-attempt.mjs +226 -0
- package/src/compute/requirements.mjs +124 -0
- package/src/compute/research-phases.mjs +48 -0
- package/src/compute/scheduler.mjs +452 -0
- package/src/compute/shared-workloads.mjs +26 -0
- package/src/compute/stage-archive.mjs +79 -0
- package/src/contracts/campaign-contract.mjs +52 -0
- package/src/contracts/execution-contract.mjs +819 -0
- package/src/contracts/execution-mode.mjs +19 -0
- package/src/contracts/execution-timeouts.mjs +45 -0
- package/src/contracts/execution-workload.mjs +68 -0
- package/src/contracts/preflight-schema.mjs +25 -0
- package/src/contracts/public-contract.mjs +63 -0
- package/src/contracts/subject-tags.mjs +31 -0
- package/src/dashboard/data.mjs +898 -0
- package/src/dashboard/server.mjs +79 -0
- package/src/dashboard/static/dashboard.css +366 -0
- package/src/dashboard/static/dashboard.js +560 -0
- package/src/dashboard/static/index.html +85 -0
- package/src/deployment/community-policy.mjs +9 -0
- package/src/deployment/environment.mjs +112 -0
- package/src/deployment/guided.mjs +98 -0
- package/src/deployment/handoff.mjs +102 -0
- package/src/deployment/local-contract.mjs +31 -0
- package/src/deployment/local.mjs +100 -0
- package/src/deployment/prepare.mjs +46 -0
- package/src/deployment/recipe.mjs +108 -0
- package/src/deployment/supplement.mjs +51 -0
- package/src/deployment/terminal.mjs +43 -0
- package/src/diagnosis/renderer.mjs +75 -0
- package/src/diagnosis/target-failure.mjs +46 -0
- package/src/evidence/parser-registry.mjs +54 -0
- package/src/evidence/parsers/fasttext-classification.mjs +82 -0
- package/src/evidence/parsers/json-scalar.mjs +96 -0
- package/src/evidence/parsers/simcse-senteval.mjs +104 -0
- package/src/evidence/parsers/starspace-classification.mjs +78 -0
- package/src/evidence/registry.mjs +147 -0
- package/src/execution/runner-audit.mjs +473 -0
- package/src/gcp/auth.mjs +106 -0
- package/src/gcp/batch-client.mjs +120 -0
- package/src/gcp/resource-discovery.mjs +177 -0
- package/src/gcp/rest.mjs +82 -0
- package/src/gcp/secret-manager.mjs +34 -0
- package/src/gcp/signed-url.mjs +133 -0
- package/src/gcp/storage.mjs +220 -0
- package/src/graph/command.mjs +41 -0
- package/src/graph/execution.mjs +97 -0
- package/src/graph/model.mjs +37 -0
- package/src/graph/presentation.mjs +110 -0
- package/src/graph/query.mjs +159 -0
- package/src/graph/research-relations.mjs +69 -0
- package/src/graph/source-page.mjs +12 -0
- package/src/graph/source-preview.mjs +34 -0
- package/src/graph/validate.mjs +76 -0
- package/src/job.mjs +496 -0
- package/src/network/autodl-routing-proxy.mjs +462 -0
- package/src/network/egress-proxy.mjs +158 -0
- package/src/observability/event-contract.mjs +230 -0
- package/src/observability/pipeline-monitor.mjs +166 -0
- package/src/pipeline/orchestrator.mjs +1281 -0
- package/src/pipeline/recovery-error.mjs +11 -0
- package/src/pipeline/replay.mjs +304 -0
- package/src/pipeline/shared-execution.mjs +115 -0
- package/src/pipeline/stage-checkpoint.mjs +86 -0
- package/src/pipeline/stage-recovery.mjs +101 -0
- package/src/pipeline/targets.mjs +110 -0
- package/src/process.mjs +143 -0
- package/src/protocol.mjs +312 -0
- package/src/provider/codex-account.mjs +44 -0
- package/src/provider/codex-completion.mjs +49 -0
- package/src/provider/completion.mjs +292 -0
- package/src/provider/model-client.mjs +44 -0
- package/src/provider/model-route.mjs +29 -0
- package/src/provider/openrouter-readiness.mjs +189 -0
- package/src/provider/reader-bridge.mjs +35 -0
- package/src/provider/relay.mjs +263 -0
- package/src/provider/runtime-auth.mjs +40 -0
- package/src/public/cap.d.mts +90 -0
- package/src/public/cap.mjs +12 -0
- package/src/public/contracts.d.mts +2 -0
- package/src/public/host.mjs +171 -0
- package/src/public/operations.d.mts +11 -0
- package/src/public/presentation.d.mts +4 -0
- package/src/records/views.mjs +26 -0
- package/src/remote/command.mjs +178 -0
- package/src/remote/ssh.mjs +59 -0
- package/src/repository-origin.mjs +81 -0
- package/src/reproduction/evidence-feedback.mjs +96 -0
- package/src/reproduction/incomplete-initialization.mjs +25 -0
- package/src/reproduction/lifecycle.mjs +253 -0
- package/src/reproduction/plan.mjs +132 -0
- package/src/reproduction/prompts.mjs +70 -0
- package/src/reproduction/runner.mjs +188 -0
- package/src/reproduction/summary.mjs +130 -0
- package/src/reproduction/workspace-mode.mjs +7 -0
- package/src/research/automatic-admission.mjs +156 -0
- package/src/research/compiler-coverage.mjs +85 -0
- package/src/research/compiler-failure.mjs +24 -0
- package/src/research/compiler-normalization-guards.mjs +112 -0
- package/src/research/compiler-repair.mjs +3 -0
- package/src/research/compiler.mjs +853 -0
- package/src/research/continuation-selection.mjs +26 -0
- package/src/research/execution-graph-context.mjs +43 -0
- package/src/research/experiment-importance.mjs +15 -0
- package/src/research/inventory-handoff.mjs +104 -0
- package/src/research/inventory-revisions.mjs +32 -0
- package/src/research/mineru-local.mjs +73 -0
- package/src/research/paper-command.mjs +19 -0
- package/src/research/paper-markdown.mjs +180 -0
- package/src/research/paper-source-map.mjs +69 -0
- package/src/research/planning-policy.mjs +88 -0
- package/src/research/reference-materials.mjs +11 -0
- package/src/research/reproduction-scope.mjs +30 -0
- package/src/research/research-map.mjs +94 -0
- package/src/research/research-objects.mjs +88 -0
- package/src/research/source-discovery.mjs +646 -0
- package/src/research/source-observations.mjs +75 -0
- package/src/research/source-review-cli-mcp.mjs +26 -0
- package/src/research/source-review-input.mjs +209 -0
- package/src/research/source-review-local-codex.mjs +36 -0
- package/src/research/source-review-model.mjs +70 -0
- package/src/research/source-review.mjs +173 -0
- package/src/research/structure.mjs +3163 -0
- package/src/research-card/renderer.mjs +277 -0
- package/src/research-card/verified-conclusion.mjs +143 -0
- package/src/results/output-registry.mjs +183 -0
- package/src/runtime/claude-code.mjs +52 -0
- package/src/runtime/codex-capacity-retry.mjs +87 -0
- package/src/runtime/codex.mjs +64 -0
- package/src/runtime/config.mjs +157 -0
- package/src/runtime/final-output.mjs +40 -0
- package/src/runtime/index.mjs +21 -0
- package/src/runtime/local-codex.mjs +74 -0
- package/src/runtime/opencode.mjs +95 -0
- package/src/runtime/prompt.mjs +13 -0
- package/src/sandbox/docker.mjs +363 -0
- package/src/settings/command.mjs +297 -0
- package/src/settings/store.mjs +119 -0
- package/src/telemetry/pricing.mjs +68 -0
- package/src/telemetry/usage.mjs +265 -0
- package/src/terminal/events.mjs +97 -0
- package/src/terminal/input.mjs +40 -0
- package/src/terminal/plain.mjs +40 -0
- package/src/terminal/remote-stream.mjs +22 -0
- package/src/terminal/screen.mjs +214 -0
- package/src/terminal/transcript.mjs +69 -0
- package/src/util.mjs +107 -0
- package/src/verification/ai-assessor.mjs +534 -0
- package/src/verification/claim-evaluator.mjs +242 -0
- package/src/verification/evidence-context.mjs +165 -0
- package/src/verification/evidence-reader.mjs +95 -0
- package/src/verification/integrity.mjs +570 -0
- package/src/verification/tolerance.mjs +32 -0
- package/src/workloads/cpu-research-preparation.mjs +56 -0
- package/src/workloads/definition.mjs +74 -0
- package/src/workloads/phase-aware-reproduction.mjs +46 -0
- package/src/workloads/reproduction.mjs +85 -0
- package/src/workspace/command.mjs +242 -0
- package/src/workspace/control.mjs +49 -0
- package/src/workspace/entry.mjs +28 -0
- package/src/workspace/input.mjs +93 -0
- package/src/workspace/interactive.mjs +94 -0
- package/src/workspace/jobs.mjs +418 -0
- package/src/workspace/session.mjs +97 -0
- package/src/workspace/worker.mjs +137 -0
- package/ui/arkgraph/ambient-motion.mjs +10 -0
- package/ui/arkgraph/app.jsx +153 -0
- package/ui/arkgraph/boot.js +6 -0
- package/ui/arkgraph/camera-motion.mjs +20 -0
- package/ui/arkgraph/context-reveal.mjs +39 -0
- package/ui/arkgraph/details.css +3 -0
- package/ui/arkgraph/entry.jsx +28 -0
- package/ui/arkgraph/experiment-curves.mjs +17 -0
- package/ui/arkgraph/experiment-selection.mjs +15 -0
- package/ui/arkgraph/experiment-style.css +26 -0
- package/ui/arkgraph/experiment-ui.jsx +32 -0
- package/ui/arkgraph/frame.html +1 -0
- package/ui/arkgraph/graph-gestures.mjs +62 -0
- package/ui/arkgraph/label-layout.mjs +57 -0
- package/ui/arkgraph/locales/en.json +229 -0
- package/ui/arkgraph/locales/source-types.json +15 -0
- package/ui/arkgraph/localization-build.mjs +27 -0
- package/ui/arkgraph/material-build.mjs +23 -0
- package/ui/arkgraph/material-colors.mjs +39 -0
- package/ui/arkgraph/material-style.css +15 -0
- package/ui/arkgraph/open-graph.jsx +326 -0
- package/ui/arkgraph/outline.jsx +49 -0
- package/ui/arkgraph/package-lock.json +888 -0
- package/ui/arkgraph/package.json +17 -0
- package/ui/arkgraph/reading-layout.mjs +130 -0
- package/ui/arkgraph/reading-presentation.mjs +73 -0
- package/ui/arkgraph/record-detail.css +51 -0
- package/ui/arkgraph/record-details.jsx +29 -0
- package/ui/arkgraph/research-types.mjs +31 -0
- package/ui/arkgraph/selection-mark.jsx +6 -0
- package/ui/arkgraph/soft-spine.mjs +26 -0
- package/ui/arkgraph/steering-style.css +187 -0
- package/ui/arkgraph/style.css +272 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { lstat, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { executionContractMeasurements } from "../contracts/execution-contract.mjs";
|
|
5
|
+
import { CiteArkError, pathExists, safeRelativePath, sha256File, writeJson } from "../util.mjs";
|
|
6
|
+
import { getEvidenceParser } from "./parser-registry.mjs";
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
listEvidenceParsers,
|
|
10
|
+
registerEvidenceParser,
|
|
11
|
+
validateEvidenceParserDescriptor,
|
|
12
|
+
} from "./parser-registry.mjs";
|
|
13
|
+
|
|
14
|
+
export async function parseContractEvidence({ contract, outputDirectory, metricsPath, parsedAt, result }) {
|
|
15
|
+
const bindings = executionContractMeasurements(contract);
|
|
16
|
+
const measurements = await Promise.all(bindings.map((binding) =>
|
|
17
|
+
parseMeasurementEvidence({ contract, binding, outputDirectory, parsedAt, result }),
|
|
18
|
+
));
|
|
19
|
+
const metrics = {
|
|
20
|
+
schemaVersion: "1.0",
|
|
21
|
+
contractDigest: contract.contractDigest,
|
|
22
|
+
...(contract.executionPlan ? { executionPlanDigest: contract.executionPlan.digest } : {}),
|
|
23
|
+
measurements,
|
|
24
|
+
parsedAt: parsedAt ?? new Date().toISOString(),
|
|
25
|
+
};
|
|
26
|
+
if (metricsPath) await writeJson(metricsPath, metrics);
|
|
27
|
+
return metrics;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function parseMeasurementEvidence({ contract, binding, outputDirectory, parsedAt, result }) {
|
|
31
|
+
const descriptor = binding.parser;
|
|
32
|
+
const parser = getEvidenceParser(descriptor);
|
|
33
|
+
if (!parser) throw new CiteArkError(`没有注册证据解析器:${descriptor.id}@${descriptor.version}`);
|
|
34
|
+
if (!safeRelativePath(descriptor.evidencePath)) throw new CiteArkError("parser.evidencePath 不是安全相对路径");
|
|
35
|
+
const evidencePath = path.resolve(outputDirectory, descriptor.evidencePath);
|
|
36
|
+
const allowUnavailableMetric = result?.execution?.status === "failed" || result?.execution?.status === "partial";
|
|
37
|
+
const resultProvided = result !== undefined && result !== null;
|
|
38
|
+
const evidencePromoted = Array.isArray(result?.evidence)
|
|
39
|
+
&& result.evidence.some((item) => item?.path === descriptor.evidencePath);
|
|
40
|
+
if (resultProvided && !evidencePromoted) {
|
|
41
|
+
const reason = `The submitted result did not promote parser evidence at ${descriptor.evidencePath}`;
|
|
42
|
+
if (!allowUnavailableMetric) throw new CiteArkError(reason);
|
|
43
|
+
return await unavailableMetrics({
|
|
44
|
+
contract,
|
|
45
|
+
binding,
|
|
46
|
+
descriptor,
|
|
47
|
+
parser,
|
|
48
|
+
parsedAt,
|
|
49
|
+
result,
|
|
50
|
+
reason,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (!(await pathExists(evidencePath))) {
|
|
54
|
+
if (!allowUnavailableMetric) throw new CiteArkError(`解析器证据不存在:${descriptor.evidencePath}`);
|
|
55
|
+
return await unavailableMetrics({
|
|
56
|
+
contract,
|
|
57
|
+
binding,
|
|
58
|
+
descriptor,
|
|
59
|
+
parser,
|
|
60
|
+
parsedAt,
|
|
61
|
+
result,
|
|
62
|
+
reason: `The execution did not produce parser evidence at ${descriptor.evidencePath}`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const [text, stat, digest] = await Promise.all([
|
|
66
|
+
readFile(evidencePath, "utf8"),
|
|
67
|
+
lstat(evidencePath),
|
|
68
|
+
sha256File(evidencePath),
|
|
69
|
+
]);
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = parser.parse(text, descriptor.config ?? {});
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (!allowUnavailableMetric) throw error;
|
|
75
|
+
return await unavailableMetrics({
|
|
76
|
+
contract,
|
|
77
|
+
binding,
|
|
78
|
+
descriptor,
|
|
79
|
+
parser,
|
|
80
|
+
parsedAt,
|
|
81
|
+
result,
|
|
82
|
+
sourceEvidence: { path: descriptor.evidencePath, sha256: digest, size: stat.size },
|
|
83
|
+
reason: `The preserved parser evidence did not contain a trustworthy metric: ${error instanceof Error ? error.message : String(error)}`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (parsed.primary.metric !== binding.metric || parsed.primary.unit !== binding.unit) {
|
|
87
|
+
throw new CiteArkError("解析器主指标与 execution contract 不一致");
|
|
88
|
+
}
|
|
89
|
+
const metrics = {
|
|
90
|
+
schemaVersion: "0.1",
|
|
91
|
+
contractDigest: contract.contractDigest,
|
|
92
|
+
measurementId: binding.measurementId,
|
|
93
|
+
parser: { id: parser.id, version: parser.version, config: descriptor.config ?? {} },
|
|
94
|
+
sourceEvidence: {
|
|
95
|
+
path: descriptor.evidencePath,
|
|
96
|
+
sha256: digest,
|
|
97
|
+
size: stat.size,
|
|
98
|
+
producer: "execution-sandbox",
|
|
99
|
+
trust: "agent-produced-contract-pinned-input",
|
|
100
|
+
},
|
|
101
|
+
observations: parsed.observations,
|
|
102
|
+
primary: parsed.primary,
|
|
103
|
+
checks: parsed.checks ?? [],
|
|
104
|
+
availability: {
|
|
105
|
+
status: "available",
|
|
106
|
+
executionStatus: result?.execution?.status ?? "unknown",
|
|
107
|
+
reason: null,
|
|
108
|
+
},
|
|
109
|
+
parsedAt: parsedAt ?? new Date().toISOString(),
|
|
110
|
+
};
|
|
111
|
+
return metrics;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function unavailableMetrics({
|
|
115
|
+
contract,
|
|
116
|
+
binding,
|
|
117
|
+
descriptor,
|
|
118
|
+
parser,
|
|
119
|
+
parsedAt,
|
|
120
|
+
result,
|
|
121
|
+
sourceEvidence = null,
|
|
122
|
+
reason,
|
|
123
|
+
}) {
|
|
124
|
+
const metrics = {
|
|
125
|
+
schemaVersion: "0.1",
|
|
126
|
+
contractDigest: contract.contractDigest,
|
|
127
|
+
measurementId: binding.measurementId,
|
|
128
|
+
parser: { id: parser.id, version: parser.version, config: descriptor.config ?? {} },
|
|
129
|
+
sourceEvidence,
|
|
130
|
+
observations: [],
|
|
131
|
+
primary: null,
|
|
132
|
+
checks: [{
|
|
133
|
+
name: "metric_available",
|
|
134
|
+
status: "failed",
|
|
135
|
+
required: false,
|
|
136
|
+
description: reason,
|
|
137
|
+
}],
|
|
138
|
+
availability: {
|
|
139
|
+
status: "unavailable",
|
|
140
|
+
executionStatus: result?.execution?.status ?? "unknown",
|
|
141
|
+
reason,
|
|
142
|
+
failure: result?.execution?.failure ?? null,
|
|
143
|
+
},
|
|
144
|
+
parsedAt: parsedAt ?? new Date().toISOString(),
|
|
145
|
+
};
|
|
146
|
+
return metrics;
|
|
147
|
+
}
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { appendFile, lstat, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { redactString } from "../cap/redaction.mjs";
|
|
6
|
+
import {
|
|
7
|
+
pathExists,
|
|
8
|
+
readJson,
|
|
9
|
+
sha256File,
|
|
10
|
+
sha256Value,
|
|
11
|
+
writeJson,
|
|
12
|
+
} from "../util.mjs";
|
|
13
|
+
|
|
14
|
+
const RUNNER_EVENTS = "runner-events.jsonl";
|
|
15
|
+
const COMMAND_RECORDS = "command-records.jsonl";
|
|
16
|
+
const RUNNER_AUDIT = "runner-audit.json";
|
|
17
|
+
const RUNNER_BOUNDARY = "runner-boundary.json";
|
|
18
|
+
|
|
19
|
+
export async function recordRunnerProcessStarted({
|
|
20
|
+
executionDirectory,
|
|
21
|
+
runId,
|
|
22
|
+
attempt,
|
|
23
|
+
executor,
|
|
24
|
+
command,
|
|
25
|
+
args,
|
|
26
|
+
workingDirectory,
|
|
27
|
+
captureBoundary,
|
|
28
|
+
}) {
|
|
29
|
+
const event = {
|
|
30
|
+
schemaVersion: "0.1",
|
|
31
|
+
type: "runner.process_started",
|
|
32
|
+
eventId: `${runId}:${attempt}:${Date.now()}`,
|
|
33
|
+
runId,
|
|
34
|
+
attempt,
|
|
35
|
+
executor,
|
|
36
|
+
command,
|
|
37
|
+
args,
|
|
38
|
+
workingDirectory,
|
|
39
|
+
captureBoundary,
|
|
40
|
+
startedAt: new Date().toISOString(),
|
|
41
|
+
};
|
|
42
|
+
await appendJsonLine(path.join(executionDirectory, RUNNER_EVENTS), event);
|
|
43
|
+
return event;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function recordRunnerProcessFinished({
|
|
47
|
+
executionDirectory,
|
|
48
|
+
startedEvent,
|
|
49
|
+
exitCode,
|
|
50
|
+
signal,
|
|
51
|
+
timedOut,
|
|
52
|
+
}) {
|
|
53
|
+
const finishedAt = new Date().toISOString();
|
|
54
|
+
await appendJsonLine(path.join(executionDirectory, RUNNER_EVENTS), {
|
|
55
|
+
schemaVersion: "0.1",
|
|
56
|
+
type: "runner.process_finished",
|
|
57
|
+
eventId: startedEvent.eventId,
|
|
58
|
+
runId: startedEvent.runId,
|
|
59
|
+
attempt: startedEvent.attempt,
|
|
60
|
+
executor: startedEvent.executor,
|
|
61
|
+
exitCode,
|
|
62
|
+
signal,
|
|
63
|
+
timedOut: Boolean(timedOut),
|
|
64
|
+
startedAt: startedEvent.startedAt,
|
|
65
|
+
finishedAt,
|
|
66
|
+
durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedEvent.startedAt)),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function buildRunnerAudit({ runDirectory, runtime, sandbox }) {
|
|
71
|
+
const executionDirectory = path.join(runDirectory, "execution");
|
|
72
|
+
const tracePath = path.join(executionDirectory, "trace.jsonl");
|
|
73
|
+
const stderrPath = path.join(executionDirectory, "claude-stderr.log");
|
|
74
|
+
const eventsPath = path.join(executionDirectory, RUNNER_EVENTS);
|
|
75
|
+
const recordsPath = path.join(executionDirectory, COMMAND_RECORDS);
|
|
76
|
+
const boundaryPath = path.join(executionDirectory, RUNNER_BOUNDARY);
|
|
77
|
+
const runPath = path.join(runDirectory, "run.json");
|
|
78
|
+
const run = (await pathExists(runPath)) ? await readJson(runPath) : null;
|
|
79
|
+
const trace = (await pathExists(tracePath)) ? await readFile(tracePath, "utf8") : "";
|
|
80
|
+
const commandRecords = extractRunnerCommandRecords(trace, { runtime });
|
|
81
|
+
await writeFile(
|
|
82
|
+
recordsPath,
|
|
83
|
+
commandRecords.length
|
|
84
|
+
? `${commandRecords.map((record) => JSON.stringify(record)).join("\n")}\n`
|
|
85
|
+
: "",
|
|
86
|
+
"utf8",
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (!(await pathExists(eventsPath)) && Array.isArray(run?.attempts)) {
|
|
90
|
+
const fallback = run.attempts.flatMap((attempt) => [{
|
|
91
|
+
schemaVersion: "0.1",
|
|
92
|
+
type: "runner.process_observed",
|
|
93
|
+
eventId: `${run.runId}:${attempt.attempt}:run-record`,
|
|
94
|
+
runId: run.runId,
|
|
95
|
+
attempt: attempt.attempt,
|
|
96
|
+
executor: attempt.executor ?? sandbox ?? run.sandbox ?? "unknown",
|
|
97
|
+
startedAt: attempt.startedAt ?? null,
|
|
98
|
+
finishedAt: attempt.finishedAt ?? null,
|
|
99
|
+
exitCode: attempt.exitCode ?? null,
|
|
100
|
+
signal: attempt.signal ?? null,
|
|
101
|
+
timedOut: Boolean(attempt.timedOut),
|
|
102
|
+
captureBoundary: "out-of-sandbox-run-record",
|
|
103
|
+
}]);
|
|
104
|
+
await writeFile(
|
|
105
|
+
eventsPath,
|
|
106
|
+
fallback.length ? `${fallback.map((event) => JSON.stringify(event)).join("\n")}\n` : "",
|
|
107
|
+
"utf8",
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const eventCount = await jsonLineCount(eventsPath);
|
|
112
|
+
const rawStreams = {
|
|
113
|
+
stdout: await fileDescriptor(tracePath, "execution/trace.jsonl"),
|
|
114
|
+
stderr: await fileDescriptor(stderrPath, "execution/claude-stderr.log"),
|
|
115
|
+
};
|
|
116
|
+
const companions = {
|
|
117
|
+
runnerEvents: await requiredFileDescriptor(eventsPath, `execution/${RUNNER_EVENTS}`),
|
|
118
|
+
commandRecords: await requiredFileDescriptor(recordsPath, `execution/${COMMAND_RECORDS}`),
|
|
119
|
+
};
|
|
120
|
+
const runnerBoundary = await fileDescriptor(boundaryPath, `execution/${RUNNER_BOUNDARY}`);
|
|
121
|
+
if (runnerBoundary) companions.runnerBoundary = runnerBoundary;
|
|
122
|
+
const captureBoundary = sandbox === "docker"
|
|
123
|
+
? "host-runner-outside-agent-mounts"
|
|
124
|
+
: new Set(["gcp-batch", "autodl-pro"]).has(sandbox)
|
|
125
|
+
? await remoteCaptureBoundary(executionDirectory, sandbox)
|
|
126
|
+
: "runner-process-stream";
|
|
127
|
+
const base = {
|
|
128
|
+
schemaVersion: "0.1",
|
|
129
|
+
kind: "citeark.runner-execution-audit",
|
|
130
|
+
runId: run?.runId ?? null,
|
|
131
|
+
runtime,
|
|
132
|
+
sandbox,
|
|
133
|
+
generatedAt: run?.finishedAt ?? new Date().toISOString(),
|
|
134
|
+
status: rawStreams.stdout && eventCount > 0 ? "complete" : "degraded",
|
|
135
|
+
captureBoundary,
|
|
136
|
+
contentPolicy: {
|
|
137
|
+
commandAndOutputRetention: "verbatim-unless-likely-secret-redacted",
|
|
138
|
+
privateReasoningRetained: false,
|
|
139
|
+
rawRuntimeStreamRetention: "digest-only",
|
|
140
|
+
},
|
|
141
|
+
rawStreams,
|
|
142
|
+
companions,
|
|
143
|
+
processEventCount: eventCount,
|
|
144
|
+
commandRecordCount: commandRecords.length,
|
|
145
|
+
completedCommandRecordCount: commandRecords.filter((record) => record.finishedAt).length,
|
|
146
|
+
commandCapture: {
|
|
147
|
+
source: "runner-captured-runtime-json-stream",
|
|
148
|
+
parser: `${runtime ?? "unknown"}-runtime-events-v0.1`,
|
|
149
|
+
recordsPath: `execution/${COMMAND_RECORDS}`,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
const audit = { ...base, auditDigest: `sha256:${sha256Value(base)}` };
|
|
153
|
+
await writeJson(path.join(executionDirectory, RUNNER_AUDIT), audit);
|
|
154
|
+
return { audit, commandRecords };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function loadAndVerifyRunnerAudit(runDirectory) {
|
|
158
|
+
const executionDirectory = path.join(runDirectory, "execution");
|
|
159
|
+
const auditPath = path.join(executionDirectory, RUNNER_AUDIT);
|
|
160
|
+
if (!(await pathExists(auditPath))) {
|
|
161
|
+
return { audit: null, commandRecords: [], issues: ["runner-audit.json is missing"] };
|
|
162
|
+
}
|
|
163
|
+
const audit = await readJson(auditPath);
|
|
164
|
+
const { auditDigest, ...base } = audit;
|
|
165
|
+
const issues = [];
|
|
166
|
+
if (auditDigest !== `sha256:${sha256Value(base)}`) issues.push("runner audit digest mismatch");
|
|
167
|
+
for (const descriptor of Object.values(audit.companions ?? {})) {
|
|
168
|
+
if (!descriptor?.path) {
|
|
169
|
+
issues.push("runner audit companion path is missing");
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const relative = descriptor.path.replace(/^execution\//, "");
|
|
173
|
+
const target = path.join(executionDirectory, relative);
|
|
174
|
+
if (!(await pathExists(target))) {
|
|
175
|
+
issues.push(`runner audit companion is missing: ${descriptor.path}`);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const stat = await lstat(target);
|
|
179
|
+
const digest = await sha256File(target);
|
|
180
|
+
if (descriptor.sha256 !== digest || descriptor.size !== stat.size) {
|
|
181
|
+
issues.push(`runner audit companion digest mismatch: ${descriptor.path}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (const descriptor of Object.values(audit.rawStreams ?? {}).filter(Boolean)) {
|
|
185
|
+
const relative = descriptor.path.replace(/^execution\//, "");
|
|
186
|
+
const target = path.join(executionDirectory, relative);
|
|
187
|
+
if (!(await pathExists(target))) {
|
|
188
|
+
issues.push(`runner raw stream is missing: ${descriptor.path}`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const stat = await lstat(target);
|
|
192
|
+
const digest = await sha256File(target);
|
|
193
|
+
if (descriptor.sha256 !== digest || descriptor.size !== stat.size) {
|
|
194
|
+
issues.push(`runner raw stream digest mismatch: ${descriptor.path}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const recordsPath = path.join(executionDirectory, COMMAND_RECORDS);
|
|
198
|
+
const commandRecords = await readJsonLines(recordsPath);
|
|
199
|
+
if (commandRecords.length !== audit.commandRecordCount) {
|
|
200
|
+
issues.push("runner audit command record count mismatch");
|
|
201
|
+
}
|
|
202
|
+
return { audit, commandRecords, issues };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function extractRunnerCommandRecords(rawTrace, { runtime } = {}) {
|
|
206
|
+
const parsed = parseTraceLines(rawTrace);
|
|
207
|
+
const normalizedRuntime = runtime ?? detectRuntime(parsed.map((item) => item.event));
|
|
208
|
+
if (normalizedRuntime === "codex") return extractCodexCommands(parsed);
|
|
209
|
+
if (normalizedRuntime === "opencode") return extractOpenCodeCommands(parsed);
|
|
210
|
+
return extractClaudeCommands(parsed, normalizedRuntime ?? "claude-code");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function extractClaudeCommands(lines, runtime) {
|
|
214
|
+
const pending = new Map();
|
|
215
|
+
const records = [];
|
|
216
|
+
let attempt = 1;
|
|
217
|
+
for (const line of lines) {
|
|
218
|
+
const event = line.event;
|
|
219
|
+
if (event.type === "citeark.attempt_started" && Number.isInteger(event.attempt)) {
|
|
220
|
+
attempt = event.attempt;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const content = Array.isArray(event.message?.content) ? event.message.content : [];
|
|
224
|
+
if (event.type === "assistant") {
|
|
225
|
+
for (const item of content) {
|
|
226
|
+
if (item?.type !== "tool_use" || !shellTool(item.name)) continue;
|
|
227
|
+
const command = item.input?.command;
|
|
228
|
+
if (typeof command !== "string" || !command.trim()) continue;
|
|
229
|
+
pending.set(item.id, {
|
|
230
|
+
schemaVersion: "0.1",
|
|
231
|
+
kind: "runtime-tool-command",
|
|
232
|
+
recordId: item.id,
|
|
233
|
+
runtime,
|
|
234
|
+
attempt,
|
|
235
|
+
tool: item.name,
|
|
236
|
+
command: protectedText(command),
|
|
237
|
+
workingDirectory: stringOrNull(item.input?.cwd),
|
|
238
|
+
startedAt: timestamp(event),
|
|
239
|
+
finishedAt: null,
|
|
240
|
+
exitCode: null,
|
|
241
|
+
exitCodeSource: "unavailable",
|
|
242
|
+
status: "started",
|
|
243
|
+
stdout: protectedText(""),
|
|
244
|
+
stderr: protectedText(""),
|
|
245
|
+
sourceEventDigests: [line.sha256],
|
|
246
|
+
provenance: "runner-captured-runtime-json-stream",
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (event.type !== "user") continue;
|
|
252
|
+
for (const item of content) {
|
|
253
|
+
if (item?.type !== "tool_result") continue;
|
|
254
|
+
const record = pending.get(item.tool_use_id);
|
|
255
|
+
if (!record) continue;
|
|
256
|
+
const result = event.tool_use_result ?? {};
|
|
257
|
+
const stdout = typeof result.stdout === "string"
|
|
258
|
+
? result.stdout
|
|
259
|
+
: typeof item.content === "string" ? item.content : "";
|
|
260
|
+
const stderr = typeof result.stderr === "string" ? result.stderr : "";
|
|
261
|
+
const explicitExit = finiteInteger(result.exitCode ?? result.exit_code);
|
|
262
|
+
const parsedExit = explicitExit ?? exitCodeFromText(item.content);
|
|
263
|
+
const isError = item.is_error === true || event.is_error === true;
|
|
264
|
+
record.finishedAt = timestamp(event);
|
|
265
|
+
record.exitCode = parsedExit ?? (isError ? null : 0);
|
|
266
|
+
record.exitCodeSource = parsedExit !== null && parsedExit !== undefined
|
|
267
|
+
? "runtime-reported"
|
|
268
|
+
: isError ? "unavailable" : "runtime-success-status";
|
|
269
|
+
record.status = isError ? "failed" : "completed";
|
|
270
|
+
record.stdout = protectedText(stdout);
|
|
271
|
+
record.stderr = protectedText(stderr);
|
|
272
|
+
record.interrupted = result.interrupted === true;
|
|
273
|
+
record.sourceEventDigests.push(line.sha256);
|
|
274
|
+
records.push(record);
|
|
275
|
+
pending.delete(item.tool_use_id);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
records.push(...pending.values());
|
|
279
|
+
return records;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function extractCodexCommands(lines) {
|
|
283
|
+
const pending = new Map();
|
|
284
|
+
const records = [];
|
|
285
|
+
let attempt = 1;
|
|
286
|
+
for (const line of lines) {
|
|
287
|
+
const event = line.event;
|
|
288
|
+
if (event.type === "citeark.attempt_started" && Number.isInteger(event.attempt)) {
|
|
289
|
+
attempt = event.attempt;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const item = event.item;
|
|
293
|
+
if (!item || item.type !== "command_execution") continue;
|
|
294
|
+
const id = item.id ?? `codex:${line.order}`;
|
|
295
|
+
if (event.type === "item.started") {
|
|
296
|
+
pending.set(id, baseCodexRecord({ item, line, attempt, id }));
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (event.type !== "item.completed") continue;
|
|
300
|
+
const record = pending.get(id) ?? baseCodexRecord({ item, line, attempt, id });
|
|
301
|
+
const exitCode = finiteInteger(item.exit_code);
|
|
302
|
+
record.finishedAt = timestamp(event);
|
|
303
|
+
record.exitCode = exitCode;
|
|
304
|
+
record.exitCodeSource = exitCode === null ? "unavailable" : "runtime-reported";
|
|
305
|
+
record.status = item.status ?? (exitCode === 0 ? "completed" : "failed");
|
|
306
|
+
record.stdout = protectedText(item.aggregated_output ?? "");
|
|
307
|
+
record.stderr = protectedText(item.stderr ?? "");
|
|
308
|
+
record.sourceEventDigests.push(line.sha256);
|
|
309
|
+
records.push(record);
|
|
310
|
+
pending.delete(id);
|
|
311
|
+
}
|
|
312
|
+
records.push(...pending.values());
|
|
313
|
+
return records;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function baseCodexRecord({ item, line, attempt, id }) {
|
|
317
|
+
return {
|
|
318
|
+
schemaVersion: "0.1",
|
|
319
|
+
kind: "runtime-tool-command",
|
|
320
|
+
recordId: id,
|
|
321
|
+
runtime: "codex",
|
|
322
|
+
attempt,
|
|
323
|
+
tool: "command_execution",
|
|
324
|
+
command: protectedText(item.command ?? ""),
|
|
325
|
+
workingDirectory: stringOrNull(item.cwd),
|
|
326
|
+
startedAt: timestamp(line.event),
|
|
327
|
+
finishedAt: null,
|
|
328
|
+
exitCode: null,
|
|
329
|
+
exitCodeSource: "unavailable",
|
|
330
|
+
status: "started",
|
|
331
|
+
stdout: protectedText(""),
|
|
332
|
+
stderr: protectedText(""),
|
|
333
|
+
sourceEventDigests: [line.sha256],
|
|
334
|
+
provenance: "runner-captured-runtime-json-stream",
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function extractOpenCodeCommands(lines) {
|
|
339
|
+
const records = [];
|
|
340
|
+
let attempt = 1;
|
|
341
|
+
for (const line of lines) {
|
|
342
|
+
const event = line.event;
|
|
343
|
+
if (event.type === "citeark.attempt_started" && Number.isInteger(event.attempt)) {
|
|
344
|
+
attempt = event.attempt;
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const part = event.part ?? event.item ?? event;
|
|
348
|
+
const state = part.state ?? {};
|
|
349
|
+
const tool = part.tool ?? part.name ?? event.tool;
|
|
350
|
+
const command = state.input?.command ?? part.input?.command;
|
|
351
|
+
if (!shellTool(tool) || typeof command !== "string" || !command.trim()) continue;
|
|
352
|
+
const exitCode = finiteInteger(state.metadata?.exit ?? state.metadata?.exitCode ?? part.exit_code);
|
|
353
|
+
const output = state.output ?? part.output ?? "";
|
|
354
|
+
records.push({
|
|
355
|
+
schemaVersion: "0.1",
|
|
356
|
+
kind: "runtime-tool-command",
|
|
357
|
+
recordId: part.callID ?? part.id ?? `opencode:${line.order}`,
|
|
358
|
+
runtime: "opencode",
|
|
359
|
+
attempt,
|
|
360
|
+
tool,
|
|
361
|
+
command: protectedText(command),
|
|
362
|
+
workingDirectory: stringOrNull(state.input?.cwd ?? part.input?.cwd),
|
|
363
|
+
startedAt: isoFromMilliseconds(state.time?.start) ?? timestamp(event),
|
|
364
|
+
finishedAt: isoFromMilliseconds(state.time?.end) ?? timestamp(event),
|
|
365
|
+
exitCode,
|
|
366
|
+
exitCodeSource: exitCode === null ? "unavailable" : "runtime-reported",
|
|
367
|
+
status: state.status ?? (exitCode === 0 ? "completed" : exitCode === null ? "unknown" : "failed"),
|
|
368
|
+
stdout: protectedText(typeof output === "string" ? output : JSON.stringify(output)),
|
|
369
|
+
stderr: protectedText(typeof state.metadata?.stderr === "string" ? state.metadata.stderr : ""),
|
|
370
|
+
sourceEventDigests: [line.sha256],
|
|
371
|
+
provenance: "runner-captured-runtime-json-stream",
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
return records;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function parseTraceLines(rawTrace) {
|
|
378
|
+
const output = [];
|
|
379
|
+
let order = 0;
|
|
380
|
+
for (const rawLine of String(rawTrace ?? "").split(/\r?\n/)) {
|
|
381
|
+
if (!rawLine.trim()) continue;
|
|
382
|
+
try {
|
|
383
|
+
const event = JSON.parse(rawLine);
|
|
384
|
+
if (event && typeof event === "object" && !Array.isArray(event)) {
|
|
385
|
+
output.push({ event, order, sha256: sha256Text(rawLine) });
|
|
386
|
+
}
|
|
387
|
+
} catch {
|
|
388
|
+
// Non-JSON runtime output remains covered by the raw stream digest.
|
|
389
|
+
}
|
|
390
|
+
order += 1;
|
|
391
|
+
}
|
|
392
|
+
return output;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function protectedText(value) {
|
|
396
|
+
const raw = String(value ?? "");
|
|
397
|
+
const retained = redactString(raw);
|
|
398
|
+
return {
|
|
399
|
+
text: retained,
|
|
400
|
+
rawSha256: sha256Text(raw),
|
|
401
|
+
rawBytes: Buffer.byteLength(raw),
|
|
402
|
+
retainedSha256: sha256Text(retained),
|
|
403
|
+
redactionsApplied: retained !== raw,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function detectRuntime(events) {
|
|
408
|
+
if (events.some((event) => event.type === "item.started" || event.type === "item.completed")) return "codex";
|
|
409
|
+
if (events.some((event) => event.part?.tool || event.part?.state)) return "opencode";
|
|
410
|
+
return "claude-code";
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function shellTool(value) {
|
|
414
|
+
return typeof value === "string" && new Set(["Bash", "bash", "Shell", "shell", "command", "command_execution"]).has(value);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function timestamp(event) {
|
|
418
|
+
return stringOrNull(event.timestamp ?? event.created_at ?? event.recorded_at);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function stringOrNull(value) {
|
|
422
|
+
return typeof value === "string" && value.length ? value : null;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function finiteInteger(value) {
|
|
426
|
+
return Number.isInteger(value) ? value : null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function exitCodeFromText(value) {
|
|
430
|
+
if (typeof value !== "string") return null;
|
|
431
|
+
const matched = value.match(/(?:exit(?:ed)?(?: with)? code|Exit code)\s*[:=]?\s*(\d+)/i);
|
|
432
|
+
return matched ? Number(matched[1]) : null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function isoFromMilliseconds(value) {
|
|
436
|
+
return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : null;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async function appendJsonLine(target, value) {
|
|
440
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
441
|
+
await appendFile(target, `${JSON.stringify(value)}\n`, "utf8");
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async function jsonLineCount(target) {
|
|
445
|
+
return (await readJsonLines(target)).length;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async function readJsonLines(target) {
|
|
449
|
+
if (!(await pathExists(target))) return [];
|
|
450
|
+
const raw = await readFile(target, "utf8");
|
|
451
|
+
return raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
async function fileDescriptor(target, relativePath) {
|
|
455
|
+
if (!(await pathExists(target))) return null;
|
|
456
|
+
const stat = await lstat(target);
|
|
457
|
+
return { path: relativePath, size: stat.size, sha256: await sha256File(target) };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async function requiredFileDescriptor(target, relativePath) {
|
|
461
|
+
return await fileDescriptor(target, relativePath) ?? { path: relativePath, size: 0, sha256: sha256Text("") };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function remoteCaptureBoundary(executionDirectory, sandbox) {
|
|
465
|
+
const boundaryPath = path.join(executionDirectory, RUNNER_BOUNDARY);
|
|
466
|
+
if (!(await pathExists(boundaryPath))) return `${sandbox}-supervisor-unavailable`;
|
|
467
|
+
const boundary = await readJson(boundaryPath).catch(() => null);
|
|
468
|
+
return boundary?.captureBoundary ?? `${sandbox}-supervisor-unavailable`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function sha256Text(value) {
|
|
472
|
+
return createHash("sha256").update(String(value ?? ""), "utf8").digest("hex");
|
|
473
|
+
}
|