firedrill-run 2.2.5 → 3.0.1
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 +176 -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 +506 -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 +1627 -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 +96 -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,315 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { lstat, open, unlink } from 'node:fs/promises';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { createInterface } from 'node:readline/promises';
|
|
6
|
+
import { api } from './auth.js';
|
|
7
|
+
import { privateDirectory } from './credentials.js';
|
|
8
|
+
import { CliError } from './http.js';
|
|
9
|
+
import { selectMany, selectOne } from './prompts.js';
|
|
10
|
+
import { selectPolicies } from './company-policies.js';
|
|
11
|
+
import { identifier } from './world-contracts.js';
|
|
12
|
+
import { selectedWorkspace, selectedWorld } from './world-access.js';
|
|
13
|
+
import { assessmentRequestText, inspectAssessmentRequest } from './assessment-requests.js';
|
|
14
|
+
import { inspectPriorityRequest, priorityRequestText } from './priority-requests.js';
|
|
15
|
+
import { assessmentAcceptanceMatches } from '../../contracts/assessment-contracts.js';
|
|
16
|
+
import { recommendationBatchMatches, recommendationAcceptanceMatches } from '../../contracts/recommendation-contracts.js';
|
|
17
|
+
const CAPACITY = 256 * 1024;
|
|
18
|
+
function interactive(options) { return !options.json && !!process.stdin.isTTY && !!process.stderr.isTTY; }
|
|
19
|
+
function paging(options) {
|
|
20
|
+
if (options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page) || (options.search?.length ?? 0) > 200) {
|
|
21
|
+
throw new CliError('arguments_invalid', 'Choose valid paging and search options.', 2);
|
|
22
|
+
}
|
|
23
|
+
return `page=${options.page ?? '1'}&q=${encodeURIComponent(options.search ?? '')}`;
|
|
24
|
+
}
|
|
25
|
+
function object(value) { return !!value && typeof value === 'object' && !Array.isArray(value); }
|
|
26
|
+
function fingerprint(value) { return createHash('sha256').update(JSON.stringify(value)).digest('hex'); }
|
|
27
|
+
async function readJson(path, privateFile = false) {
|
|
28
|
+
const absolute = resolve(path), before = await lstat(absolute);
|
|
29
|
+
if (!before.isFile() || before.isSymbolicLink() || before.size < 2 || before.size > CAPACITY
|
|
30
|
+
|| privateFile && (process.getuid && before.uid !== process.getuid() || process.platform !== 'win32' && !!(before.mode & 0o077))) {
|
|
31
|
+
throw new CliError('evaluation_file_invalid', 'Choose a regular, bounded JSON file. Recovery records must remain private to this OS user.', 2);
|
|
32
|
+
}
|
|
33
|
+
const file = await open(absolute, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
34
|
+
try {
|
|
35
|
+
const actual = await file.stat();
|
|
36
|
+
if (!actual.isFile() || actual.dev !== before.dev || actual.ino !== before.ino || actual.size !== before.size || actual.mtimeMs !== before.mtimeMs
|
|
37
|
+
|| privateFile && (process.getuid && actual.uid !== process.getuid() || process.platform !== 'win32' && !!(actual.mode & 0o077)))
|
|
38
|
+
throw new Error();
|
|
39
|
+
const buffer = Buffer.alloc(actual.size);
|
|
40
|
+
let offset = 0;
|
|
41
|
+
while (offset < buffer.length) {
|
|
42
|
+
const part = await file.read(buffer, offset, buffer.length - offset, offset);
|
|
43
|
+
if (!part.bytesRead)
|
|
44
|
+
throw new Error();
|
|
45
|
+
offset += part.bytesRead;
|
|
46
|
+
}
|
|
47
|
+
const after = await file.stat();
|
|
48
|
+
if (after.size !== actual.size || after.mtimeMs !== actual.mtimeMs || after.ctimeMs !== actual.ctimeMs)
|
|
49
|
+
throw new Error();
|
|
50
|
+
const data = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(buffer), (_key, value) => {
|
|
51
|
+
if (typeof value === 'number' && !Number.isFinite(value))
|
|
52
|
+
throw new Error();
|
|
53
|
+
return value;
|
|
54
|
+
});
|
|
55
|
+
if (!object(data))
|
|
56
|
+
throw new Error();
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new CliError('evaluation_file_invalid', 'The JSON file changed or could not be read. No assessment or criterion was requested.', 2);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
await file.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function journalPath(scope, key) {
|
|
67
|
+
return join(await privateDirectory(), `evaluation-${identifier(scope.owner)}-${identifier(scope.workspace)}-${identifier(key)}.json`);
|
|
68
|
+
}
|
|
69
|
+
function validPath(kind, path, workspace) {
|
|
70
|
+
const id = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
|
|
71
|
+
return new RegExp(`^/api/workspaces/${identifier(workspace)}/${kind === 'criterion'
|
|
72
|
+
? `worlds/${id}/evaluations(?:/${id})?` : kind === 'priority' ? `worlds/${id}/recommendation-batches` : `campaigns/${id}/trials/${id}/assessments`}$`).test(path);
|
|
73
|
+
}
|
|
74
|
+
async function retained(scope, key, kind) {
|
|
75
|
+
const value = await readJson(await journalPath(scope, key), true);
|
|
76
|
+
if (value.schema !== 'firedrill.native-evaluation-intent.v1' || value.owner !== scope.owner || value.workspace !== scope.workspace
|
|
77
|
+
|| value.key !== identifier(key) || value.kind !== kind || typeof value.path !== 'string' || !validPath(kind, value.path, scope.workspace)
|
|
78
|
+
|| !object(value.body) || value.digest !== fingerprint(value.body))
|
|
79
|
+
throw new CliError('evaluation_request_invalid', 'The retained evaluation request cannot be confirmed. It was not submitted.');
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
async function submit(intent, scope, options, output) {
|
|
83
|
+
output('evaluation_request', { request_key: intent.key, kind: intent.kind, body_sha256: intent.digest }, `Request: ${intent.key}\nIf the reply is interrupted, use firedrill ${intent.kind === 'criterion' ? 'criteria' : intent.kind === 'priority' ? 'priority' : 'assess'} resume --workspace ${scope.workspace} --request-key ${intent.key}.`);
|
|
84
|
+
if (intent.kind === 'criterion') {
|
|
85
|
+
const saved = await api(intent.path, { actor: scope.owner, workspace: scope.workspace,
|
|
86
|
+
signal: options.signal, method: 'POST', idempotencyKey: intent.key, body: intent.body });
|
|
87
|
+
const receipt = await inspectCriterion(intent, scope, options, output, saved);
|
|
88
|
+
if (receipt.resource_available)
|
|
89
|
+
output('criterion_saved', { criterion: saved, request: receipt }, `${saved.name} · Revision ${saved.revision}\n${saved.id}\nCriterion saved. Existing plans and results are unchanged.`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (intent.kind === 'priority') {
|
|
93
|
+
const world = identifier(intent.path.slice(0, -'/recommendation-batches'.length).split('/').at(-1));
|
|
94
|
+
const turn = identifier(typeof intent.body.turn_id === 'string' ? intent.body.turn_id : '');
|
|
95
|
+
const result = await api(intent.path, { actor: scope.owner, workspace: scope.workspace, signal: options.signal, method: 'POST', idempotencyKey: intent.key, body: intent.body });
|
|
96
|
+
if (!recommendationBatchMatches(result, { workspace: scope.workspace, world, turn, actor: scope.owner }))
|
|
97
|
+
throw new CliError('recommendation_request_invalid', 'Priority acceptance could not be confirmed. Inspect the original saved request.');
|
|
98
|
+
await inspectPriority(intent, scope, options, output, result);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const result = await api(intent.path, { actor: scope.owner, workspace: scope.workspace, signal: options.signal, method: 'POST', idempotencyKey: intent.key, body: intent.body });
|
|
102
|
+
const prefix = intent.path.slice(0, -'/assessments'.length);
|
|
103
|
+
const receipt = await inspectAssessmentRequest(prefix, intent.key, scope, options);
|
|
104
|
+
if (!assessmentAcceptanceMatches(receipt, result, { actor: scope.owner, workspace: scope.workspace,
|
|
105
|
+
campaign: prefix.split('/').at(-3), execution: prefix.split('/').at(-1) }, intent.key)) {
|
|
106
|
+
throw new CliError('assessment_request_invalid', 'The original assessment acceptance could not be confirmed. Resume this saved request; no evaluation was repeated.');
|
|
107
|
+
}
|
|
108
|
+
output('result', result, `${result.name} · ${result.state}\n${result.id}\nAssessment requested for saved evidence. The agent was not run again.`);
|
|
109
|
+
}
|
|
110
|
+
async function inspectCriterion(intent, scope, options, output, saved) {
|
|
111
|
+
const prefix = intent.path.split('/evaluations')[0], world = prefix.split('/').at(-1);
|
|
112
|
+
const value = await api(`${prefix}/evaluation-request?request_key=${encodeURIComponent(intent.key)}`, { actor: scope.owner, workspace: scope.workspace, signal: options.signal });
|
|
113
|
+
if (value.schema !== 'firedrill.evaluation-change-receipt.v1' || value.actor_id !== scope.owner || value.workspace_id !== scope.workspace
|
|
114
|
+
|| value.world_id !== world || value.request_key !== intent.key || !['accepted', 'withdrawn', 'not_received'].includes(value.state)
|
|
115
|
+
|| typeof value.resource_available !== 'boolean' || value.resource_available && (value.state !== 'accepted' || !value.item_id)
|
|
116
|
+
|| saved && (saved.world_id !== world || value.state !== 'accepted' || !value.resource_available
|
|
117
|
+
|| value.item_id !== saved.id || value.revision !== saved.revision))
|
|
118
|
+
throw new CliError('evaluation_request_invalid', 'The original criterion receipt could not be confirmed. No save was repeated.');
|
|
119
|
+
if (!saved)
|
|
120
|
+
output('result', value, `Request: ${intent.key}\n${value.state === 'accepted'
|
|
121
|
+
? value.resource_available ? `${value.name} · Revision ${value.revision}\n${value.item_id}\nCriterion saved. No save was repeated.` : 'The change was saved, but its criterion is no longer available.'
|
|
122
|
+
: value.state === 'withdrawn' ? 'The unsaved request was canceled. No save was repeated.'
|
|
123
|
+
: `No acceptance is recorded yet. The original request may still arrive.\nCancel only that unsaved request: firedrill criteria withdraw-request ${intent.key} --world ${world} --workspace ${scope.workspace}`}`);
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
async function inspectAssessment(intent, scope, options, output) {
|
|
127
|
+
const prefix = intent.path.slice(0, -'/assessments'.length);
|
|
128
|
+
const value = await inspectAssessmentRequest(prefix, intent.key, scope, options);
|
|
129
|
+
output('result', value, assessmentRequestText(value));
|
|
130
|
+
}
|
|
131
|
+
async function inspectPriority(intent, scope, options, output, accepted) {
|
|
132
|
+
const prefix = intent.path.slice(0, -'/recommendation-batches'.length), world = identifier(prefix.split('/').at(-1));
|
|
133
|
+
const turn = identifier(typeof intent.body.turn_id === 'string' ? intent.body.turn_id : '');
|
|
134
|
+
const value = await inspectPriorityRequest({ ...scope, world, prefix }, turn, intent.key, options.signal);
|
|
135
|
+
if (accepted !== undefined && !recommendationAcceptanceMatches(value, accepted, { actor: scope.owner, workspace: scope.workspace, world, turn }, intent.key)) {
|
|
136
|
+
throw new CliError('recommendation_request_invalid', 'The original priority acceptance could not be confirmed. Resume this saved request; no evaluation was repeated.');
|
|
137
|
+
}
|
|
138
|
+
output('result', value, priorityRequestText(value) + (value.resource_available
|
|
139
|
+
? `\nView: firedrill priority show ${value.batch_id} --workspace ${scope.workspace} --world ${world}` : ''));
|
|
140
|
+
}
|
|
141
|
+
export async function remember(kind, path, body, scope, options, output) {
|
|
142
|
+
const key = options.request ? identifier(options.request) : randomUUID();
|
|
143
|
+
if (!validPath(kind, path, scope.workspace))
|
|
144
|
+
throw new CliError('evaluation_request_invalid', 'The evaluation destination could not be confirmed.');
|
|
145
|
+
const intent = { schema: 'firedrill.native-evaluation-intent.v1', key, owner: scope.owner, workspace: scope.workspace,
|
|
146
|
+
kind, path, body, digest: fingerprint(body) };
|
|
147
|
+
const encoded = JSON.stringify(intent);
|
|
148
|
+
if (Buffer.byteLength(encoded) > CAPACITY)
|
|
149
|
+
throw new CliError('evaluation_input_capacity', 'Use a more focused criterion. No request was submitted.', 2);
|
|
150
|
+
try {
|
|
151
|
+
const file = await open(await journalPath(scope, key), 'wx', 0o600);
|
|
152
|
+
try {
|
|
153
|
+
await file.writeFile(encoded);
|
|
154
|
+
await file.sync();
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
await file.close();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
if (!error || typeof error !== 'object' || !('code' in error) || error.code !== 'EEXIST')
|
|
162
|
+
throw error;
|
|
163
|
+
const previous = await retained(scope, key, kind);
|
|
164
|
+
if (previous.path !== path || previous.digest !== intent.digest)
|
|
165
|
+
throw new CliError('evaluation_request_conflict', 'This request key belongs to different inputs. Resume its exact saved request instead.', 2);
|
|
166
|
+
if (kind === 'criterion')
|
|
167
|
+
return inspectCriterion(previous, scope, options, output);
|
|
168
|
+
if (kind === 'priority')
|
|
169
|
+
return inspectPriority(previous, scope, options, output);
|
|
170
|
+
return inspectAssessment(previous, scope, options, output);
|
|
171
|
+
}
|
|
172
|
+
await submit(intent, scope, options, output);
|
|
173
|
+
}
|
|
174
|
+
async function line(label, maximum, signal, optional = false) {
|
|
175
|
+
const prompt = createInterface({ input: process.stdin, output: process.stderr, terminal: false });
|
|
176
|
+
try {
|
|
177
|
+
const value = (await prompt.question(`${label}: `, { signal })).trim();
|
|
178
|
+
if ((!optional && !value) || value.length > maximum || /[\u0000-\u001f\u007f]/u.test(value))
|
|
179
|
+
throw new CliError('criterion_input_invalid', 'Use non-empty text within the displayed field capacity. No criterion was saved.', 2);
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
prompt.close();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function rubric(scope, agent, signal) {
|
|
187
|
+
const name = await line('Criterion name (up to 240 characters)', 240, signal);
|
|
188
|
+
const instructions = await line('Behavior to assess (up to 16,000 characters)', 16000, signal);
|
|
189
|
+
const count = await selectOne('Number of anchored score levels', Array.from({ length: 9 }, (_, index) => ({ value: index + 2, label: String(index + 2) })), signal);
|
|
190
|
+
const levels = [];
|
|
191
|
+
for (let score = 0; score < count; score++)
|
|
192
|
+
levels.push(await line(`Score ${score} description (up to 4,000 characters)`, 4000, signal));
|
|
193
|
+
if (new Set(levels).size !== levels.length)
|
|
194
|
+
throw new CliError('criterion_input_invalid', 'Use distinct score descriptions.', 2);
|
|
195
|
+
const direction = await selectOne('Better outcome', [{ value: 'higher', label: 'Higher score' }, { value: 'lower', label: 'Lower score' }], signal);
|
|
196
|
+
const sources = await selectMany('Required retained evidence', [{ value: 'conversation', label: 'Conversation' }, { value: 'actions', label: 'Tool actions' }, { value: 'state', label: 'Observed controlled-service state from this drill' }], signal, ['conversation']);
|
|
197
|
+
if (!sources.length)
|
|
198
|
+
throw new CliError('criterion_evidence_required', 'Choose at least one evidence source.', 2);
|
|
199
|
+
const applicability = await line('When this applies (optional, up to 8,000 characters)', 8000, signal, true);
|
|
200
|
+
const policies = await selectPolicies(scope, agent, signal);
|
|
201
|
+
return { name, instructions, levels, direction, sources, applicability, ...(policies.length ? { company_policies: policies } : {}) };
|
|
202
|
+
}
|
|
203
|
+
async function approval(kind, options) {
|
|
204
|
+
const accepted = options.approve || interactive(options) && await selectOne(kind === 'assessment'
|
|
205
|
+
? 'Use selected saved drill evidence and policy passages for AI scoring and cited explanations? Keep a private recovery record for this request.'
|
|
206
|
+
: 'Save this criterion and keep an exact private local recovery copy? This does not run or evaluate the agent.', [{ value: false, label: 'Cancel' }, { value: true, label: kind === 'assessment' ? 'Evaluate saved evidence' : 'Save criterion' }], options.signal);
|
|
207
|
+
if (!accepted)
|
|
208
|
+
throw new CliError('evaluation_consent_required', `Nothing was submitted. Use --approve-${kind} only after reviewing these inputs and local recovery retention.`, 2);
|
|
209
|
+
}
|
|
210
|
+
export async function recovery(kind, args, scope, options, output) {
|
|
211
|
+
if (args.length !== 1 || !options.request || options.file || options.page || options.search || options.world)
|
|
212
|
+
throw new CliError('arguments_invalid', 'Use resume or forget with --request-key and --workspace only.', 2);
|
|
213
|
+
const intent = await retained(scope, options.request, kind);
|
|
214
|
+
if (args[0] === 'forget') {
|
|
215
|
+
const accepted = options.approve || interactive(options) && await selectOne('Remove only this private local recovery copy? This does not cancel or revoke a server request.', [{ value: false, label: 'Keep recovery copy' }, { value: true, label: 'Remove local copy' }], options.signal);
|
|
216
|
+
if (!accepted)
|
|
217
|
+
throw new CliError('evaluation_consent_required', 'The local recovery copy was kept.', 2);
|
|
218
|
+
await unlink(await journalPath(scope, intent.key));
|
|
219
|
+
output('result', { request_key: intent.key, local_copy_removed: true }, 'Local recovery copy removed. Any saved server criterion or assessment is unchanged.');
|
|
220
|
+
}
|
|
221
|
+
else if (kind === 'criterion')
|
|
222
|
+
await inspectCriterion(intent, scope, options, output);
|
|
223
|
+
else if (kind === 'priority')
|
|
224
|
+
await inspectPriority(intent, scope, options, output);
|
|
225
|
+
else
|
|
226
|
+
await inspectAssessment(intent, scope, options, output);
|
|
227
|
+
}
|
|
228
|
+
export async function criteriaCommand(args, options, output) {
|
|
229
|
+
paging(options);
|
|
230
|
+
const action = args[0] ?? 'list';
|
|
231
|
+
if (!['list', 'show', 'history', 'save', 'resume', 'forget'].includes(action) || args.length > 2)
|
|
232
|
+
throw new CliError('arguments_invalid', 'Use criteria list, show|history <id>, save [id], resume or forget.', 2);
|
|
233
|
+
if (['resume', 'forget'].includes(action))
|
|
234
|
+
return recovery('criterion', args, await selectedWorkspace({ ...options, interactive: interactive(options) }), options, output);
|
|
235
|
+
if (action !== 'save' && (options.file || options.request || options.approve) || action === 'list' && args.length > 1
|
|
236
|
+
|| ['show', 'save'].includes(action) && (options.page || options.search) || action === 'history' && options.search) {
|
|
237
|
+
throw new CliError('arguments_invalid', 'Use only the options for this criterion action.', 2);
|
|
238
|
+
}
|
|
239
|
+
const context = await selectedWorld({ ...options, interactive: interactive(options) });
|
|
240
|
+
const auth = { actor: context.owner, workspace: context.workspace, signal: options.signal };
|
|
241
|
+
const prefix = `${context.prefix}/evaluations`;
|
|
242
|
+
if (action === 'list' || action === 'history') {
|
|
243
|
+
const data = await api(`${prefix}${action === 'history' ? `/${identifier(args[1] ?? '')}/history` : ''}?${paging(options)}`, auth);
|
|
244
|
+
output('result', data, [data.results.map(row => `${row.name} · Revision ${row.revision} · ${row.state}\n${row.id}`).join('\n\n') || 'No matching criteria.',
|
|
245
|
+
...(data.next ? [`Next page: --page ${data.page + 1}`] : [])].join('\n'));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (action === 'show') {
|
|
249
|
+
const data = await api(`${prefix}/${identifier(args[1] ?? '')}`, auth);
|
|
250
|
+
output('result', data, `${data.name} · Revision ${data.revision} · ${data.state}\n${data.rubric ? JSON.stringify(data.rubric, null, 2) : 'This native check is inspected through its world contract.'}`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const item = args[1] ? identifier(args[1]) : undefined;
|
|
254
|
+
let body;
|
|
255
|
+
if (options.file)
|
|
256
|
+
body = await readJson(options.file);
|
|
257
|
+
else {
|
|
258
|
+
if (!interactive(options) || item)
|
|
259
|
+
throw new CliError('criterion_file_required', 'Use --file with the exact criterion body to edit a criterion or automate creation. Include world_revision, basis_revision, rubric, scenario_keys, enabled and archived.', 2);
|
|
260
|
+
const data = await api(prefix, auth);
|
|
261
|
+
if (!data.can_write)
|
|
262
|
+
throw new CliError('write_access_required', 'Write access is required to save criteria.', 2);
|
|
263
|
+
const definition = await rubric(context, identifier(data.agent_id), options.signal);
|
|
264
|
+
const enabled = await selectOne('Use this criterion in future simulation plans?', [{ value: false, label: 'Save disabled' }, { value: true, label: 'Enable for all scenarios' }], options.signal);
|
|
265
|
+
const change = { world_revision: data.world_revision, basis_revision: null, rubric: definition, scenario_keys: [], enabled, archived: false };
|
|
266
|
+
body = change;
|
|
267
|
+
}
|
|
268
|
+
const fields = ['world_revision', 'basis_revision', 'rubric', 'scenario_keys', 'enabled', 'archived'];
|
|
269
|
+
if (Object.keys(body).length !== fields.length || fields.some(key => !(key in body)))
|
|
270
|
+
throw new CliError('criterion_file_invalid', 'Use the complete criterion body: world_revision, basis_revision, rubric, scenario_keys, enabled and archived. No request was submitted.', 2);
|
|
271
|
+
await approval('criterion', options);
|
|
272
|
+
await remember('criterion', `${prefix}${item ? `/${item}` : ''}`, body, context, options, output);
|
|
273
|
+
}
|
|
274
|
+
export async function assessCommand(args, options, output) {
|
|
275
|
+
paging(options);
|
|
276
|
+
const scope = await selectedWorkspace({ ...options, interactive: interactive(options) });
|
|
277
|
+
if (['resume', 'forget'].includes(args[0]))
|
|
278
|
+
return recovery('assessment', args, scope, options, output);
|
|
279
|
+
if (!args.length || args.length > 2 || options.world || options.page || options.search)
|
|
280
|
+
throw new CliError('arguments_invalid', 'Use assess <campaign-id> [drill-id] with --file <rubric.json>, or run in a terminal for guided input.', 2);
|
|
281
|
+
const campaign = identifier(args[0]), prefix = `/api/workspaces/${scope.workspace}/campaigns/${campaign}`;
|
|
282
|
+
const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal };
|
|
283
|
+
let trial = args[1] ? identifier(args[1]) : undefined;
|
|
284
|
+
if (!trial && interactive(options)) {
|
|
285
|
+
let number = 1;
|
|
286
|
+
while (!trial) {
|
|
287
|
+
const data = await api(`${prefix}/trials?page=${number}`, auth);
|
|
288
|
+
const choices = data.results.map(row => ({ value: row.id, label: `Drill ${row.position + 1}: ${row.scenario_name}`, detail: row.state }));
|
|
289
|
+
if (number > 1)
|
|
290
|
+
choices.unshift({ value: 'previous', label: 'Previous page', detail: '' });
|
|
291
|
+
if (data.next)
|
|
292
|
+
choices.push({ value: 'next', label: 'Next page', detail: '' });
|
|
293
|
+
if (!choices.length)
|
|
294
|
+
throw new CliError('trial_required', 'This simulation has no retained drills.');
|
|
295
|
+
const picked = await selectOne('Saved drill', choices, options.signal);
|
|
296
|
+
if (picked === 'next')
|
|
297
|
+
number++;
|
|
298
|
+
else if (picked === 'previous')
|
|
299
|
+
number--;
|
|
300
|
+
else
|
|
301
|
+
trial = identifier(picked);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (!trial)
|
|
305
|
+
throw new CliError('trial_required', 'Choose a saved drill ID. This command does not run a simulation.', 2);
|
|
306
|
+
const path = `${prefix}/trials/${trial}/assessments`;
|
|
307
|
+
const data = await api(path, auth);
|
|
308
|
+
if (!data.can_assess)
|
|
309
|
+
throw new CliError('assessment_unavailable', 'Evaluation permission, retained evidence or evaluator configuration is unavailable. No assessment was requested.');
|
|
310
|
+
const definition = options.file ? await readJson(options.file) : interactive(options) ? await rubric(scope, identifier(data.agent_id), options.signal) : null;
|
|
311
|
+
if (!definition)
|
|
312
|
+
throw new CliError('rubric_file_required', 'Pass --file <rubric.json> containing the reviewed criterion.', 2);
|
|
313
|
+
await approval('assessment', options);
|
|
314
|
+
await remember('assessment', path, { rubric: definition, share_evidence: true, share_explanation: true }, scope, options, output);
|
|
315
|
+
}
|