firedrill-run 2.2.5 → 3.0.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/README.md +178 -22
- package/bin/firedrill.mjs +29 -5
- package/dist/assembly.json +13 -0
- package/dist/cli/runtime/candidate_copy.py +395 -0
- package/dist/cli/runtime/source_snapshot.py +391 -0
- package/dist/cli/src/administration.js +295 -0
- package/dist/cli/src/agent-changes.js +496 -0
- package/dist/cli/src/assessment-requests.js +67 -0
- package/dist/cli/src/auth.js +465 -0
- package/dist/cli/src/billing-commerce-contracts.js +1 -0
- package/dist/cli/src/billing-usage.js +227 -0
- package/dist/cli/src/billing-wallet-contracts.js +1 -0
- package/dist/cli/src/billing.js +170 -0
- package/dist/cli/src/bootstrap-process-lock.js +136 -0
- package/dist/cli/src/browser-links.js +14 -0
- package/dist/cli/src/campaign-controls.js +122 -0
- package/dist/cli/src/campaign-diagnoses.js +118 -0
- package/dist/cli/src/campaign-reports.js +1215 -0
- package/dist/cli/src/campaign-world.js +156 -0
- package/dist/cli/src/candidate-copy.js +376 -0
- package/dist/cli/src/catalog-contracts.js +1 -0
- package/dist/cli/src/ci-access-contracts.js +1 -0
- package/dist/cli/src/ci-configuration-contracts.js +1 -0
- package/dist/cli/src/ci-coverage.js +64 -0
- package/dist/cli/src/ci-delivery-contracts.js +1 -0
- package/dist/cli/src/ci-result-contracts.js +1 -0
- package/dist/cli/src/ci-run-contracts.js +1 -0
- package/dist/cli/src/ci-setup-proposals.js +52 -0
- package/dist/cli/src/company-policies.js +229 -0
- package/dist/cli/src/completion.js +79 -0
- package/dist/cli/src/configuration-intents.js +277 -0
- package/dist/cli/src/configuration.js +369 -0
- package/dist/cli/src/continuous-simulation.js +277 -0
- package/dist/cli/src/coordinated-checkpoints.js +133 -0
- package/dist/cli/src/credential-mode.js +139 -0
- package/dist/cli/src/credentials.js +331 -0
- package/dist/cli/src/credit-format.js +8 -0
- package/dist/cli/src/dataset-contracts.js +1 -0
- package/dist/cli/src/dataset-export.js +154 -0
- package/dist/cli/src/dataset-reads.js +37 -0
- package/dist/cli/src/dependency-preparation.js +239 -0
- package/dist/cli/src/director-tool-setup.js +104 -0
- package/dist/cli/src/driver-review.js +225 -0
- package/dist/cli/src/evaluation-authoring.js +315 -0
- package/dist/cli/src/evidence-bundles.js +367 -0
- package/dist/cli/src/execution-proposals.js +36 -0
- package/dist/cli/src/http.js +302 -0
- package/dist/cli/src/local-container-boundary.js +38 -0
- package/dist/cli/src/local-container-policy.js +201 -0
- package/dist/cli/src/local-execution-setup.js +81 -0
- package/dist/cli/src/local-source-basis.js +73 -0
- package/dist/cli/src/local-source.js +66 -0
- package/dist/cli/src/local-value-store.js +91 -0
- package/dist/cli/src/local-values.js +163 -0
- package/dist/cli/src/login-organizations.js +70 -0
- package/dist/cli/src/main.js +1622 -0
- package/dist/cli/src/mcp-connection.js +109 -0
- package/dist/cli/src/native/bindings.js +137 -0
- package/dist/cli/src/native/canonical.js +370 -0
- package/dist/cli/src/native/cloud-resources.js +942 -0
- package/dist/cli/src/native/companion-channel.js +421 -0
- package/dist/cli/src/native/companion-ipc.js +473 -0
- package/dist/cli/src/native/contracts.js +218 -0
- package/dist/cli/src/native/control-plane.js +1354 -0
- package/dist/cli/src/native/credit-approval.js +15 -0
- package/dist/cli/src/native/dependency-proxy.js +1006 -0
- package/dist/cli/src/native/drill-cleanup.js +115 -0
- package/dist/cli/src/native/drill-runner.js +1562 -0
- package/dist/cli/src/native/drivers/action-trace.js +210 -0
- package/dist/cli/src/native/drivers/function-wrapper.js +152 -0
- package/dist/cli/src/native/drivers/http.js +442 -0
- package/dist/cli/src/native/drivers/index.js +47 -0
- package/dist/cli/src/native/drivers/jsonl-process.js +538 -0
- package/dist/cli/src/native/drivers/playwright.js +600 -0
- package/dist/cli/src/native/drivers/signed-event.js +244 -0
- package/dist/cli/src/native/drivers/types.js +578 -0
- package/dist/cli/src/native/evidence-filter.js +255 -0
- package/dist/cli/src/native/isolated-access.js +241 -0
- package/dist/cli/src/native/library-sharing.js +220 -0
- package/dist/cli/src/native/local-run.js +667 -0
- package/dist/cli/src/native/local-secrets.js +19 -0
- package/dist/cli/src/native/mcp-config.js +123 -0
- package/dist/cli/src/native/metadata-requests.js +120 -0
- package/dist/cli/src/native/observations.js +1159 -0
- package/dist/cli/src/native/owned-groups.js +46 -0
- package/dist/cli/src/native/plan.js +594 -0
- package/dist/cli/src/native/png.js +159 -0
- package/dist/cli/src/native/ports.js +43 -0
- package/dist/cli/src/native/readiness.js +139 -0
- package/dist/cli/src/native/reassessments.js +206 -0
- package/dist/cli/src/native/redaction.js +235 -0
- package/dist/cli/src/native/registration.js +155 -0
- package/dist/cli/src/native/roots.js +110 -0
- package/dist/cli/src/native/run-cleanup.js +199 -0
- package/dist/cli/src/native/scratch.js +117 -0
- package/dist/cli/src/native/screenshots.js +60 -0
- package/dist/cli/src/native/session.js +556 -0
- package/dist/cli/src/native/standalone-proxy.js +580 -0
- package/dist/cli/src/native/standalone-renewals.js +183 -0
- package/dist/cli/src/native/standalone-state.js +193 -0
- package/dist/cli/src/native/standalone-tools.js +187 -0
- package/dist/cli/src/native/supervisor.js +831 -0
- package/dist/cli/src/native/target-auth.js +161 -0
- package/dist/cli/src/native/target-state.js +330 -0
- package/dist/cli/src/native/tool-reuse.js +364 -0
- package/dist/cli/src/native/tool-setups.js +336 -0
- package/dist/cli/src/native/world-reuse.js +288 -0
- package/dist/cli/src/onboarding.js +23 -0
- package/dist/cli/src/priority-requests.js +70 -0
- package/dist/cli/src/priority.js +223 -0
- package/dist/cli/src/prompts.js +184 -0
- package/dist/cli/src/real-operations.js +221 -0
- package/dist/cli/src/recipe-intents.js +58 -0
- package/dist/cli/src/recorded-context-intents.js +59 -0
- package/dist/cli/src/recorded-context.js +248 -0
- package/dist/cli/src/recordings.js +69 -0
- package/dist/cli/src/registration-guide.js +39 -0
- package/dist/cli/src/report-exports.js +213 -0
- package/dist/cli/src/request-input.js +47 -0
- package/dist/cli/src/resource-lifecycle.js +141 -0
- package/dist/cli/src/revision-requests.js +55 -0
- package/dist/cli/src/runner-abandon.js +53 -0
- package/dist/cli/src/runner-campaign-controls.js +262 -0
- package/dist/cli/src/runner-claim-reads.js +36 -0
- package/dist/cli/src/runner-completion.js +65 -0
- package/dist/cli/src/runner-configuration.js +546 -0
- package/dist/cli/src/runner-container.js +794 -0
- package/dist/cli/src/runner-evidence-erasure.js +351 -0
- package/dist/cli/src/runner-evidence-privacy.js +37 -0
- package/dist/cli/src/runner-evidence-retention.js +161 -0
- package/dist/cli/src/runner-evidence.js +326 -0
- package/dist/cli/src/runner-execute.js +92 -0
- package/dist/cli/src/runner-execution-deadline.js +25 -0
- package/dist/cli/src/runner-grants.js +307 -0
- package/dist/cli/src/runner-identity.js +615 -0
- package/dist/cli/src/runner-inputs.js +308 -0
- package/dist/cli/src/runner-jobs.js +49 -0
- package/dist/cli/src/runner-model-transport.js +392 -0
- package/dist/cli/src/runner-ownership.js +332 -0
- package/dist/cli/src/runner-process-lock.js +175 -0
- package/dist/cli/src/runner-runtime.js +396 -0
- package/dist/cli/src/runner-session-channel.js +377 -0
- package/dist/cli/src/runner-trial-completion.js +34 -0
- package/dist/cli/src/runner-trial-journal.js +358 -0
- package/dist/cli/src/runner-trial-live.js +393 -0
- package/dist/cli/src/runner-trial-protocol.js +289 -0
- package/dist/cli/src/runner-trial-sync.js +110 -0
- package/dist/cli/src/runner-trial-transfer.js +107 -0
- package/dist/cli/src/runner-voice.js +40 -0
- package/dist/cli/src/runner-world-claim.js +21 -0
- package/dist/cli/src/runner-world-collector.js +159 -0
- package/dist/cli/src/runner-world-command-evidence.js +487 -0
- package/dist/cli/src/runner-world-commands.js +268 -0
- package/dist/cli/src/runner-world-completion.js +76 -0
- package/dist/cli/src/runner-world-erasure.js +95 -0
- package/dist/cli/src/runner-world-evidence-sync.js +91 -0
- package/dist/cli/src/runner-world-journal.js +510 -0
- package/dist/cli/src/runner-world-native-filters.js +134 -0
- package/dist/cli/src/runner-world-observations.js +50 -0
- package/dist/cli/src/runner-world-session-protocol.js +134 -0
- package/dist/cli/src/saved-records.js +40 -0
- package/dist/cli/src/scenario-authoring.js +212 -0
- package/dist/cli/src/scenario-discovery.js +216 -0
- package/dist/cli/src/scenario-paths.js +68 -0
- package/dist/cli/src/scenario-requests.js +4 -0
- package/dist/cli/src/service-receipts.js +105 -0
- package/dist/cli/src/simple/authoring.js +270 -0
- package/dist/cli/src/simple/credit-approval.js +16 -0
- package/dist/cli/src/simple/help.js +98 -0
- package/dist/cli/src/simple/init.js +61 -0
- package/dist/cli/src/simple/library-sharing.js +414 -0
- package/dist/cli/src/simple/metadata-requests.js +46 -0
- package/dist/cli/src/simple/native-report.js +841 -0
- package/dist/cli/src/simple/native-tools.js +780 -0
- package/dist/cli/src/simple/plan-apply.js +506 -0
- package/dist/cli/src/simple/reassessment.js +323 -0
- package/dist/cli/src/simple/register.js +123 -0
- package/dist/cli/src/simple/run.js +122 -0
- package/dist/cli/src/simple/status.js +49 -0
- package/dist/cli/src/simple/tool-renewal.js +308 -0
- package/dist/cli/src/simple/tool-reuse.js +526 -0
- package/dist/cli/src/simple/tool-start-catalog.js +100 -0
- package/dist/cli/src/simple/tool-state.js +426 -0
- package/dist/cli/src/simple/tools-author.js +222 -0
- package/dist/cli/src/simple/watch.js +28 -0
- package/dist/cli/src/simple/world-reuse.js +586 -0
- package/dist/cli/src/simulation-intents.js +406 -0
- package/dist/cli/src/simulation-run.js +1058 -0
- package/dist/cli/src/source-batch.js +322 -0
- package/dist/cli/src/source-connection.js +451 -0
- package/dist/cli/src/source-runtime.js +346 -0
- package/dist/cli/src/source-selection-intents.js +75 -0
- package/dist/cli/src/source-sets.js +214 -0
- package/dist/cli/src/target-models.js +109 -0
- package/dist/cli/src/tool-catalog.js +224 -0
- package/dist/cli/src/tool-command-contracts.js +1 -0
- package/dist/cli/src/tool-command-export.js +245 -0
- package/dist/cli/src/tool-connection-contracts.js +1 -0
- package/dist/cli/src/tool-connection-reads.js +23 -0
- package/dist/cli/src/tool-contributions.js +58 -0
- package/dist/cli/src/tool-data.js +116 -0
- package/dist/cli/src/tool-datasets.js +176 -0
- package/dist/cli/src/tool-group-contracts.js +1 -0
- package/dist/cli/src/tool-group-reads.js +26 -0
- package/dist/cli/src/tool-group-state.js +147 -0
- package/dist/cli/src/tool-groups.js +311 -0
- package/dist/cli/src/tool-instances.js +186 -0
- package/dist/cli/src/tool-lifecycle-contracts.js +1 -0
- package/dist/cli/src/tool-lifecycle-reads.js +30 -0
- package/dist/cli/src/tool-lifecycle.js +323 -0
- package/dist/cli/src/tool-package-contracts.js +1 -0
- package/dist/cli/src/tool-package-reads.js +41 -0
- package/dist/cli/src/tool-records.js +53 -0
- package/dist/cli/src/tool-runtime.js +96 -0
- package/dist/cli/src/tool-state-contracts.js +1 -0
- package/dist/cli/src/tool-state-reads.js +31 -0
- package/dist/cli/src/tools.js +306 -0
- package/dist/cli/src/trial-actions.js +93 -0
- package/dist/cli/src/trial-checkpoints.js +133 -0
- package/dist/cli/src/trial-checks.js +70 -0
- package/dist/cli/src/trial-state.js +197 -0
- package/dist/cli/src/vault.js +266 -0
- package/dist/cli/src/workspace-registration.js +383 -0
- package/dist/cli/src/workspace-team-contracts.js +1 -0
- package/dist/cli/src/world-access.js +231 -0
- package/dist/cli/src/world-agent.js +4 -0
- package/dist/cli/src/world-checkpoint-scopes.js +15 -0
- package/dist/cli/src/world-contracts.js +6 -0
- package/dist/cli/src/world-creation.js +201 -0
- package/dist/cli/src/world-datasets.js +207 -0
- package/dist/cli/src/world-inspection.js +257 -0
- package/dist/cli/src/world-recipes.js +103 -0
- package/dist/cli/src/world-requests.js +241 -0
- package/dist/cli/src/world-session-checkpoints.js +196 -0
- package/dist/cli/src/world-session-lifecycle.js +207 -0
- package/dist/cli/src/world-session-requests.js +215 -0
- package/dist/cli/src/world-session-state.js +145 -0
- package/dist/cli/src/world-session-voice.js +66 -0
- package/dist/cli/src/world-sessions.js +288 -0
- package/dist/cli/src/world-stream.js +99 -0
- package/dist/cli/src/world-time-report.js +58 -0
- package/dist/contracts/agent-change-contracts.js +75 -0
- package/dist/contracts/agent-execution-contracts.js +38 -0
- package/dist/contracts/application-state-contracts.js +26 -0
- package/dist/contracts/assessment-aggregate-contracts.js +15 -0
- package/dist/contracts/assessment-contracts.js +74 -0
- package/dist/contracts/billing-commerce-contracts.js +107 -0
- package/dist/contracts/billing-wallet-contracts.js +43 -0
- package/dist/contracts/campaign-lifecycle.js +1 -0
- package/dist/contracts/campaign-world-contracts.js +80 -0
- package/dist/contracts/catalog-contracts.js +73 -0
- package/dist/contracts/ci-access-contracts.js +68 -0
- package/dist/contracts/ci-configuration-contracts.js +88 -0
- package/dist/contracts/ci-coverage-contracts.js +65 -0
- package/dist/contracts/ci-coverage-labels.js +25 -0
- package/dist/contracts/ci-delivery-contracts.js +61 -0
- package/dist/contracts/ci-result-contracts.js +103 -0
- package/dist/contracts/ci-run-contracts.js +51 -0
- package/dist/contracts/ci-setup-contracts.js +88 -0
- package/dist/contracts/clip-contracts.js +32 -0
- package/dist/contracts/comparison-contracts.js +53 -0
- package/dist/contracts/composite-contracts.js +34 -0
- package/dist/contracts/context-contracts.js +76 -0
- package/dist/contracts/continuation-report-contracts.js +1 -0
- package/dist/contracts/continuity-requirements.js +8 -0
- package/dist/contracts/coordinated-checkpoint-contracts.js +34 -0
- package/dist/contracts/dataset-contracts.js +88 -0
- package/dist/contracts/dependency-decision-contracts.js +36 -0
- package/dist/contracts/dependency-protocol-contracts.js +43 -0
- package/dist/contracts/director-execution.js +51 -0
- package/dist/contracts/driver-review-contracts.js +99 -0
- package/dist/contracts/evidence-contracts.js +65 -0
- package/dist/contracts/evidence-coverage-contracts.js +10 -0
- package/dist/contracts/export-contracts.js +44 -0
- package/dist/contracts/github-directory-contracts.js +96 -0
- package/dist/contracts/insight-contracts.js +11 -0
- package/dist/contracts/inspection-contracts.js +1 -0
- package/dist/contracts/local-claim-contracts.js +39 -0
- package/dist/contracts/local-configuration-contracts.js +7 -0
- package/dist/contracts/local-evidence-contracts.js +32 -0
- package/dist/contracts/local-grant-contracts.js +83 -0
- package/dist/contracts/local-input-contracts.js +19 -0
- package/dist/contracts/local-runner-contracts.js +42 -0
- package/dist/contracts/local-trial-live-contracts.js +16 -0
- package/dist/contracts/local-value-source-contracts.js +92 -0
- package/dist/contracts/local-world-native-contracts.js +8 -0
- package/dist/contracts/mcp-contracts.js +42 -0
- package/dist/contracts/media-contracts.js +28 -0
- package/dist/contracts/native-check-contracts.js +31 -0
- package/dist/contracts/native-identity-contracts.js +52 -0
- package/dist/contracts/onboarding-contracts.js +32 -0
- package/dist/contracts/performance-contracts.js +85 -0
- package/dist/contracts/planning-contracts.js +81 -0
- package/dist/contracts/real-operation-contracts.js +30 -0
- package/dist/contracts/recommendation-contracts.js +54 -0
- package/dist/contracts/report-contracts.js +1 -0
- package/dist/contracts/resource-lifecycle-contracts.js +57 -0
- package/dist/contracts/revision-contracts.js +12 -0
- package/dist/contracts/revision-request-contracts.js +39 -0
- package/dist/contracts/saved-record-contracts.js +76 -0
- package/dist/contracts/scenario-draft-comparison.js +91 -0
- package/dist/contracts/scenario-world-contracts.js +14 -0
- package/dist/contracts/service-contracts.js +1 -0
- package/dist/contracts/session-command-contracts.js +56 -0
- package/dist/contracts/session-contracts.js +65 -0
- package/dist/contracts/session-state-contracts.js +67 -0
- package/dist/contracts/session-voice-contracts.js +45 -0
- package/dist/contracts/source-set-contracts.js +26 -0
- package/dist/contracts/source-upload-contracts.js +22 -0
- package/dist/contracts/tool-command-contracts.js +57 -0
- package/dist/contracts/tool-connection-contracts.js +87 -0
- package/dist/contracts/tool-contribution-contracts.js +35 -0
- package/dist/contracts/tool-group-contracts.js +53 -0
- package/dist/contracts/tool-group-state-contracts.js +63 -0
- package/dist/contracts/tool-lifecycle-contracts.js +94 -0
- package/dist/contracts/tool-package-contracts.js +91 -0
- package/dist/contracts/tool-protocol-contracts.js +71 -0
- package/dist/contracts/tool-runtime-billing-contracts.js +28 -0
- package/dist/contracts/tool-setup-proposal.js +59 -0
- package/dist/contracts/tool-state-contracts.js +79 -0
- package/dist/contracts/trial-state-contracts.js +30 -0
- package/dist/contracts/typescript/agent-change.js +1 -0
- package/dist/contracts/typescript/assessment-aggregate.js +1 -0
- package/dist/contracts/typescript/assessment.js +1 -0
- package/dist/contracts/typescript/billing-commerce.js +1 -0
- package/dist/contracts/typescript/campaigns.js +1 -0
- package/dist/contracts/typescript/catalog.js +1 -0
- package/dist/contracts/typescript/ci-access.js +1 -0
- package/dist/contracts/typescript/ci-configuration.js +1 -0
- package/dist/contracts/typescript/ci-delivery.js +1 -0
- package/dist/contracts/typescript/ci-result.js +1 -0
- package/dist/contracts/typescript/ci-runs.js +1 -0
- package/dist/contracts/typescript/clips.js +1 -0
- package/dist/contracts/typescript/comparison.js +1 -0
- package/dist/contracts/typescript/composite.js +1 -0
- package/dist/contracts/typescript/connections.js +1 -0
- package/dist/contracts/typescript/context.js +1 -0
- package/dist/contracts/typescript/datasets.js +1 -0
- package/dist/contracts/typescript/evidence-coverage.js +1 -0
- package/dist/contracts/typescript/export.js +1 -0
- package/dist/contracts/typescript/github-directory.js +1 -0
- package/dist/contracts/typescript/insight.js +1 -0
- package/dist/contracts/typescript/inspection.js +1 -0
- package/dist/contracts/typescript/intake.js +1 -0
- package/dist/contracts/typescript/local-claim.js +1 -0
- package/dist/contracts/typescript/local-configuration.js +1 -0
- package/dist/contracts/typescript/local-evidence.js +1 -0
- package/dist/contracts/typescript/local-grant.js +1 -0
- package/dist/contracts/typescript/local-input.js +1 -0
- package/dist/contracts/typescript/local-runner.js +1 -0
- package/dist/contracts/typescript/local-trial-live.js +1 -0
- package/dist/contracts/typescript/local-value-source.js +1 -0
- package/dist/contracts/typescript/local-world-native.js +1 -0
- package/dist/contracts/typescript/mcp.js +1 -0
- package/dist/contracts/typescript/media.js +1 -0
- package/dist/contracts/typescript/native-check.js +1 -0
- package/dist/contracts/typescript/native-identity.js +1 -0
- package/dist/contracts/typescript/onboarding.js +1 -0
- package/dist/contracts/typescript/performance.js +1 -0
- package/dist/contracts/typescript/planning.js +1 -0
- package/dist/contracts/typescript/preparation.js +1 -0
- package/dist/contracts/typescript/real-operation.js +1 -0
- package/dist/contracts/typescript/recommendation.js +1 -0
- package/dist/contracts/typescript/reports.js +1 -0
- package/dist/contracts/typescript/resource-lifecycle.js +1 -0
- package/dist/contracts/typescript/saved-record.js +1 -0
- package/dist/contracts/typescript/scenarios.js +1 -0
- package/dist/contracts/typescript/services.js +1 -0
- package/dist/contracts/typescript/session-command.js +1 -0
- package/dist/contracts/typescript/session-state.js +1 -0
- package/dist/contracts/typescript/session-voice.js +1 -0
- package/dist/contracts/typescript/session.js +1 -0
- package/dist/contracts/typescript/tool-command.js +1 -0
- package/dist/contracts/typescript/tool-connection.js +1 -0
- package/dist/contracts/typescript/tool-contribution.js +1 -0
- package/dist/contracts/typescript/tool-groups.js +1 -0
- package/dist/contracts/typescript/tool-lifecycle.js +1 -0
- package/dist/contracts/typescript/tool-package.js +1 -0
- package/dist/contracts/typescript/tool-state.js +1 -0
- package/dist/contracts/typescript/trial-state.js +1 -0
- package/dist/contracts/typescript/vault.js +1 -0
- package/dist/contracts/typescript/workspace-team.js +1 -0
- package/dist/contracts/typescript/world-stream.js +1 -0
- package/dist/contracts/workspace-team-contracts.js +55 -0
- package/dist/contracts/world-command.js +65 -0
- package/dist/contracts/world-lifetime-contracts.js +74 -0
- package/dist/contracts/world-recipe-contracts.js +27 -0
- package/dist/contracts/world-stream-contracts.js +27 -0
- package/dist/package.json +1 -0
- package/package.json +16 -16
- package/LICENSE.md +0 -23
- package/PACKAGING.json +0 -50
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { gitHubId } from './github-directory-contracts.js';
|
|
2
|
+
import { nativeMetadataCanonical } from './local-value-source-contracts.js';
|
|
3
|
+
const uuid = (v) => typeof v === 'string'
|
|
4
|
+
&& /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
|
|
5
|
+
const sha = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
6
|
+
const integer = (v, minimum = 0) => typeof v === 'number' && Number.isSafeInteger(v) && v >= minimum;
|
|
7
|
+
const text = (v, maximum) => typeof v === 'string'
|
|
8
|
+
&& v.length > 0 && v.length <= maximum && !/[\x00-\x1f\x7f]/.test(v);
|
|
9
|
+
const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
|
|
10
|
+
const requestKeys = ['title', 'world_id', 'source_id', 'source_revision', 'world_revision', 'configuration_id',
|
|
11
|
+
'configuration_revision', 'configuration_sha256', 'plan_id', 'plan_revision', 'plan_sha256', 'source_alias',
|
|
12
|
+
'component_source_id', 'component_revision', 'connection_id', 'installation_id', 'repository_id', 'repository',
|
|
13
|
+
'connection_access_revision', 'connection_verified_at', 'source_set_sha256', 'graph_sha256', 'triggers', 'mode',
|
|
14
|
+
'nightly_hour_utc', 'scenario_count', 'repetitions', 'planned_trials', 'scenario_policy'].sort();
|
|
15
|
+
const requirements = new Set(['managed_plan_required', 'github_source_required', 'github_connection_required',
|
|
16
|
+
'repository_directory_required', 'repository_access_required', 'preparation_budget_review_required',
|
|
17
|
+
'simulation_budget_review_required', 'input_access_review_required', 'separate_activation_required', 'pr_plan_too_long']);
|
|
18
|
+
export function ciSetupProposalId(value) {
|
|
19
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
20
|
+
return null;
|
|
21
|
+
const row = value;
|
|
22
|
+
return row.schema === 'firedrill.ci-setup-proposal.v1' && uuid(row.ci_setup_proposal_id)
|
|
23
|
+
? row.ci_setup_proposal_id : null;
|
|
24
|
+
}
|
|
25
|
+
export function ciSetupProposalMatches(value, world, id, actor) {
|
|
26
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
27
|
+
return false;
|
|
28
|
+
const row = value;
|
|
29
|
+
const r = row.request;
|
|
30
|
+
if (row.schema !== 'firedrill.ci-setup-proposal.v1' || row.id !== id || row.world_id !== world
|
|
31
|
+
|| ![row.id, row.actor_id, row.world_id].every(uuid) || actor !== undefined && row.actor_id !== actor
|
|
32
|
+
|| !sha(row.request_sha256) || !r || typeof r !== 'object' || Array.isArray(r)
|
|
33
|
+
|| Object.keys(r).sort().join(',') !== requestKeys.join(',') || r.world_id !== world
|
|
34
|
+
|| ![r.world_id, r.source_id, r.configuration_id, r.plan_id, r.component_source_id, r.connection_id].every(uuid)
|
|
35
|
+
|| ![r.world_revision, r.plan_revision, r.repetitions].every(v => integer(v, 1))
|
|
36
|
+
|| ![r.configuration_revision, r.connection_access_revision].every(v => integer(v))
|
|
37
|
+
|| ![r.configuration_sha256, r.plan_sha256].every(sha) || !text(r.source_revision, 128)
|
|
38
|
+
|| !/^[a-f0-9]{40}$/.test(r.component_revision) || !/^[a-z][a-z0-9_-]{0,63}$/.test(r.source_alias)
|
|
39
|
+
|| !gitHubId(r.installation_id) || !gitHubId(r.repository_id) || !text(r.repository, 512) || !text(r.title, 120)
|
|
40
|
+
|| r.connection_verified_at !== null && !date(r.connection_verified_at)
|
|
41
|
+
|| [r.source_set_sha256, r.graph_sha256].some(v => v !== null && !sha(v))
|
|
42
|
+
|| !Array.isArray(r.triggers) || r.triggers.length < 1 || r.triggers.length > 3
|
|
43
|
+
|| new Set(r.triggers).size !== r.triggers.length
|
|
44
|
+
|| !r.triggers.every(v => ['pull_request', 'default_branch', 'nightly'].includes(v))
|
|
45
|
+
|| !['report_only', 'hard_constraints'].includes(r.mode)
|
|
46
|
+
|| (r.triggers.includes('nightly') ? !integer(r.nightly_hour_utc) || r.nightly_hour_utc > 23 : r.nightly_hour_utc !== null)
|
|
47
|
+
|| !integer(r.scenario_count) || !integer(r.planned_trials) || r.planned_trials !== r.scenario_count * r.repetitions
|
|
48
|
+
|| !r.scenario_policy || Object.keys(r.scenario_policy).sort().join(',') !== 'mandatory_scenario_ids,mode,sha256'
|
|
49
|
+
|| !['all', 'change_relevant'].includes(r.scenario_policy.mode) || !sha(r.scenario_policy.sha256)
|
|
50
|
+
|| !Array.isArray(r.scenario_policy.mandatory_scenario_ids) || r.scenario_policy.mandatory_scenario_ids.length > 100
|
|
51
|
+
|| !r.scenario_policy.mandatory_scenario_ids.every(uuid)
|
|
52
|
+
|| new Set(r.scenario_policy.mandatory_scenario_ids).size !== r.scenario_policy.mandatory_scenario_ids.length
|
|
53
|
+
|| !['available', 'current', 'can_review'].every(key => typeof row[key] === 'boolean')
|
|
54
|
+
|| row.can_review && (!row.available || !row.current)
|
|
55
|
+
|| !Array.isArray(row.missing_requirements) || row.missing_requirements.length > 16
|
|
56
|
+
|| new Set(row.missing_requirements).size !== row.missing_requirements.length
|
|
57
|
+
|| !row.missing_requirements.every(v => typeof v === 'string' && requirements.has(v))
|
|
58
|
+
|| row.handoff_path !== `/ci/new?world=${world}&proposal=${id}`
|
|
59
|
+
|| row.requires_user_approval !== true || row.configuration_created !== false
|
|
60
|
+
|| row.activation_started !== false || row.consent_granted !== false)
|
|
61
|
+
return false;
|
|
62
|
+
const summary = row.configuration_summary;
|
|
63
|
+
if (!summary || Object.keys(summary).sort().join(',') !== [
|
|
64
|
+
'variables', 'build_variables', 'runtime_variables', 'unresolved_variables', 'dependencies',
|
|
65
|
+
'simulated_dependencies', 'real_dependencies', 'model_connections'
|
|
66
|
+
].sort().join(',')
|
|
67
|
+
|| !Object.values(summary).every(v => integer(v)) || summary.build_variables > summary.variables
|
|
68
|
+
|| summary.runtime_variables > summary.variables || summary.unresolved_variables > summary.variables
|
|
69
|
+
|| summary.simulated_dependencies + summary.real_dependencies > summary.dependencies)
|
|
70
|
+
return false;
|
|
71
|
+
const plan = row.plan;
|
|
72
|
+
if (plan !== null && (!plan || plan.id !== r.plan_id || plan.world_id !== world || plan.revision !== r.plan_revision
|
|
73
|
+
|| plan.scenario_count !== r.scenario_count || plan.planned_trials !== r.planned_trials
|
|
74
|
+
|| typeof plan.world_name !== 'string' || !date(plan.created_at) || plan.source_sha !== r.component_revision
|
|
75
|
+
|| !Array.isArray(plan.source_aliases) || plan.source_aliases.length !== 1
|
|
76
|
+
|| plan.source_aliases[0]?.alias !== r.source_alias || plan.source_aliases[0]?.revision !== r.component_revision))
|
|
77
|
+
return false;
|
|
78
|
+
const repository = row.repository_option;
|
|
79
|
+
if (repository !== null && (!repository || repository.id !== r.repository_id || repository.full_name !== r.repository
|
|
80
|
+
|| repository.default_branch !== null && !text(repository.default_branch, 512)))
|
|
81
|
+
return false;
|
|
82
|
+
return !row.can_review || plan !== null && repository !== null;
|
|
83
|
+
}
|
|
84
|
+
/** Check the original immutable request after the structural/context guard. */
|
|
85
|
+
export async function ciSetupRequestDigest(request) {
|
|
86
|
+
const bytes = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(nativeMetadataCanonical(request)));
|
|
87
|
+
return Array.from(new Uint8Array(bytes), one => one.toString(16).padStart(2, '0')).join('');
|
|
88
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const uuid = (value) => /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value);
|
|
2
|
+
export function captureClipMatches(row, prefix, expected) {
|
|
3
|
+
if (!uuid(row.id) || !uuid(row.request_key) || expected !== undefined && row.id !== expected
|
|
4
|
+
|| row.origin !== 'browser_derived_capture_playback')
|
|
5
|
+
return false;
|
|
6
|
+
const path = `${prefix}/${row.id}/file`;
|
|
7
|
+
return row.file === null || row.state === 'retained' && row.file.path === path
|
|
8
|
+
&& row.file.download_path === `${path}?download=1` && row.file.content_type === 'video/mp4';
|
|
9
|
+
}
|
|
10
|
+
export function sameCaptureSelection(actual, original) {
|
|
11
|
+
return actual.id === original.id && actual.request_key === original.request_key && actual.source_kind === original.source_kind
|
|
12
|
+
&& actual.attempt_number === original.attempt_number && actual.frames === original.frames
|
|
13
|
+
&& actual.started_at === original.started_at && actual.ended_at === original.ended_at
|
|
14
|
+
&& actual.duration_us === original.duration_us && actual.source_sha256 === original.source_sha256
|
|
15
|
+
&& actual.width === original.width && actual.height === original.height && actual.origin === original.origin;
|
|
16
|
+
}
|
|
17
|
+
/** Corroborate the original private destination before sending encoded bytes. */
|
|
18
|
+
export function captureClipUploadDestination(ticket, prefix, clip) {
|
|
19
|
+
const workspace = prefix.match(/^\/api\/workspaces\/([a-f0-9-]{36})\/campaigns\/[a-f0-9-]{36}\/trials\/[a-f0-9-]{36}\/capture-clips$/)?.[1];
|
|
20
|
+
const url = new URL(ticket.upload_url);
|
|
21
|
+
const parts = url.pathname.split('/');
|
|
22
|
+
if (!workspace || !uuid(workspace) || url.origin !== 'https://storage.googleapis.com' || url.username || url.password || url.hash
|
|
23
|
+
|| parts.length !== 7 || !parts[1] || parts[2] !== 'private' || !uuid(parts[3]) || parts[4] !== workspace
|
|
24
|
+
|| parts[5] !== 'capture-clips' || parts[6] !== `${clip.id}.mp4`
|
|
25
|
+
|| ticket.method !== 'PUT' || Object.keys(ticket.headers).sort().join(',') !== 'Content-Type,x-goog-if-generation-match,x-goog-meta-firedrill-clip-artifact,x-goog-meta-firedrill-clip-id'
|
|
26
|
+
|| ticket.headers['Content-Type'] !== 'video/mp4' || ticket.headers['x-goog-if-generation-match'] !== '0'
|
|
27
|
+
|| !uuid(ticket.headers['x-goog-meta-firedrill-clip-artifact']) || ticket.headers['x-goog-meta-firedrill-clip-id'] !== clip.id
|
|
28
|
+
|| !Number.isFinite(Date.parse(ticket.expires_at)) || !captureClipMatches(ticket.clip, prefix, clip.id)
|
|
29
|
+
|| !sameCaptureSelection(ticket.clip, clip) || ticket.clip.state !== 'uploading')
|
|
30
|
+
throw new Error('The original private clip upload could not be confirmed. No bytes were transferred.');
|
|
31
|
+
return url;
|
|
32
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { memoryProbeMatches } from './revision-contracts.js';
|
|
2
|
+
const count = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
3
|
+
function summaryMatches(value, cohort, kind) {
|
|
4
|
+
const c = value?.scenario;
|
|
5
|
+
if (!c || c.scope !== 'scenario_schedule' || !['planned', 'retained', 'invoked', 'completed', 'target_failed',
|
|
6
|
+
'platform_failed', 'timed_out', 'canceled', 'unfinished', 'changed_world', 'unconfirmed_world'].every(key => count(c[key]))
|
|
7
|
+
|| c.invoked > c.retained || c.changed_world + c.unconfirmed_world > c.retained
|
|
8
|
+
|| c.completed + c.target_failed + c.platform_failed + c.timed_out + c.canceled + c.unfinished !== c.retained
|
|
9
|
+
|| ![value.observed, value.assessed, value.not_assessable, value.not_applicable, value.missing, value.without_assessment].every(count)
|
|
10
|
+
|| value.observed + c.changed_world + c.unconfirmed_world + value.without_assessment !== c.retained
|
|
11
|
+
|| value.assessed + value.not_assessable + value.not_applicable + value.missing !== value.observed
|
|
12
|
+
|| [value.mean, value.minimum, value.maximum].some(one => one !== null && (!Number.isFinite(one) || one < 0))
|
|
13
|
+
|| (value.assessed > 0) !== (value.mean !== null && value.minimum !== null && value.maximum !== null))
|
|
14
|
+
return false;
|
|
15
|
+
if (kind === 'style')
|
|
16
|
+
return true;
|
|
17
|
+
if (!('distribution' in value) || !Array.isArray(value.distribution) || value.distribution.length !== cohort.levels.length - 1)
|
|
18
|
+
return false;
|
|
19
|
+
return value.distribution.every((bin, index) => bin.index === index && bin.minimum === index && bin.maximum === index + 1
|
|
20
|
+
&& bin.upper_inclusive === (index === value.distribution.length - 1) && count(bin.count))
|
|
21
|
+
&& value.distribution.reduce((total, bin) => total + bin.count, 0) === value.assessed;
|
|
22
|
+
}
|
|
23
|
+
function cohortKindMatches(cohort, kind) {
|
|
24
|
+
return kind === 'memory' ? 'memory' in cohort && Array.isArray(cohort.memory.probes)
|
|
25
|
+
: kind === 'style' ? 'parser' in cohort && typeof cohort.parser === 'string' && !!cohort.parser
|
|
26
|
+
: !('memory' in cohort) && !('parser' in cohort);
|
|
27
|
+
}
|
|
28
|
+
function selectedRunsMatch(page, selected) {
|
|
29
|
+
return !!page && !!page.current && !!page.baseline && Number.isSafeInteger(page.count) && page.count >= 0
|
|
30
|
+
&& Number.isSafeInteger(page.page_size) && page.page_size > 0 && page.page_size <= 100
|
|
31
|
+
&& Array.isArray(page.results) && page.results.length <= page.page_size
|
|
32
|
+
&& page.current.id === selected.current && page.baseline.id === selected.baseline
|
|
33
|
+
&& selected.current !== selected.baseline && page.kind === selected.kind;
|
|
34
|
+
}
|
|
35
|
+
export function comparisonPageMatches(page, selected) {
|
|
36
|
+
return selectedRunsMatch(page, selected) && page.results.every(row => cohortKindMatches(row.cohort, selected.kind)
|
|
37
|
+
&& summaryMatches(row.current, row.cohort, selected.kind) && summaryMatches(row.baseline, row.cohort, selected.kind));
|
|
38
|
+
}
|
|
39
|
+
export function comparisonObservationsMatch(page, selected) {
|
|
40
|
+
if (!selectedRunsMatch(page, selected) || page.cohort.assessment_id !== selected.assessment
|
|
41
|
+
|| !cohortKindMatches(page.cohort, selected.kind)
|
|
42
|
+
|| !summaryMatches(page.summary.current, page.cohort, selected.kind)
|
|
43
|
+
|| !summaryMatches(page.summary.baseline, page.cohort, selected.kind)
|
|
44
|
+
|| selected.cohort && page.cohort.identity !== selected.cohort.identity
|
|
45
|
+
|| !page.results.every(row => (row.side === 'current' ? row.campaign_id === selected.current
|
|
46
|
+
: row.side === 'baseline' && row.campaign_id === selected.baseline)
|
|
47
|
+
&& (selected.side === 'both' || row.side === selected.side)))
|
|
48
|
+
return false;
|
|
49
|
+
if (page.kind !== 'memory')
|
|
50
|
+
return page.probe === null && selected.probe === undefined;
|
|
51
|
+
const probe = page.cohort.memory.probes[selected.probe ?? 0];
|
|
52
|
+
return !!probe && !!page.probe && memoryProbeMatches(page.probe, probe);
|
|
53
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function availableValueMatches(row) {
|
|
2
|
+
return row.state === 'available' ? row.value !== null && Number.isFinite(row.value) && row.value >= 0 && row.value <= 100
|
|
3
|
+
: row.value === null;
|
|
4
|
+
}
|
|
5
|
+
export function compositeMatches(value, campaign, execution) {
|
|
6
|
+
return value.campaign_id === campaign && (!execution || value.execution_id === execution) && availableValueMatches(value)
|
|
7
|
+
&& (value.state === 'not_configured' ? value.dimensions.length === 0 && value.required_checks.length === 0
|
|
8
|
+
: value.dimensions.every(row => row.levels.length === row.values.length));
|
|
9
|
+
}
|
|
10
|
+
export function compositePageMatches(value, campaign) {
|
|
11
|
+
return value.campaign_id === campaign && value.results.every(row => compositeMatches(row, campaign));
|
|
12
|
+
}
|
|
13
|
+
function selectedRunsMatch(value, current, baseline) {
|
|
14
|
+
return current !== baseline && value.current.id === current && value.baseline.id === baseline;
|
|
15
|
+
}
|
|
16
|
+
function countsMatch(cohort) {
|
|
17
|
+
return [cohort.current, cohort.baseline].every(side => [side.planned, side.retained, side.changed_world, side.unconfirmed_world].every(value => Number.isSafeInteger(value) && value >= 0) && side.changed_world + side.unconfirmed_world <= side.retained);
|
|
18
|
+
}
|
|
19
|
+
export function compositeGroupsMatch(value, current, baseline) {
|
|
20
|
+
return selectedRunsMatch(value, current, baseline) && value.results.every(row => /^[a-f0-9]{64}$/.test(row.id)
|
|
21
|
+
&& countsMatch(row) && row.dimensions.every(dimension => dimension.levels.length === dimension.values.length));
|
|
22
|
+
}
|
|
23
|
+
export function compositeModelsMatch(value, current, baseline, cohort) {
|
|
24
|
+
return selectedRunsMatch(value, current, baseline) && value.cohort.id === cohort && countsMatch(value.cohort);
|
|
25
|
+
}
|
|
26
|
+
export function compositeObservationsMatch(value, selected) {
|
|
27
|
+
return selectedRunsMatch(value, selected.current, selected.baseline) && value.cohort.id === selected.cohort
|
|
28
|
+
&& countsMatch(value.cohort)
|
|
29
|
+
&& value.results.every(row => (selected.side === 'both' || row.side === selected.side)
|
|
30
|
+
&& (selected.status === 'all' || (row.value === null ? 'unavailable' : 'available') === selected.status)
|
|
31
|
+
&& (selected.model === undefined || row.model === selected.model) && availableValueMatches(row)
|
|
32
|
+
&& (row.side === 'current' ? row.campaign_id === selected.current
|
|
33
|
+
: row.side === 'baseline' && row.campaign_id === selected.baseline));
|
|
34
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const recordingId = (value) => typeof value === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value);
|
|
2
|
+
export function recordingMatches(value, selectedId) {
|
|
3
|
+
const row = value;
|
|
4
|
+
return !!row && recordingId(row.id) && (!selectedId || row.id === selectedId) && typeof row.filename === 'string' && row.filename.length <= 255
|
|
5
|
+
&& ['otlp_json', 'langfuse_json', 'incident_json', 'har_json'].includes(row.format)
|
|
6
|
+
&& ['uploading', 'processing', 'review', 'accepted', 'rejected', 'revoked'].includes(row.state)
|
|
7
|
+
&& typeof row.can_complete === 'boolean' && typeof row.source_revision === 'string' && row.source_revision.length <= 256
|
|
8
|
+
&& Number.isFinite(Date.parse(row.created_at)) && (row.reviewed_at === null || Number.isFinite(Date.parse(row.reviewed_at)))
|
|
9
|
+
&& (row.failure === null || typeof row.failure === 'string') && !!row.summary && typeof row.summary === 'object' && !Array.isArray(row.summary)
|
|
10
|
+
&& (row.summary.record_count === undefined || Number.isSafeInteger(row.summary.record_count) && row.summary.record_count > 0)
|
|
11
|
+
&& (row.summary.gaps === undefined || Array.isArray(row.summary.gaps) && row.summary.gaps.every(one => typeof one === 'string'));
|
|
12
|
+
}
|
|
13
|
+
function recordingPage(value, page, maximum = 100) {
|
|
14
|
+
return !!value && value.page === page && Number.isSafeInteger(value.count) && value.count >= 0 && Number.isSafeInteger(value.page_size)
|
|
15
|
+
&& value.page_size > 0 && value.page_size <= maximum && Array.isArray(value.results) && value.results.length <= value.page_size && value.results.length <= value.count;
|
|
16
|
+
}
|
|
17
|
+
export function recordingsMatch(value, scope, page, query = '') {
|
|
18
|
+
return !!value && value.workspace_id === scope.workspace && value.world_id === scope.world && value.query === query.trim()
|
|
19
|
+
&& recordingPage(value, page) && Number.isSafeInteger(value.limits?.max_upload_bytes) && value.limits.max_upload_bytes > 0
|
|
20
|
+
&& value.results.every(row => recordingMatches(row));
|
|
21
|
+
}
|
|
22
|
+
export function recordingRequestMatches(value, scope, key) {
|
|
23
|
+
return !!value && value.workspace_id === scope.workspace && value.world_id === scope.world && value.request_key === key
|
|
24
|
+
&& (value.state === 'not_retained' ? value.recording === null : value.state === 'received' && recordingMatches(value.recording));
|
|
25
|
+
}
|
|
26
|
+
export function recordedObservationsMatch(value, scope, context, page, query = '') {
|
|
27
|
+
return !!value && value.workspace_id === scope.workspace && value.world_id === scope.world && value.context_id === context
|
|
28
|
+
&& value.query === query.trim() && recordingPage(value, page, 25) && value.results.every(row => !!row && typeof row.pointer === 'string'
|
|
29
|
+
&& typeof row.content_omitted === 'boolean' && typeof row.name === 'string' && row.name.length <= 512
|
|
30
|
+
&& ['tool', 'conversation', 'observation', 'task', 'policy'].includes(row.kind)
|
|
31
|
+
&& (row.operation === null || typeof row.operation === 'string' && row.operation.length <= 512)
|
|
32
|
+
&& (!row.content_omitted || row.input === null && row.output === null));
|
|
33
|
+
}
|
|
34
|
+
export function samePolicyVersion(actual, expected) {
|
|
35
|
+
return actual.id === expected.id && actual.agent_id === expected.agent_id
|
|
36
|
+
&& actual.policy_id === expected.policy_id && actual.version === expected.version;
|
|
37
|
+
}
|
|
38
|
+
export function policyActionConfirmed(actual, before, action) {
|
|
39
|
+
return samePolicyVersion(actual, before) && Number.isSafeInteger(actual.latest_version)
|
|
40
|
+
&& actual.latest_version >= before.latest_version && (action === 'archive' ? actual.archived
|
|
41
|
+
: action === 'restore' ? !actual.archived && !actual.removed
|
|
42
|
+
: action === 'remove' ? actual.removed && !actual.available
|
|
43
|
+
: actual.state === 'revoked' && !actual.available);
|
|
44
|
+
}
|
|
45
|
+
export function policyUploadDestination(ticket, workspace, format) {
|
|
46
|
+
const types = { text: 'text/plain', markdown: 'text/markdown',
|
|
47
|
+
pdf: 'application/pdf', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' };
|
|
48
|
+
const url = new URL(ticket.upload_url ?? '');
|
|
49
|
+
if (!/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(ticket.id)
|
|
50
|
+
|| url.origin !== 'https://storage.googleapis.com' || url.username || url.password || url.hash
|
|
51
|
+
|| !url.pathname.endsWith(`/${workspace}/policies/${ticket.id}/original`)
|
|
52
|
+
|| ticket.method !== 'PUT' || !ticket.headers
|
|
53
|
+
|| Object.keys(ticket.headers).sort().join(',') !== 'Content-Type,x-goog-if-generation-match'
|
|
54
|
+
|| ticket.headers['Content-Type'] !== types[format] || ticket.headers['x-goog-if-generation-match'] !== '0') {
|
|
55
|
+
throw new Error('The private policy upload destination could not be confirmed. No document was transferred.');
|
|
56
|
+
}
|
|
57
|
+
return url.href;
|
|
58
|
+
}
|
|
59
|
+
export function recordingProcessingConfirmed(row, id) {
|
|
60
|
+
return row.id === id && ['processing', 'review', 'accepted'].includes(row.state);
|
|
61
|
+
}
|
|
62
|
+
export function recordingUploadDestination(ticket, workspace, expectedId) {
|
|
63
|
+
const uuid = (value) => /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value);
|
|
64
|
+
const url = new URL(ticket.upload_url);
|
|
65
|
+
if (!uuid(ticket.id) || expectedId && ticket.id !== expectedId || ticket.method !== 'PUT'
|
|
66
|
+
|| url.origin !== 'https://storage.googleapis.com' || url.username || url.password || url.hash
|
|
67
|
+
|| !url.pathname.endsWith(`/${workspace}/context/${ticket.id}/original`)
|
|
68
|
+
|| Object.keys(ticket.headers).sort().join(',') !== 'Content-Type,x-goog-if-generation-match,x-goog-meta-firedrill-recording-artifact,x-goog-meta-firedrill-recording-context,x-goog-meta-firedrill-recording-purpose'
|
|
69
|
+
|| ticket.headers['Content-Type'] !== 'application/json' || ticket.headers['x-goog-if-generation-match'] !== '0'
|
|
70
|
+
|| !uuid(ticket.headers['x-goog-meta-firedrill-recording-artifact'])
|
|
71
|
+
|| ticket.headers['x-goog-meta-firedrill-recording-context'] !== ticket.id
|
|
72
|
+
|| ticket.headers['x-goog-meta-firedrill-recording-purpose'] !== 'recorded_context') {
|
|
73
|
+
throw new Error('The recording upload destination could not be confirmed. No file was transferred.');
|
|
74
|
+
}
|
|
75
|
+
return url.href;
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function continuityRequirementsMatch(value, world, revision, selectionRevision) {
|
|
2
|
+
const row = value;
|
|
3
|
+
return !!row && typeof row === 'object' && row.schema === 'firedrill.continuity-requirements.v1'
|
|
4
|
+
&& row.world_id === world && row.world_revision === revision && row.selection_revision === selectionRevision && /^[a-f0-9]{64}$/.test(row.selection_sha256)
|
|
5
|
+
&& Number.isSafeInteger(row.continuous_scenarios) && row.continuous_scenarios >= 0
|
|
6
|
+
&& Number.isSafeInteger(row.minimum_trial_seconds) && row.minimum_trial_seconds >= 0 && row.minimum_trial_seconds <= 2592000
|
|
7
|
+
&& (row.continuous_scenarios === 0 ? row.minimum_trial_seconds === 0 : row.minimum_trial_seconds > 5);
|
|
8
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const uuid = (value) => typeof value === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value);
|
|
2
|
+
const count = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
3
|
+
export const nativeCheckpointScopes = ['owned_postgres_rows_sequences_and_sql_clock', 'declared_sqlite_files', 'owned_postgres_and_declared_sqlite_files'];
|
|
4
|
+
export function coordinatedCheckpointMatches(value, world, selected = {}) {
|
|
5
|
+
return !!value && uuid(value.id) && value.world_id === world && uuid(value.session_id) && uuid(value.request_key)
|
|
6
|
+
&& (selected.session === undefined || value.session_id === selected.session) && (selected.id === undefined || value.id === selected.id)
|
|
7
|
+
&& (selected.key === undefined || value.request_key === selected.key) && (selected.name === undefined || value.name === selected.name)
|
|
8
|
+
&& typeof value.name === 'string' && value.name.length > 0 && value.name.length <= 120
|
|
9
|
+
&& ['pending', 'retained', 'unavailable'].includes(value.state) && typeof value.available === 'boolean'
|
|
10
|
+
&& Number.isFinite(Date.parse(value.created_at)) && (value.captured_at === null || Number.isFinite(Date.parse(value.captured_at)))
|
|
11
|
+
&& count(value.application_component_count) && value.application_component_count <= 1000 && count(value.excluded_count) && value.excluded_count <= 1000
|
|
12
|
+
&& (value.native_scope === null || nativeCheckpointScopes.includes(value.native_scope))
|
|
13
|
+
&& (value.controlled_services_included === null || value.controlled_services_included === true)
|
|
14
|
+
&& ['application_consistent', 'mixed_or_crash_consistent', 'not_observed'].includes(value.consistency)
|
|
15
|
+
&& ['source_attested_quiescence_and_native_capture', 'not_observed'].includes(value.consistency_basis)
|
|
16
|
+
&& (value.problem === null || typeof value.problem === 'string')
|
|
17
|
+
&& (!value.available || value.state === 'retained' && value.captured_at !== null && value.application_component_count > 0
|
|
18
|
+
&& value.controlled_services_included === true && value.native_scope !== null && value.consistency !== 'not_observed'
|
|
19
|
+
&& value.consistency_basis === 'source_attested_quiescence_and_native_capture');
|
|
20
|
+
}
|
|
21
|
+
export function coordinatedCheckpointDetailMatches(value, world, selected = {}) {
|
|
22
|
+
return coordinatedCheckpointMatches(value, world, selected) && Array.isArray(value.components) && value.components.length <= 1000
|
|
23
|
+
&& value.components.every(one => !!one && typeof one.name === 'string' && one.name.length > 0
|
|
24
|
+
&& ['application_memory', 'database', 'files', 'queue_cursor', 'conversation', 'scheduled_jobs'].includes(one.kind)
|
|
25
|
+
&& ['application_consistent', 'crash_consistent'].includes(one.consistency))
|
|
26
|
+
&& Array.isArray(value.excluded) && value.excluded.length <= 1000 && value.excluded.every(one => typeof one === 'string' && one.length > 0 && one.length <= 256)
|
|
27
|
+
&& (value.event_watermark === null || typeof value.event_watermark === 'string' && value.event_watermark.length > 0 && value.event_watermark.length <= 256)
|
|
28
|
+
&& typeof value.barrier_released === 'boolean' && (value.record_count === null || count(value.record_count))
|
|
29
|
+
&& (value.collection_count === null || count(value.collection_count))
|
|
30
|
+
&& (value.available ? value.components.length === value.application_component_count && value.excluded.length === value.excluded_count
|
|
31
|
+
&& value.barrier_released && value.event_watermark !== null && value.record_count !== null && value.collection_count !== null
|
|
32
|
+
: value.components.length === 0 && value.excluded.length === 0 && !value.barrier_released && value.event_watermark === null
|
|
33
|
+
&& value.record_count === null && value.collection_count === null);
|
|
34
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
2
|
+
const hash = /^[a-f0-9]{64}$/;
|
|
3
|
+
const optionalId = (value) => value === null || uuid.test(value);
|
|
4
|
+
export function datasetRead(value, matches) {
|
|
5
|
+
if (!matches)
|
|
6
|
+
throw new Error('The dataset response did not match this saved version or original request. Inspect its retained history.');
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
export function datasetScope(prefix, actor) {
|
|
10
|
+
const parts = prefix.split('/');
|
|
11
|
+
if (parts[0] !== '' || parts[1] !== 'api' || parts[2] !== 'workspaces' || !uuid.test(parts[3] ?? ''))
|
|
12
|
+
return null;
|
|
13
|
+
return parts.length === 5 && parts[4] === 'tools' ? { workspace: parts[3], actor, kind: 'tool' }
|
|
14
|
+
: parts.length === 6 && parts[4] === 'worlds' && uuid.test(parts[5] ?? '') ? { workspace: parts[3], actor, kind: 'world', world: parts[5] } : null;
|
|
15
|
+
}
|
|
16
|
+
export function datasetMatches(value, scope, selected = {}) {
|
|
17
|
+
if (!value || value.workspace_id !== scope.workspace || value.kind !== scope.kind
|
|
18
|
+
|| ![value.id, value.workspace_id, value.actor_id, value.request_key, value.series_id].every(one => uuid.test(one))
|
|
19
|
+
|| !optionalId(value.previous_version_id) || !hash.test(value.request_digest) || !Number.isSafeInteger(value.version) || value.version < 1
|
|
20
|
+
|| !Number.isSafeInteger(value.edit_count) || value.edit_count < 0 || value.edit_count > 100000
|
|
21
|
+
|| !['saving', 'retained', 'failed', 'withdrawn'].includes(value.state)
|
|
22
|
+
|| selected.id !== undefined && selected.id !== value.id || selected.key !== undefined && (selected.key !== value.request_key || value.actor_id !== scope.actor)
|
|
23
|
+
|| selected.digest !== undefined && selected.digest !== value.request_digest || selected.name !== undefined && selected.name !== value.name
|
|
24
|
+
|| selected.previous !== undefined && selected.previous !== value.previous_version_id || selected.editCount !== undefined && selected.editCount !== value.edit_count
|
|
25
|
+
|| selected.withdrawn && (value.state !== 'withdrawn' || !value.withdrawn_at || value.available)
|
|
26
|
+
|| value.available !== (value.file !== null) || value.available && (value.state !== 'retained' || !!value.withdrawn_at)
|
|
27
|
+
|| value.file && (!Number.isSafeInteger(value.file.size) || value.file.size < 1 || value.file.size > 8 * 1024 ** 2 || !hash.test(value.file.sha256)))
|
|
28
|
+
return false;
|
|
29
|
+
return value.kind === 'tool' ? uuid.test(value.implementation_id) && uuid.test(value.checkpoint_id)
|
|
30
|
+
&& (!selected.source || selected.source === value.checkpoint_id) && (!selected.implementation || selected.implementation === value.implementation_id)
|
|
31
|
+
: scope.kind === 'world' && value.world_id === scope.world && uuid.test(value.observation_id)
|
|
32
|
+
&& (!selected.source || selected.source === value.observation_id) && (!value.can_start_session || value.available);
|
|
33
|
+
}
|
|
34
|
+
export function datasetPageMatches(value, scope, filters = {}) {
|
|
35
|
+
if (!value || value.workspace_id !== scope.workspace || value.query !== (filters.query ?? '').trim()
|
|
36
|
+
|| value.state_filter !== (filters.state ?? 'all') || value.series_id_filter !== (filters.series ?? null)
|
|
37
|
+
|| !Number.isSafeInteger(value.page) || value.page < 1 || filters.page !== undefined && value.page !== filters.page
|
|
38
|
+
|| !Number.isSafeInteger(value.page_size) || value.page_size < 1 || value.page_size > 100 || value.results.length > value.page_size
|
|
39
|
+
|| !Number.isSafeInteger(value.count) || value.count < 0 || !Number.isSafeInteger(value.capacity.recipe_bytes)
|
|
40
|
+
|| value.capacity.recipe_bytes < 1 || value.capacity.recipe_bytes > 8 * 1024 ** 2)
|
|
41
|
+
return false;
|
|
42
|
+
if ('world_id' in value ? scope.kind !== 'world' || value.world_id !== scope.world || value.observation_id_filter !== (filters.source ?? null)
|
|
43
|
+
|| value.startable_filter !== (filters.startable ?? false)
|
|
44
|
+
: scope.kind !== 'tool' || value.checkpoint_id_filter !== (filters.source ?? null) || value.implementation_id_filter !== (filters.implementation ?? null))
|
|
45
|
+
return false;
|
|
46
|
+
return value.results.every(row => datasetMatches(row, scope, { source: filters.source, implementation: filters.implementation })
|
|
47
|
+
&& (value.state_filter === 'all' || row.state === value.state_filter) && (!filters.series || row.series_id === filters.series));
|
|
48
|
+
}
|
|
49
|
+
export function datasetDocumentMatches(value, scope, row) {
|
|
50
|
+
if (!datasetMatches(row, scope) || !row.available || !value || value.id !== row.id || !Array.isArray(value.edits) || value.edits.length !== row.edit_count)
|
|
51
|
+
return false;
|
|
52
|
+
return value.schema === 'firedrill.tool-dataset.v1' ? row.kind === 'tool' && value.implementation_id === row.implementation_id
|
|
53
|
+
&& value.checkpoint_id === row.checkpoint_id && [value.checkpoint_sha256, value.contract_digest, value.schema_digest].every(one => hash.test(one))
|
|
54
|
+
: row.kind === 'world' && value.world_id === row.world_id && value.observation_id === row.observation_id && uuid.test(value.source_id)
|
|
55
|
+
&& Number.isSafeInteger(value.world_revision) && value.world_revision >= 1 && [value.configuration_digest, value.archive_digest, value.index_digest].every(one => hash.test(one));
|
|
56
|
+
}
|
|
57
|
+
function cell(value, depth = 0) {
|
|
58
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || depth > 24)
|
|
59
|
+
return false;
|
|
60
|
+
const item = value, keys = Object.keys(item).sort().join(',');
|
|
61
|
+
if (item.type === 'null')
|
|
62
|
+
return keys === 'type';
|
|
63
|
+
if (item.type === 'binary' && item.encoding === 'base64')
|
|
64
|
+
return keys === 'encoding,type,value' && typeof item.value === 'string';
|
|
65
|
+
if (keys !== 'type,value')
|
|
66
|
+
return false;
|
|
67
|
+
if (item.type === 'boolean')
|
|
68
|
+
return typeof item.value === 'boolean';
|
|
69
|
+
if (['string', 'integer', 'decimal', 'datetime', 'date', 'time', 'uuid', 'binary'].includes(String(item.type)))
|
|
70
|
+
return typeof item.value === 'string';
|
|
71
|
+
if (item.type === 'float')
|
|
72
|
+
return typeof item.value === 'number' && Number.isFinite(item.value) && (!Number.isInteger(item.value) || Number.isSafeInteger(item.value))
|
|
73
|
+
|| typeof item.value === 'string' && ['nan', 'inf', '-inf'].includes(item.value);
|
|
74
|
+
if (item.type === 'array')
|
|
75
|
+
return Array.isArray(item.value) && item.value.every(one => cell(one, depth + 1));
|
|
76
|
+
return item.type === 'object' && !!item.value && typeof item.value === 'object' && !Array.isArray(item.value)
|
|
77
|
+
&& Object.values(item.value).every(one => cell(one, depth + 1));
|
|
78
|
+
}
|
|
79
|
+
export function datasetEdits(value) {
|
|
80
|
+
return Array.isArray(value) && value.length <= 100000 && value.every(edit => {
|
|
81
|
+
if (!edit || typeof edit !== 'object' || Array.isArray(edit) || Object.keys(edit).sort().join(',') !== 'action,collection,position,values'
|
|
82
|
+
|| typeof edit.collection !== 'string' || !hash.test(edit.collection) || !['insert', 'update', 'delete'].includes(edit.action)
|
|
83
|
+
|| (edit.action === 'insert' ? edit.position !== null : !Number.isSafeInteger(edit.position) || edit.position < 0))
|
|
84
|
+
return false;
|
|
85
|
+
return edit.action === 'delete' ? edit.values === null : !!edit.values && typeof edit.values === 'object' && !Array.isArray(edit.values)
|
|
86
|
+
&& (edit.action !== 'update' || Object.keys(edit.values).length > 0) && Object.values(edit.values).every(one => cell(one));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const object = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
2
|
+
const sha = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
|
|
3
|
+
const id = (value) => typeof value === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value);
|
|
4
|
+
export function dependencyDecisionMatches(value) {
|
|
5
|
+
if (!object(value))
|
|
6
|
+
return false;
|
|
7
|
+
const keys = Object.keys(value).sort().join(',');
|
|
8
|
+
return value.action === 'catalog' ? keys === 'acknowledge_coverage_gaps,action,candidate_id' && sha(value.candidate_id) && value.acknowledge_coverage_gaps === true
|
|
9
|
+
: value.action === 'generated' ? keys === 'acknowledge_coverage_gaps,action' && value.acknowledge_coverage_gaps === true
|
|
10
|
+
: value.action === 'continue' ? keys === 'action,continuation_digest' && sha(value.continuation_digest)
|
|
11
|
+
: value.action === 'withdraw' && keys === 'action';
|
|
12
|
+
}
|
|
13
|
+
export function dependencyDecisionCanonical(value) {
|
|
14
|
+
if (!dependencyDecisionMatches(value))
|
|
15
|
+
throw new Error('Choose the exact original dependency decision.');
|
|
16
|
+
return '{' + Object.keys(value).sort().map(key => JSON.stringify(key) + ':' + JSON.stringify(value[key])).join(',') + '}';
|
|
17
|
+
}
|
|
18
|
+
export async function dependencyDecisionDigest(value) {
|
|
19
|
+
const bytes = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(dependencyDecisionCanonical(value)));
|
|
20
|
+
return Array.from(new Uint8Array(bytes), one => one.toString(16).padStart(2, '0')).join('');
|
|
21
|
+
}
|
|
22
|
+
export function dependencyDecisionReceiptMatches(value, scope, digest) {
|
|
23
|
+
if (!object(value) || Object.keys(value).sort().join(',') !== 'accepted_at,accepted_state,action,actor_id,decision_digest,preparation_id,schema,state,workspace_id,world_id'
|
|
24
|
+
|| value.schema !== 'firedrill.dependency-decision-receipt.v1' || value.actor_id !== scope.actor || !id(scope.actor)
|
|
25
|
+
|| value.workspace_id !== scope.workspace || !id(scope.workspace) || value.world_id !== scope.world || !id(scope.world)
|
|
26
|
+
|| value.preparation_id !== scope.preparation || !id(scope.preparation) || value.decision_digest !== digest || !sha(digest)
|
|
27
|
+
|| !['accepted', 'not_observed', 'unavailable'].includes(String(value.state)))
|
|
28
|
+
return false;
|
|
29
|
+
const accepted = ['catalog', 'generated', 'withdraw', 'continue'].includes(String(value.action))
|
|
30
|
+
&& ['queued', 'adopting', 'applied', 'withdrawn'].includes(String(value.accepted_state))
|
|
31
|
+
&& (value.action === 'catalog' ? ['adopting', 'applied'].includes(String(value.accepted_state))
|
|
32
|
+
: value.action === 'generated' ? value.accepted_state === 'applied' : value.action === 'continue' ? value.accepted_state === 'queued' : value.accepted_state === 'withdrawn')
|
|
33
|
+
&& typeof value.accepted_at === 'string' && Number.isFinite(Date.parse(value.accepted_at));
|
|
34
|
+
const absent = value.action === null && value.accepted_state === null && value.accepted_at === null;
|
|
35
|
+
return value.state === 'accepted' ? accepted : value.state === 'not_observed' ? absent : absent || accepted;
|
|
36
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const object = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
2
|
+
const names = (v, maximum = 100) => Array.isArray(v) && v.length <= maximum
|
|
3
|
+
&& v.every(one => typeof one === 'string' && /^[A-Za-z0-9_.~-]{1,128}$/.test(one));
|
|
4
|
+
const optionalName = (v) => v === null || typeof v === 'string' && /^[A-Za-z_][A-Za-z0-9_.~-]{0,127}$/.test(v);
|
|
5
|
+
export function protocolProfileMatches(v) {
|
|
6
|
+
return object(v) && typeof v.available === 'boolean' && typeof v.http_declared === 'boolean'
|
|
7
|
+
&& typeof v.aliases_declared === 'boolean' && (v.request_encoding === null || ['json', 'query', 'form', 'multipart', 'text', 'base64', 'none'].includes(String(v.request_encoding)))
|
|
8
|
+
&& (v.response_encoding === null || ['json', 'text', 'binary'].includes(String(v.response_encoding)))
|
|
9
|
+
&& names(v.mapped_header_names) && names(v.ignored_metadata_headers) && names(v.query_names) && names(v.multipart_part_names)
|
|
10
|
+
&& v.multipart_part_names.every(name => /^[A-Za-z_][A-Za-z0-9_.-]{0,99}$/.test(name))
|
|
11
|
+
&& optionalName(v.body_argument) && optionalName(v.content_type_argument) && (v.idempotency_header === null || v.idempotency_header === 'Idempotency-Key')
|
|
12
|
+
&& optionalName(v.idempotency_argument) && names(v.mcp_aliases, 16);
|
|
13
|
+
}
|
|
14
|
+
export function protocolComparisonMatches(v) {
|
|
15
|
+
return object(v) && protocolProfileMatches(v.source) && protocolProfileMatches(v.package)
|
|
16
|
+
&& (v.explicit_expectations_match === null || typeof v.explicit_expectations_match === 'boolean') && v.runtime_validated === false;
|
|
17
|
+
}
|
|
18
|
+
export function compatibilityPageMatches(v, request, candidate, page) {
|
|
19
|
+
return object(v) && v.request_id === request && v.candidate_id === candidate && v.page === page
|
|
20
|
+
&& v.page_size === 25 && typeof v.count === 'number' && Number.isSafeInteger(v.count) && v.count >= 0
|
|
21
|
+
&& v.behavior_validated === false && Array.isArray(v.results) && v.results.length <= 25 && v.results.every(one => object(one)
|
|
22
|
+
&& (one.name === null || typeof one.name === 'string' && one.name.length <= 255)
|
|
23
|
+
&& ['declaration_match', 'missing', 'mismatch', 'unsupported'].includes(String(one.state))
|
|
24
|
+
&& (one.reason === null || typeof one.reason === 'string') && Array.isArray(one.gaps)
|
|
25
|
+
&& one.gaps.length <= 30 && one.gaps.every(gap => typeof gap === 'string' && gap.length <= 2000)
|
|
26
|
+
&& (one.protocol_metadata === undefined || protocolComparisonMatches(one.protocol_metadata)));
|
|
27
|
+
}
|
|
28
|
+
export function protocolProfileLines(value) {
|
|
29
|
+
if (!value.available)
|
|
30
|
+
return ['Protocol metadata is unavailable. Inspect the original declaration.'];
|
|
31
|
+
return [
|
|
32
|
+
...(value.http_declared ? [`Request ${value.request_encoding}; response ${value.response_encoding}.`,
|
|
33
|
+
...(value.mapped_header_names.length ? ['Mapped headers: ' + value.mapped_header_names.join(', ')] : []),
|
|
34
|
+
...(value.ignored_metadata_headers.length ? ['Ignored request metadata: ' + value.ignored_metadata_headers.join(', ')] : []),
|
|
35
|
+
...(value.query_names.length ? ['Query parameters: ' + value.query_names.join(', ')] : []),
|
|
36
|
+
...(value.multipart_part_names.length ? ['Multipart parts: ' + value.multipart_part_names.join(', ')] : []),
|
|
37
|
+
...(value.body_argument ? ['Body argument: ' + value.body_argument] : []),
|
|
38
|
+
...(value.content_type_argument ? ['Body media type argument: ' + value.content_type_argument] : []),
|
|
39
|
+
...(value.idempotency_header ? [`Idempotency: ${value.idempotency_header} from ${value.idempotency_argument}.`] : [])]
|
|
40
|
+
: ['HTTP data mapping not declared.']),
|
|
41
|
+
...(value.mcp_aliases.length ? ['MCP aliases: ' + value.mcp_aliases.join(', ')] : []),
|
|
42
|
+
];
|
|
43
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const uuid = (value) => typeof value === 'string'
|
|
2
|
+
&& /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(value);
|
|
3
|
+
const positive = (value) => Number.isSafeInteger(value) && Number(value) > 0;
|
|
4
|
+
export function executionProposalId(value) {
|
|
5
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
6
|
+
return null;
|
|
7
|
+
const row = value;
|
|
8
|
+
return row.schema === 'firedrill.director-execution-proposal.v1' && uuid(row.execution_proposal_id)
|
|
9
|
+
? row.execution_proposal_id : null;
|
|
10
|
+
}
|
|
11
|
+
export function isExecutionProposal(value) {
|
|
12
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
13
|
+
return false;
|
|
14
|
+
const row = value;
|
|
15
|
+
return row.schema === 'firedrill.director-execution-proposal.v1'
|
|
16
|
+
&& ['id', 'actor_id', 'world_id', 'source_id', 'plan_request_key', 'admission_request_key', 'validation_request_key']
|
|
17
|
+
.every(key => uuid(row[key]))
|
|
18
|
+
&& (row.previous_plan_id === null || uuid(row.previous_plan_id))
|
|
19
|
+
&& typeof row.title === 'string' && row.title.length > 0 && row.title.length <= 160
|
|
20
|
+
&& ['validate', 'simulate'].includes(row.action) && ['choose', 'managed', 'local'].includes(row.location)
|
|
21
|
+
&& [row.world_revision, row.repetitions, row.parallelism, row.trial_seconds].every(positive)
|
|
22
|
+
&& Number.isSafeInteger(row.configuration_revision) && row.configuration_revision >= 0
|
|
23
|
+
&& Number.isSafeInteger(row.scenario_count) && row.scenario_count >= 0
|
|
24
|
+
&& Number.isSafeInteger(row.planned_trials) && row.planned_trials === row.scenario_count * row.repetitions
|
|
25
|
+
&& typeof row.selection_revision === 'string' && /^[a-z0-9-]{1,128}$/.test(row.selection_revision)
|
|
26
|
+
&& typeof row.evaluation_revision === 'string' && /^[a-f0-9]{64}$/.test(row.evaluation_revision)
|
|
27
|
+
&& ['available', 'current', 'can_execute'].every(key => typeof row[key] === 'boolean')
|
|
28
|
+
&& !!row.selector && ['ids', 'all_matching'].includes(row.selector.mode) && row.selector.query === ''
|
|
29
|
+
&& Array.isArray(row.selector.ids) && row.selector.ids.every(uuid)
|
|
30
|
+
&& row.selector.ids.length <= 100 && new Set(row.selector.ids).size === row.selector.ids.length
|
|
31
|
+
&& Array.isArray(row.selector.excluded) && row.selector.excluded.length === 0
|
|
32
|
+
&& (row.selector.mode === 'ids' ? row.selector.ids.length > 0 : row.selector.ids.length === 0)
|
|
33
|
+
&& (row.action === 'validate' ? row.scenario_count === 0 && row.planned_trials === 0
|
|
34
|
+
: row.scenario_count > 0 && (row.selector.mode !== 'ids' || row.scenario_count === row.selector.ids.length))
|
|
35
|
+
&& (!row.can_execute || row.available && row.current)
|
|
36
|
+
&& (row.plan === null || uuid(row.plan?.id) && typeof row.plan.current === 'boolean')
|
|
37
|
+
&& (row.campaign === null || uuid(row.campaign?.id) && typeof row.campaign.state === 'string'
|
|
38
|
+
&& ['managed', 'local'].includes(row.campaign.execution_location))
|
|
39
|
+
&& (row.validation === null || uuid(row.validation?.id) && typeof row.validation.state === 'string'
|
|
40
|
+
&& typeof row.validation.active === 'boolean' && typeof row.validation.current === 'boolean'
|
|
41
|
+
&& ['managed', 'local'].includes(row.validation.execution_location));
|
|
42
|
+
}
|
|
43
|
+
export function executionPlanMatches(proposal, plan) {
|
|
44
|
+
const ids = (values) => [...new Set(values)].sort().join(',');
|
|
45
|
+
return !!plan && plan.current && plan.scenario_count === proposal.scenario_count
|
|
46
|
+
&& plan.planned_trials === proposal.planned_trials && plan.repetitions === proposal.repetitions
|
|
47
|
+
&& plan.parallelism === proposal.parallelism && plan.trial_seconds === proposal.trial_seconds
|
|
48
|
+
&& (proposal.location === 'choose' || plan.execution.location === proposal.location)
|
|
49
|
+
&& plan.selector.mode === proposal.selector.mode && plan.selector.query === ''
|
|
50
|
+
&& ids(plan.selector.ids) === ids(proposal.selector.ids) && ids(plan.selector.excluded) === '';
|
|
51
|
+
}
|