@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,92 @@
|
|
|
1
|
+
import { CiteArkError } from "../util.mjs";
|
|
2
|
+
|
|
3
|
+
const CONFIGURATION_FAILURE = /(?:CODE_GCE_BAD_REQUEST|INVALID_ARGUMENT|invalid value for field|boot disk|disk size|sizeGb|must be at least|cannot be smaller|image.*(?:invalid|not found)|machine type.*invalid)/i;
|
|
4
|
+
const CAPACITY_FAILURE = /(?:RESOURCE_POOL_EXHAUSTED|not enough resources|capacity is unavailable|资源不足|容量不足)/i;
|
|
5
|
+
const AUTHORIZATION_FAILURE = /(?:PERMISSION_DENIED|permission denied|not authorized|forbidden|service account.*(?:missing|invalid|permission))/i;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert a provider terminal state into a typed platform failure. Unknown
|
|
9
|
+
* terminal failures fail closed: only explicitly classified transient
|
|
10
|
+
* infrastructure failures may ask the outer Worker for another attempt.
|
|
11
|
+
*/
|
|
12
|
+
export function assertSuccessfulGcpBatchTerminal({
|
|
13
|
+
job,
|
|
14
|
+
jobId,
|
|
15
|
+
workload = "workload",
|
|
16
|
+
infrastructureFailure = null,
|
|
17
|
+
}) {
|
|
18
|
+
const state = job?.status?.state ?? "STATE_UNSPECIFIED";
|
|
19
|
+
if (state === "SUCCEEDED") return;
|
|
20
|
+
|
|
21
|
+
const descriptions = gcpBatchStatusDescriptions(job);
|
|
22
|
+
const detail = descriptions.slice(-3).join(";");
|
|
23
|
+
const evidence = descriptions.join("\n");
|
|
24
|
+
const base = `GCP Batch ${workload} ${jobId} 以 ${state} 结束${detail ? `:${detail}` : ""}`;
|
|
25
|
+
|
|
26
|
+
if (CONFIGURATION_FAILURE.test(evidence)) {
|
|
27
|
+
throw new CiteArkError(base, {
|
|
28
|
+
retryable: false,
|
|
29
|
+
failureCode: "gcp.batch_configuration_invalid",
|
|
30
|
+
failureCategory: "configuration",
|
|
31
|
+
retryDisposition: "never",
|
|
32
|
+
failureOwner: "platform",
|
|
33
|
+
recoveryAction: "修正镜像、启动盘、机器规格或 Batch Job 配置并通过确定性预检后,再创建新执行",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (AUTHORIZATION_FAILURE.test(evidence)) {
|
|
37
|
+
throw new CiteArkError(base, {
|
|
38
|
+
retryable: false,
|
|
39
|
+
failureCode: "gcp.batch_authorization_invalid",
|
|
40
|
+
failureCategory: "configuration",
|
|
41
|
+
retryDisposition: "human_input",
|
|
42
|
+
failureOwner: "platform",
|
|
43
|
+
recoveryAction: "修正 GCP 服务账号、IAM 或资源访问配置后再创建新执行",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (CAPACITY_FAILURE.test(evidence)) {
|
|
47
|
+
throw new CiteArkError(base, {
|
|
48
|
+
retryable: true,
|
|
49
|
+
failureCode: "gcp.capacity_unavailable",
|
|
50
|
+
failureCategory: "infrastructure",
|
|
51
|
+
retryDisposition: "different_profile",
|
|
52
|
+
failureOwner: "provider",
|
|
53
|
+
recoveryAction: "保持科研计划不变,改用尚未尝试的兼容可用区或资源规格",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (infrastructureFailure?.category === "timeout") {
|
|
57
|
+
throw new CiteArkError(base, {
|
|
58
|
+
retryable: false,
|
|
59
|
+
failureCode: "execution.runtime_budget_exhausted",
|
|
60
|
+
failureCategory: "timeout",
|
|
61
|
+
retryDisposition: "new_research_plan",
|
|
62
|
+
failureOwner: "scientific_plan",
|
|
63
|
+
recoveryAction: "提高合规算力或创建声明更长时限的新 Research Plan;不要原样自动重试",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (infrastructureFailure?.retryable === true) {
|
|
67
|
+
throw new CiteArkError(base, {
|
|
68
|
+
retryable: true,
|
|
69
|
+
failureCode: `gcp.infrastructure.${infrastructureFailure.exitCode ?? "transient"}`,
|
|
70
|
+
failureCategory: "infrastructure",
|
|
71
|
+
retryDisposition: "same_batch_task",
|
|
72
|
+
failureOwner: "provider",
|
|
73
|
+
recoveryAction: "从最近检查点恢复同一不可变目标",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw new CiteArkError(base, {
|
|
78
|
+
retryable: false,
|
|
79
|
+
failureCode: "gcp.batch_terminal_unclassified",
|
|
80
|
+
failureCategory: infrastructureFailure?.category ?? "infrastructure",
|
|
81
|
+
retryDisposition: "never",
|
|
82
|
+
failureOwner: "platform",
|
|
83
|
+
recoveryAction: "保留完整 Batch 状态事件并完成错误分类;不得用新 Worker 盲目重试",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function gcpBatchStatusDescriptions(job) {
|
|
88
|
+
return (job?.status?.statusEvents ?? [])
|
|
89
|
+
.map((event) => event?.description)
|
|
90
|
+
.filter((description) => typeof description === "string" && description.trim())
|
|
91
|
+
.map((description) => description.trim());
|
|
92
|
+
}
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { modelRoute } from '../provider/model-route.mjs';
|
|
2
|
+
import { hasSessionEvents } from '../terminal/events.mjs';
|
|
3
|
+
import {
|
|
4
|
+
checkpointObjectPrefix,
|
|
5
|
+
checkpointStatusObject,
|
|
6
|
+
GCP_BATCH_RETRYABLE_INFRASTRUCTURE_EXIT_CODES,
|
|
7
|
+
resolveCheckpointPolicy,
|
|
8
|
+
} from "./checkpoint-protocol.mjs";
|
|
9
|
+
import { buildCheckpointedWorkerScript } from "./gcp-checkpoint-worker.mjs";
|
|
10
|
+
import {
|
|
11
|
+
nativeDeepSeekProvider,
|
|
12
|
+
openCodeModelDefinition,
|
|
13
|
+
openCodeModelReference,
|
|
14
|
+
} from "../runtime/opencode.mjs";
|
|
15
|
+
import { gcpBatchControllerWaitTimeoutMinutes } from "../contracts/execution-timeouts.mjs";
|
|
16
|
+
|
|
17
|
+
export const GCP_STAGING_MOUNT = "/mnt/disks/citeark-staging";
|
|
18
|
+
export const GCP_RESTORE_MOUNT = "/mnt/disks/citeark-restore";
|
|
19
|
+
export const GCP_BATCH_SUPERVISOR_GRACE_SECONDS = 10 * 60;
|
|
20
|
+
export const GCP_COMPILER_PROVIDER_REQUEST_TIMEOUT_SECONDS = 10 * 60;
|
|
21
|
+
export const GCP_COMPILER_AGENT_STALL_TIMEOUT_SECONDS = 20 * 60;
|
|
22
|
+
|
|
23
|
+
export function buildGcpBatchJob({
|
|
24
|
+
bundle,
|
|
25
|
+
invocation,
|
|
26
|
+
resumeInvocation,
|
|
27
|
+
attempt,
|
|
28
|
+
config,
|
|
29
|
+
stageObject,
|
|
30
|
+
resultObject,
|
|
31
|
+
externalObjectPrefix = `${resultObject}.object`,
|
|
32
|
+
checkpointPrefix,
|
|
33
|
+
checkpointStatus,
|
|
34
|
+
restoreCheckpointPrefix,
|
|
35
|
+
completionRelativePath = "output/result.json",
|
|
36
|
+
completionHasEvidence = true,
|
|
37
|
+
completionValidatorRelativePath = null,
|
|
38
|
+
completionSuccessJq = '.execution.status == "succeeded"',
|
|
39
|
+
preserveRebuildableCache = false,
|
|
40
|
+
finalCheckpointKind = "critical",
|
|
41
|
+
finalCheckpointReason = "publication-ready",
|
|
42
|
+
allowCpuHandoff = false,
|
|
43
|
+
runtimeBudgetMinutes,
|
|
44
|
+
workload = "reproduction",
|
|
45
|
+
restoreCheckpointOnStart = false,
|
|
46
|
+
reuseCompletedResultOnRestore = true,
|
|
47
|
+
}) {
|
|
48
|
+
const agent = bundle.runtimeAgent;
|
|
49
|
+
const providerRelay = !agent.codexAccountId && agent.billingMode !== "user_byok"
|
|
50
|
+
&& (usesProviderRelay(agent) || Boolean(bundle.paperBudget));
|
|
51
|
+
const providerRequestTimeoutSeconds = workload === "compilation"
|
|
52
|
+
? GCP_COMPILER_PROVIDER_REQUEST_TIMEOUT_SECONDS
|
|
53
|
+
: 30 * 60;
|
|
54
|
+
const provider = remoteProviderEnvironment(agent, config.providerSecret, {
|
|
55
|
+
providerRelay,
|
|
56
|
+
providerRequestTimeoutSeconds,
|
|
57
|
+
providerCredentialBroker: config.providerCredentialBroker,
|
|
58
|
+
});
|
|
59
|
+
if (bundle.paperBudget && !agent.codexAccountId && agent.billingMode !== "user_byok") {
|
|
60
|
+
provider.variables.CITEARK_PAPER_BUDGET_REQUIRED = "1";
|
|
61
|
+
}
|
|
62
|
+
if (agent.codexAccountId) Object.assign(provider.variables, {
|
|
63
|
+
CITEARK_CODEX_AUTH_SECRET: `projects/${config.projectId}/secrets/citeark-codex-${agent.codexAccountId}/versions/latest`,
|
|
64
|
+
CITEARK_CODEX_ACCOUNT_BUCKET: config.stagingBucket,
|
|
65
|
+
CITEARK_CODEX_ACCOUNT_OWNER: `${bundle.runId}-${attempt}`,
|
|
66
|
+
CITEARK_CODEX_ACCOUNT_LOCK_MODE: "coordinator",
|
|
67
|
+
});
|
|
68
|
+
const instance = {
|
|
69
|
+
...(config.instanceTemplate
|
|
70
|
+
? { instanceTemplate: config.instanceTemplate }
|
|
71
|
+
: { policy: directInstancePolicy({ bundle, config }) }),
|
|
72
|
+
blockProjectSshKeys: true,
|
|
73
|
+
...(bundle.executionEnvironment.gpu === "all"
|
|
74
|
+
? { installGpuDrivers: !(config.instanceTemplate || config.gpuDriversPreinstalled) }
|
|
75
|
+
: {}),
|
|
76
|
+
};
|
|
77
|
+
const restoreMount = restoreCheckpointPrefix
|
|
78
|
+
&& config.restoreCheckpointBucket !== config.stagingBucket
|
|
79
|
+
? GCP_RESTORE_MOUNT
|
|
80
|
+
: GCP_STAGING_MOUNT;
|
|
81
|
+
const taskSpec = {
|
|
82
|
+
runnables: [{
|
|
83
|
+
displayName: `citeark-${workload}-attempt-${attempt}`,
|
|
84
|
+
container: {
|
|
85
|
+
imageUri: bundle.executionEnvironment.image,
|
|
86
|
+
entrypoint: "/bin/bash",
|
|
87
|
+
commands: ["-lc", buildCheckpointedWorkerScript({
|
|
88
|
+
runId: bundle.runId,
|
|
89
|
+
initialInvocation: invocation,
|
|
90
|
+
resumeInvocation,
|
|
91
|
+
attempt,
|
|
92
|
+
stagePath: `${GCP_STAGING_MOUNT}/${stageObject}`,
|
|
93
|
+
resultPath: `${GCP_STAGING_MOUNT}/${resultObject}`,
|
|
94
|
+
liveTracePath: hasSessionEvents() ? `${GCP_STAGING_MOUNT}/${resultObject}.trace.jsonl` : undefined,
|
|
95
|
+
externalObjectPrefixPath: `${GCP_STAGING_MOUNT}/${externalObjectPrefix}`,
|
|
96
|
+
checkpointPrefixPath: `${GCP_STAGING_MOUNT}/${checkpointPrefix}`,
|
|
97
|
+
checkpointStatusPath: `${GCP_STAGING_MOUNT}/${checkpointStatus}`,
|
|
98
|
+
restoreCheckpointPrefixPath: `${restoreMount}/${restoreCheckpointPrefix ?? checkpointPrefix}`,
|
|
99
|
+
checkpointPolicy: config.checkpointing,
|
|
100
|
+
runtimeBudgetSeconds: Math.ceil((Number.isFinite(runtimeBudgetMinutes) && runtimeBudgetMinutes > 0
|
|
101
|
+
? Math.min(runtimeBudgetMinutes, bundle.runtimeTask.environment.timeoutMinutes)
|
|
102
|
+
: bundle.executionEnvironment.timeoutMinutes) * 60),
|
|
103
|
+
completionRelativePath,
|
|
104
|
+
completionHasEvidence,
|
|
105
|
+
completionValidatorRelativePath,
|
|
106
|
+
completionSuccessJq,
|
|
107
|
+
preserveRebuildableCache,
|
|
108
|
+
finalCheckpointKind,
|
|
109
|
+
finalCheckpointReason,
|
|
110
|
+
allowCpuHandoff,
|
|
111
|
+
providerRelay,
|
|
112
|
+
providerRelayPath: "/job/input/runner-provider-relay.mjs",
|
|
113
|
+
agentStallTimeoutSeconds: workload === "compilation"
|
|
114
|
+
? GCP_COMPILER_AGENT_STALL_TIMEOUT_SECONDS
|
|
115
|
+
: 0,
|
|
116
|
+
restoreCheckpointOnStart,
|
|
117
|
+
reuseCompletedResultOnRestore,
|
|
118
|
+
})],
|
|
119
|
+
volumes: [
|
|
120
|
+
`${GCP_STAGING_MOUNT}:${GCP_STAGING_MOUNT}:rw`,
|
|
121
|
+
...(restoreMount === GCP_RESTORE_MOUNT
|
|
122
|
+
? [`${GCP_RESTORE_MOUNT}:${GCP_RESTORE_MOUNT}:ro`]
|
|
123
|
+
: []),
|
|
124
|
+
],
|
|
125
|
+
options: `--shm-size=${bundle.executionEnvironment.shmGb}g`,
|
|
126
|
+
blockExternalNetwork: false,
|
|
127
|
+
},
|
|
128
|
+
}],
|
|
129
|
+
computeResource: {
|
|
130
|
+
cpuMilli: String(Math.ceil(bundle.executionEnvironment.cpus * 1000)),
|
|
131
|
+
memoryMib: String(Math.ceil(bundle.executionEnvironment.memoryGb * 1024)),
|
|
132
|
+
},
|
|
133
|
+
// The Agent enforces the immutable scientific budget itself. Batch gets a
|
|
134
|
+
// small supervisor grace window so a timed-out Agent can still checkpoint,
|
|
135
|
+
// write an auditable failure envelope, and upload the result archive.
|
|
136
|
+
maxRunDuration: `${Math.ceil(bundle.executionEnvironment.timeoutMinutes * 60) + GCP_BATCH_SUPERVISOR_GRACE_SECONDS}s`,
|
|
137
|
+
maxRetryCount: config.infrastructureRetryCount,
|
|
138
|
+
...(config.infrastructureRetryCount > 0 ? {
|
|
139
|
+
lifecyclePolicies: [{
|
|
140
|
+
action: "RETRY_TASK",
|
|
141
|
+
actionCondition: {
|
|
142
|
+
exitCodes: [...GCP_BATCH_RETRYABLE_INFRASTRUCTURE_EXIT_CODES],
|
|
143
|
+
},
|
|
144
|
+
}],
|
|
145
|
+
} : {}),
|
|
146
|
+
volumes: [{
|
|
147
|
+
// Mount the bucket root without mountOptions. The current Batch COS
|
|
148
|
+
// image expands `implicit-dirs` into an invalid GCSFuse v3 command.
|
|
149
|
+
// Root-level, run-unique object names do not need directory emulation.
|
|
150
|
+
gcs: { remotePath: config.stagingBucket },
|
|
151
|
+
mountPath: GCP_STAGING_MOUNT,
|
|
152
|
+
}, ...(restoreMount === GCP_RESTORE_MOUNT ? [{
|
|
153
|
+
gcs: { remotePath: config.restoreCheckpointBucket },
|
|
154
|
+
mountPath: GCP_RESTORE_MOUNT,
|
|
155
|
+
}] : [])],
|
|
156
|
+
environment: provider,
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
taskGroups: [{
|
|
160
|
+
taskSpec,
|
|
161
|
+
taskCount: "1",
|
|
162
|
+
parallelism: "1",
|
|
163
|
+
taskCountPerNode: "1",
|
|
164
|
+
}],
|
|
165
|
+
allocationPolicy: {
|
|
166
|
+
instances: [instance],
|
|
167
|
+
...(config.allowedLocations?.length ? {
|
|
168
|
+
location: { allowedLocations: config.allowedLocations },
|
|
169
|
+
} : {}),
|
|
170
|
+
...(config.serviceAccount ? {
|
|
171
|
+
serviceAccount: {
|
|
172
|
+
email: config.serviceAccount,
|
|
173
|
+
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
|
|
174
|
+
},
|
|
175
|
+
} : {}),
|
|
176
|
+
labels: labels(bundle.runId, attempt, workload),
|
|
177
|
+
},
|
|
178
|
+
labels: labels(bundle.runId, attempt, workload),
|
|
179
|
+
logsPolicy: { destination: "CLOUD_LOGGING" },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function resolveGcpBatchConfig({ bundle, options = {} }) {
|
|
184
|
+
const profile = bundle.computeDecision?.selectedProfile?.executorConfig ?? {};
|
|
185
|
+
const projectId = first(options.projectId, profile.projectId, process.env.CITEARK_GCP_PROJECT, process.env.GOOGLE_CLOUD_PROJECT);
|
|
186
|
+
const region = first(options.region, profile.region, process.env.CITEARK_GCP_BATCH_REGION);
|
|
187
|
+
const stagingBucket = first(options.stagingBucket, profile.stagingBucket, process.env.CITEARK_GCP_STAGING_BUCKET);
|
|
188
|
+
const restoreCheckpointBucket = first(options.restoreCheckpointBucket, stagingBucket);
|
|
189
|
+
const serviceAccount = first(options.serviceAccount, profile.serviceAccount, process.env.CITEARK_GCP_BATCH_SERVICE_ACCOUNT);
|
|
190
|
+
const subscription = modelRoute(bundle.runtimeAgent).transport === "codex_subscription";
|
|
191
|
+
const providerSecret = subscription ? undefined : first(options.providerSecret, process.env.CITEARK_GCP_PROVIDER_SECRET);
|
|
192
|
+
const machineType = first(options.machineType, profile.machineType);
|
|
193
|
+
const instanceTemplate = first(options.instanceTemplate, profile.instanceTemplate);
|
|
194
|
+
const bootDiskImage = first(options.bootDiskImage, profile.bootDiskImage);
|
|
195
|
+
const provisioningModel = first(options.provisioningModel, profile.provisioningModel, "SPOT").toUpperCase();
|
|
196
|
+
const issues = [];
|
|
197
|
+
const providerCredentialBroker = normalizeProviderCredentialBroker(options.providerCredentialBroker, issues);
|
|
198
|
+
if (!projectId) issues.push("缺少 GCP projectId(--gcp-project 或 CITEARK_GCP_PROJECT)");
|
|
199
|
+
if (!region) issues.push("缺少 GCP region(--gcp-region 或 CITEARK_GCP_BATCH_REGION)");
|
|
200
|
+
if (!stagingBucket) issues.push("缺少临时 GCS bucket(--gcp-staging-bucket 或 CITEARK_GCP_STAGING_BUCKET)");
|
|
201
|
+
if (options.restoreCheckpointBucket && !safeBucketName(restoreCheckpointBucket)) {
|
|
202
|
+
issues.push("restoreCheckpointBucket 不是安全的 GCS bucket 名称");
|
|
203
|
+
}
|
|
204
|
+
if (!machineType && !instanceTemplate) {
|
|
205
|
+
issues.push("gcp-batch profile 至少需要 executorConfig.machineType 或 executorConfig.instanceTemplate");
|
|
206
|
+
}
|
|
207
|
+
if (!subscription && !providerSecret) issues.push("缺少模型 API Key 对应的 Secret Manager 版本(--gcp-provider-secret)");
|
|
208
|
+
if (providerSecret && !/^projects\/[^/]+\/secrets\/[^/]+\/versions\/[^/]+$/.test(providerSecret)) {
|
|
209
|
+
issues.push("gcpProviderSecret 必须是 projects/.../secrets/.../versions/... 格式");
|
|
210
|
+
}
|
|
211
|
+
if (!new Set(["STANDARD", "SPOT", "FLEX_START"]).has(provisioningModel)) {
|
|
212
|
+
issues.push("provisioningModel 必须是 STANDARD、SPOT 或 FLEX_START");
|
|
213
|
+
}
|
|
214
|
+
if (!remoteImage(bundle.executionEnvironment.image)) {
|
|
215
|
+
issues.push("gcp-batch 必须使用可由云端拉取的完整镜像 URI(例如 us-docker.pkg.dev/project/repo/image@sha256:...)");
|
|
216
|
+
}
|
|
217
|
+
if (
|
|
218
|
+
options.reuseCompletedResultOnRestore !== undefined
|
|
219
|
+
&& typeof options.reuseCompletedResultOnRestore !== "boolean"
|
|
220
|
+
) {
|
|
221
|
+
issues.push("reuseCompletedResultOnRestore 必须是布尔值");
|
|
222
|
+
}
|
|
223
|
+
if (issues.length) throw new TypeError(`GCP Batch 执行参数无效:\n- ${issues.join("\n- ")}`);
|
|
224
|
+
const infrastructureRetryCount = nonNegativeInteger(
|
|
225
|
+
options.infrastructureRetryCount ?? profile.infrastructureRetryCount,
|
|
226
|
+
provisioningModel === "SPOT" ? 2 : 0,
|
|
227
|
+
);
|
|
228
|
+
const checkpointing = resolveCheckpointPolicy({ options, profile });
|
|
229
|
+
const restoreCheckpointPrefix = optionalObjectPrefix(options.restoreCheckpointPrefix);
|
|
230
|
+
const allowedLocations = normalizeAllowedLocations(
|
|
231
|
+
options.allowedLocations ?? profile.allowedLocations,
|
|
232
|
+
region,
|
|
233
|
+
);
|
|
234
|
+
return {
|
|
235
|
+
projectId,
|
|
236
|
+
region,
|
|
237
|
+
stagingBucket,
|
|
238
|
+
restoreCheckpointBucket,
|
|
239
|
+
serviceAccount,
|
|
240
|
+
providerSecret,
|
|
241
|
+
providerCredentialBroker,
|
|
242
|
+
machineType,
|
|
243
|
+
instanceTemplate,
|
|
244
|
+
bootDiskImage,
|
|
245
|
+
gpuDriversPreinstalled: options.gpuDriversPreinstalled === true
|
|
246
|
+
|| profile.gpuDriversPreinstalled === true,
|
|
247
|
+
allowedLocations,
|
|
248
|
+
hourlyUsd: bundle.computeDecision?.selectedProfile?.hourlyUsd ?? null,
|
|
249
|
+
provisioningModel,
|
|
250
|
+
acceleratorType: first(options.acceleratorType, profile.acceleratorType),
|
|
251
|
+
bootDiskType: first(options.bootDiskType, profile.bootDiskType, "pd-balanced"),
|
|
252
|
+
bootDiskGb: positiveInteger(options.bootDiskGb ?? profile.bootDiskGb, 100),
|
|
253
|
+
infrastructureRetryCount,
|
|
254
|
+
checkpointing,
|
|
255
|
+
restoreCheckpointPrefix,
|
|
256
|
+
reuseCompletedResultOnRestore: options.reuseCompletedResultOnRestore === true,
|
|
257
|
+
pollIntervalMs: positiveInteger(options.pollIntervalMs, 10_000),
|
|
258
|
+
waitTimeoutMinutes: positiveInteger(
|
|
259
|
+
options.waitTimeoutMinutes ?? profile.waitTimeoutMinutes,
|
|
260
|
+
gcpBatchControllerWaitTimeoutMinutes({
|
|
261
|
+
executionTimeoutMinutes: bundle.executionEnvironment.timeoutMinutes,
|
|
262
|
+
infrastructureRetryCount,
|
|
263
|
+
}),
|
|
264
|
+
),
|
|
265
|
+
keepJob: options.keepJob === true,
|
|
266
|
+
keepStaging: options.keepStaging === true,
|
|
267
|
+
stagingPrefix: `citeark/jobs/${bundle.runId}`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function normalizeAllowedLocations(value, region) {
|
|
272
|
+
if (value === undefined || value === null) return [];
|
|
273
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item)) {
|
|
274
|
+
throw new TypeError("allowedLocations 必须是非空位置字符串数组");
|
|
275
|
+
}
|
|
276
|
+
const unique = [...new Set(value)];
|
|
277
|
+
const invalid = unique.filter((item) =>
|
|
278
|
+
!new RegExp(`^(?:zones/${escapeRegExp(region)}-[a-z]|regions/${escapeRegExp(region)})$`).test(item));
|
|
279
|
+
if (invalid.length) {
|
|
280
|
+
throw new TypeError(`allowedLocations 必须位于任务区域 ${region}:${invalid.join("、")}`);
|
|
281
|
+
}
|
|
282
|
+
return unique;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function escapeRegExp(value) {
|
|
286
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function directInstancePolicy({ bundle, config }) {
|
|
290
|
+
const policy = {
|
|
291
|
+
machineType: config.machineType,
|
|
292
|
+
provisioningModel: config.provisioningModel,
|
|
293
|
+
bootDisk: {
|
|
294
|
+
type: config.bootDiskType,
|
|
295
|
+
sizeGb: String(config.bootDiskGb),
|
|
296
|
+
...(config.bootDiskImage ? { image: config.bootDiskImage } : {}),
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
if (bundle.executionEnvironment.gpu === "all") {
|
|
300
|
+
policy.accelerators = [{
|
|
301
|
+
type: config.acceleratorType ?? normalizeAcceleratorType(bundle.computeDecision?.selectedProfile?.gpu?.type),
|
|
302
|
+
count: String(bundle.computeDecision?.selectedProfile?.gpu?.count ?? 1),
|
|
303
|
+
}];
|
|
304
|
+
}
|
|
305
|
+
return policy;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function optionalObjectPrefix(value) {
|
|
309
|
+
if (value === undefined || value === null || value === "") return null;
|
|
310
|
+
if (typeof value !== "string" || !/^[a-zA-Z0-9_.-]{1,900}$/.test(value)) {
|
|
311
|
+
throw new TypeError("restoreCheckpointPrefix 必须是 GCS bucket 根目录下的安全对象前缀");
|
|
312
|
+
}
|
|
313
|
+
return value;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function safeBucketName(value) {
|
|
317
|
+
return typeof value === "string"
|
|
318
|
+
&& /^[a-z0-9][a-z0-9._-]{1,220}[a-z0-9]$/.test(value)
|
|
319
|
+
&& !value.includes("..")
|
|
320
|
+
&& !value.startsWith("goog");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function remoteProviderSession(agent) {
|
|
324
|
+
return {
|
|
325
|
+
containerBaseUrl:
|
|
326
|
+
agent.runtime === "codex" && (agent.reasoningMode === "pro" || agent.paperBudgetEnforced)
|
|
327
|
+
? "http://127.0.0.1:4317"
|
|
328
|
+
: agent.runtime === "claude-code"
|
|
329
|
+
? agent.api.baseUrl.replace(/\/$/, "")
|
|
330
|
+
: stripOpenAiVersion(agent.api.baseUrl),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function remoteProviderEnvironment(
|
|
335
|
+
agent,
|
|
336
|
+
secretResource,
|
|
337
|
+
{
|
|
338
|
+
providerRelay = usesProviderRelay(agent),
|
|
339
|
+
providerRequestTimeoutSeconds = 30 * 60,
|
|
340
|
+
providerCredentialBroker,
|
|
341
|
+
} = {},
|
|
342
|
+
) {
|
|
343
|
+
if (agent.codexAccountId) {
|
|
344
|
+
if (agent.runtime !== "codex") throw new TypeError("订阅登录仅支持 Codex");
|
|
345
|
+
return { variables: { HOME: "/job/runtime-home", CODEX_HOME: "/job/runtime-home/codex", CITEARK_CODEX_ACCOUNT_ID: agent.codexAccountId } };
|
|
346
|
+
}
|
|
347
|
+
if (agent.runtime === "claude-code") {
|
|
348
|
+
return runtimeSecretEnvironment({
|
|
349
|
+
credentialName: "ANTHROPIC_API_KEY",
|
|
350
|
+
secretResource,
|
|
351
|
+
providerCredentialBroker,
|
|
352
|
+
variables: {
|
|
353
|
+
HOME: "/job/runtime-home",
|
|
354
|
+
ANTHROPIC_BASE_URL: agent.api.baseUrl,
|
|
355
|
+
ANTHROPIC_MODEL: agent.model,
|
|
356
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: agent.model,
|
|
357
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: agent.model,
|
|
358
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: agent.subagentModel,
|
|
359
|
+
CLAUDE_CODE_SUBAGENT_MODEL: agent.subagentModel,
|
|
360
|
+
CLAUDE_CODE_EFFORT_LEVEL: agent.effort,
|
|
361
|
+
CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(agent.autoCompactWindow),
|
|
362
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
if (agent.runtime === "codex") {
|
|
367
|
+
return runtimeSecretEnvironment({
|
|
368
|
+
credentialName: "OPENAI_API_KEY",
|
|
369
|
+
secretResource,
|
|
370
|
+
providerCredentialBroker,
|
|
371
|
+
variables: {
|
|
372
|
+
HOME: "/job/runtime-home",
|
|
373
|
+
CODEX_HOME: "/job/runtime-home/codex",
|
|
374
|
+
...(providerRelay ? {
|
|
375
|
+
CITEARK_PROVIDER_UPSTREAM_BASE_URL: stripOpenAiVersion(agent.api.baseUrl),
|
|
376
|
+
CITEARK_CODEX_UPSTREAM_BASE_URL: stripOpenAiVersion(agent.api.baseUrl),
|
|
377
|
+
CITEARK_CODEX_REASONING_MODE: agent.reasoningMode,
|
|
378
|
+
CITEARK_CODEX_RELAY_PORT: "4317",
|
|
379
|
+
CITEARK_PROVIDER_REQUEST_TIMEOUT_SECONDS: String(providerRequestTimeoutSeconds),
|
|
380
|
+
} : {}),
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (nativeDeepSeekProvider(agent)) {
|
|
385
|
+
return runtimeSecretEnvironment({
|
|
386
|
+
credentialName: "DEEPSEEK_API_KEY",
|
|
387
|
+
secretResource,
|
|
388
|
+
providerCredentialBroker,
|
|
389
|
+
variables: {
|
|
390
|
+
HOME: "/job/runtime-home",
|
|
391
|
+
OPENCODE_DISABLE_AUTOUPDATE: "1",
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
const config = {
|
|
396
|
+
$schema: "https://opencode.ai/config.json",
|
|
397
|
+
model: openCodeModelReference(agent),
|
|
398
|
+
// Task-local scientific inputs and outputs sit outside repository cwd.
|
|
399
|
+
// Explicit rules also reach child sessions, whose asks --auto may not handle.
|
|
400
|
+
permission: {
|
|
401
|
+
external_directory: {
|
|
402
|
+
"/job/input/paper/**": "allow",
|
|
403
|
+
"/job/input/reference-materials.json": "allow",
|
|
404
|
+
"/job/output/**": "allow",
|
|
405
|
+
// Tool-created temporary files belong to this disposable worker too.
|
|
406
|
+
"/tmp/**": "allow",
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
provider: {
|
|
410
|
+
citeark: {
|
|
411
|
+
npm: "@ai-sdk/openai-compatible",
|
|
412
|
+
name: "CiteArk Direct Provider",
|
|
413
|
+
options: {
|
|
414
|
+
baseURL: providerRelay
|
|
415
|
+
? "http://127.0.0.1:4317/v1"
|
|
416
|
+
: agent.api.baseUrl,
|
|
417
|
+
apiKey: "{env:CITEARK_API_KEY}",
|
|
418
|
+
},
|
|
419
|
+
models: { [agent.model]: openCodeModelDefinition(agent) },
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
return runtimeSecretEnvironment({
|
|
424
|
+
credentialName: "CITEARK_API_KEY",
|
|
425
|
+
secretResource,
|
|
426
|
+
providerCredentialBroker,
|
|
427
|
+
variables: {
|
|
428
|
+
HOME: "/job/runtime-home",
|
|
429
|
+
OPENCODE_CONFIG_CONTENT: JSON.stringify(config),
|
|
430
|
+
OPENCODE_DISABLE_AUTOUPDATE: "1",
|
|
431
|
+
...(providerRelay ? {
|
|
432
|
+
CITEARK_PROVIDER_UPSTREAM_BASE_URL: stripOpenAiVersion(agent.api.baseUrl),
|
|
433
|
+
CITEARK_CODEX_REASONING_MODE: agent.reasoningMode,
|
|
434
|
+
CITEARK_CODEX_RELAY_PORT: "4317",
|
|
435
|
+
CITEARK_PROVIDER_REQUEST_TIMEOUT_SECONDS: String(providerRequestTimeoutSeconds),
|
|
436
|
+
} : {}),
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function usesProviderRelay(agent) {
|
|
442
|
+
if (agent.billingMode === "user_byok") return false;
|
|
443
|
+
return agent.reasoningMode === "pro"
|
|
444
|
+
|| (agent.runtime === "opencode" && !nativeDeepSeekProvider(agent));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function runtimeSecretEnvironment({ credentialName, secretResource, providerCredentialBroker, variables }) {
|
|
448
|
+
return {
|
|
449
|
+
variables: {
|
|
450
|
+
...variables,
|
|
451
|
+
CITEARK_PROVIDER_SECRET_ENV_NAME: credentialName,
|
|
452
|
+
...(providerCredentialBroker
|
|
453
|
+
? {
|
|
454
|
+
CITEARK_PROVIDER_CREDENTIAL_URL: providerCredentialBroker.url,
|
|
455
|
+
CITEARK_PROVIDER_CREDENTIAL_TOKEN: providerCredentialBroker.token,
|
|
456
|
+
}
|
|
457
|
+
: { CITEARK_PROVIDER_SECRET_RESOURCE: secretResource }),
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function normalizeProviderCredentialBroker(value, issues) {
|
|
463
|
+
if (value === undefined || value === null) return null;
|
|
464
|
+
try {
|
|
465
|
+
const url = new URL(value.url);
|
|
466
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) {
|
|
467
|
+
issues.push("BYOK 凭据代理必须是无凭据、无查询参数的 HTTPS 地址");
|
|
468
|
+
}
|
|
469
|
+
} catch {
|
|
470
|
+
issues.push("BYOK 凭据代理地址无效");
|
|
471
|
+
}
|
|
472
|
+
if (typeof value.token !== "string" || value.token.length < 32 || value.token.length > 4096) {
|
|
473
|
+
issues.push("BYOK 凭据代理令牌无效");
|
|
474
|
+
}
|
|
475
|
+
return { url: value.url, token: value.token };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export function resolveCheckpointObjects(config, attempt) {
|
|
479
|
+
const prefix = checkpointObjectPrefix(config.stagingPrefix, attempt);
|
|
480
|
+
return { prefix, status: checkpointStatusObject(prefix) };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function labels(runId, attempt, workload = "reproduction") {
|
|
484
|
+
return {
|
|
485
|
+
citeark: workload,
|
|
486
|
+
"citeark-run": runId.toLowerCase().replaceAll(/[^a-z0-9_-]/g, "-").slice(0, 63),
|
|
487
|
+
"citeark-attempt": String(attempt),
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function normalizeAcceleratorType(value) {
|
|
492
|
+
const normalized = String(value ?? "").toLowerCase();
|
|
493
|
+
const known = {
|
|
494
|
+
l4: "nvidia-l4",
|
|
495
|
+
t4: "nvidia-tesla-t4",
|
|
496
|
+
a100: "nvidia-tesla-a100",
|
|
497
|
+
"a100-40gb": "nvidia-tesla-a100",
|
|
498
|
+
"a100-80gb": "nvidia-a100-80gb",
|
|
499
|
+
h100: "nvidia-h100-80gb",
|
|
500
|
+
};
|
|
501
|
+
return known[normalized] ?? value;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function stripOpenAiVersion(value) {
|
|
505
|
+
return value.replace(/\/$/, "").replace(/\/v1$/, "");
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function first(...values) {
|
|
509
|
+
return values.find((value) => typeof value === "string" && value.trim())?.trim() ?? null;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function remoteImage(value) {
|
|
513
|
+
const registry = String(value ?? "").split("/")[0];
|
|
514
|
+
return registry.includes(".") || registry === "localhost";
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function positiveInteger(value, fallback) {
|
|
518
|
+
const numeric = value === undefined || value === null ? fallback : Number(value);
|
|
519
|
+
if (!Number.isInteger(numeric) || numeric <= 0) throw new TypeError("GCP Batch 数值参数必须是正整数");
|
|
520
|
+
return numeric;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function nonNegativeInteger(value, fallback) {
|
|
524
|
+
const numeric = value === undefined || value === null ? fallback : Number(value);
|
|
525
|
+
if (!Number.isInteger(numeric) || numeric < 0) throw new TypeError("GCP Batch 重试次数必须是非负整数");
|
|
526
|
+
return numeric;
|
|
527
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { recordResearchCleanup } from '../workspace/control.mjs';
|
|
2
|
+
import {
|
|
3
|
+
acquireCodexAccountLock,
|
|
4
|
+
codexAccountLockReference,
|
|
5
|
+
releaseCodexAccountLock,
|
|
6
|
+
} from "./codex-account-lock.mjs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Batch 资源和 Codex 账号锁必须由同一个生命周期拥有。
|
|
10
|
+
* 创建失败在这里回滚锁;调用方只记录成功创建的 Job。
|
|
11
|
+
*/
|
|
12
|
+
export async function createLockedBatchJob({
|
|
13
|
+
storage,
|
|
14
|
+
batch,
|
|
15
|
+
bucket,
|
|
16
|
+
accountId,
|
|
17
|
+
owner,
|
|
18
|
+
jobId,
|
|
19
|
+
job,
|
|
20
|
+
}) {
|
|
21
|
+
const jobName = batch.jobName(jobId);
|
|
22
|
+
const lock = await acquireCodexAccountLock({
|
|
23
|
+
storage,
|
|
24
|
+
batch,
|
|
25
|
+
bucket,
|
|
26
|
+
accountId,
|
|
27
|
+
owner,
|
|
28
|
+
jobName,
|
|
29
|
+
});
|
|
30
|
+
try {
|
|
31
|
+
return await batch.createJob(jobId, job);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
await releaseCodexAccountLock({ storage, lock }).catch(() => undefined);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function releaseBatchJobAccountLock({
|
|
39
|
+
storage,
|
|
40
|
+
bucket,
|
|
41
|
+
accountId,
|
|
42
|
+
owner,
|
|
43
|
+
jobName,
|
|
44
|
+
record,
|
|
45
|
+
}) {
|
|
46
|
+
const lock = codexAccountLockReference({
|
|
47
|
+
bucket,
|
|
48
|
+
accountId,
|
|
49
|
+
owner,
|
|
50
|
+
jobName,
|
|
51
|
+
});
|
|
52
|
+
if (!lock || record?.codexAccountLockReleased) return;
|
|
53
|
+
try {
|
|
54
|
+
const result = await releaseCodexAccountLock({ storage, lock });
|
|
55
|
+
if (result.reason !== "owner-mismatch" && record) {
|
|
56
|
+
record.codexAccountLockReleased = true;
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (record) {
|
|
60
|
+
record.cleanupErrors = [
|
|
61
|
+
...(record.cleanupErrors ?? []),
|
|
62
|
+
error instanceof Error ? error.message : String(error),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function deleteBatchJob({ batch, record }) {
|
|
69
|
+
if (!record?.name || record.jobDeletionRequested === true) return;
|
|
70
|
+
try {
|
|
71
|
+
await batch.deleteJob(record.name);
|
|
72
|
+
record.jobDeletionRequested = true;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
record.jobDeletionRequested = false;
|
|
75
|
+
record.cleanupErrors = [
|
|
76
|
+
...(record.cleanupErrors ?? []),
|
|
77
|
+
error instanceof Error ? error.message : String(error),
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
await recordResearchCleanup({ provider: 'gcp-batch', resource: record.name, status: record.jobDeletionRequested ? 'requested' : 'failed', ...(record.cleanupErrors?.length ? { error: record.cleanupErrors.at(-1) } : {}) });
|
|
81
|
+
}
|