modelrig 0.0.1 → 0.2.0
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/CHANGELOG.md +94 -0
- package/LICENSE +201 -0
- package/README.md +145 -8
- package/data/PROVENANCE.md +19 -0
- package/data/pricing-manual.json +22 -0
- package/data/pricing-map.json +46362 -0
- package/data/pricing-overlay.json +419 -0
- package/dist/adapters/anthropic.d.ts +90 -0
- package/dist/adapters/anthropic.d.ts.map +1 -0
- package/dist/adapters/anthropic.js +296 -0
- package/dist/adapters/build-adapters.d.ts +11 -0
- package/dist/adapters/build-adapters.d.ts.map +1 -0
- package/dist/adapters/build-adapters.js +45 -0
- package/dist/adapters/deepseek.d.ts +47 -0
- package/dist/adapters/deepseek.d.ts.map +1 -0
- package/dist/adapters/deepseek.js +216 -0
- package/dist/adapters/gemini.d.ts +44 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +262 -0
- package/dist/adapters/grok.d.ts +53 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +232 -0
- package/dist/adapters/openai-compat.d.ts +80 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -0
- package/dist/adapters/openai-compat.js +244 -0
- package/dist/adapters/openai-stream.d.ts +58 -0
- package/dist/adapters/openai-stream.d.ts.map +1 -0
- package/dist/adapters/openai-stream.js +120 -0
- package/dist/adapters/openai.d.ts +55 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +281 -0
- package/dist/adapters/sampling.d.ts +28 -0
- package/dist/adapters/sampling.d.ts.map +1 -0
- package/dist/adapters/sampling.js +34 -0
- package/dist/adapters/tool-mapping.d.ts +92 -0
- package/dist/adapters/tool-mapping.d.ts.map +1 -0
- package/dist/adapters/tool-mapping.js +143 -0
- package/dist/artifacts/artifact.d.ts +137 -0
- package/dist/artifacts/artifact.d.ts.map +1 -0
- package/dist/artifacts/artifact.js +377 -0
- package/dist/artifacts/content-uploader.d.ts +87 -0
- package/dist/artifacts/content-uploader.d.ts.map +1 -0
- package/dist/artifacts/content-uploader.js +124 -0
- package/dist/artifacts/index.d.ts +63 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +80 -0
- package/dist/artifacts/run-context.d.ts +129 -0
- package/dist/artifacts/run-context.d.ts.map +1 -0
- package/dist/artifacts/run-context.js +208 -0
- package/dist/artifacts/scrub.d.ts +60 -0
- package/dist/artifacts/scrub.d.ts.map +1 -0
- package/dist/artifacts/scrub.js +139 -0
- package/dist/artifacts/step.d.ts +26 -0
- package/dist/artifacts/step.d.ts.map +1 -0
- package/dist/artifacts/step.js +37 -0
- package/dist/artifacts/taint.d.ts +63 -0
- package/dist/artifacts/taint.d.ts.map +1 -0
- package/dist/artifacts/taint.js +76 -0
- package/dist/bakeoff/cache-sim.d.ts +99 -0
- package/dist/bakeoff/cache-sim.d.ts.map +1 -0
- package/dist/bakeoff/cache-sim.js +126 -0
- package/dist/bakeoff/engine.d.ts +52 -0
- package/dist/bakeoff/engine.d.ts.map +1 -0
- package/dist/bakeoff/engine.js +163 -0
- package/dist/bakeoff/eval-cases.d.ts +96 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -0
- package/dist/bakeoff/eval-cases.js +105 -0
- package/dist/bakeoff/replay.d.ts +52 -0
- package/dist/bakeoff/replay.d.ts.map +1 -0
- package/dist/bakeoff/replay.js +94 -0
- package/dist/bakeoff/report.d.ts +41 -0
- package/dist/bakeoff/report.d.ts.map +1 -0
- package/dist/bakeoff/report.js +142 -0
- package/dist/bakeoff/samples.d.ts +77 -0
- package/dist/bakeoff/samples.d.ts.map +1 -0
- package/dist/bakeoff/samples.js +148 -0
- package/dist/bakeoff/stats.d.ts +13 -0
- package/dist/bakeoff/stats.d.ts.map +1 -0
- package/dist/bakeoff/stats.js +48 -0
- package/dist/bakeoff/types.d.ts +37 -0
- package/dist/bakeoff/types.d.ts.map +1 -0
- package/dist/bakeoff/types.js +10 -0
- package/dist/cli.d.ts +25 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +590 -0
- package/dist/config.d.ts +204 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +252 -0
- package/dist/deprecations.d.ts +32 -0
- package/dist/deprecations.d.ts.map +1 -0
- package/dist/deprecations.js +59 -0
- package/dist/feedback/coverage.d.ts +32 -0
- package/dist/feedback/coverage.d.ts.map +1 -0
- package/dist/feedback/coverage.js +95 -0
- package/dist/feedback/feedback.d.ts +35 -0
- package/dist/feedback/feedback.d.ts.map +1 -0
- package/dist/feedback/feedback.js +39 -0
- package/dist/gateway/http-protocol.d.ts +175 -0
- package/dist/gateway/http-protocol.d.ts.map +1 -0
- package/dist/gateway/http-protocol.js +105 -0
- package/dist/index.d.ts +242 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +576 -0
- package/dist/init/finder.d.ts +94 -0
- package/dist/init/finder.d.ts.map +1 -0
- package/dist/init/finder.js +343 -0
- package/dist/init/init-cli.d.ts +10 -0
- package/dist/init/init-cli.d.ts.map +1 -0
- package/dist/init/init-cli.js +86 -0
- package/dist/init/observe-cli.d.ts +19 -0
- package/dist/init/observe-cli.d.ts.map +1 -0
- package/dist/init/observe-cli.js +73 -0
- package/dist/init/observe.d.ts +81 -0
- package/dist/init/observe.d.ts.map +1 -0
- package/dist/init/observe.js +245 -0
- package/dist/init/run.d.ts +99 -0
- package/dist/init/run.d.ts.map +1 -0
- package/dist/init/run.js +281 -0
- package/dist/init/signatures.d.ts +39 -0
- package/dist/init/signatures.d.ts.map +1 -0
- package/dist/init/signatures.js +116 -0
- package/dist/matrix/aggregate.d.ts +89 -0
- package/dist/matrix/aggregate.d.ts.map +1 -0
- package/dist/matrix/aggregate.js +186 -0
- package/dist/query/grammar.d.ts +35 -0
- package/dist/query/grammar.d.ts.map +1 -0
- package/dist/query/grammar.js +279 -0
- package/dist/query/run.d.ts +8 -0
- package/dist/query/run.d.ts.map +1 -0
- package/dist/query/run.js +21 -0
- package/dist/registry/build.d.ts +219 -0
- package/dist/registry/build.d.ts.map +1 -0
- package/dist/registry/build.js +313 -0
- package/dist/registry/cache-policy.d.ts +94 -0
- package/dist/registry/cache-policy.d.ts.map +1 -0
- package/dist/registry/cache-policy.js +238 -0
- package/dist/registry/call-notes.d.ts +11 -0
- package/dist/registry/call-notes.d.ts.map +1 -0
- package/dist/registry/call-notes.js +110 -0
- package/dist/registry/claims.d.ts +63 -0
- package/dist/registry/claims.d.ts.map +1 -0
- package/dist/registry/claims.js +107 -0
- package/dist/registry/effective-rate.d.ts +151 -0
- package/dist/registry/effective-rate.d.ts.map +1 -0
- package/dist/registry/effective-rate.js +156 -0
- package/dist/registry/facts.d.ts +32 -0
- package/dist/registry/facts.d.ts.map +1 -0
- package/dist/registry/facts.js +136 -0
- package/dist/registry/fingerprint.d.ts +20 -0
- package/dist/registry/fingerprint.d.ts.map +1 -0
- package/dist/registry/fingerprint.js +150 -0
- package/dist/registry/parity.d.ts +52 -0
- package/dist/registry/parity.d.ts.map +1 -0
- package/dist/registry/parity.js +82 -0
- package/dist/registry/pricing-shape.d.ts +113 -0
- package/dist/registry/pricing-shape.d.ts.map +1 -0
- package/dist/registry/pricing-shape.js +123 -0
- package/dist/registry/pricing.d.ts +43 -0
- package/dist/registry/pricing.d.ts.map +1 -0
- package/dist/registry/pricing.js +127 -0
- package/dist/registry/rows.d.ts +16 -0
- package/dist/registry/rows.d.ts.map +1 -0
- package/dist/registry/rows.js +57 -0
- package/dist/registry/seed.d.ts +43 -0
- package/dist/registry/seed.d.ts.map +1 -0
- package/dist/registry/seed.js +97 -0
- package/dist/registry/select.d.ts +68 -0
- package/dist/registry/select.d.ts.map +1 -0
- package/dist/registry/select.js +141 -0
- package/dist/registry/staleness.d.ts +18 -0
- package/dist/registry/staleness.d.ts.map +1 -0
- package/dist/registry/staleness.js +39 -0
- package/dist/rigindex/bootstrap.d.ts +36 -0
- package/dist/rigindex/bootstrap.d.ts.map +1 -0
- package/dist/rigindex/bootstrap.js +97 -0
- package/dist/rigindex/fit.d.ts +24 -0
- package/dist/rigindex/fit.d.ts.map +1 -0
- package/dist/rigindex/fit.js +76 -0
- package/dist/rigindex/gates.d.ts +52 -0
- package/dist/rigindex/gates.d.ts.map +1 -0
- package/dist/rigindex/gates.js +109 -0
- package/dist/rigindex/graph.d.ts +31 -0
- package/dist/rigindex/graph.d.ts.map +1 -0
- package/dist/rigindex/graph.js +120 -0
- package/dist/rigindex/index.d.ts +23 -0
- package/dist/rigindex/index.d.ts.map +1 -0
- package/dist/rigindex/index.js +112 -0
- package/dist/rigindex/types.d.ts +76 -0
- package/dist/rigindex/types.d.ts.map +1 -0
- package/dist/rigindex/types.js +8 -0
- package/dist/routes/cache-lint.d.ts +41 -0
- package/dist/routes/cache-lint.d.ts.map +1 -0
- package/dist/routes/cache-lint.js +81 -0
- package/dist/routes/draft.d.ts +83 -0
- package/dist/routes/draft.d.ts.map +1 -0
- package/dist/routes/draft.js +135 -0
- package/dist/routes/loader.d.ts +63 -0
- package/dist/routes/loader.d.ts.map +1 -0
- package/dist/routes/loader.js +574 -0
- package/dist/routes/manifest.d.ts +24 -0
- package/dist/routes/manifest.d.ts.map +1 -0
- package/dist/routes/manifest.js +118 -0
- package/dist/routes/resolved-bundle.d.ts +79 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -0
- package/dist/routes/resolved-bundle.js +148 -0
- package/dist/routes/sampling-config.d.ts +32 -0
- package/dist/routes/sampling-config.d.ts.map +1 -0
- package/dist/routes/sampling-config.js +91 -0
- package/dist/routes/serveability.d.ts +52 -0
- package/dist/routes/serveability.d.ts.map +1 -0
- package/dist/routes/serveability.js +73 -0
- package/dist/routes/template.d.ts +18 -0
- package/dist/routes/template.d.ts.map +1 -0
- package/dist/routes/template.js +94 -0
- package/dist/run/ajv.d.ts +22 -0
- package/dist/run/ajv.d.ts.map +1 -0
- package/dist/run/ajv.js +32 -0
- package/dist/run/cache-handle.d.ts +58 -0
- package/dist/run/cache-handle.d.ts.map +1 -0
- package/dist/run/cache-handle.js +54 -0
- package/dist/run/cache-stamp.d.ts +65 -0
- package/dist/run/cache-stamp.d.ts.map +1 -0
- package/dist/run/cache-stamp.js +0 -0
- package/dist/run/capture-write.d.ts +33 -0
- package/dist/run/capture-write.d.ts.map +1 -0
- package/dist/run/capture-write.js +55 -0
- package/dist/run/cost.d.ts +26 -0
- package/dist/run/cost.d.ts.map +1 -0
- package/dist/run/cost.js +41 -0
- package/dist/run/dispatch.d.ts +99 -0
- package/dist/run/dispatch.d.ts.map +1 -0
- package/dist/run/dispatch.js +191 -0
- package/dist/run/envelope.d.ts +21 -0
- package/dist/run/envelope.d.ts.map +1 -0
- package/dist/run/envelope.js +45 -0
- package/dist/run/grounding.d.ts +56 -0
- package/dist/run/grounding.d.ts.map +1 -0
- package/dist/run/grounding.js +89 -0
- package/dist/run/raw.d.ts +88 -0
- package/dist/run/raw.d.ts.map +1 -0
- package/dist/run/raw.js +286 -0
- package/dist/run/record-attempt.d.ts +40 -0
- package/dist/run/record-attempt.d.ts.map +1 -0
- package/dist/run/record-attempt.js +99 -0
- package/dist/run/repair.d.ts +36 -0
- package/dist/run/repair.d.ts.map +1 -0
- package/dist/run/repair.js +61 -0
- package/dist/run/resolve.d.ts +99 -0
- package/dist/run/resolve.d.ts.map +1 -0
- package/dist/run/resolve.js +203 -0
- package/dist/run/retry.d.ts +19 -0
- package/dist/run/retry.d.ts.map +1 -0
- package/dist/run/retry.js +50 -0
- package/dist/run/run.d.ts +55 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +540 -0
- package/dist/run/stream.d.ts +49 -0
- package/dist/run/stream.d.ts.map +1 -0
- package/dist/run/stream.js +87 -0
- package/dist/run/validate.d.ts +15 -0
- package/dist/run/validate.d.ts.map +1 -0
- package/dist/run/validate.js +55 -0
- package/dist/savings/cache-ledger.d.ts +108 -0
- package/dist/savings/cache-ledger.d.ts.map +1 -0
- package/dist/savings/cache-ledger.js +174 -0
- package/dist/savings/cache-reconcile.d.ts +64 -0
- package/dist/savings/cache-reconcile.d.ts.map +1 -0
- package/dist/savings/cache-reconcile.js +59 -0
- package/dist/savings/cache-stats.d.ts +63 -0
- package/dist/savings/cache-stats.d.ts.map +1 -0
- package/dist/savings/cache-stats.js +108 -0
- package/dist/savings/emit.d.ts +38 -0
- package/dist/savings/emit.d.ts.map +1 -0
- package/dist/savings/emit.js +100 -0
- package/dist/savings/odometer.d.ts +32 -0
- package/dist/savings/odometer.d.ts.map +1 -0
- package/dist/savings/odometer.js +70 -0
- package/dist/savings/swap-execute.d.ts +58 -0
- package/dist/savings/swap-execute.d.ts.map +1 -0
- package/dist/savings/swap-execute.js +130 -0
- package/dist/savings/swaps-client.d.ts +55 -0
- package/dist/savings/swaps-client.d.ts.map +1 -0
- package/dist/savings/swaps-client.js +95 -0
- package/dist/settings/api-fetcher.d.ts +39 -0
- package/dist/settings/api-fetcher.d.ts.map +1 -0
- package/dist/settings/api-fetcher.js +92 -0
- package/dist/settings/index.d.ts +10 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +15 -0
- package/dist/settings/task-settings.d.ts +91 -0
- package/dist/settings/task-settings.d.ts.map +1 -0
- package/dist/settings/task-settings.js +116 -0
- package/dist/status.d.ts +77 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +113 -0
- package/dist/telemetry/artifact-store.d.ts +48 -0
- package/dist/telemetry/artifact-store.d.ts.map +1 -0
- package/dist/telemetry/artifact-store.js +189 -0
- package/dist/telemetry/cache-miss-streak.d.ts +79 -0
- package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
- package/dist/telemetry/cache-miss-streak.js +99 -0
- package/dist/telemetry/capture.d.ts +31 -0
- package/dist/telemetry/capture.d.ts.map +1 -0
- package/dist/telemetry/capture.js +81 -0
- package/dist/telemetry/exporter-setup.d.ts +28 -0
- package/dist/telemetry/exporter-setup.d.ts.map +1 -0
- package/dist/telemetry/exporter-setup.js +34 -0
- package/dist/telemetry/exporter.d.ts +137 -0
- package/dist/telemetry/exporter.d.ts.map +1 -0
- package/dist/telemetry/exporter.js +510 -0
- package/dist/telemetry/health.d.ts +76 -0
- package/dist/telemetry/health.d.ts.map +1 -0
- package/dist/telemetry/health.js +92 -0
- package/dist/telemetry/ingest-protocol.d.ts +310 -0
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
- package/dist/telemetry/ingest-protocol.js +363 -0
- package/dist/telemetry/load-error-report.d.ts +24 -0
- package/dist/telemetry/load-error-report.d.ts.map +1 -0
- package/dist/telemetry/load-error-report.js +50 -0
- package/dist/telemetry/otel-map.d.ts +121 -0
- package/dist/telemetry/otel-map.d.ts.map +1 -0
- package/dist/telemetry/otel-map.js +224 -0
- package/dist/telemetry/otel-normalize.d.ts +104 -0
- package/dist/telemetry/otel-normalize.d.ts.map +1 -0
- package/dist/telemetry/otel-normalize.js +530 -0
- package/dist/telemetry/otlp-emitter.d.ts +52 -0
- package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
- package/dist/telemetry/otlp-emitter.js +144 -0
- package/dist/telemetry/otlp-proto.d.ts +29 -0
- package/dist/telemetry/otlp-proto.d.ts.map +1 -0
- package/dist/telemetry/otlp-proto.js +266 -0
- package/dist/telemetry/sinks.d.ts +90 -0
- package/dist/telemetry/sinks.d.ts.map +1 -0
- package/dist/telemetry/sinks.js +180 -0
- package/dist/telemetry/store.d.ts +65 -0
- package/dist/telemetry/store.d.ts.map +1 -0
- package/dist/telemetry/store.js +547 -0
- package/dist/tenancy/api-key-format.d.ts +77 -0
- package/dist/tenancy/api-key-format.d.ts.map +1 -0
- package/dist/tenancy/api-key-format.js +110 -0
- package/dist/tenancy/constants.d.ts +23 -0
- package/dist/tenancy/constants.d.ts.map +1 -0
- package/dist/tenancy/constants.js +25 -0
- package/dist/tenancy/scrub.d.ts +47 -0
- package/dist/tenancy/scrub.d.ts.map +1 -0
- package/dist/tenancy/scrub.js +85 -0
- package/dist/types/config-teaching.d.ts +62 -0
- package/dist/types/config-teaching.d.ts.map +1 -0
- package/dist/types/config-teaching.js +183 -0
- package/dist/types/failures-teaching.d.ts +56 -0
- package/dist/types/failures-teaching.d.ts.map +1 -0
- package/dist/types/failures-teaching.js +177 -0
- package/dist/types/failures.d.ts +27 -0
- package/dist/types/failures.d.ts.map +1 -0
- package/dist/types/failures.js +41 -0
- package/dist/types/provider.d.ts +200 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +40 -0
- package/dist/types/route.d.ts +205 -0
- package/dist/types/route.d.ts.map +1 -0
- package/dist/types/route.js +6 -0
- package/dist/types/telemetry.d.ts +206 -0
- package/dist/types/telemetry.d.ts.map +1 -0
- package/dist/types/telemetry.js +6 -0
- package/dist/validate-cli.d.ts +12 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate-cli.js +57 -0
- package/dist/verify/capture.d.ts +66 -0
- package/dist/verify/capture.d.ts.map +1 -0
- package/dist/verify/capture.js +180 -0
- package/dist/verify/cli-run.d.ts +23 -0
- package/dist/verify/cli-run.d.ts.map +1 -0
- package/dist/verify/cli-run.js +57 -0
- package/dist/verify/diff.d.ts +48 -0
- package/dist/verify/diff.d.ts.map +1 -0
- package/dist/verify/diff.js +157 -0
- package/dist/verify/index.d.ts +17 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +24 -0
- package/dist/verify/report.d.ts +14 -0
- package/dist/verify/report.d.ts.map +1 -0
- package/dist/verify/report.js +88 -0
- package/dist/verify/types.d.ts +101 -0
- package/dist/verify/types.d.ts.map +1 -0
- package/dist/verify/types.js +17 -0
- package/dist/verify/verify.d.ts +29 -0
- package/dist/verify/verify.d.ts.map +1 -0
- package/dist/verify/verify.js +148 -0
- package/dist/watch/budget.d.ts +19 -0
- package/dist/watch/budget.d.ts.map +1 -0
- package/dist/watch/budget.js +35 -0
- package/dist/watch/evaluate.d.ts +225 -0
- package/dist/watch/evaluate.d.ts.map +1 -0
- package/dist/watch/evaluate.js +155 -0
- package/dist/watch/monitor.d.ts +116 -0
- package/dist/watch/monitor.d.ts.map +1 -0
- package/dist/watch/monitor.js +199 -0
- package/dist/watch/proposals.d.ts +89 -0
- package/dist/watch/proposals.d.ts.map +1 -0
- package/dist/watch/proposals.js +197 -0
- package/dist/watch/run-record.d.ts +64 -0
- package/dist/watch/run-record.d.ts.map +1 -0
- package/dist/watch/run-record.js +86 -0
- package/dist/watch/run-reported.d.ts +20 -0
- package/dist/watch/run-reported.d.ts.map +1 -0
- package/dist/watch/run-reported.js +53 -0
- package/dist/watch/run.d.ts +73 -0
- package/dist/watch/run.d.ts.map +1 -0
- package/dist/watch/run.js +83 -0
- package/dist/watch/state-store.d.ts +48 -0
- package/dist/watch/state-store.d.ts.map +1 -0
- package/dist/watch/state-store.js +86 -0
- package/dist/watch/state.d.ts +21 -0
- package/dist/watch/state.d.ts.map +1 -0
- package/dist/watch/state.js +93 -0
- package/package.json +56 -14
- package/registry/LICENSE +20 -0
- package/registry/leaderboard.json +3639 -0
- package/registry/registry.json +7263 -0
- package/index.js +0 -3
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact namespace — Expansion E2 chunk B (spec §2.2, §9).
|
|
3
|
+
*
|
|
4
|
+
* `save/load/link/evaluate` over a durable work product's METADATA. The
|
|
5
|
+
* serialized value is hashed (sha256, node:crypto — no new dep) and the bytes
|
|
6
|
+
* are DISCARDED; nothing but the metadata row is persisted (the 00030
|
|
7
|
+
* counter-annotation). Lineage v0: artifacts saved within a run get
|
|
8
|
+
* `derived_from` edges to artifacts `load`ed since the last save (coverage
|
|
9
|
+
* measured, never promised). Instrumentation never throws into customer code.
|
|
10
|
+
*
|
|
11
|
+
* ── zeroRetention (ratified invariant, named test) ────────────────────────
|
|
12
|
+
* A run with `zeroRetention: true` emits NO artifact rows of any kind: `save`
|
|
13
|
+
* refuses fail-closed at THIS gate (before any row is built) with a counted,
|
|
14
|
+
* logged skip. Structural, not policy-checked at the server.
|
|
15
|
+
*/
|
|
16
|
+
import type { ArtifactStore } from "../telemetry/artifact-store";
|
|
17
|
+
import type { EvalCaseOrigin } from "../types/telemetry";
|
|
18
|
+
import { type ScrubMode } from "./scrub";
|
|
19
|
+
import type { ContentUploadInput, ContentUploadOutcome } from "./content-uploader";
|
|
20
|
+
/** The upload transport `save` fires content through — injected so the SDK's
|
|
21
|
+
* networked implementation and the tests share one seam. */
|
|
22
|
+
export type ArtifactContentUploader = (input: ContentUploadInput) => Promise<ContentUploadOutcome>;
|
|
23
|
+
/** Per-rig content-custody wiring passed to the artifact api (E2-C). Absent =
|
|
24
|
+
* metadata + hash only, today's behaviour. */
|
|
25
|
+
export interface ArtifactContentDeps {
|
|
26
|
+
readonly scrub: ScrubMode;
|
|
27
|
+
readonly upload: ArtifactContentUploader;
|
|
28
|
+
}
|
|
29
|
+
export interface ArtifactHandle {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly type: string;
|
|
33
|
+
readonly version: number;
|
|
34
|
+
}
|
|
35
|
+
export interface ArtifactSaveOptions {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly type: string;
|
|
38
|
+
readonly task?: string;
|
|
39
|
+
/** The pipeline step this artifact belongs to — the semantic grouping key the
|
|
40
|
+
* console's chain view orders on (E3 spec §2.1). E2 deferred step attribution
|
|
41
|
+
* ("refined when @step scopes persist, E3"); an EXPLICIT key set by the caller
|
|
42
|
+
* is the E3 refinement — safe under parallel step waves where an ambient
|
|
43
|
+
* mutable current-step would cross-attribute. Null (absent) ⇒ ungrouped. */
|
|
44
|
+
readonly stepKey?: string;
|
|
45
|
+
/** The step attempt this artifact came from (mirrors steps.attempt_no). Only
|
|
46
|
+
* meaningful alongside stepKey; null for artifacts saved outside a step. */
|
|
47
|
+
readonly attemptNo?: number;
|
|
48
|
+
readonly classification?: string;
|
|
49
|
+
/** OPAQUE customer URI (their bucket). We store the string + our hash, never content. */
|
|
50
|
+
readonly contentRef?: string;
|
|
51
|
+
/** Declared JSON Schema (fingerprinted with the same pure fn the loader uses). */
|
|
52
|
+
readonly schema?: object | null;
|
|
53
|
+
/** The artifact this one supersedes (immutable versioning; ruling c column). */
|
|
54
|
+
readonly supersedes?: ArtifactHandle;
|
|
55
|
+
readonly expiresAt?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ArtifactEvaluateOptions {
|
|
58
|
+
readonly evaluator: string;
|
|
59
|
+
readonly kind: "deterministic" | "model-judge" | "human";
|
|
60
|
+
readonly score?: number;
|
|
61
|
+
readonly passed?: boolean;
|
|
62
|
+
readonly label?: string;
|
|
63
|
+
readonly explanation?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface ArtifactPromoteOptions {
|
|
66
|
+
readonly note?: string;
|
|
67
|
+
/** The route/task this case belongs to. Falls back to the task the source
|
|
68
|
+
* artifact was saved with this session; required (explicitly or via fallback)
|
|
69
|
+
* because an eval case is keyed by task. */
|
|
70
|
+
readonly task?: string;
|
|
71
|
+
/** The corrected/golden artifact — set ⇒ a golden-corrected pair. */
|
|
72
|
+
readonly corrected?: ArtifactHandle | string;
|
|
73
|
+
/** How it entered the suite. Default: `human-corrected` when `corrected` is
|
|
74
|
+
* set, else `promoted-failure`. */
|
|
75
|
+
readonly origin?: EvalCaseOrigin;
|
|
76
|
+
}
|
|
77
|
+
export interface ArtifactApi {
|
|
78
|
+
/** Hash the value's serialization, record metadata + inferred lineage, return
|
|
79
|
+
* a handle. Returns null when the namespace is disabled or a zeroRetention run
|
|
80
|
+
* refuses it (fail-closed). */
|
|
81
|
+
save(value: unknown, opts: ArtifactSaveOptions): ArtifactHandle | null;
|
|
82
|
+
/** Mark an artifact consumed by the current run (feeds derived_from lineage). */
|
|
83
|
+
load(ref: ArtifactHandle | string): void;
|
|
84
|
+
/** Explicit lineage edge the runtime cannot infer. */
|
|
85
|
+
link(from: ArtifactHandle | string, to: ArtifactHandle | string, kind: "consumed" | "derived_from"): void;
|
|
86
|
+
/** Record an evaluation against an artifact. */
|
|
87
|
+
evaluate(ref: ArtifactHandle | string, opts: ArtifactEvaluateOptions): void;
|
|
88
|
+
/** Promote an artifact into the org's durable eval suite (E4 chunk A) — a
|
|
89
|
+
* regression case replayable via `bakeoff --from-eval-cases`. `corrected` set
|
|
90
|
+
* ⇒ a golden-corrected pair; absent ⇒ a failure-case. The `task` binds the
|
|
91
|
+
* case to a route: taken from opts, else the task the artifact was saved with
|
|
92
|
+
* this session; unresolvable ⇒ a warned no-op (returns null, never throws).
|
|
93
|
+
* Returns the new case id on success. */
|
|
94
|
+
promoteToEval(ref: ArtifactHandle | string, opts?: ArtifactPromoteOptions): string | null;
|
|
95
|
+
/** Run `fn` inside a single store transaction and return its value (H8), so a
|
|
96
|
+
* step's several save/link/evaluate writes commit with ONE fsync. On a
|
|
97
|
+
* disabled/inert namespace `fn` runs directly (no store, no transaction). */
|
|
98
|
+
batch<T>(fn: () => T): T;
|
|
99
|
+
/** Await every in-flight content upload (E2-C). Content upload is fire-and-
|
|
100
|
+
* forget off the customer's hot path; a graceful shutdown (and the tests)
|
|
101
|
+
* call this so a process does not exit mid-PUT. No-op when custody is off. */
|
|
102
|
+
flushContent(): Promise<void>;
|
|
103
|
+
/** Re-attempt content uploads that came back `pending` — a 404 from the grant
|
|
104
|
+
* endpoint because the artifact metadata row had not yet been exported when
|
|
105
|
+
* the upload first fired (the 404-until-durable race, E4 chunk 0). The
|
|
106
|
+
* exporter calls this AFTER a confirmed flush of the artifacts stream, so by
|
|
107
|
+
* now the row is durable and the grant succeeds. Each call is exactly ONE
|
|
108
|
+
* retry per queued item (bounded 2 total, then a counted skip). NEVER throws.
|
|
109
|
+
* No-op when custody is off or the queue is empty. */
|
|
110
|
+
retryPendingContent(): Promise<void>;
|
|
111
|
+
/** Inferred-lineage + content-custody coverage counters (measured, never
|
|
112
|
+
* promised). Content counters update asynchronously as uploads settle. */
|
|
113
|
+
coverage(): {
|
|
114
|
+
readonly saves: number;
|
|
115
|
+
readonly inferredEdges: number;
|
|
116
|
+
readonly skippedZeroRetention: number;
|
|
117
|
+
/** Content uploads that reached the store. */
|
|
118
|
+
readonly contentUploaded: number;
|
|
119
|
+
/** Content uploads that degraded to metadata-only (refusal or transient). */
|
|
120
|
+
readonly contentDegraded: number;
|
|
121
|
+
/** Content uploads refused by the scrub `block` mode. */
|
|
122
|
+
readonly contentBlocked: number;
|
|
123
|
+
/** Content uploads awaiting the metadata-durable retry (E4 chunk 0). Drains
|
|
124
|
+
* to 0 as the exporter's post-flush hook re-attempts them; a still-pending
|
|
125
|
+
* item that exhausts its 2 retries moves to `contentDegraded`. */
|
|
126
|
+
readonly contentPending: number;
|
|
127
|
+
/** Total scrub findings (counts across types) seen this session. */
|
|
128
|
+
readonly scrubFindings: number;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export declare function createArtifactApi(deps: {
|
|
132
|
+
readonly store: ArtifactStore;
|
|
133
|
+
readonly enabled: boolean;
|
|
134
|
+
/** Content custody wiring (E2-C). Absent = metadata + hash only. */
|
|
135
|
+
readonly content?: ArtifactContentDeps;
|
|
136
|
+
}): ArtifactApi;
|
|
137
|
+
//# sourceMappingURL=artifact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../../src/artifacts/artifact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Artifact namespace — Expansion E2 chunk B (spec §2.2, §9).
|
|
4
|
+
*
|
|
5
|
+
* `save/load/link/evaluate` over a durable work product's METADATA. The
|
|
6
|
+
* serialized value is hashed (sha256, node:crypto — no new dep) and the bytes
|
|
7
|
+
* are DISCARDED; nothing but the metadata row is persisted (the 00030
|
|
8
|
+
* counter-annotation). Lineage v0: artifacts saved within a run get
|
|
9
|
+
* `derived_from` edges to artifacts `load`ed since the last save (coverage
|
|
10
|
+
* measured, never promised). Instrumentation never throws into customer code.
|
|
11
|
+
*
|
|
12
|
+
* ── zeroRetention (ratified invariant, named test) ────────────────────────
|
|
13
|
+
* A run with `zeroRetention: true` emits NO artifact rows of any kind: `save`
|
|
14
|
+
* refuses fail-closed at THIS gate (before any row is built) with a counted,
|
|
15
|
+
* logged skip. Structural, not policy-checked at the server.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.createArtifactApi = createArtifactApi;
|
|
19
|
+
const node_crypto_1 = require("node:crypto");
|
|
20
|
+
const fingerprint_1 = require("../registry/fingerprint");
|
|
21
|
+
const run_context_1 = require("./run-context");
|
|
22
|
+
const scrub_1 = require("./scrub");
|
|
23
|
+
/** Map an artifact `type` to a content-type for the object store. */
|
|
24
|
+
function contentTypeFor(type) {
|
|
25
|
+
const t = type.toLowerCase();
|
|
26
|
+
if (t === "json")
|
|
27
|
+
return "application/json";
|
|
28
|
+
if (t === "text" || t === "txt" || t === "md" || t === "markdown")
|
|
29
|
+
return "text/plain; charset=utf-8";
|
|
30
|
+
if (t === "csv")
|
|
31
|
+
return "text/csv";
|
|
32
|
+
if (t === "html")
|
|
33
|
+
return "text/html; charset=utf-8";
|
|
34
|
+
return "application/octet-stream";
|
|
35
|
+
}
|
|
36
|
+
function idOf(ref) {
|
|
37
|
+
return typeof ref === "string" ? ref : ref.id;
|
|
38
|
+
}
|
|
39
|
+
function warn(scope, err) {
|
|
40
|
+
console.warn(`[modelrig artifacts] ${scope} buffer write failed (dropped, run unaffected): ` +
|
|
41
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
42
|
+
}
|
|
43
|
+
/** A disabled/inert namespace — every entry is a no-op (spec §1.4). */
|
|
44
|
+
function inertApi() {
|
|
45
|
+
return {
|
|
46
|
+
save: () => null,
|
|
47
|
+
load: () => { },
|
|
48
|
+
link: () => { },
|
|
49
|
+
evaluate: () => { },
|
|
50
|
+
promoteToEval: () => null,
|
|
51
|
+
batch: (fn) => fn(),
|
|
52
|
+
flushContent: () => Promise.resolve(),
|
|
53
|
+
retryPendingContent: () => Promise.resolve(),
|
|
54
|
+
coverage: () => ({
|
|
55
|
+
saves: 0,
|
|
56
|
+
inferredEdges: 0,
|
|
57
|
+
skippedZeroRetention: 0,
|
|
58
|
+
contentUploaded: 0,
|
|
59
|
+
contentDegraded: 0,
|
|
60
|
+
contentBlocked: 0,
|
|
61
|
+
contentPending: 0,
|
|
62
|
+
scrubFindings: 0,
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Bounded post-flush retries for a `pending` (404-until-durable) content upload
|
|
67
|
+
* before it becomes a counted skip (E4 chunk 0, flag 2). Two retries with the
|
|
68
|
+
* exporter interval between them is a multi-flush window for the metadata row to
|
|
69
|
+
* land — long enough for a real export lag, short enough to bound the wait. */
|
|
70
|
+
const CONTENT_RETRY_BUDGET = 2;
|
|
71
|
+
function createArtifactApi(deps) {
|
|
72
|
+
if (!deps.enabled)
|
|
73
|
+
return inertApi();
|
|
74
|
+
const { store, content } = deps;
|
|
75
|
+
let saves = 0;
|
|
76
|
+
let inferredEdges = 0;
|
|
77
|
+
let skippedZeroRetention = 0;
|
|
78
|
+
let contentUploaded = 0;
|
|
79
|
+
let contentDegraded = 0;
|
|
80
|
+
let contentBlocked = 0;
|
|
81
|
+
let scrubFindings = 0;
|
|
82
|
+
/** In-flight uploads; flushContent awaits them (graceful shutdown + tests). */
|
|
83
|
+
const pendingUploads = new Set();
|
|
84
|
+
/** Uploads that came back `pending` (grant 404 — metadata not yet durable),
|
|
85
|
+
* awaiting the exporter's post-flush retry hook. `retriesLeft` decrements once
|
|
86
|
+
* per hook call; at 0 the item becomes a counted skip (E4 chunk 0, flag 2). */
|
|
87
|
+
const pendingContent = [];
|
|
88
|
+
/** artifact id → the task it was saved with THIS session (E4 chunk A). Lets
|
|
89
|
+
* `promoteToEval(ref)` resolve the case's task without the caller re-supplying
|
|
90
|
+
* it. A promote of an artifact from a prior session must pass `opts.task`. */
|
|
91
|
+
const savedTasks = new Map();
|
|
92
|
+
/** Fire one content upload off the hot path. NEVER rejects into caller code —
|
|
93
|
+
* every outcome updates a counter (or queues a `pending` retry) and resolves. */
|
|
94
|
+
function fireUpload(input, upload) {
|
|
95
|
+
const p = upload(input)
|
|
96
|
+
.then((outcome) => {
|
|
97
|
+
if (outcome.kind === "uploaded")
|
|
98
|
+
contentUploaded += 1;
|
|
99
|
+
// A 404-until-durable race: queue for the post-flush retry rather than
|
|
100
|
+
// counting a degrade — the row simply has not been exported yet.
|
|
101
|
+
else if (outcome.kind === "pending") {
|
|
102
|
+
pendingContent.push({ input, retriesLeft: CONTENT_RETRY_BUDGET });
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
contentDegraded += 1; // "degraded" | "blocked"
|
|
106
|
+
})
|
|
107
|
+
.catch(() => {
|
|
108
|
+
contentDegraded += 1;
|
|
109
|
+
})
|
|
110
|
+
.finally(() => {
|
|
111
|
+
pendingUploads.delete(p);
|
|
112
|
+
});
|
|
113
|
+
pendingUploads.add(p);
|
|
114
|
+
}
|
|
115
|
+
/** Re-attempt every queued `pending` upload exactly once (the exporter's
|
|
116
|
+
* post-flush hook). Items that stay pending are re-queued with a decremented
|
|
117
|
+
* budget; at 0 they become a counted skip. Off/empty ⇒ a resolved no-op. */
|
|
118
|
+
async function retryPendingContent() {
|
|
119
|
+
if (content === undefined || pendingContent.length === 0)
|
|
120
|
+
return;
|
|
121
|
+
// Snapshot then clear: an item that stays pending is re-pushed with a lower
|
|
122
|
+
// budget, so THIS pass is exactly one retry per currently-queued item.
|
|
123
|
+
const batch = pendingContent.splice(0, pendingContent.length);
|
|
124
|
+
for (const item of batch) {
|
|
125
|
+
let outcome;
|
|
126
|
+
try {
|
|
127
|
+
outcome = await content.upload(item.input);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
contentDegraded += 1; // an unexpected throw is a counted skip, never rethrown
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (outcome.kind === "uploaded") {
|
|
134
|
+
contentUploaded += 1;
|
|
135
|
+
}
|
|
136
|
+
else if (outcome.kind === "pending") {
|
|
137
|
+
const retriesLeft = item.retriesLeft - 1;
|
|
138
|
+
if (retriesLeft > 0) {
|
|
139
|
+
pendingContent.push({ input: item.input, retriesLeft });
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
contentDegraded += 1; // exhausted the bounded retries → metadata-only
|
|
143
|
+
console.warn(`[modelrig artifacts] content for artifact ${item.input.artifactId} still not durable ` +
|
|
144
|
+
`after ${CONTENT_RETRY_BUDGET} post-flush retries — metadata-only (counted skip).`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
contentDegraded += 1; // degraded | blocked on retry → counted skip
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
save(value, opts) {
|
|
154
|
+
const ctx = (0, run_context_1.getActiveRunContext)();
|
|
155
|
+
// zeroRetention: refuse fail-closed BEFORE hashing or building any row.
|
|
156
|
+
if (ctx !== null && ctx.zeroRetention) {
|
|
157
|
+
skippedZeroRetention += 1;
|
|
158
|
+
console.warn(`[modelrig artifacts] zeroRetention run — artifact "${opts.name}" NOT recorded ` +
|
|
159
|
+
"(fail-closed at the SDK gate; no metadata, no hash persisted).");
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
// Hash the serialization, then DISCARD the bytes — metadata + hash only.
|
|
163
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value ?? null);
|
|
164
|
+
const bytes = Buffer.byteLength(serialized, "utf8");
|
|
165
|
+
const contentHash = `sha256:${(0, node_crypto_1.createHash)("sha256").update(serialized).digest("hex")}`;
|
|
166
|
+
const schema = opts.schema ?? null;
|
|
167
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
168
|
+
const version = opts.supersedes !== undefined ? opts.supersedes.version + 1 : 1;
|
|
169
|
+
// Custody applies only when a rig content policy exists AND the caller left
|
|
170
|
+
// the bytes to us (a caller-supplied contentRef = their own store; managed
|
|
171
|
+
// custody must not double-home it).
|
|
172
|
+
const custodyApplies = content !== undefined && opts.contentRef === undefined;
|
|
173
|
+
// Scrub is computed HERE (before the row is written) so its counts-only
|
|
174
|
+
// summary can ride the immutable artifact row (00039). Only when custody
|
|
175
|
+
// applies; null otherwise (custody off ⇒ nothing was scanned).
|
|
176
|
+
const scrubbed = custodyApplies && content !== undefined ? (0, scrub_1.scrubContent)(serialized, content.scrub) : null;
|
|
177
|
+
const scrubSummary = scrubbed !== null && content !== undefined ? (0, scrub_1.summarizeScrub)(content.scrub, scrubbed) : null;
|
|
178
|
+
if (scrubbed !== null)
|
|
179
|
+
scrubFindings += scrubbed.findings.reduce((n, f) => n + f.count, 0);
|
|
180
|
+
try {
|
|
181
|
+
store.recordArtifact({
|
|
182
|
+
id,
|
|
183
|
+
runId: ctx?.runId ?? null,
|
|
184
|
+
// E3: step attribution is now an explicit caller-supplied key (see
|
|
185
|
+
// ArtifactSaveOptions.stepKey). Absent ⇒ null, exactly as before.
|
|
186
|
+
stepKey: opts.stepKey ?? null,
|
|
187
|
+
attemptNo: opts.attemptNo ?? null,
|
|
188
|
+
task: opts.task ?? null,
|
|
189
|
+
name: opts.name,
|
|
190
|
+
type: opts.type,
|
|
191
|
+
version,
|
|
192
|
+
supersedesArtifactId: opts.supersedes?.id ?? null,
|
|
193
|
+
declaredSchema: schema,
|
|
194
|
+
fingerprintCell: (0, fingerprint_1.fingerprintSchema)(schema)?.cellKey ?? null,
|
|
195
|
+
contentRef: opts.contentRef ?? null,
|
|
196
|
+
contentHash,
|
|
197
|
+
contentBytes: bytes,
|
|
198
|
+
dataClassification: opts.classification ?? null,
|
|
199
|
+
expiresAt: opts.expiresAt ?? null,
|
|
200
|
+
// Per-row scrub findings ride the original (scanned) row (00039).
|
|
201
|
+
scrub: scrubSummary,
|
|
202
|
+
});
|
|
203
|
+
saves += 1;
|
|
204
|
+
if (opts.task !== undefined)
|
|
205
|
+
savedTasks.set(id, opts.task); // for promoteToEval (chunk A)
|
|
206
|
+
// Lineage v0: derived_from edges to everything loaded since the last save.
|
|
207
|
+
if (ctx !== null) {
|
|
208
|
+
for (const loadedId of ctx.takeLoadedIds()) {
|
|
209
|
+
if (loadedId === id)
|
|
210
|
+
continue; // edges_no_self
|
|
211
|
+
store.recordArtifactEdge({ fromArtifactId: id, toArtifactId: loadedId, kind: "derived_from" });
|
|
212
|
+
inferredEdges += 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
warn("save", err);
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
// ── Content custody (E2-C) ──────────────────────────────────────────
|
|
221
|
+
// Off by default: only when a rig has configured content custody. The
|
|
222
|
+
// metadata row above is written regardless — content is layered on, never
|
|
223
|
+
// a precondition. zeroRetention already returned above, so we never reach
|
|
224
|
+
// here for a zeroRetention run (structural, not re-checked).
|
|
225
|
+
if (content !== undefined && scrubbed !== null) {
|
|
226
|
+
if (scrubbed.blocked) {
|
|
227
|
+
// `block` mode found PII/PHI → content upload refused (counted). The
|
|
228
|
+
// artifact keeps its metadata + hash; it simply stores no content.
|
|
229
|
+
contentBlocked += 1;
|
|
230
|
+
console.warn(`[modelrig artifacts] scrub blocked content for "${opts.name}" ` +
|
|
231
|
+
`(${scrubbed.findings.length} finding type(s)); metadata + hash kept, no content stored.`);
|
|
232
|
+
}
|
|
233
|
+
else if (content.scrub === "redact" && scrubbed.findings.length > 0 && scrubbed.text !== null) {
|
|
234
|
+
// Redaction is a SUPERSEDING version (immutability): the original row
|
|
235
|
+
// keeps its hash and stores NO content; a new version records the
|
|
236
|
+
// masked content's hash and uploads the masked bytes.
|
|
237
|
+
const maskedSerialized = scrubbed.text;
|
|
238
|
+
const maskedBytes = Buffer.byteLength(maskedSerialized, "utf8");
|
|
239
|
+
const maskedHash = `sha256:${(0, node_crypto_1.createHash)("sha256").update(maskedSerialized).digest("hex")}`;
|
|
240
|
+
const redactedId = (0, node_crypto_1.randomUUID)();
|
|
241
|
+
const redactedVersion = version + 1;
|
|
242
|
+
try {
|
|
243
|
+
store.recordArtifact({
|
|
244
|
+
id: redactedId,
|
|
245
|
+
runId: ctx?.runId ?? null,
|
|
246
|
+
stepKey: opts.stepKey ?? null,
|
|
247
|
+
attemptNo: opts.attemptNo ?? null,
|
|
248
|
+
task: opts.task ?? null,
|
|
249
|
+
name: opts.name,
|
|
250
|
+
type: opts.type,
|
|
251
|
+
version: redactedVersion,
|
|
252
|
+
supersedesArtifactId: id, // supersedes the original (ruling c column)
|
|
253
|
+
declaredSchema: schema,
|
|
254
|
+
fingerprintCell: (0, fingerprint_1.fingerprintSchema)(schema)?.cellKey ?? null,
|
|
255
|
+
contentRef: null,
|
|
256
|
+
contentHash: maskedHash,
|
|
257
|
+
contentBytes: maskedBytes,
|
|
258
|
+
dataClassification: opts.classification ?? null,
|
|
259
|
+
expiresAt: opts.expiresAt ?? null,
|
|
260
|
+
// Findings ride the original scanned row; the masked version stores
|
|
261
|
+
// no PII, so its own scrub is null (avoid double-counting).
|
|
262
|
+
scrub: null,
|
|
263
|
+
});
|
|
264
|
+
saves += 1;
|
|
265
|
+
fireUpload({
|
|
266
|
+
artifactId: redactedId,
|
|
267
|
+
contentType: contentTypeFor(opts.type),
|
|
268
|
+
body: Buffer.from(maskedSerialized, "utf8"),
|
|
269
|
+
contentHash: maskedHash,
|
|
270
|
+
}, content.upload);
|
|
271
|
+
// The live version is the redacted one — hand its handle back.
|
|
272
|
+
return { id: redactedId, name: opts.name, type: opts.type, version: redactedVersion };
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
warn("save (redacted version)", err);
|
|
276
|
+
// Fall through to returning the original handle (metadata-only).
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
// off / detect / redact-with-no-findings: upload the bytes verbatim.
|
|
281
|
+
fireUpload({
|
|
282
|
+
artifactId: id,
|
|
283
|
+
contentType: contentTypeFor(opts.type),
|
|
284
|
+
body: Buffer.from(scrubbed.text ?? serialized, "utf8"),
|
|
285
|
+
contentHash,
|
|
286
|
+
}, content.upload);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return { id, name: opts.name, type: opts.type, version };
|
|
290
|
+
},
|
|
291
|
+
load(ref) {
|
|
292
|
+
(0, run_context_1.getActiveRunContext)()?.noteLoad(idOf(ref));
|
|
293
|
+
},
|
|
294
|
+
link(from, to, kind) {
|
|
295
|
+
const fromId = idOf(from);
|
|
296
|
+
const toId = idOf(to);
|
|
297
|
+
if (fromId === toId)
|
|
298
|
+
return; // edges_no_self
|
|
299
|
+
try {
|
|
300
|
+
store.recordArtifactEdge({ fromArtifactId: fromId, toArtifactId: toId, kind });
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
warn("link", err);
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
evaluate(ref, opts) {
|
|
307
|
+
try {
|
|
308
|
+
store.recordArtifactEvaluation({
|
|
309
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
310
|
+
artifactId: idOf(ref),
|
|
311
|
+
runId: null,
|
|
312
|
+
evaluator: opts.evaluator,
|
|
313
|
+
evaluatorKind: opts.kind,
|
|
314
|
+
score: opts.score ?? null,
|
|
315
|
+
passed: opts.passed ?? null,
|
|
316
|
+
label: opts.label ?? null,
|
|
317
|
+
explanation: opts.explanation ?? null,
|
|
318
|
+
createdBy: null,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
catch (err) {
|
|
322
|
+
warn("evaluate", err);
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
promoteToEval(ref, opts) {
|
|
326
|
+
const sourceArtifactId = idOf(ref);
|
|
327
|
+
// Resolve the task: explicit wins, else the task this artifact was saved
|
|
328
|
+
// with this session. Unresolvable ⇒ a warned no-op (eval_cases needs task).
|
|
329
|
+
const task = opts?.task ?? savedTasks.get(sourceArtifactId);
|
|
330
|
+
if (task === undefined) {
|
|
331
|
+
console.warn(`[modelrig artifacts] promoteToEval("${sourceArtifactId}") skipped: no task known for this ` +
|
|
332
|
+
"artifact (it was not saved this session) — pass { task } to promote it.");
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
const correctedArtifactId = opts?.corrected !== undefined ? idOf(opts.corrected) : null;
|
|
336
|
+
const origin = opts?.origin ?? (correctedArtifactId !== null ? "human-corrected" : "promoted-failure");
|
|
337
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
338
|
+
try {
|
|
339
|
+
store.recordEvalCase({
|
|
340
|
+
id,
|
|
341
|
+
task,
|
|
342
|
+
sourceArtifactId,
|
|
343
|
+
correctedArtifactId,
|
|
344
|
+
origin,
|
|
345
|
+
note: opts?.note ?? null,
|
|
346
|
+
createdBy: null,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
warn("promoteToEval", err);
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
return id;
|
|
354
|
+
},
|
|
355
|
+
batch(fn) {
|
|
356
|
+
return store.batch(fn);
|
|
357
|
+
},
|
|
358
|
+
async flushContent() {
|
|
359
|
+
// Settle every in-flight upload. Individual failures already resolve (they
|
|
360
|
+
// update contentDegraded), so allSettled never throws here.
|
|
361
|
+
await Promise.allSettled([...pendingUploads]);
|
|
362
|
+
},
|
|
363
|
+
retryPendingContent,
|
|
364
|
+
coverage() {
|
|
365
|
+
return {
|
|
366
|
+
saves,
|
|
367
|
+
inferredEdges,
|
|
368
|
+
skippedZeroRetention,
|
|
369
|
+
contentUploaded,
|
|
370
|
+
contentDegraded,
|
|
371
|
+
contentBlocked,
|
|
372
|
+
contentPending: pendingContent.length,
|
|
373
|
+
scrubFindings,
|
|
374
|
+
};
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed content upload path — E2-C chunk A (spec §2.4).
|
|
3
|
+
*
|
|
4
|
+
* When a rig has opted a route into content custody, `save` records the metadata
|
|
5
|
+
* row (as always) and THEN hands the serialized bytes here. This module talks to
|
|
6
|
+
* modelrig-server's grant endpoint, receives a short-lived presigned PUT, and
|
|
7
|
+
* uploads the bytes straight to the object store — the bytes NEVER touch our
|
|
8
|
+
* control-plane database, only the customer-designated store.
|
|
9
|
+
*
|
|
10
|
+
* ── THE POISON LESSON, APPLIED ────────────────────────────────────────────
|
|
11
|
+
* A deterministic refusal (403 posture-off, 409 already has content, 413 too
|
|
12
|
+
* big, 422 malformed) is NOT retried — it will fail identically forever, and
|
|
13
|
+
* retrying a poison request is how a fail-closed refusal turns into a hot loop.
|
|
14
|
+
* Every refusal DEGRADES to metadata-only: the artifact still exists (its row
|
|
15
|
+
* was written before we were called), it simply has no stored content.
|
|
16
|
+
* Degradation is counted and warned, never thrown — this runs off the
|
|
17
|
+
* customer's hot path and must never surface as a failed run.
|
|
18
|
+
*
|
|
19
|
+
* ── THE 404-UNTIL-DURABLE RACE (E4 chunk 0, flag 2) ───────────────────────
|
|
20
|
+
* A 404 is the ONE 4xx that is NOT deterministic. The grant endpoint verifies
|
|
21
|
+
* the artifact metadata row exists (and belongs to the org — T11) before
|
|
22
|
+
* issuing a URL, but that row reaches the control plane on the exporter's NEXT
|
|
23
|
+
* flush — so a content upload fired during `save` races ahead of its own
|
|
24
|
+
* metadata and 404s until the row lands. TRANSIENT by nature: it clears once
|
|
25
|
+
* the metadata is durable. So a 404 returns a distinct `pending` outcome that
|
|
26
|
+
* the exporter's post-flush hook re-attempts (bounded: 2 retries, then a
|
|
27
|
+
* counted skip). A genuinely foreign/missing artifact 404s the same way and
|
|
28
|
+
* simply exhausts those retries into the metadata-only end state — delayed, but
|
|
29
|
+
* never a hot loop.
|
|
30
|
+
*
|
|
31
|
+
* ── C2 ────────────────────────────────────────────────────────────────────
|
|
32
|
+
* The api key rides in an Authorization header, never a URL. The presigned URL
|
|
33
|
+
* and the content hash are NEVER logged. Warnings name the artifact + a status,
|
|
34
|
+
* nothing else.
|
|
35
|
+
*/
|
|
36
|
+
export type ContentUploadOutcome =
|
|
37
|
+
/** Bytes are in the store; `contentRef` is the server's canonical pointer. */
|
|
38
|
+
{
|
|
39
|
+
readonly kind: "uploaded";
|
|
40
|
+
readonly contentRef: string;
|
|
41
|
+
}
|
|
42
|
+
/** Metadata-only fallback (deterministic refusal or transient failure). The
|
|
43
|
+
* `status` is the grant HTTP status when there was one, else null. */
|
|
44
|
+
| {
|
|
45
|
+
readonly kind: "degraded";
|
|
46
|
+
readonly reason: string;
|
|
47
|
+
readonly status: number | null;
|
|
48
|
+
}
|
|
49
|
+
/** The artifact metadata row is not yet durable in the control plane — a 404
|
|
50
|
+
* from the grant endpoint (which verifies the row before issuing a URL, T11).
|
|
51
|
+
* TRANSIENT: it clears once the exporter flushes the metadata. NOT counted as
|
|
52
|
+
* a degrade; the caller queues it for the post-flush hook to re-attempt
|
|
53
|
+
* (bounded 2 retries, then a counted skip). E4 chunk 0, flag 2. */
|
|
54
|
+
| {
|
|
55
|
+
readonly kind: "pending";
|
|
56
|
+
readonly reason: string;
|
|
57
|
+
readonly status: 404;
|
|
58
|
+
}
|
|
59
|
+
/** The scrub layer BLOCKED upload (chunk B). Distinct from degraded: this is a
|
|
60
|
+
* policy refusal, not a failure. */
|
|
61
|
+
| {
|
|
62
|
+
readonly kind: "blocked";
|
|
63
|
+
readonly reason: string;
|
|
64
|
+
};
|
|
65
|
+
export interface ContentUploadConfig {
|
|
66
|
+
/** Full grant endpoint, e.g. https://server/v1/artifacts/uploads. */
|
|
67
|
+
readonly uploadsUrl: string;
|
|
68
|
+
/** `rig_sk_` key carrying the `artifact-write` scope. Header only (C2). */
|
|
69
|
+
readonly apiKey: string;
|
|
70
|
+
/** Client-side ceiling — a short-circuit before the round trip; the server
|
|
71
|
+
* enforces the authoritative one. */
|
|
72
|
+
readonly maxBytes: number;
|
|
73
|
+
}
|
|
74
|
+
export interface ContentUploadInput {
|
|
75
|
+
readonly artifactId: string;
|
|
76
|
+
readonly contentType: string;
|
|
77
|
+
/** Serialized bytes to upload — already scrubbed by the caller (chunk B). */
|
|
78
|
+
readonly body: Buffer;
|
|
79
|
+
/** `sha256:<hex>` of `body`. */
|
|
80
|
+
readonly contentHash: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Upload one artifact's content. NEVER throws — every failure resolves to a
|
|
84
|
+
* `degraded` outcome the caller records as a metadata-only skip.
|
|
85
|
+
*/
|
|
86
|
+
export declare function uploadArtifactContent(input: ContentUploadInput, config: ContentUploadConfig, fetchFn?: typeof fetch): Promise<ContentUploadOutcome>;
|
|
87
|
+
//# sourceMappingURL=content-uploader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-uploader.d.ts","sourceRoot":"","sources":["../../src/artifacts/content-uploader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,MAAM,oBAAoB;AAC9B,8EAA8E;AAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAC5D;sEACsE;GACpE;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AACxF;;;;mEAImE;GACjE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAA;CAAE;AAC7E;oCACoC;GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;yCACqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAeD;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,oBAAoB,CAAC,CAoE/B"}
|