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,57 @@
|
|
|
1
|
+
import { sessionStateCounter } from './session-state-contracts.js';
|
|
2
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
3
|
+
const states = ['queued', 'publishing', 'accepted', 'observed', 'not_dispatched', 'unknown'];
|
|
4
|
+
const positive = (value) => sessionStateCounter(value) !== null && value !== '0';
|
|
5
|
+
function fileMatches(value, available, maximum) {
|
|
6
|
+
if (!available)
|
|
7
|
+
return value === null;
|
|
8
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
9
|
+
return false;
|
|
10
|
+
const file = value;
|
|
11
|
+
return Object.keys(file).sort().join(',') === 'sha256,size' && typeof file.size === 'number'
|
|
12
|
+
&& Number.isSafeInteger(file.size) && file.size >= 0 && file.size <= maximum
|
|
13
|
+
&& typeof file.sha256 === 'string' && /^[a-f0-9]{64}$/.test(file.sha256);
|
|
14
|
+
}
|
|
15
|
+
export function toolCommandRead(value, matches) {
|
|
16
|
+
if (!matches)
|
|
17
|
+
throw new Error('The operation response did not match this instance or original request. Inspect its retained activity.');
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
export function toolCommandScope(base, actor) {
|
|
21
|
+
const parts = base.split('/');
|
|
22
|
+
return parts.length === 7 && parts[0] === '' && parts[1] === 'api' && parts[2] === 'workspaces'
|
|
23
|
+
&& parts[4] === 'tools' && parts[5] === 'instances' && [parts[3], parts[6]].every(one => uuid.test(one ?? ''))
|
|
24
|
+
? { workspace: parts[3], instance: parts[6], ...(actor ? { actor } : {}) } : null;
|
|
25
|
+
}
|
|
26
|
+
export function toolCommandMatches(value, scope, selected = {}) {
|
|
27
|
+
return !!value && value.workspace_id === scope.workspace && value.instance_id === scope.instance
|
|
28
|
+
&& [scope.workspace, scope.instance, value.id, value.actor_id, value.request_key].every(one => uuid.test(one))
|
|
29
|
+
&& /^[a-f0-9]{64}$/.test(value.request_digest) && (!selected.digest || value.request_digest === selected.digest)
|
|
30
|
+
&& positive(value.sequence) && positive(value.state_epoch) && states.includes(value.state)
|
|
31
|
+
&& ['operation', 'surface_open', 'surface_action'].includes(value.kind)
|
|
32
|
+
&& (value.kind === 'operation' ? value.surface_id === null : !!value.surface_id && uuid.test(value.surface_id))
|
|
33
|
+
&& (!selected.id || value.id === selected.id) && (!selected.key || value.request_key === selected.key && value.actor_id === scope.actor)
|
|
34
|
+
&& (!selected.service || value.service_key === selected.service && value.kind === 'operation')
|
|
35
|
+
&& (!selected.operation || value.operation_name === selected.operation && value.kind === 'operation')
|
|
36
|
+
&& (value.state === 'observed' ? !!value.response : value.response === null)
|
|
37
|
+
&& typeof value.request_available === 'boolean' && typeof value.response_available === 'boolean'
|
|
38
|
+
&& typeof value.response_withheld === 'boolean'
|
|
39
|
+
&& fileMatches(value.request_file, value.request_available, 8 * 1024 ** 2)
|
|
40
|
+
&& fileMatches(value.response_file, value.response_available, 16 * 1024 ** 2)
|
|
41
|
+
&& (!value.response_file || value.response_file.size === value.response?.body_size)
|
|
42
|
+
&& (!value.response_withheld || !value.response_available)
|
|
43
|
+
&& (!value.response_withheld || value.response?.transport !== 'http' || value.response.headers === undefined)
|
|
44
|
+
&& (!value.response || Number.isSafeInteger(value.response.body_size) && value.response.body_size >= 0
|
|
45
|
+
&& value.response.body_size <= 16 * 1024 ** 2 && (value.kind === 'operation'
|
|
46
|
+
? value.response.transport === 'http' || value.response.transport === 'mcp' : value.response.transport === 'surface'));
|
|
47
|
+
}
|
|
48
|
+
export function toolCommandsMatch(value, scope, selected = {}) {
|
|
49
|
+
if (!value || value.workspace_id !== scope.workspace || value.instance_id !== scope.instance
|
|
50
|
+
|| value.query !== (selected.query ?? '').trim() || value.outcome !== (selected.outcome ?? '')
|
|
51
|
+
|| selected.anchor !== undefined && value.anchor !== selected.anchor || sessionStateCounter(value.anchor) === null
|
|
52
|
+
|| !Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > 100 || value.results.length > value.limit
|
|
53
|
+
|| [value.next_before, value.previous_after].some(one => one !== null && (!positive(one) || BigInt(one) > BigInt(value.anchor))))
|
|
54
|
+
return false;
|
|
55
|
+
return value.results.every((row, index) => toolCommandMatches(row, scope) && BigInt(row.sequence) <= BigInt(value.anchor)
|
|
56
|
+
&& (!index || BigInt(value.results[index - 1].sequence) > BigInt(row.sequence)) && (!value.outcome || row.state === value.outcome));
|
|
57
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { sessionStateCounter } from './session-state-contracts.js';
|
|
2
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
3
|
+
const serviceKey = /^[a-z][a-z0-9_-]{0,79}$/;
|
|
4
|
+
const operationName = /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/;
|
|
5
|
+
const pair = (one) => JSON.stringify([one.service_key, one.operation_name]);
|
|
6
|
+
export function toolConnectionRead(value, matches) {
|
|
7
|
+
if (!matches)
|
|
8
|
+
throw new Error('The connection response did not match this instance or original permission. Inspect and revoke the original connection before replacing it.');
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
function protocols(value) {
|
|
12
|
+
return value.length >= 1 && value.length <= 2 && new Set(value).size === value.length && value.every(one => one === 'http' || one === 'mcp');
|
|
13
|
+
}
|
|
14
|
+
function permissions(value) {
|
|
15
|
+
return value.length >= 1 && new Set(value.map(pair)).size === value.length
|
|
16
|
+
&& value.every(one => serviceKey.test(one.service_key) && operationName.test(one.operation_name));
|
|
17
|
+
}
|
|
18
|
+
function scopeMatches(value, scope) {
|
|
19
|
+
return !!value && uuid.test(scope.workspace) && uuid.test(scope.instance) && value.workspace_id === scope.workspace && value.instance_id === scope.instance;
|
|
20
|
+
}
|
|
21
|
+
function epochMatches(value, scope) {
|
|
22
|
+
return sessionStateCounter(value) !== null && value !== '0' && (!scope.epoch || scope.epoch === value);
|
|
23
|
+
}
|
|
24
|
+
function audienceMatches(value, instance) {
|
|
25
|
+
try {
|
|
26
|
+
const url = new URL(value);
|
|
27
|
+
return url.protocol === 'https:' && !url.username && !url.password && !url.port && !url.search && !url.hash
|
|
28
|
+
&& url.pathname === `/tools/serve/${instance}` && url.origin + url.pathname === value;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function pageMatches(value, page) {
|
|
35
|
+
return Number.isSafeInteger(value.page) && value.page >= 1 && (!page || page === value.page) && Number.isSafeInteger(value.page_size)
|
|
36
|
+
&& value.page_size >= 1 && value.page_size <= 100 && Number.isSafeInteger(value.count) && value.count >= 0 && value.results.length <= value.page_size;
|
|
37
|
+
}
|
|
38
|
+
export function toolConnectionMatches(value, scope, selected = {}) {
|
|
39
|
+
return scopeMatches(value, scope) && [value.id, value.actor_id, value.request_key].every(one => uuid.test(one))
|
|
40
|
+
&& (!selected.id || selected.id === value.id) && (!selected.key || value.request_key === selected.key && value.actor_id === scope.actor)
|
|
41
|
+
&& /^[a-f0-9]{64}$/.test(value.request_digest) && (!selected.digest || selected.digest === value.request_digest)
|
|
42
|
+
&& epochMatches(value.state_epoch, scope) && permissions(value.operations) && protocols(value.protocols)
|
|
43
|
+
&& audienceMatches(value.audience, scope.instance)
|
|
44
|
+
&& value.http_url === (value.protocols.includes('http') ? value.audience + '/operations' : null)
|
|
45
|
+
&& value.mcp_url === (value.protocols.includes('mcp') ? value.audience + '/mcp' : null)
|
|
46
|
+
&& value.http_result_url === (value.http_services.length ? value.audience + '/http-requests/{request_id}' : null)
|
|
47
|
+
&& (value.protocols.includes('http') || value.http_services.length === 0) && (!selected.revoked || !!value.revoked_at)
|
|
48
|
+
&& new Set(value.http_services.map(one => one.service_key)).size === value.http_services.length
|
|
49
|
+
&& value.http_services.every(service => serviceKey.test(service.service_key) && service.base_url === value.audience + '/http/' + service.service_key
|
|
50
|
+
&& service.operations.every(operation => operationName.test(operation.name)
|
|
51
|
+
&& value.operations.some(one => one.service_key === service.service_key && one.operation_name === operation.name)
|
|
52
|
+
&& ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'].includes(operation.method) && operation.path.startsWith('/')
|
|
53
|
+
&& ['uuid_request', 'declared_vendor_key'].includes(operation.idempotency_key_mode)
|
|
54
|
+
&& !operation.path.startsWith('//') && !/[\\?#%\u0000-\u0020\u007f]/.test(operation.path)));
|
|
55
|
+
}
|
|
56
|
+
export async function toolConnectionAccepted(value, scope, key, input, selected = {}) {
|
|
57
|
+
if (!toolConnectionMatches(value, scope, { ...selected, key }) || !permissions(input.operations) || !protocols(input.protocols)
|
|
58
|
+
|| !Number.isSafeInteger(input.duration_seconds) || input.duration_seconds < 60)
|
|
59
|
+
return false;
|
|
60
|
+
const operations = input.operations.map(one => ({ operation_name: one.operation_name, service_key: one.service_key }))
|
|
61
|
+
.sort((left, right) => left.service_key < right.service_key ? -1 : left.service_key > right.service_key ? 1
|
|
62
|
+
: left.operation_name < right.operation_name ? -1 : left.operation_name > right.operation_name ? 1 : 0);
|
|
63
|
+
const sorted = [...input.protocols].sort();
|
|
64
|
+
if (JSON.stringify(value.operations.map(pair)) !== JSON.stringify(operations.map(pair)) || JSON.stringify(value.protocols) !== JSON.stringify(sorted))
|
|
65
|
+
return false;
|
|
66
|
+
const bytes = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify({
|
|
67
|
+
duration_seconds: input.duration_seconds, operations, protocols: sorted
|
|
68
|
+
})));
|
|
69
|
+
return value.request_digest === Array.from(new Uint8Array(bytes), one => one.toString(16).padStart(2, '0')).join('');
|
|
70
|
+
}
|
|
71
|
+
export function toolConnectionTokenMatches(value) {
|
|
72
|
+
return value.token_available_once ? typeof value.token === 'string' && /^fd_tool_[a-f0-9-]{36}\.[A-Za-z0-9_-]{64}$/.test(value.token)
|
|
73
|
+
&& value.token.startsWith('fd_tool_' + value.id + '.') : value.token === null;
|
|
74
|
+
}
|
|
75
|
+
export function toolConnectionsMatch(value, scope, query = '', page) {
|
|
76
|
+
return scopeMatches(value, scope) && value.query === query.trim() && pageMatches(value, page)
|
|
77
|
+
&& value.results.every(row => toolConnectionMatches(row, scope));
|
|
78
|
+
}
|
|
79
|
+
export function toolConnectionOptionsMatch(value, scope, query = '', page) {
|
|
80
|
+
if (!scopeMatches(value, scope) || value.query !== query.trim() || !pageMatches(value, page) || !epochMatches(value.state_epoch, scope)
|
|
81
|
+
|| !protocols(value.protocols) || !Number.isSafeInteger(value.duration_seconds) || value.duration_seconds < 0
|
|
82
|
+
|| !audienceMatches(value.http_url.replace(/\/operations$/, ''), scope.instance)
|
|
83
|
+
|| value.mcp_url !== value.http_url.replace(/\/operations$/, '/mcp'))
|
|
84
|
+
return false;
|
|
85
|
+
return value.results.every(one => serviceKey.test(one.service_key) && operationName.test(one.operation_name)
|
|
86
|
+
&& ['none', 'read', 'write', 'mixed'].includes(one.effect));
|
|
87
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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 digest = /^[a-f0-9]{64}$/;
|
|
3
|
+
const states = ['submitted', 'reviewed', 'rejected', 'withdrawn', 'published'];
|
|
4
|
+
export function contributionRead(value, scope, selected = {}) {
|
|
5
|
+
if (!value || value.workspace_id !== scope.workspace || value.actor_id !== scope.actor
|
|
6
|
+
|| ![value.id, value.workspace_id, value.actor_id, value.request_key, value.implementation_id].every(one => uuid.test(one))
|
|
7
|
+
|| selected.key !== undefined && value.request_key !== selected.key || selected.implementation !== undefined && value.implementation_id !== selected.implementation
|
|
8
|
+
|| !digest.test(value.archive_sha256) || !digest.test(value.contract_digest) || !['Apache-2.0', 'MIT'].includes(value.license)
|
|
9
|
+
|| value.consent_version !== '2026-11-05' || !states.includes(value.state) || value.automatically_shared !== false
|
|
10
|
+
|| typeof value.can_withdraw !== 'boolean' || !Number.isFinite(Date.parse(value.submitted_at))
|
|
11
|
+
|| !['', 'clean_native_version', 'rights_unclear', 'private_material', 'native_rework', 'out_of_scope'].includes(value.review_code)
|
|
12
|
+
|| [value.reviewed_at, value.withdrawn_at].some(one => one !== null && !Number.isFinite(Date.parse(one)))
|
|
13
|
+
|| value.release_id !== null && !uuid.test(value.release_id)
|
|
14
|
+
|| value.state === 'published' && (!value.release_id || value.can_withdraw)
|
|
15
|
+
|| value.state === 'withdrawn' && (!value.withdrawn_at || value.can_withdraw)
|
|
16
|
+
|| value.can_withdraw && !['submitted', 'reviewed', 'rejected'].includes(value.state)
|
|
17
|
+
|| selected.input && ['implementation_id', 'archive_sha256', 'contract_digest', 'license', 'consent_version'].some(field => value[field] !== selected.input[field])) {
|
|
18
|
+
throw new Error('The contribution response did not match this exact version and original review. Recover its status before another action.');
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
export function contributionPageRead(value, scope, selected) {
|
|
23
|
+
if (!value || value.workspace_id !== scope.workspace || value.actor_id !== scope.actor || value.page !== selected.page
|
|
24
|
+
|| typeof value.can_nominate !== 'boolean'
|
|
25
|
+
|| value.implementation_id !== (selected.implementation ?? null) || value.state_filter !== (selected.state ?? 'all')
|
|
26
|
+
|| !Number.isSafeInteger(value.count) || value.count < 0 || !Number.isSafeInteger(value.page_size) || value.page_size < 1 || value.page_size > 100
|
|
27
|
+
|| !Array.isArray(value.results) || value.results.length > value.page_size)
|
|
28
|
+
throw new Error('The private contribution history changed. Reload it before continuing.');
|
|
29
|
+
value.results.forEach(row => {
|
|
30
|
+
contributionRead(row, scope, selected);
|
|
31
|
+
if (selected.state && selected.state !== 'all' && row.state !== selected.state)
|
|
32
|
+
throw new Error('The private contribution history did not match its selected state.');
|
|
33
|
+
});
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
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 optionalId = (value) => value === null || uuid.test(value);
|
|
3
|
+
export function toolGroupRead(value, matches) {
|
|
4
|
+
if (!matches)
|
|
5
|
+
throw new Error('The tool setup response did not match this workspace or original request. Inspect its retained setup history.');
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
export function toolGroupMatches(value, scope, selected = {}) {
|
|
9
|
+
if (!value || value.workspace_id !== scope.workspace || ![value.id, value.workspace_id, value.actor_id, value.request_key].every(one => uuid.test(one))
|
|
10
|
+
|| !/^[a-f0-9]{64}$/.test(value.request_digest) || selected.id !== undefined && selected.id !== value.id
|
|
11
|
+
|| selected.key !== undefined && (selected.key !== value.request_key || scope.actor !== value.actor_id)
|
|
12
|
+
|| selected.digest !== undefined && selected.digest !== value.request_digest || selected.stopped && !value.stop_requested_at
|
|
13
|
+
|| !Number.isSafeInteger(value.duration_seconds) || value.duration_seconds < 60
|
|
14
|
+
|| !value.counts || Object.values(value.counts).some(one => !Number.isSafeInteger(one) || one < 0) || value.counts.selected < 1)
|
|
15
|
+
return false;
|
|
16
|
+
const counts = value.counts;
|
|
17
|
+
return counts.selected === counts.pending + counts.refused + counts.canceled + counts.created
|
|
18
|
+
&& ['running', 'closing', 'failed', 'closed'].every(key => counts[key] <= counts.created)
|
|
19
|
+
&& value.dispatch_complete === (counts.pending === 0) && value.all_closed === (counts.pending === 0 && counts.closed === counts.created)
|
|
20
|
+
&& (!value.stop_requested_at || counts.running === 0);
|
|
21
|
+
}
|
|
22
|
+
export async function toolGroupAccepted(value, scope, key, input, selected = {}) {
|
|
23
|
+
if (!toolGroupMatches(value, scope, { ...selected, key }) || value.name !== input.name || value.duration_seconds !== input.duration_seconds
|
|
24
|
+
|| input.start_consent !== true || value.counts.selected !== input.selections.length)
|
|
25
|
+
return false;
|
|
26
|
+
const selections = input.selections.map(one => 'implementation_id' in one
|
|
27
|
+
? { dataset_id: one.dataset_id, implementation_id: one.implementation_id, usage_quote_id: one.usage_quote_id } : { dataset_id: null, release_id: one.release_id, usage_quote_id: one.usage_quote_id });
|
|
28
|
+
const bytes = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify({
|
|
29
|
+
duration_seconds: input.duration_seconds, name: input.name, selections, start_consent: true
|
|
30
|
+
})));
|
|
31
|
+
return value.request_digest === Array.from(new Uint8Array(bytes), one => one.toString(16).padStart(2, '0')).join('');
|
|
32
|
+
}
|
|
33
|
+
function pageMatches(value, page) {
|
|
34
|
+
return Number.isSafeInteger(value.page) && value.page >= 1 && (!page || page === value.page) && Number.isSafeInteger(value.page_size)
|
|
35
|
+
&& value.page_size >= 1 && value.page_size <= 100 && Number.isSafeInteger(value.count) && value.count >= 0 && value.results.length <= value.page_size;
|
|
36
|
+
}
|
|
37
|
+
export function toolGroupsMatch(value, scope, query = '', page) {
|
|
38
|
+
return !!value && value.workspace_id === scope.workspace && value.query === query.trim() && pageMatches(value, page)
|
|
39
|
+
&& value.results.every(row => toolGroupMatches(row, scope));
|
|
40
|
+
}
|
|
41
|
+
export function toolGroupMembersMatch(value, scope, group, query = '', page) {
|
|
42
|
+
return !!value && uuid.test(group) && value.workspace_id === scope.workspace && value.group_id === group
|
|
43
|
+
&& value.query === query.trim() && pageMatches(value, page) && value.results.every((row, index) => {
|
|
44
|
+
if (row.workspace_id !== scope.workspace || row.group_id !== group || !uuid.test(row.id) || !Number.isSafeInteger(row.position) || row.position < 0
|
|
45
|
+
|| index > 0 && row.position <= value.results[index - 1].position || !optionalId(row.release_id) || !optionalId(row.implementation_id)
|
|
46
|
+
|| !optionalId(row.catalog_request_key) || row.release_id === null && row.implementation_id === null
|
|
47
|
+
|| !['pending', 'preparing', 'created', 'failed', 'canceled'].includes(row.state)
|
|
48
|
+
|| row.dataset && (!uuid.test(row.dataset.id) || !Number.isSafeInteger(row.dataset.version) || row.dataset.version < 1))
|
|
49
|
+
return false;
|
|
50
|
+
return !row.instance || uuid.test(row.instance.id) && ['queued', 'starting', 'active', 'closing', 'reconciling', 'stopped', 'failed', 'canceled'].includes(row.instance.state)
|
|
51
|
+
&& (!row.instance.observation_current || row.instance.state === 'active' && row.instance.terminal_at === null && row.instance.removal === null);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { toolAllocationQuoteMatches } from './tool-runtime-billing-contracts.js';
|
|
2
|
+
const id = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;
|
|
3
|
+
const date = (value) => typeof value === 'string' && Number.isFinite(Date.parse(value));
|
|
4
|
+
export function groupStateInputMatches(value) {
|
|
5
|
+
const row = value;
|
|
6
|
+
return !!row && typeof row === 'object' && !Array.isArray(row)
|
|
7
|
+
&& Object.keys(row).sort().join(',') === 'action,duration_seconds,member_quotes,name,source_operation_id,state_consent'
|
|
8
|
+
&& ['capture', 'reset', 'fork'].includes(row.action) && typeof row.name === 'string'
|
|
9
|
+
&& row.name.trim().length > 0 && row.name.trim().length <= 120 && !/[\u0000-\u001f\u007f]/.test(row.name) && row.state_consent === true
|
|
10
|
+
&& Array.isArray(row.member_quotes) && new Set(row.member_quotes.map(one => one?.position)).size === row.member_quotes.length
|
|
11
|
+
&& row.member_quotes.every(one => !!one && Object.keys(one).sort().join(',') === 'position,usage_quote_id'
|
|
12
|
+
&& Number.isSafeInteger(one.position) && one.position >= 0 && typeof one.usage_quote_id === 'string' && id.test(one.usage_quote_id))
|
|
13
|
+
&& (row.action === 'capture' ? row.source_operation_id === null && row.duration_seconds === null && row.member_quotes.length === 0
|
|
14
|
+
: typeof row.source_operation_id === 'string' && id.test(row.source_operation_id) && Number.isSafeInteger(row.duration_seconds)
|
|
15
|
+
&& row.duration_seconds >= 60 && row.member_quotes.length > 0);
|
|
16
|
+
}
|
|
17
|
+
export function groupStateInputsEqual(left, right) {
|
|
18
|
+
return groupStateInputMatches(left) && groupStateInputMatches(right) && left.action === right.action && left.name === right.name
|
|
19
|
+
&& left.source_operation_id === right.source_operation_id && left.duration_seconds === right.duration_seconds
|
|
20
|
+
&& left.member_quotes.length === right.member_quotes.length && left.member_quotes.every((one, index) => one.position === right.member_quotes[index].position && one.usage_quote_id === right.member_quotes[index].usage_quote_id);
|
|
21
|
+
}
|
|
22
|
+
export function groupStateMatches(value, workspace, group, actor, key) {
|
|
23
|
+
return !!value && id.test(value.id) && value.workspace_id === workspace && value.group_id === group
|
|
24
|
+
&& id.test(value.actor_id) && id.test(value.request_key) && (!actor || value.actor_id === actor) && (!key || value.request_key === key)
|
|
25
|
+
&& groupStateInputMatches(value.request) && value.action === value.request.action && value.name === value.request.name.trim()
|
|
26
|
+
&& value.source_operation_id === value.request.source_operation_id && date(value.created_at) && (value.finished_at === null || date(value.finished_at))
|
|
27
|
+
&& ['preparing', 'holding', 'capturing', 'stopping', 'starting', 'releasing', 'recovery', 'completed', 'failed'].includes(value.state)
|
|
28
|
+
&& !!value.counts && Object.keys(value.counts).sort().join(',') === 'held,ready,released,saved,total' && Number.isSafeInteger(value.counts.total)
|
|
29
|
+
&& value.counts.total > 0 && Object.values(value.counts).every(one => Number.isSafeInteger(one) && one >= 0 && one <= value.counts.total)
|
|
30
|
+
&& value.process_memory_included === false && value.external_state_included === false
|
|
31
|
+
&& value.coordinated === (value.action === 'capture' && value.state === 'completed');
|
|
32
|
+
}
|
|
33
|
+
export function groupStatePageMatches(value, workspace, group, page) {
|
|
34
|
+
return !!value && value.page === page && Number.isSafeInteger(value.count) && value.count >= 0
|
|
35
|
+
&& Number.isSafeInteger(value.page_size) && value.page_size > 0 && value.page_size <= 100
|
|
36
|
+
&& Array.isArray(value.results) && value.results.length <= value.page_size && value.results.length <= value.count
|
|
37
|
+
&& value.results.every(one => groupStateMatches(one, workspace, group)) && new Set(value.results.map(one => one.id)).size === value.results.length;
|
|
38
|
+
}
|
|
39
|
+
export function groupStateMemberPageMatches(value, page) {
|
|
40
|
+
return !!value && value.page === page && Number.isSafeInteger(value.count) && value.count >= 0
|
|
41
|
+
&& Number.isSafeInteger(value.page_size) && value.page_size > 0 && value.page_size <= 100
|
|
42
|
+
&& Array.isArray(value.results) && value.results.length <= value.page_size && value.results.length <= value.count
|
|
43
|
+
&& new Set(value.results.map(one => one?.id)).size === value.results.length
|
|
44
|
+
&& new Set(value.results.map(one => one?.position)).size === value.results.length
|
|
45
|
+
&& value.results.every(one => !!one && id.test(one.id) && id.test(one.instance_id) && typeof one.name === 'string'
|
|
46
|
+
&& Number.isSafeInteger(one.position) && one.position >= 0 && /^[1-9][0-9]{0,18}$/.test(one.state_epoch)
|
|
47
|
+
&& (one.checkpoint_id === null || id.test(one.checkpoint_id)) && (one.successor_id === null || id.test(one.successor_id))
|
|
48
|
+
&& (one.checkpoint_sha256 === null || /^[a-f0-9]{64}$/.test(one.checkpoint_sha256))
|
|
49
|
+
&& [one.held_at, one.released_at, one.captured_at].every(at => at === null || date(at))
|
|
50
|
+
&& (one.event_watermark === null || /^(0|[1-9][0-9]{0,18})$/.test(one.event_watermark)));
|
|
51
|
+
}
|
|
52
|
+
export function groupStateQuoteMatches(value, workspace, group, key, selected) {
|
|
53
|
+
return !!value && value.group_id === group && value.operation_request_key === key && value.action === selected.action
|
|
54
|
+
&& value.source_operation_id === selected.source_operation_id && value.duration_seconds === selected.duration_seconds
|
|
55
|
+
&& Array.isArray(value.members) && value.members.length > 0 && new Set(value.members.map(one => one?.position)).size === value.members.length
|
|
56
|
+
&& new Set(value.members.map(one => one?.quote?.id)).size === value.members.length && value.members.every(one => !!one
|
|
57
|
+
&& Number.isSafeInteger(one.position) && one.position >= 0 && typeof one.name === 'string' && !!one.selection
|
|
58
|
+
&& one.selection.release_id === null && one.selection.dataset_id === null && one.selection.duration_seconds === selected.duration_seconds
|
|
59
|
+
&& id.test(one.selection.implementation_id ?? '') && id.test(one.selection.launch_request_key)
|
|
60
|
+
&& id.test(one.selection.restore?.instance_id ?? '') && id.test(one.selection.restore?.checkpoint_id ?? '')
|
|
61
|
+
&& one.selection.restore?.action === (selected.action === 'fork' ? 'fork' : 'restore')
|
|
62
|
+
&& toolAllocationQuoteMatches(one.quote, workspace, one.selection) && Date.parse(one.quote.expires_at) > Date.now());
|
|
63
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { sessionStateCounter } from './session-state-contracts.js';
|
|
2
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
3
|
+
const digest = /^[a-f0-9]{64}$/;
|
|
4
|
+
const states = ['queued', 'starting', 'active', 'closing', 'reconciling', 'stopped', 'failed', 'canceled'];
|
|
5
|
+
const terminal = ['stopped', 'failed', 'canceled'];
|
|
6
|
+
const optionalId = (value) => value === null || uuid.test(value);
|
|
7
|
+
export function toolLifecycleRead(value, matches) {
|
|
8
|
+
if (!matches)
|
|
9
|
+
throw new Error('The instance response did not match this selection. Inspect the original instance or request.');
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
export function toolInstanceMatches(value, workspace, selected = {}) {
|
|
13
|
+
return !!value && value.workspace_id === workspace && [workspace, value.id, value.actor_id, value.request_key, value.implementation_id].every(one => uuid.test(one))
|
|
14
|
+
&& (!selected.id || value.id === selected.id) && (!selected.key || value.request_key === selected.key && value.actor_id === selected.actor)
|
|
15
|
+
&& typeof value.tool_key === 'string' && value.tool_key.length > 0 && value.tool_key.length <= 160
|
|
16
|
+
&& digest.test(value.request_digest) && sessionStateCounter(value.state_epoch) !== null && value.state_epoch !== '0'
|
|
17
|
+
&& states.includes(value.state) && (terminal.includes(value.state) ? !!value.terminal_at : value.terminal_at === null)
|
|
18
|
+
&& (!value.observation_current || value.state === 'active' && !value.terminal_at && !!value.last_observed_at)
|
|
19
|
+
&& (!value.removal || uuid.test(value.removal.request_key) && (!value.removal.completed_at || !!value.terminal_at));
|
|
20
|
+
}
|
|
21
|
+
export function toolInstanceDetailMatches(value, workspace, selected = {}) {
|
|
22
|
+
return toolInstanceMatches(value, workspace, selected) && [value.parent_instance_id, value.restored_checkpoint_id, value.dataset_id].every(optionalId)
|
|
23
|
+
&& ['declared_stateless', 'stored_data', 'unspecified'].includes(value.data_mode)
|
|
24
|
+
&& value.visual_interfaces.every(one => /^[a-z][a-z0-9_-]{0,79}$/.test(one.service_key))
|
|
25
|
+
&& !!value.lifetime && ['standalone', 'setup_member'].includes(value.lifetime.owner)
|
|
26
|
+
&& value.lifetime.expires_at === value.expires_at && sessionStateCounter(value.lifetime.revision) !== null
|
|
27
|
+
&& Number.isSafeInteger(value.lifetime.idle_timeout_seconds) && value.lifetime.idle_timeout_seconds >= 0
|
|
28
|
+
&& Number.isSafeInteger(value.lifetime.maximum_extension_seconds) && value.lifetime.maximum_extension_seconds >= 60
|
|
29
|
+
&& typeof value.lifetime.can_extend === 'boolean' && (!value.lifetime.can_extend || !value.terminal_at && value.state === 'active')
|
|
30
|
+
&& ['supported_data_only', 'not_available', 'declared_stateless'].includes(value.lifetime.saved_state);
|
|
31
|
+
}
|
|
32
|
+
export function toolExtensionMatches(value, scope, selected = {}) {
|
|
33
|
+
if (!value || value.workspace_id !== scope.workspace || value.instance_id !== scope.instance
|
|
34
|
+
|| ![value.id, value.workspace_id, value.instance_id, value.actor_id, value.request_key].every(one => typeof one === 'string' && uuid.test(one))
|
|
35
|
+
|| !digest.test(value.request_digest) || sessionStateCounter(value.state_epoch) === null || value.state_epoch === '0'
|
|
36
|
+
|| value.usage_quote_id !== null && !uuid.test(value.usage_quote_id)
|
|
37
|
+
|| sessionStateCounter(value.revision) === null || value.revision === '0'
|
|
38
|
+
|| !['queued', 'publishing', 'accepted', 'confirmed', 'failed'].includes(value.state)
|
|
39
|
+
|| !Number.isSafeInteger(value.idle_timeout_seconds) || value.idle_timeout_seconds < 0
|
|
40
|
+
|| !Number.isFinite(Date.parse(value.expected_expires_at)) || !Number.isFinite(Date.parse(value.requested_expires_at))
|
|
41
|
+
|| Date.parse(value.requested_expires_at) <= Date.parse(value.expected_expires_at)
|
|
42
|
+
|| (['confirmed', 'failed'].includes(value.state) ? !value.finished_at : value.finished_at !== null)
|
|
43
|
+
|| (value.state === 'confirmed' ? value.confirmed_expires_at !== value.requested_expires_at : value.confirmed_expires_at !== null)
|
|
44
|
+
|| selected.key && (value.request_key !== selected.key || value.actor_id !== scope.actor))
|
|
45
|
+
return false;
|
|
46
|
+
const input = selected.input;
|
|
47
|
+
return !input || value.usage_quote_id === input.usage_quote_id && value.state_epoch === input.state_epoch && value.idle_timeout_seconds === input.idle_timeout_seconds
|
|
48
|
+
&& Date.parse(value.expected_expires_at) === Date.parse(input.expected_expires_at)
|
|
49
|
+
&& Date.parse(value.requested_expires_at) === Date.parse(input.expected_expires_at) + input.additional_seconds * 1000;
|
|
50
|
+
}
|
|
51
|
+
export function toolExtensionPageMatches(value, scope, page) {
|
|
52
|
+
return !!value && value.workspace_id === scope.workspace && value.instance_id === scope.instance && value.page === page
|
|
53
|
+
&& Number.isSafeInteger(value.page_size) && value.page_size >= 1 && value.page_size <= 100
|
|
54
|
+
&& Number.isSafeInteger(value.count) && value.count >= 0 && Array.isArray(value.results) && value.results.length <= value.page_size
|
|
55
|
+
&& value.results.every(one => toolExtensionMatches(one, scope));
|
|
56
|
+
}
|
|
57
|
+
export function toolRuntimeQuoteMatches(value, tool, input) {
|
|
58
|
+
return !!value && uuid.test(value.id) && value.workspace_id === tool.workspace_id && value.activity === 'tool_runtime'
|
|
59
|
+
&& value.execution_location === 'managed' && digest.test(value.scope_digest) && digest.test(value.tariff_digest)
|
|
60
|
+
&& Number.isSafeInteger(value.credit_scale) && value.credit_scale >= 1
|
|
61
|
+
&& Number.isSafeInteger(value.estimated_units) && value.estimated_units >= 0
|
|
62
|
+
&& Number.isSafeInteger(value.maximum_units) && value.maximum_units >= value.estimated_units
|
|
63
|
+
&& Number.isFinite(Date.parse(value.expires_at)) && value.scope?.instance_id === tool.id
|
|
64
|
+
&& value.scope.state_epoch === input.state_epoch && value.scope.idle_timeout_seconds === input.idle_timeout_seconds
|
|
65
|
+
&& Date.parse(String(value.scope.expected_expires_at)) === Date.parse(input.expected_expires_at)
|
|
66
|
+
&& Date.parse(String(value.scope.requested_expires_at)) === Date.parse(input.expected_expires_at) + input.additional_seconds * 1000;
|
|
67
|
+
}
|
|
68
|
+
export function toolInstancePageMatches(value, workspace, query = '', state = 'all', selectedPage) {
|
|
69
|
+
return !!value && value.workspace_id === workspace && value.query === query.trim() && value.state_filter === state
|
|
70
|
+
&& Number.isSafeInteger(value.page) && value.page >= 1 && (!selectedPage || value.page === selectedPage)
|
|
71
|
+
&& Number.isSafeInteger(value.count) && value.count >= 0 && Number.isSafeInteger(value.page_size) && value.page_size >= 1 && value.page_size <= 100
|
|
72
|
+
&& value.results.length <= value.page_size && value.results.every(row => toolInstanceMatches(row, workspace) && row.details_included === false
|
|
73
|
+
&& (state === 'removed' ? !!row.removal?.completed_at : !row.removal?.completed_at)
|
|
74
|
+
&& (state === 'active' ? !row.terminal_at : state === 'closed' ? !!row.terminal_at : true))
|
|
75
|
+
&& value.setup.default_duration_seconds >= 60 && value.setup.default_duration_seconds <= value.setup.maximum_duration_seconds
|
|
76
|
+
&& Number.isSafeInteger(value.setup.maximum_tools) && value.setup.maximum_tools >= 1;
|
|
77
|
+
}
|
|
78
|
+
export async function toolStartReceiptMatches(value, scope, key, input) {
|
|
79
|
+
if (!toolInstanceDetailMatches(value, scope.workspace, { key, actor: scope.actor }) || value.implementation_id !== input.implementation_id
|
|
80
|
+
|| value.dataset_id !== input.dataset_id)
|
|
81
|
+
return false;
|
|
82
|
+
// The server retains a digest, not a guessed duration derived from timestamps.
|
|
83
|
+
// These fixed keys are Python canonical_digest's sorted original input shape.
|
|
84
|
+
const original = JSON.stringify({ checkpoint_id: null, dataset_id: input.dataset_id, duration_seconds: input.duration_seconds,
|
|
85
|
+
generation_kind: '', implementation_id: input.implementation_id, parent_instance_id: null, usage_quote_id: input.usage_quote_id });
|
|
86
|
+
const bytes = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(original));
|
|
87
|
+
return value.request_digest === Array.from(new Uint8Array(bytes), one => one.toString(16).padStart(2, '0')).join('');
|
|
88
|
+
}
|
|
89
|
+
export function toolRemovalMatches(value, scope, selected = {}) {
|
|
90
|
+
return !!value && value.workspace_id === scope.workspace && [value.id, value.workspace_id, value.actor_id, value.instance_id, value.request_key].every(one => uuid.test(one))
|
|
91
|
+
&& (!selected.instance || value.instance_id === selected.instance) && (!selected.id || value.id === selected.id)
|
|
92
|
+
&& (!selected.key || value.request_key === selected.key && value.actor_id === scope.actor)
|
|
93
|
+
&& value.retains_saved_data === true && (value.completed_at ? value.state === 'removed' : value.state === 'removing');
|
|
94
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { toolProtocolMetadataMatches } from './tool-protocol-contracts.js';
|
|
2
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
3
|
+
const digest = /^[a-f0-9]{64}$/;
|
|
4
|
+
const serviceKey = /^[a-z][a-z0-9_-]{0,79}$/;
|
|
5
|
+
const operationName = /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/;
|
|
6
|
+
export function packageRead(value, matches) {
|
|
7
|
+
if (!matches)
|
|
8
|
+
throw new Error('The tool package response did not match this selection. Inspect the original version or request.');
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
function page(value, selected) {
|
|
12
|
+
return Number.isSafeInteger(value.page) && value.page >= 1 && (!selected || value.page === selected)
|
|
13
|
+
&& Number.isSafeInteger(value.page_size) && value.page_size >= 1 && value.page_size <= 100
|
|
14
|
+
&& Number.isSafeInteger(value.count) && value.count >= 0 && value.results.length <= value.page_size;
|
|
15
|
+
}
|
|
16
|
+
export function toolVersionMatches(value, workspace, id) {
|
|
17
|
+
return !!value && uuid.test(workspace) && value.workspace_id === workspace && [value.id, value.tool_id].every(one => uuid.test(one))
|
|
18
|
+
&& (!id || value.id === id) && ['inspected', 'revoked', 'unavailable'].includes(value.state) && value.runtime_started === false
|
|
19
|
+
&& digest.test(value.archive_sha256) && digest.test(value.contract_digest)
|
|
20
|
+
&& [value.service_count, value.operation_count].every(one => one === null || Number.isSafeInteger(one) && one >= 1)
|
|
21
|
+
&& value.interfaces.every(one => one === 'http' || one === 'mcp');
|
|
22
|
+
}
|
|
23
|
+
export function toolVersionPageMatches(value, workspace, selected = {}) {
|
|
24
|
+
return !!value && value.workspace_id === workspace && value.query === (selected.query ?? '').trim()
|
|
25
|
+
&& value.state_filter === (selected.state ?? 'inspected') && value.single_service === (selected.single ?? false) && page(value, selected.page)
|
|
26
|
+
&& value.results.every(row => toolVersionMatches(row, workspace) && (value.state_filter === 'all'
|
|
27
|
+
|| row.state === value.state_filter || value.state_filter === 'inspected' && row.state === 'unavailable'))
|
|
28
|
+
&& Number.isSafeInteger(value.package_capacity.upload_bytes) && value.package_capacity.upload_bytes >= 1
|
|
29
|
+
&& Number.isSafeInteger(value.package_capacity.contract_bytes) && value.package_capacity.contract_bytes >= 1;
|
|
30
|
+
}
|
|
31
|
+
function selection(value, version) {
|
|
32
|
+
return value.workspace_id === version.workspace_id && value.implementation_id === version.id && value.contract_digest === version.contract_digest;
|
|
33
|
+
}
|
|
34
|
+
export function toolServiceMatches(value, version, key) {
|
|
35
|
+
return !!value && selection(value, version) && serviceKey.test(key) && value.id === key && value.key === key;
|
|
36
|
+
}
|
|
37
|
+
export function toolServicesMatch(value, version, query = '', selectedPage) {
|
|
38
|
+
return !!value && selection(value, version) && value.query === query.trim() && page(value, selectedPage)
|
|
39
|
+
&& value.results.every(row => row.id === row.key && serviceKey.test(row.key) && Number.isSafeInteger(row.operation_count) && row.operation_count >= 1);
|
|
40
|
+
}
|
|
41
|
+
export function toolOperationsMatch(value, version, service, query = '', selectedPage) {
|
|
42
|
+
return !!value && selection(value, version) && serviceKey.test(service) && value.service_key === service
|
|
43
|
+
&& value.query === query.trim() && page(value, selectedPage) && value.results.every(row => operationName.test(row.name)
|
|
44
|
+
&& toolProtocolMetadataMatches(row)
|
|
45
|
+
&& ['http', 'mcp'].includes(row.transport.kind) && ['none', 'read', 'write', 'mixed'].includes(row.effect));
|
|
46
|
+
}
|
|
47
|
+
export function toolOperationMatches(value, version, service, name) {
|
|
48
|
+
return !!value && selection(value, version) && serviceKey.test(service) && value.service_key === service && value.name === name
|
|
49
|
+
&& operationName.test(name) && !!value.input_schema && (value.output_schema !== null
|
|
50
|
+
|| value.transport.kind === 'http' && value.transport.response_encoding !== undefined && value.transport.response_encoding !== 'json')
|
|
51
|
+
&& toolProtocolMetadataMatches(value);
|
|
52
|
+
}
|
|
53
|
+
export function toolPackageMatches(value, scope, selected = {}) {
|
|
54
|
+
const input = selected.input;
|
|
55
|
+
return !!value && value.workspace_id === scope.workspace && value.actor_id === scope.actor
|
|
56
|
+
&& [scope.workspace, scope.actor, value.id, value.request_key].every(one => uuid.test(one))
|
|
57
|
+
&& (!selected.key || value.request_key === selected.key) && (!selected.id || value.id === selected.id)
|
|
58
|
+
&& (!input || value.filename === input.filename && value.size === input.size && value.sha256 === input.sha256)
|
|
59
|
+
&& Number.isSafeInteger(value.size) && value.size >= 1 && digest.test(value.sha256)
|
|
60
|
+
&& ['uploading', 'inspecting', 'inspected', 'rejected', 'withdrawn'].includes(value.state)
|
|
61
|
+
&& (value.implementation_id === null || uuid.test(value.implementation_id))
|
|
62
|
+
&& (value.state !== 'inspected' || value.implementation_id !== null && value.finished_at !== null)
|
|
63
|
+
&& (value.catalog_setup_id === null && value.catalog_setup_request_key === null
|
|
64
|
+
|| !!value.catalog_setup_id && !!value.catalog_setup_request_key && uuid.test(value.catalog_setup_id) && uuid.test(value.catalog_setup_request_key))
|
|
65
|
+
&& (!value.can_upload || value.state === 'uploading' && !value.withdrawn_at && !value.catalog_setup_id)
|
|
66
|
+
&& (!value.can_withdraw || ['uploading', 'inspecting'].includes(value.state) && !value.finished_at && !value.withdrawn_at);
|
|
67
|
+
}
|
|
68
|
+
export function toolPackagePageMatches(value, scope, query = '', state = 'all', selectedPage) {
|
|
69
|
+
return !!value && value.workspace_id === scope.workspace && value.actor_id === scope.actor && value.query === query.trim()
|
|
70
|
+
&& value.state_filter === state && page(value, selectedPage)
|
|
71
|
+
&& value.results.every(row => toolPackageMatches(row, scope) && (state === 'all' || row.state === state));
|
|
72
|
+
}
|
|
73
|
+
export function toolPackageUploadDestination(value, scope, request) {
|
|
74
|
+
let url;
|
|
75
|
+
try {
|
|
76
|
+
url = new URL(value.upload_url);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new Error('Package upload access could not be confirmed.');
|
|
80
|
+
}
|
|
81
|
+
const parts = url.pathname.split('/');
|
|
82
|
+
packageRead(value, toolPackageMatches(request, scope) && request.can_upload
|
|
83
|
+
&& value.workspace_id === scope.workspace && value.actor_id === scope.actor && value.id === request.id
|
|
84
|
+
&& value.request_key === request.request_key && value.size === request.size && value.sha256 === request.sha256
|
|
85
|
+
&& url.origin === 'https://storage.googleapis.com' && !url.username && !url.password && !url.hash && value.method === 'PUT'
|
|
86
|
+
&& parts.length === 7 && !!parts[1] && parts[2] === 'private' && uuid.test(parts[3] ?? '') && parts[4] === scope.workspace
|
|
87
|
+
&& parts[5] === 'tool-uploads' && parts[6] === `${request.id}.zip`
|
|
88
|
+
&& Object.keys(value.headers).sort().join(',') === 'Content-Type,x-goog-if-generation-match'
|
|
89
|
+
&& value.headers['Content-Type'] === 'application/zip' && value.headers['x-goog-if-generation-match'] === '0');
|
|
90
|
+
return url;
|
|
91
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const names = /^[A-Za-z_][A-Za-z0-9_.-]{0,127}$/;
|
|
2
|
+
const field = /^[A-Za-z_][A-Za-z0-9_]{0,79}$/;
|
|
3
|
+
const headers = new Set(['notion-version', 'stripe-version', 'stripe-account', 'x-goog-api-client']);
|
|
4
|
+
export function toolProtocolMetadataMatches(value) {
|
|
5
|
+
if (!value || !value.transport || typeof value.transport !== 'object' || Array.isArray(value.transport)
|
|
6
|
+
|| !['http', 'mcp'].includes(value.transport.kind))
|
|
7
|
+
return false;
|
|
8
|
+
if (value.mcp_aliases !== undefined && (!Array.isArray(value.mcp_aliases) || value.mcp_aliases.length > 16
|
|
9
|
+
|| value.mcp_aliases.some(name => typeof name !== 'string' || !names.test(name))
|
|
10
|
+
|| new Set(value.mcp_aliases).size !== value.mcp_aliases.length))
|
|
11
|
+
return false;
|
|
12
|
+
if (value.transport.kind !== 'http')
|
|
13
|
+
return value.transport.idempotency === undefined && value.transport.ignored_headers === undefined
|
|
14
|
+
&& value.transport.content_type_argument === undefined && value.transport.parts === undefined;
|
|
15
|
+
const metadata = value.transport;
|
|
16
|
+
if (metadata.content_type_argument !== undefined && (typeof metadata.content_type_argument !== 'string'
|
|
17
|
+
|| !field.test(metadata.content_type_argument) || !['text', 'base64'].includes(String(metadata.encoding))
|
|
18
|
+
|| metadata.content_type_argument === metadata.body))
|
|
19
|
+
return false;
|
|
20
|
+
if (metadata.idempotency !== undefined) {
|
|
21
|
+
if (!metadata.idempotency || typeof metadata.idempotency !== 'object' || Array.isArray(metadata.idempotency))
|
|
22
|
+
return false;
|
|
23
|
+
const mapping = metadata.idempotency;
|
|
24
|
+
if (Object.keys(mapping).sort().join(',') !== 'argument,header' || mapping.header !== 'Idempotency-Key'
|
|
25
|
+
|| typeof mapping.argument !== 'string' || !field.test(mapping.argument))
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (metadata.encoding === 'multipart' || metadata.parts !== undefined) {
|
|
29
|
+
if (metadata.encoding !== 'multipart' || !metadata.parts || typeof metadata.parts !== 'object' || Array.isArray(metadata.parts)
|
|
30
|
+
|| metadata.body !== undefined || metadata.content_type !== undefined || metadata.content_type_argument !== undefined)
|
|
31
|
+
return false;
|
|
32
|
+
const parts = Object.entries(metadata.parts), argumentsUsed = new Set();
|
|
33
|
+
if (parts.length < 1 || parts.length > 100)
|
|
34
|
+
return false;
|
|
35
|
+
for (const [name, descriptor] of parts) {
|
|
36
|
+
if (!/^[A-Za-z_][A-Za-z0-9_.-]{0,99}$/.test(name) || !descriptor || typeof descriptor !== 'object' || Array.isArray(descriptor))
|
|
37
|
+
return false;
|
|
38
|
+
const part = descriptor;
|
|
39
|
+
if (Object.keys(part).some(key => !['argument', 'encoding', 'filename_argument', 'content_type_argument', 'content_type'].includes(key))
|
|
40
|
+
|| !['text', 'json', 'base64'].includes(String(part.encoding)) || typeof part.argument !== 'string' || !field.test(part.argument)
|
|
41
|
+
|| part.encoding !== 'base64' && ['filename_argument', 'content_type_argument', 'content_type'].some(key => key in part))
|
|
42
|
+
return false;
|
|
43
|
+
for (const key of ['argument', 'filename_argument', 'content_type_argument']) {
|
|
44
|
+
if (!(key in part))
|
|
45
|
+
continue;
|
|
46
|
+
const argument = part[key];
|
|
47
|
+
if (typeof argument !== 'string' || !field.test(argument) || argumentsUsed.has(argument))
|
|
48
|
+
return false;
|
|
49
|
+
argumentsUsed.add(argument);
|
|
50
|
+
}
|
|
51
|
+
if (part.content_type !== undefined && (typeof part.content_type !== 'string' || part.content_type.length > 128
|
|
52
|
+
|| !/^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+(?:;\s*charset=utf-8)?$/i.test(part.content_type)))
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const idempotency = metadata.idempotency;
|
|
56
|
+
if (idempotency && argumentsUsed.has(idempotency.argument))
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (metadata.ignored_headers !== undefined && (!Array.isArray(metadata.ignored_headers) || metadata.ignored_headers.length > 4
|
|
60
|
+
|| metadata.ignored_headers.some(name => typeof name !== 'string' || !headers.has(name.toLowerCase()))
|
|
61
|
+
|| new Set(metadata.ignored_headers.map(name => String(name).toLowerCase())).size !== metadata.ignored_headers.length))
|
|
62
|
+
return false;
|
|
63
|
+
if (Array.isArray(metadata.ignored_headers) && metadata.headers !== undefined) {
|
|
64
|
+
if (!metadata.headers || typeof metadata.headers !== 'object' || Array.isArray(metadata.headers))
|
|
65
|
+
return false;
|
|
66
|
+
const mapped = new Set(Object.keys(metadata.headers).map(name => name.toLowerCase()));
|
|
67
|
+
if (metadata.ignored_headers.some(name => mapped.has(String(name).toLowerCase())))
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const id = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/, digest = /^[a-f0-9]{64}$/;
|
|
2
|
+
export function toolAllocationQuoteMatches(value, workspace, selected) {
|
|
3
|
+
const scope = value?.scope, source = scope?.source;
|
|
4
|
+
return !!value && value.workspace_id === workspace && id.test(value.id) && value.activity === 'tool_runtime' && value.execution_location === 'managed'
|
|
5
|
+
&& digest.test(value.scope_digest) && digest.test(value.tariff_digest) && typeof value.tariff_version === 'string' && value.tariff_version.length > 0
|
|
6
|
+
&& Number.isSafeInteger(value.credit_scale) && value.credit_scale > 0 && /^10*$/.test(String(value.credit_scale))
|
|
7
|
+
&& typeof value.denomination === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,31}$/.test(value.denomination)
|
|
8
|
+
&& Number.isSafeInteger(value.estimated_units) && value.estimated_units >= 0
|
|
9
|
+
&& Number.isSafeInteger(value.maximum_units) && value.maximum_units >= value.estimated_units && Number.isFinite(Date.parse(value.expires_at))
|
|
10
|
+
&& scope?.schema === 'firedrill.tool-runtime-budget.v1' && scope.launch_request_key === selected.launch_request_key
|
|
11
|
+
&& scope.duration_seconds === selected.duration_seconds && source?.id === (selected.release_id ?? selected.implementation_id)
|
|
12
|
+
&& source.kind === (selected.release_id ? 'catalog' : 'version') && [source.digest, source.contract_digest].every(one => digest.test(one)) && /^sha256:[a-f0-9]{64}$/.test(source.bundle_digest)
|
|
13
|
+
&& (scope.dataset?.id ?? null) === selected.dataset_id
|
|
14
|
+
&& (selected.restore === null ? scope.restore === null : !!scope.restore && scope.restore.instance_id === selected.restore.instance_id
|
|
15
|
+
&& (selected.restore.checkpoint_id === null ? id.test(scope.restore.checkpoint_id) : scope.restore.checkpoint_id === selected.restore.checkpoint_id) && scope.restore.action === selected.restore.action)
|
|
16
|
+
&& !!scope.resources && Object.values(scope.resources).every(one => Number.isSafeInteger(one) && one > 0);
|
|
17
|
+
}
|
|
18
|
+
export function allocationCredits(value, kind = 'maximum') {
|
|
19
|
+
const units = kind === 'estimated' ? value.estimated_units : value.maximum_units;
|
|
20
|
+
if (!Number.isSafeInteger(units) || units < 0 || !Number.isSafeInteger(value.credit_scale) || value.credit_scale < 1
|
|
21
|
+
|| !/^10*$/.test(String(value.credit_scale)))
|
|
22
|
+
return 'Not available';
|
|
23
|
+
const exact = BigInt(units), scale = BigInt(value.credit_scale);
|
|
24
|
+
const whole = new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(exact / scale);
|
|
25
|
+
const fraction = (exact % scale).toString().padStart(scale.toString().length - 1, '0').replace(/0+$/, '');
|
|
26
|
+
const separator = new Intl.NumberFormat().formatToParts(1.1).find(one => one.type === 'decimal')?.value ?? '.';
|
|
27
|
+
return whole + (fraction ? separator + fraction : '');
|
|
28
|
+
}
|