humanish 0.107.0 → 0.109.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/ARCHITECTURE.md +73 -69
- package/CHANGELOG.md +27 -0
- package/CONTEXT.md +51 -44
- package/README.md +148 -175
- package/SECURITY.md +16 -26
- package/TELEMETRY.md +8 -6
- package/dist/actors/affordance.d.ts +4 -4
- package/dist/actors/affordance.js +12 -12
- package/dist/actors/agent-session.d.ts +2 -2
- package/dist/actors/agent-session.js +7 -7
- package/dist/actors/codex/app-server-actor-trace.d.ts +1 -1
- package/dist/actors/codex/app-server-actor-trace.js +1 -1
- package/dist/actors/codex/app-server-trace.js +2 -1
- package/dist/actors/codex/app-server-ui.js +1 -1
- package/dist/actors/codex/codex-admission.d.ts +91 -0
- package/dist/actors/codex/codex-admission.js +150 -0
- package/dist/actors/codex/restricted-executable.d.ts +4 -4
- package/dist/actors/codex/restricted-executable.js +8 -8
- package/dist/actors/codex/restricted-launch.d.ts +2 -1
- package/dist/actors/codex/restricted-launch.js +1 -1
- package/dist/actors/codex/restricted-participant.js +20 -18
- package/dist/actors/codex/restricted-policy.d.ts +2 -2
- package/dist/actors/codex/restricted-policy.js +2 -2
- package/dist/actors/codex/restricted-session.d.ts +3 -3
- package/dist/actors/codex/restricted-session.js +10 -5
- package/dist/actors/computer-use/actor.d.ts +18 -18
- package/dist/actors/computer-use/actor.js +4 -4
- package/dist/actors/computer-use/admission-limit.d.ts +3 -3
- package/dist/actors/computer-use/admission-limit.js +4 -4
- package/dist/actors/computer-use/executor-error.d.ts +2 -2
- package/dist/actors/computer-use/executor-error.js +3 -3
- package/dist/actors/computer-use/loop/actions.d.ts +3 -3
- package/dist/actors/computer-use/loop/actions.js +15 -15
- package/dist/actors/computer-use/loop/backstop.d.ts +3 -3
- package/dist/actors/computer-use/loop/backstop.js +14 -12
- package/dist/actors/computer-use/loop/debrief.js +2 -2
- package/dist/actors/computer-use/loop/ending.d.ts +6 -6
- package/dist/actors/computer-use/loop/ending.js +18 -17
- package/dist/actors/computer-use/loop/observation.d.ts +2 -2
- package/dist/actors/computer-use/loop/observation.js +15 -14
- package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
- package/dist/actors/computer-use/loop/provider-call.js +12 -12
- package/dist/actors/computer-use/loop/session.d.ts +4 -4
- package/dist/actors/computer-use/loop/session.js +4 -4
- package/dist/actors/computer-use/loop/spend.d.ts +1 -1
- package/dist/actors/computer-use/loop/spend.js +1 -1
- package/dist/actors/computer-use/loop/trace.js +11 -9
- package/dist/actors/computer-use/loop/types.d.ts +47 -45
- package/dist/actors/computer-use/loop/usage.d.ts +2 -2
- package/dist/actors/computer-use/loop/usage.js +2 -2
- package/dist/actors/computer-use/loop.d.ts +6 -1
- package/dist/actors/computer-use/loop.js +26 -4
- package/dist/actors/computer-use/openai-context.d.ts +37 -0
- package/dist/actors/computer-use/openai-context.js +214 -0
- package/dist/actors/computer-use/openai-provider.d.ts +13 -10
- package/dist/actors/computer-use/openai-provider.js +52 -41
- package/dist/actors/computer-use/openai-requests.d.ts +36 -10
- package/dist/actors/computer-use/openai-requests.js +82 -15
- package/dist/actors/computer-use/openai-wire.d.ts +19 -7
- package/dist/actors/computer-use/openai-wire.js +36 -34
- package/dist/actors/computer-use/provider-error.d.ts +4 -4
- package/dist/actors/computer-use/provider-error.js +6 -6
- package/dist/actors/contract.d.ts +82 -47
- package/dist/actors/contract.js +30 -24
- package/dist/actors/local-agent/claude-session.d.ts +1 -1
- package/dist/actors/local-agent/claude-session.js +13 -12
- package/dist/actors/local-agent/cli.d.ts +9 -7
- package/dist/actors/local-agent/cli.js +24 -28
- package/dist/actors/local-agent/readiness.d.ts +2 -2
- package/dist/actors/local-agent/readiness.js +4 -4
- package/dist/actors/reasoning-effort.js +8 -8
- package/dist/actors/registry.d.ts +12 -12
- package/dist/actors/registry.js +21 -20
- package/dist/actors/scripted-browser/actor.d.ts +10 -10
- package/dist/actors/scripted-browser/actor.js +17 -17
- package/dist/actors/scripted-browser/browser-command.js +1 -1
- package/dist/actors/scripted-browser/steps.js +1 -1
- package/dist/actors/scripted-browser/types.d.ts +1 -1
- package/dist/actors/stop-conditions.d.ts +1 -1
- package/dist/analysis/automatic-completion.d.ts +7 -7
- package/dist/analysis/automatic-completion.js +1 -1
- package/dist/analysis/automatic-config.d.ts +6 -6
- package/dist/analysis/automatic-config.js +1 -1
- package/dist/analysis/codex-config.d.ts +6 -2
- package/dist/analysis/codex-config.js +22 -14
- package/dist/analysis/execute.d.ts +1 -1
- package/dist/analysis/execute.js +1 -1
- package/dist/analysis/service.js +5 -5
- package/dist/analysis/types.d.ts +6 -0
- package/dist/browser-control/client.js +16 -16
- package/dist/browser-control/dispatcher.js +4 -4
- package/dist/browser-control/protocol.js +14 -14
- package/dist/browser-control/recording-transfer.js +4 -4
- package/dist/browser-control/transport.js +7 -7
- package/dist/cli/commands/analysis-signals.d.ts +2 -2
- package/dist/cli/commands/analyze.js +19 -16
- package/dist/cli/commands/codex.js +9 -9
- package/dist/cli/commands/comms.js +48 -35
- package/dist/cli/commands/feedback.js +20 -21
- package/dist/cli/commands/migrate.d.ts +4 -0
- package/dist/cli/commands/migrate.js +65 -0
- package/dist/cli/commands/observe.d.ts +0 -1
- package/dist/cli/commands/observe.js +136 -112
- package/dist/cli/commands/run-command.d.ts +10 -0
- package/dist/cli/commands/run-command.js +85 -0
- package/dist/cli/commands/runs.js +136 -139
- package/dist/cli/commands/setup.d.ts +2 -2
- package/dist/cli/commands/setup.js +27 -24
- package/dist/cli/commands/study-format.d.ts +9 -0
- package/dist/cli/commands/{lab-format.js → study-format.js} +39 -27
- package/dist/cli/commands/{lab-route-computer-use.d.ts → study-route-computer-use.d.ts} +6 -6
- package/dist/cli/commands/{lab-route-computer-use.js → study-route-computer-use.js} +17 -18
- package/dist/cli/commands/{lab-route-open.d.ts → study-route-open.d.ts} +4 -4
- package/dist/cli/commands/{lab-route-open.js → study-route-open.js} +2 -2
- package/dist/cli/commands/{lab-route-preview.d.ts → study-route-preview.d.ts} +7 -7
- package/dist/cli/commands/{lab-route-preview.js → study-route-preview.js} +7 -6
- package/dist/cli/commands/study-route-run.d.ts +19 -0
- package/dist/cli/commands/{lab-route-run.js → study-route-run.js} +6 -6
- package/dist/cli/commands/study-route-scripted.d.ts +17 -0
- package/dist/cli/commands/{lab-route-scripted.js → study-route-scripted.js} +4 -4
- package/dist/cli/commands/{lab-route-shared-world.d.ts → study-route-shared-world.d.ts} +5 -5
- package/dist/cli/commands/{lab-route-shared-world.js → study-route-shared-world.js} +10 -10
- package/dist/cli/commands/study-route-terminal.d.ts +17 -0
- package/dist/cli/commands/{lab-route-terminal.js → study-route-terminal.js} +4 -4
- package/dist/cli/commands/study-run.d.ts +9 -0
- package/dist/cli/commands/{lab-run.js → study-run.js} +44 -45
- package/dist/cli/commands/{lab-scorer.d.ts → study-scorer.d.ts} +7 -7
- package/dist/cli/commands/{lab-scorer.js → study-scorer.js} +2 -2
- package/dist/cli/commands/study.d.ts +3 -0
- package/dist/cli/commands/study.js +146 -0
- package/dist/cli/commands/tui.d.ts +4 -4
- package/dist/cli/commands/tui.js +31 -31
- package/dist/cli/commands/watch.js +71 -121
- package/dist/cli/deprecations.d.ts +6 -0
- package/dist/cli/deprecations.js +18 -0
- package/dist/cli/doctor.d.ts +18 -8
- package/dist/cli/doctor.js +114 -59
- package/dist/cli/first-run-path.d.ts +15 -13
- package/dist/cli/first-run-path.js +35 -33
- package/dist/cli/io.d.ts +44 -9
- package/dist/cli/io.js +98 -23
- package/dist/cli/observer-follow.d.ts +3 -3
- package/dist/cli/observer-follow.js +6 -7
- package/dist/cli/orientation.d.ts +10 -7
- package/dist/cli/orientation.js +89 -47
- package/dist/cli/product-sentence.d.ts +6 -0
- package/dist/cli/product-sentence.js +6 -0
- package/dist/cli/program.d.ts +6 -1
- package/dist/cli/program.js +111 -43
- package/dist/cli/telemetry.d.ts +16 -16
- package/dist/cli/telemetry.js +46 -44
- package/dist/cli.js +1 -1
- package/dist/comms/capture-surface.d.ts +13 -12
- package/dist/comms/capture-surface.js +23 -22
- package/dist/comms/catch-host.d.ts +7 -7
- package/dist/comms/catch-host.js +17 -17
- package/dist/comms/connections.d.ts +1 -1
- package/dist/comms/connections.js +2 -2
- package/dist/comms/email-catch.d.ts +3 -3
- package/dist/comms/email-catch.js +8 -8
- package/dist/comms/external-evidence.d.ts +5 -5
- package/dist/comms/external-evidence.js +5 -4
- package/dist/comms/extract.js +2 -2
- package/dist/comms/fake-inbox.d.ts +3 -3
- package/dist/comms/fake-inbox.js +3 -3
- package/dist/comms/receiving-render.d.ts +1 -1
- package/dist/comms/receiving-render.js +2 -2
- package/dist/comms/receiving-runtime.d.ts +4 -4
- package/dist/comms/sandbox-catch-script.d.ts +6 -6
- package/dist/comms/sandbox-catch-script.js +6 -6
- package/dist/comms/sandbox-catch.d.ts +38 -39
- package/dist/comms/sandbox-catch.js +36 -37
- package/dist/comms/setup.d.ts +2 -2
- package/dist/comms/setup.js +56 -25
- package/dist/comms/thread-evidence.d.ts +1 -1
- package/dist/comms/thread-evidence.js +6 -6
- package/dist/comms/types.d.ts +6 -6
- package/dist/comms/types.js +3 -3
- package/dist/evidence/frame-signature.d.ts +1 -1
- package/dist/evidence/frame-signature.js +4 -4
- package/dist/evidence/redaction.d.ts +3 -3
- package/dist/evidence/redaction.js +4 -4
- package/dist/feedback/draft.js +38 -80
- package/dist/feedback/export-bundle.js +1 -1
- package/dist/feedback/export.d.ts +5 -1
- package/dist/feedback/export.js +10 -10
- package/dist/feedback/feedback.d.ts +2 -2
- package/dist/feedback/feedback.js +21 -3
- package/dist/feedback/proof.d.ts +1 -1
- package/dist/feedback/proof.js +2 -2
- package/dist/guest/bootstrap.js +6 -6
- package/dist/guest/browser-tools.js +8 -8
- package/dist/guest/chromium-text.js +19 -19
- package/dist/guest/desktop-executor.d.ts +2 -2
- package/dist/guest/desktop-executor.js +20 -20
- package/dist/guest/desktop-keys.js +4 -4
- package/dist/guest/desktop-media.d.ts +2 -2
- package/dist/guest/desktop-media.js +16 -16
- package/dist/guest/desktop-native.d.ts +1 -1
- package/dist/guest/desktop-native.js +10 -10
- package/dist/guest/runtime-desktop.js +2 -2
- package/dist/guest/runtime-processes.d.ts +2 -2
- package/dist/guest/runtime-processes.js +2 -2
- package/dist/guest/runtime.js +6 -6
- package/dist/index.d.ts +14 -14
- package/dist/index.js +8 -8
- package/dist/keys/key-resolution.d.ts +5 -5
- package/dist/keys/key-resolution.js +23 -23
- package/dist/observer/artifact.d.ts +2 -2
- package/dist/observer/artifact.js +7 -7
- package/dist/observer/data.d.ts +6 -6
- package/dist/observer/data.js +5 -4
- package/dist/observer/exposure.d.ts +1 -1
- package/dist/observer/exposure.js +11 -11
- package/dist/observer/http.js +1 -1
- package/dist/observer/library.d.ts +3 -1
- package/dist/observer/library.js +8 -3
- package/dist/observer/listen.js +5 -4
- package/dist/observer/render.d.ts +1 -1
- package/dist/observer/render.js +17 -6
- package/dist/observer/run-routes.d.ts +4 -3
- package/dist/observer/run-routes.js +38 -13
- package/dist/observer/serve.d.ts +1 -1
- package/dist/observer/serve.js +7 -3
- package/dist/observer/tunnel.js +1 -1
- package/dist/observer-app.html +4 -4
- package/dist/routes/computer-use/bundle-parts.d.ts +9 -9
- package/dist/routes/computer-use/bundle-parts.js +18 -17
- package/dist/routes/computer-use/bundle.d.ts +2 -2
- package/dist/routes/computer-use/bundle.js +10 -9
- package/dist/routes/computer-use/e2b-desktop/comms.d.ts +6 -6
- package/dist/routes/computer-use/e2b-desktop/comms.js +18 -17
- package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -4
- package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +5 -4
- package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -10
- package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +1 -1
- package/dist/routes/computer-use/e2b-desktop/prepare.js +10 -10
- package/dist/routes/computer-use/e2b-desktop/start.js +7 -7
- package/dist/routes/computer-use/e2b-desktop/state.d.ts +2 -2
- package/dist/routes/computer-use/e2b-desktop/teardown.js +2 -2
- package/dist/routes/computer-use/external-comms.d.ts +4 -4
- package/dist/routes/computer-use/external-comms.js +1 -1
- package/dist/routes/computer-use/fanout-bundle.js +27 -26
- package/dist/routes/computer-use/fanout-records.js +12 -13
- package/dist/routes/computer-use/live-flush.d.ts +4 -4
- package/dist/routes/computer-use/live-flush.js +6 -6
- package/dist/routes/computer-use/live-phase.d.ts +5 -4
- package/dist/routes/computer-use/live-phase.js +8 -8
- package/dist/routes/computer-use/local-tree-pack.d.ts +5 -5
- package/dist/routes/computer-use/local-tree-pack.js +1 -1
- package/dist/routes/computer-use/local-vm.d.ts +8 -8
- package/dist/routes/computer-use/local-vm.js +3 -3
- package/dist/routes/computer-use/participant-desktop.d.ts +7 -7
- package/dist/routes/computer-use/participant-desktop.js +5 -5
- package/dist/routes/computer-use/participant-execution.d.ts +4 -4
- package/dist/routes/computer-use/participant-execution.js +10 -10
- package/dist/routes/computer-use/participant-model.d.ts +6 -0
- package/dist/routes/computer-use/participant-model.js +33 -15
- package/dist/routes/computer-use/participant-prompt.d.ts +15 -15
- package/dist/routes/computer-use/participant-prompt.js +9 -9
- package/dist/routes/computer-use/participant-runs.d.ts +16 -16
- package/dist/routes/computer-use/participant-runs.js +26 -25
- package/dist/routes/computer-use/plan.d.ts +12 -12
- package/dist/routes/computer-use/plan.js +42 -42
- package/dist/routes/computer-use/preflight.d.ts +5 -5
- package/dist/routes/computer-use/preflight.js +25 -25
- package/dist/routes/computer-use/rerun-selection.d.ts +1 -1
- package/dist/routes/computer-use/rerun-selection.js +4 -3
- package/dist/routes/computer-use/result.d.ts +4 -4
- package/dist/routes/computer-use/result.js +22 -21
- package/dist/routes/computer-use/route.d.ts +12 -11
- package/dist/routes/computer-use/route.js +31 -30
- package/dist/routes/computer-use/self-report.d.ts +7 -7
- package/dist/routes/computer-use/self-report.js +32 -32
- package/dist/routes/computer-use/setup.d.ts +23 -23
- package/dist/routes/computer-use/setup.js +28 -29
- package/dist/routes/computer-use/single-bundle.d.ts +11 -11
- package/dist/routes/computer-use/single-bundle.js +40 -41
- package/dist/routes/computer-use/subject-projection.d.ts +4 -4
- package/dist/routes/computer-use/subject-projection.js +6 -6
- package/dist/routes/computer-use/types.d.ts +71 -73
- package/dist/routes/computer-use/types.js +12 -12
- package/dist/routes/preview.d.ts +13 -7
- package/dist/routes/preview.js +11 -7
- package/dist/routes/scripted/bundle.d.ts +7 -7
- package/dist/routes/scripted/bundle.js +20 -18
- package/dist/routes/scripted/plan.d.ts +8 -8
- package/dist/routes/scripted/plan.js +13 -13
- package/dist/routes/scripted/result.d.ts +3 -3
- package/dist/routes/scripted/result.js +13 -13
- package/dist/routes/scripted/route.d.ts +9 -9
- package/dist/routes/scripted/route.js +15 -16
- package/dist/routes/scripted/scenario.d.ts +1 -1
- package/dist/routes/scripted/scenario.js +5 -5
- package/dist/routes/scripted/setup.d.ts +6 -6
- package/dist/routes/scripted/setup.js +8 -8
- package/dist/routes/scripted/subject.d.ts +5 -5
- package/dist/routes/scripted/subject.js +4 -4
- package/dist/routes/scripted/surface-records.d.ts +1 -1
- package/dist/routes/scripted/surface-records.js +4 -4
- package/dist/routes/scripted/surface-sessions.d.ts +3 -3
- package/dist/routes/scripted/types.d.ts +18 -20
- package/dist/routes/scripted/types.js +2 -2
- package/dist/routes/shared-world/bundle-records.js +11 -10
- package/dist/routes/shared-world/bundle.d.ts +5 -5
- package/dist/routes/shared-world/bundle.js +46 -43
- package/dist/routes/shared-world/checkpoints.d.ts +4 -4
- package/dist/routes/shared-world/checkpoints.js +2 -2
- package/dist/routes/shared-world/comms.d.ts +14 -14
- package/dist/routes/shared-world/comms.js +19 -18
- package/dist/routes/shared-world/external-public.d.ts +4 -3
- package/dist/routes/shared-world/external-public.js +16 -12
- package/dist/routes/shared-world/handoff.d.ts +7 -6
- package/dist/routes/shared-world/handoff.js +65 -57
- package/dist/routes/shared-world/lobby-code.d.ts +2 -2
- package/dist/routes/shared-world/lobby-code.js +9 -9
- package/dist/routes/shared-world/participant-specs.d.ts +10 -9
- package/dist/routes/shared-world/participant-specs.js +38 -31
- package/dist/routes/shared-world/plan.d.ts +9 -9
- package/dist/routes/shared-world/plan.js +16 -16
- package/dist/routes/shared-world/provenance.d.ts +2 -2
- package/dist/routes/shared-world/provenance.js +2 -2
- package/dist/routes/shared-world/provisioned.d.ts +8 -8
- package/dist/routes/shared-world/provisioned.js +18 -17
- package/dist/routes/shared-world/result.d.ts +6 -6
- package/dist/routes/shared-world/result.js +29 -29
- package/dist/routes/shared-world/route.d.ts +11 -11
- package/dist/routes/shared-world/route.js +40 -39
- package/dist/routes/shared-world/setup.d.ts +16 -16
- package/dist/routes/shared-world/setup.js +23 -23
- package/dist/routes/shared-world/types.d.ts +55 -53
- package/dist/routes/shared-world/types.js +4 -4
- package/dist/routes/terminal/adapter.d.ts +5 -5
- package/dist/routes/terminal/adapter.js +9 -9
- package/dist/routes/terminal/bundle.d.ts +13 -13
- package/dist/routes/terminal/bundle.js +28 -27
- package/dist/routes/terminal/credentials.d.ts +8 -8
- package/dist/routes/terminal/credentials.js +12 -12
- package/dist/routes/terminal/dry-run.d.ts +4 -4
- package/dist/routes/terminal/dry-run.js +12 -12
- package/dist/routes/terminal/encoding.js +2 -2
- package/dist/routes/terminal/ledger.d.ts +5 -5
- package/dist/routes/terminal/ledger.js +14 -14
- package/dist/routes/terminal/lifetime.d.ts +1 -1
- package/dist/routes/terminal/lifetime.js +1 -1
- package/dist/routes/terminal/live-finish.d.ts +2 -2
- package/dist/routes/terminal/live-finish.js +10 -10
- package/dist/routes/terminal/live-sandbox.d.ts +2 -2
- package/dist/routes/terminal/live-sandbox.js +15 -15
- package/dist/routes/terminal/participant-text.js +2 -1
- package/dist/routes/terminal/persona.d.ts +2 -2
- package/dist/routes/terminal/persona.js +3 -3
- package/dist/routes/terminal/plan.d.ts +7 -7
- package/dist/routes/terminal/plan.js +15 -15
- package/dist/routes/terminal/recorder.js +2 -2
- package/dist/routes/terminal/result.d.ts +5 -5
- package/dist/routes/terminal/result.js +14 -14
- package/dist/routes/terminal/route.d.ts +9 -9
- package/dist/routes/terminal/route.js +19 -20
- package/dist/routes/terminal/sandbox.d.ts +1 -1
- package/dist/routes/terminal/sandbox.js +1 -1
- package/dist/routes/terminal/session.d.ts +5 -5
- package/dist/routes/terminal/session.js +14 -13
- package/dist/routes/terminal/token-usage.d.ts +1 -1
- package/dist/routes/terminal/token-usage.js +7 -8
- package/dist/routes/terminal/trace.d.ts +4 -4
- package/dist/routes/terminal/trace.js +2 -2
- package/dist/routes/terminal/types.d.ts +48 -50
- package/dist/routes/terminal/types.js +5 -5
- package/dist/run/bundle-shape.js +3 -3
- package/dist/run/bundle.d.ts +49 -49
- package/dist/run/bundle.js +3 -2
- package/dist/run/cost-summary.js +3 -3
- package/dist/run/costs.d.ts +23 -0
- package/dist/run/costs.js +79 -44
- package/dist/run/detail.d.ts +6 -6
- package/dist/run/detail.js +4 -4
- package/dist/run/dry-run-inputs.d.ts +1 -1
- package/dist/run/dry-run-inputs.js +2 -2
- package/dist/run/dry-run.js +20 -20
- package/dist/run/judge.d.ts +19 -13
- package/dist/run/judge.js +18 -8
- package/dist/run/outcomes.d.ts +1 -1
- package/dist/run/participant.d.ts +1 -1
- package/dist/run/paths.d.ts +1 -1
- package/dist/run/paths.js +1 -1
- package/dist/run/preflight-receipts.js +1 -1
- package/dist/run/pricing.d.ts +11 -6
- package/dist/run/pricing.js +22 -15
- package/dist/run/projection.d.ts +46 -46
- package/dist/run/projection.js +57 -57
- package/dist/run/reclaim.d.ts +1 -1
- package/dist/run/reclaim.js +3 -3
- package/dist/run/results.d.ts +5 -5
- package/dist/run/run-cost.d.ts +53 -0
- package/dist/run/run-cost.js +105 -0
- package/dist/run/run-index-analysis.d.ts +15 -0
- package/dist/run/run-index-analysis.js +77 -0
- package/dist/run/run-index.d.ts +19 -7
- package/dist/run/run-index.js +77 -64
- package/dist/run/run-not-found.d.ts +5 -0
- package/dist/run/run-not-found.js +15 -0
- package/dist/run/run.d.ts +6 -5
- package/dist/run/run.js +3 -3
- package/dist/run/sandbox-receipts.d.ts +2 -2
- package/dist/run/sandbox-receipts.js +3 -3
- package/dist/run/shared-world-evidence.d.ts +37 -36
- package/dist/run/shared-world-evidence.js +4 -4
- package/dist/run/shared-world-shape.d.ts +1 -1
- package/dist/run/shared-world-shape.js +7 -7
- package/dist/run/stats.d.ts +14 -8
- package/dist/run/stats.js +121 -63
- package/dist/run/status.d.ts +16 -29
- package/dist/run/status.js +10 -29
- package/dist/run/stored-runs.js +2 -1
- package/dist/run/stream-shape.js +1 -1
- package/dist/run/streams.d.ts +7 -7
- package/dist/run/study-provenance.d.ts +49 -0
- package/dist/run/study-provenance.js +66 -0
- package/dist/run/study-result.d.ts +11 -0
- package/dist/run/study-result.js +9 -0
- package/dist/run/text.d.ts +2 -0
- package/dist/run/text.js +4 -0
- package/dist/run-study.d.ts +112 -0
- package/dist/{run-lab.js → run-study.js} +52 -47
- package/dist/{lab → study}/adapter-extension.d.ts +15 -27
- package/dist/{lab → study}/adapter-extension.js +10 -45
- package/dist/{lab → study}/adapter-scorer-loader.d.ts +17 -17
- package/dist/{lab → study}/adapter-scorer-loader.js +34 -34
- package/dist/study/composition-rules.d.ts +7 -0
- package/dist/{lab → study}/composition-rules.js +53 -53
- package/dist/study/config.d.ts +15 -0
- package/dist/{lab → study}/config.js +98 -41
- package/dist/study/convert.d.ts +33 -0
- package/dist/study/convert.js +432 -0
- package/dist/{lab → study}/device-presets.d.ts +10 -10
- package/dist/{lab → study}/device-presets.js +11 -11
- package/dist/study/discover.d.ts +75 -0
- package/dist/{lab → study}/discover.js +134 -95
- package/dist/{lab → study}/doctor.d.ts +19 -12
- package/dist/{lab → study}/doctor.js +58 -20
- package/dist/study/field-paths.d.ts +12 -0
- package/dist/study/field-paths.js +126 -0
- package/dist/study/files.d.ts +26 -0
- package/dist/study/files.js +44 -0
- package/dist/{lab → study}/init-templates.d.ts +2 -2
- package/dist/study/init-templates.js +421 -0
- package/dist/{lab → study}/init.d.ts +2 -2
- package/dist/{lab → study}/init.js +46 -11
- package/dist/study/keys.d.ts +4 -0
- package/dist/{lab → study}/keys.js +46 -12
- package/dist/study/migrate.d.ts +48 -0
- package/dist/study/migrate.js +408 -0
- package/dist/study/owned-files.d.ts +38 -0
- package/dist/study/owned-files.js +182 -0
- package/dist/{lab → study}/parse/actors.d.ts +5 -5
- package/dist/{lab → study}/parse/actors.js +14 -14
- package/dist/study/parse/comms.d.ts +12 -0
- package/dist/{lab → study}/parse/comms.js +5 -5
- package/dist/study/parse/execution.d.ts +19 -0
- package/dist/{lab → study}/parse/execution.js +13 -13
- package/dist/study/parse/study-v3.d.ts +21 -0
- package/dist/study/parse/study-v3.js +259 -0
- package/dist/study/parse/subject-state.d.ts +30 -0
- package/dist/{lab → study}/parse/subject-state.js +19 -19
- package/dist/{lab → study}/parse/subject.d.ts +3 -3
- package/dist/{lab → study}/parse/subject.js +34 -34
- package/dist/{lab → study}/parse/values.d.ts +3 -3
- package/dist/{lab → study}/parse/values.js +3 -3
- package/dist/{lab → study}/persona-resolve.d.ts +8 -8
- package/dist/{lab → study}/persona-resolve.js +6 -6
- package/dist/{lab → study}/plan-base.d.ts +7 -7
- package/dist/{lab → study}/plan-base.js +1 -1
- package/dist/{lab → study}/plan-participants.d.ts +12 -12
- package/dist/{lab → study}/plan-participants.js +4 -4
- package/dist/{lab → study}/plan-types.d.ts +31 -31
- package/dist/{lab → study}/plan-types.js +2 -2
- package/dist/study/plan.d.ts +13 -0
- package/dist/study/plan.js +101 -0
- package/dist/study/preflight-probes.d.ts +3 -0
- package/dist/{lab → study}/preflight-probes.js +20 -19
- package/dist/study/preflight-result.d.ts +8 -0
- package/dist/{lab → study}/preflight-result.js +5 -4
- package/dist/study/preflight.d.ts +94 -0
- package/dist/{lab → study}/preflight.js +12 -12
- package/dist/{lab → study}/requirements.d.ts +3 -3
- package/dist/{lab → study}/requirements.js +1 -1
- package/dist/study/route-inputs.d.ts +28 -0
- package/dist/{lab → study}/route-inputs.js +1 -1
- package/dist/{lab → study}/routing.d.ts +39 -33
- package/dist/{lab → study}/routing.js +60 -29
- package/dist/{lab/run-lab-events.d.ts → study/run-study-events.d.ts} +10 -13
- package/dist/{lab/run-lab-events.js → study/run-study-events.js} +3 -9
- package/dist/{lab/run-lab-homes.d.ts → study/run-study-homes.d.ts} +21 -18
- package/dist/{lab/run-lab-homes.js → study/run-study-homes.js} +1 -1
- package/dist/study/run-study-options.d.ts +28 -0
- package/dist/{lab/run-lab-options.js → study/run-study-options.js} +29 -25
- package/dist/{lab/lab-deps.d.ts → study/study-deps.d.ts} +8 -13
- package/dist/{lab/lab-deps.js → study/study-deps.js} +2 -2
- package/dist/{lab → study}/summary.d.ts +16 -15
- package/dist/{lab → study}/summary.js +21 -21
- package/dist/{lab → study}/tasks.d.ts +18 -18
- package/dist/{lab → study}/tasks.js +14 -14
- package/dist/{lab → study}/types.d.ts +202 -196
- package/dist/{lab → study}/types.js +3 -1
- package/dist/{lab → study}/validation.d.ts +27 -27
- package/dist/{lab → study}/validation.js +53 -51
- package/dist/study/warnings.d.ts +12 -0
- package/dist/{lab → study}/warnings.js +62 -34
- package/dist/subject/clone.d.ts +7 -7
- package/dist/subject/clone.js +2 -2
- package/dist/subject/desktop-cli.d.ts +1 -1
- package/dist/subject/desktop-cli.js +2 -2
- package/dist/subject/local-tree-archive.d.ts +2 -2
- package/dist/subject/local-tree-archive.js +4 -4
- package/dist/subject/local-tree.d.ts +4 -4
- package/dist/subject/node-bootstrap.js +1 -1
- package/dist/subject/runtime.js +9 -10
- package/dist/subject/serve.d.ts +5 -5
- package/dist/subject/serve.js +3 -3
- package/dist/subject/state.d.ts +4 -4
- package/dist/subject/state.js +1 -1
- package/dist/subject/steps.d.ts +6 -6
- package/dist/subject/steps.js +5 -5
- package/dist/substrates/command-failure.d.ts +3 -3
- package/dist/substrates/command-failure.js +3 -3
- package/dist/substrates/desktop-session.js +2 -2
- package/dist/substrates/detached.d.ts +6 -6
- package/dist/substrates/detached.js +7 -7
- package/dist/substrates/e2b/cdp-probe.d.ts +9 -9
- package/dist/substrates/e2b/cdp-probe.js +14 -17
- package/dist/substrates/e2b/desktop-browser.d.ts +6 -6
- package/dist/substrates/e2b/desktop-browser.js +9 -9
- package/dist/substrates/e2b/desktop-cdp.d.ts +10 -10
- package/dist/substrates/e2b/desktop-cdp.js +9 -9
- package/dist/substrates/e2b/desktop-executor.d.ts +2 -2
- package/dist/substrates/e2b/desktop-executor.js +13 -13
- package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
- package/dist/substrates/e2b/desktop-geometry.js +8 -8
- package/dist/substrates/e2b/desktop-media.d.ts +4 -4
- package/dist/substrates/e2b/desktop-media.js +1 -1
- package/dist/substrates/e2b/desktop-screenshot-cleanup.d.ts +3 -3
- package/dist/substrates/e2b/desktop-screenshot-cleanup.js +3 -3
- package/dist/substrates/e2b/lifetime.js +1 -1
- package/dist/substrates/e2b/sandbox.d.ts +4 -4
- package/dist/substrates/e2b/sandbox.js +2 -2
- package/dist/substrates/e2b/sdk.d.ts +12 -12
- package/dist/substrates/e2b/sdk.js +7 -7
- package/dist/substrates/local/runtime-config.d.ts +4 -4
- package/dist/substrates/local/runtime-config.js +4 -4
- package/dist/tui/actions.d.ts +1 -1
- package/dist/tui/actions.js +13 -13
- package/dist/tui/contract.d.ts +23 -23
- package/dist/tui/contract.js +11 -11
- package/dist/tui/launch.d.ts +7 -7
- package/dist/tui/launch.js +36 -31
- package/dist/tui/project.js +5 -5
- package/dist/tui-app.js +139 -139
- package/dist/verify/actor.d.ts +8 -8
- package/dist/verify/actor.js +36 -36
- package/dist/verify/artifacts.d.ts +2 -2
- package/dist/verify/artifacts.js +3 -3
- package/dist/verify/context-growth.d.ts +9 -0
- package/dist/verify/context-growth.js +62 -0
- package/dist/verify/costs.d.ts +6 -6
- package/dist/verify/costs.js +8 -8
- package/dist/verify/shared-world-concurrent.d.ts +2 -2
- package/dist/verify/shared-world-concurrent.js +79 -76
- package/dist/verify/shared-world.d.ts +2 -2
- package/dist/verify/shared-world.js +16 -16
- package/dist/verify/subject.d.ts +5 -5
- package/dist/verify/subject.js +19 -19
- package/dist/verify/verify.js +94 -44
- package/docs/README.md +25 -32
- package/docs/architecture/actor-contract.md +60 -91
- package/docs/architecture/browser-control.md +1 -1
- package/docs/architecture/desktop-sessions.md +7 -7
- package/docs/architecture/external-public-shared-world.md +68 -65
- package/docs/architecture/github-feedback-loop.md +2 -5
- package/docs/architecture/guest-desktop.md +2 -2
- package/docs/architecture/observer.md +34 -34
- package/docs/architecture/restricted-codex-analysis.md +34 -28
- package/docs/architecture/serve.md +34 -37
- package/docs/architecture/state-driven-executor.md +55 -58
- package/docs/architecture/task-protocol-support.md +6 -6
- package/docs/architecture/terminal-product-route.md +70 -110
- package/docs/contracts/README.md +24 -0
- package/docs/contracts/adapter-admission.md +3 -3
- package/docs/contracts/adapter-fixtures.md +4 -7
- package/docs/contracts/core.md +1 -3
- package/docs/contracts/errors.md +62 -0
- package/docs/contracts/feedback.md +8 -9
- package/docs/contracts/policy.md +7 -12
- package/docs/contracts/run-bundle.md +42 -44
- package/docs/contracts/schemas.md +446 -368
- package/docs/contracts/study-analysis.md +7 -4
- package/docs/contracts/study-costs.md +28 -9
- package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +1 -1
- package/docs/decisions/0002-observer-is-one-self-contained-file.md +2 -2
- package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +3 -3
- package/docs/principles/actor-fidelity.md +4 -4
- package/docs/principles/engineering.md +3 -3
- package/docs/principles/invariants-and-defaults.md +38 -38
- package/docs/principles/self-driving-harness.md +1 -5
- package/docs/principles/three-roles.md +1 -1
- package/docs/product/automatic-analysis.md +7 -7
- package/examples/README.md +1 -1
- package/examples/participant/README.md +5 -5
- package/examples/participant/run.mjs +11 -10
- package/examples/scorer/README.md +3 -3
- package/examples/scorer/run.mjs +11 -12
- package/examples/scorer/scorer.mjs +3 -3
- package/package.json +16 -17
- package/skills/humanish/SKILL.md +64 -60
- package/skills/humanish/agents/openai.yaml +1 -1
- package/AGENTS.md +0 -99
- package/CONTRIBUTING.md +0 -148
- package/dist/actors/codex/qualified-versions.d.ts +0 -72
- package/dist/actors/codex/qualified-versions.js +0 -132
- package/dist/cli/commands/lab-format.d.ts +0 -8
- package/dist/cli/commands/lab-route-run.d.ts +0 -19
- package/dist/cli/commands/lab-route-scripted.d.ts +0 -17
- package/dist/cli/commands/lab-route-terminal.d.ts +0 -17
- package/dist/cli/commands/lab-run.d.ts +0 -9
- package/dist/cli/commands/lab.d.ts +0 -3
- package/dist/cli/commands/lab.js +0 -199
- package/dist/cli/renamed-options.d.ts +0 -11
- package/dist/cli/renamed-options.js +0 -18
- package/dist/lab/composition-rules.d.ts +0 -7
- package/dist/lab/config.d.ts +0 -7
- package/dist/lab/discover.d.ts +0 -65
- package/dist/lab/init-templates.js +0 -416
- package/dist/lab/keys.d.ts +0 -2
- package/dist/lab/parse/comms.d.ts +0 -12
- package/dist/lab/parse/execution.d.ts +0 -19
- package/dist/lab/parse/subject-state.d.ts +0 -30
- package/dist/lab/plan.d.ts +0 -19
- package/dist/lab/plan.js +0 -132
- package/dist/lab/preflight-probes.d.ts +0 -3
- package/dist/lab/preflight-result.d.ts +0 -7
- package/dist/lab/preflight.d.ts +0 -92
- package/dist/lab/route-inputs.d.ts +0 -28
- package/dist/lab/run-lab-options.d.ts +0 -28
- package/dist/lab/warnings.d.ts +0 -2
- package/dist/observer/static.d.ts +0 -39
- package/dist/observer/static.js +0 -369
- package/dist/run-lab.d.ts +0 -112
- package/docs/architecture/comms-inbox.md +0 -122
- package/docs/architecture/desktop-recording.md +0 -80
- package/docs/architecture/local-browser-runtime.md +0 -178
- package/docs/architecture/observer-review.md +0 -144
- package/docs/architecture/participant-media.md +0 -90
- package/docs/architecture/project-layout.md +0 -62
- package/docs/architecture/real-email-receiving.md +0 -136
- package/docs/assets/humanish-drawdb-hero.png +0 -0
- package/docs/assets/humanish-observer-hero.png +0 -0
- package/docs/goals/current.md +0 -207
- package/docs/product/lobby-trivia-3player-external-public.md +0 -77
- package/docs/product/open-source-install-experience.md +0 -240
- package/docs/ramp/README.md +0 -295
- package/docs/release/open-source-readiness.md +0 -222
- package/docs/release/public-readiness-standard.md +0 -210
- package/docs/roadmap/world-class-open-source-v0.md +0 -311
- /package/dist/{lab → study}/init-paths.d.ts +0 -0
- /package/dist/{lab → study}/init-paths.js +0 -0
- /package/dist/{lab → study}/init-plan.d.ts +0 -0
- /package/dist/{lab → study}/init-plan.js +0 -0
- /package/dist/{lab → study}/participant-assignment.d.ts +0 -0
- /package/dist/{lab → study}/participant-assignment.js +0 -0
- /package/dist/{lab → study}/persona.d.ts +0 -0
- /package/dist/{lab → study}/persona.js +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StudyActor, StudyParticipantEntry, StudyParticipantFocus, StudyParseFailure } from "../types.js";
|
|
2
2
|
export declare const PARTICIPANT_ID_PATTERN: RegExp;
|
|
3
3
|
export declare const PARTICIPANT_ID_MAX_CHARS = 40;
|
|
4
4
|
/** The participant entries an actor declares, after roster groups expand into them. The manifest
|
|
5
5
|
* spells them `actors[].lanes`. Undefined when the actor declares none. */
|
|
6
|
-
export declare function rosterOf<Entry =
|
|
6
|
+
export declare function rosterOf<Entry = StudyParticipantEntry>(actor: {
|
|
7
7
|
readonly lanes?: readonly Entry[];
|
|
8
8
|
} | undefined): readonly Entry[] | undefined;
|
|
9
9
|
/** An actor's per-participant focus on the app-url route. The manifest spells it `laneFocus`. */
|
|
10
|
-
export declare function focusOf(actor: Pick<
|
|
10
|
+
export declare function focusOf(actor: Pick<StudyActor, "laneFocus"> | undefined): StudyParticipantFocus | undefined;
|
|
11
11
|
export declare function parseActors(raw: unknown): {
|
|
12
12
|
ok: true;
|
|
13
|
-
value:
|
|
14
|
-
} |
|
|
13
|
+
value: StudyActor[];
|
|
14
|
+
} | StudyParseFailure;
|
|
@@ -6,7 +6,7 @@ import { registeredComputerUseActors, registeredScriptedBrowserActors, registere
|
|
|
6
6
|
import { invalid, posInt, str } from "./values.js";
|
|
7
7
|
import { isRecord } from "../../run/type-guards.js";
|
|
8
8
|
// A participant id interpolates into its evidence paths (screenshots/<id>/, actors/<id>.json), so
|
|
9
|
-
// it must be a public-safe path token, same shape as a
|
|
9
|
+
// it must be a public-safe path token, same shape as a study id.
|
|
10
10
|
export const PARTICIPANT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
|
|
11
11
|
export const PARTICIPANT_ID_MAX_CHARS = 40;
|
|
12
12
|
const METADATA_MAX_CHARS = 80;
|
|
@@ -19,11 +19,11 @@ export function rosterOf(actor) {
|
|
|
19
19
|
export function focusOf(actor) {
|
|
20
20
|
return actor?.laneFocus;
|
|
21
21
|
}
|
|
22
|
-
// Actor ids humanish no longer registers. Rejecting them at parse keeps a
|
|
22
|
+
// Actor ids humanish no longer registers. Rejecting them at parse keeps a study that names one
|
|
23
23
|
// from running on a route that ignores actors[0].type, such as a this-repo dry run.
|
|
24
24
|
const REMOVED_ACTOR_TYPES = new Set(["pi-agent-core", "claude-agent-sdk"]);
|
|
25
|
-
// Only actors
|
|
26
|
-
// "code"
|
|
25
|
+
// Only actors of a run kind a route dispatches. codex-app-server is registered but declares only
|
|
26
|
+
// the "code" run kind, which no study route runs, so naming it would lead to the same dead end.
|
|
27
27
|
function routableActorTypes() {
|
|
28
28
|
return [
|
|
29
29
|
...registeredComputerUseActors(),
|
|
@@ -33,7 +33,7 @@ function routableActorTypes() {
|
|
|
33
33
|
}
|
|
34
34
|
export function parseActors(raw) {
|
|
35
35
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
36
|
-
return invalid("
|
|
36
|
+
return invalid("`actors` must be a non-empty array.");
|
|
37
37
|
}
|
|
38
38
|
// Multi-actor fan-out is not wired yet (only actors[0] is consumed). Fail closed rather than
|
|
39
39
|
// silently ignore actors[1..]; multi-actor support lands in a later slice.
|
|
@@ -50,17 +50,17 @@ export function parseActors(raw) {
|
|
|
50
50
|
return invalid(`actors[${index}].type is required.`);
|
|
51
51
|
}
|
|
52
52
|
if (REMOVED_ACTOR_TYPES.has(type)) {
|
|
53
|
-
return invalid(`actors[${index}].type "${type}" is no longer a humanish actor. Use an actor a
|
|
53
|
+
return invalid(`actors[${index}].type "${type}" is no longer a humanish actor. Use an actor a study route runs (one of: ${routableActorTypes().join(", ")}). To drive a study with a signed-in Claude Code, use type: local-agent with localAgent: claude.`);
|
|
54
54
|
}
|
|
55
55
|
const actor = { type };
|
|
56
56
|
const count = posInt(entry.count);
|
|
57
57
|
if (count !== undefined)
|
|
58
58
|
actor.count = count;
|
|
59
59
|
if (entry.lanes !== undefined && entry.roster !== undefined) {
|
|
60
|
-
return invalid(`actors[${index}].lanes
|
|
60
|
+
return invalid(`Set either actors[${index}].lanes or actors[${index}].roster, not both: list participants one by one in \`lanes\`, or in groups in \`roster\`.`);
|
|
61
61
|
}
|
|
62
62
|
if (entry.roster !== undefined && count !== undefined) {
|
|
63
|
-
return invalid(`actors[${index}].roster
|
|
63
|
+
return invalid(`Set either actors[${index}].roster or actors[${index}].count, not both: \`roster\` declares groups of distinct participants, and \`count\` declares identical ones.`);
|
|
64
64
|
}
|
|
65
65
|
if (entry.roster !== undefined && entry.laneFocus !== undefined) {
|
|
66
66
|
return invalid(`actors[${index}].roster and actors[${index}].laneFocus are mutually exclusive: a roster group's instruction is each participant's steer.`);
|
|
@@ -160,7 +160,7 @@ function parseRosterGroups(raw, actorIndex) {
|
|
|
160
160
|
return invalid(`actors[${actorIndex}].roster[${groupIndex}].id is required and must be a public-safe token matching ${PARTICIPANT_ID_PATTERN}.`);
|
|
161
161
|
}
|
|
162
162
|
if (!PARTICIPANT_ID_PATTERN.test(groupId) || groupId.length > PARTICIPANT_ID_MAX_CHARS - 3) {
|
|
163
|
-
return invalid(`actors[${actorIndex}].roster[${groupIndex}].id must
|
|
163
|
+
return invalid(`actors[${actorIndex}].roster[${groupIndex}].id must match ${PARTICIPANT_ID_PATTERN} and be at most ${PARTICIPANT_ID_MAX_CHARS - 3} characters, because each generated participant id adds a suffix like "-01"; "${groupId}" is not.`);
|
|
164
164
|
}
|
|
165
165
|
if (seenGroupIds.has(groupId)) {
|
|
166
166
|
return invalid(`actors[${actorIndex}].roster group ids must be unique (duplicate "${groupId}").`);
|
|
@@ -189,7 +189,7 @@ function parseRosterGroups(raw, actorIndex) {
|
|
|
189
189
|
* Participant ids (when declared) must be public-safe path tokens and unique; grouping metadata
|
|
190
190
|
* must be public-safe tokens; a device must be a known preset name. The
|
|
191
191
|
* route-scoped cross-validation (`lanes` XOR `count`/`laneFocus`, device XOR raw resolution, cap 16)
|
|
192
|
-
* runs in
|
|
192
|
+
* runs in parseStudy where the route is known.
|
|
193
193
|
*/
|
|
194
194
|
function parseParticipantEntries(raw, actorIndex) {
|
|
195
195
|
if (raw === undefined) {
|
|
@@ -272,7 +272,7 @@ function parseParticipantEntries(raw, actorIndex) {
|
|
|
272
272
|
const entryPath = str(entry.entry);
|
|
273
273
|
if (entryPath !== undefined)
|
|
274
274
|
parsedEntry.entry = entryPath;
|
|
275
|
-
// `host` marks the designated host
|
|
275
|
+
// `host` marks the designated host on the external-public shared-world route; the
|
|
276
276
|
// exactly-one-host check runs in externalPublicSharedWorldValidationReason (route context).
|
|
277
277
|
if (entry.host !== undefined) {
|
|
278
278
|
if (typeof entry.host !== "boolean") {
|
|
@@ -287,7 +287,7 @@ function parseParticipantEntries(raw, actorIndex) {
|
|
|
287
287
|
}
|
|
288
288
|
/**
|
|
289
289
|
* The researcher's protocol. Each task carries what the participant is asked to do and, optionally,
|
|
290
|
-
* the observation that proves it happened
|
|
290
|
+
* the observation that proves it happened, reusing `stopWhen`, so a criterion is exactly as
|
|
291
291
|
* expressive as a stop condition and an author who knows one knows the other.
|
|
292
292
|
*
|
|
293
293
|
* A task with no `success` is allowed on purpose: some things you ask a participant to do (think
|
|
@@ -314,7 +314,7 @@ function parseTasks(raw, field) {
|
|
|
314
314
|
seen.add(id);
|
|
315
315
|
const goal = str(entry.goal);
|
|
316
316
|
if (goal === undefined) {
|
|
317
|
-
return invalid(`\`${field}[${index}].goal\` is required
|
|
317
|
+
return invalid(`\`${field}[${index}].goal\` is required: what the participant is asked to do, in their own words.`);
|
|
318
318
|
}
|
|
319
319
|
const successResult = parseStopWhen(entry.success, `${field}[${index}].success`);
|
|
320
320
|
if (!successResult.ok)
|
|
@@ -327,7 +327,7 @@ function parseTasks(raw, field) {
|
|
|
327
327
|
}
|
|
328
328
|
return { ok: true, value: tasks };
|
|
329
329
|
}
|
|
330
|
-
/** The bounds a dwell window
|
|
330
|
+
/** The bounds a dwell window must sit inside: at least one frame, at most an hour. */
|
|
331
331
|
const DWELL_MIN_MS = 1_000;
|
|
332
332
|
const DWELL_MAX_MS = 3_600_000;
|
|
333
333
|
const DWELL_DEFAULT_EVERY_MS = 10_000;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StudyComms, StudyCommsEmail, StudyCommsRecipient, StudyParseFailure } from "../types.js";
|
|
2
|
+
export declare function parseComms(raw: unknown): {
|
|
3
|
+
ok: true;
|
|
4
|
+
value: StudyComms | undefined;
|
|
5
|
+
} | StudyParseFailure;
|
|
6
|
+
/** The participant a declared inbox recipient belongs to. The manifest spells it `lane`. */
|
|
7
|
+
export declare function recipientParticipantId(recipient: StudyCommsRecipient): string;
|
|
8
|
+
/** The declared recipients that name an address, in declaration order, by participant id. */
|
|
9
|
+
export declare function addressedRecipients(email: Pick<StudyCommsEmail, "recipients">): {
|
|
10
|
+
participantId: string;
|
|
11
|
+
address: string;
|
|
12
|
+
}[];
|
|
@@ -30,8 +30,8 @@ function parseCommsEmail(raw) {
|
|
|
30
30
|
if (raw.kind !== undefined && raw.kind !== "fake") {
|
|
31
31
|
return invalid("`comms.email.kind` must be `fake`.");
|
|
32
32
|
}
|
|
33
|
-
// external ingress
|
|
34
|
-
// inject and `injectEnv`
|
|
33
|
+
// external ingress: the adopter runs the catch, so there is no subject env for humanish to
|
|
34
|
+
// inject and `injectEnv` has nothing to name: the operator points their
|
|
35
35
|
// own app at their own catch. Parse it first so the injectEnv requirement can key off it.
|
|
36
36
|
const external = parseExternalCatch(raw.external);
|
|
37
37
|
if (!external.ok)
|
|
@@ -127,7 +127,7 @@ function parseExternalCatch(raw) {
|
|
|
127
127
|
return invalid("`comms.email.external` must be a mapping.");
|
|
128
128
|
const catchBaseUrl = str(raw.catchBaseUrl);
|
|
129
129
|
if (catchBaseUrl === undefined) {
|
|
130
|
-
return invalid("`comms.email.external.catchBaseUrl` is required
|
|
130
|
+
return invalid("`comms.email.external.catchBaseUrl` is required: the base URL of the catch you run. Your app sends its mail to it, and humanish reads GET /deliveries from it.");
|
|
131
131
|
}
|
|
132
132
|
for (const [field, value] of [
|
|
133
133
|
["catchBaseUrl", catchBaseUrl],
|
|
@@ -148,7 +148,7 @@ function parseExternalCatch(raw) {
|
|
|
148
148
|
}
|
|
149
149
|
const authTokenEnv = str(raw.authTokenEnv);
|
|
150
150
|
if (authTokenEnv !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(authTokenEnv)) {
|
|
151
|
-
return invalid(`\`comms.email.external.authTokenEnv\` must be
|
|
151
|
+
return invalid(`\`comms.email.external.authTokenEnv\` must be an environment variable name, and "${authTokenEnv}" is not one. Its value is read when the run starts and never stored.`);
|
|
152
152
|
}
|
|
153
153
|
const inboxBaseUrl = str(raw.inboxBaseUrl);
|
|
154
154
|
return {
|
|
@@ -175,7 +175,7 @@ function parseSmtp(raw) {
|
|
|
175
175
|
const hostEnv = envName(raw.hostEnv, "hostEnv");
|
|
176
176
|
const portEnv = envName(raw.portEnv, "portEnv");
|
|
177
177
|
if (hostEnv === undefined || portEnv === undefined) {
|
|
178
|
-
return invalid("`comms.email.smtp` needs both `hostEnv` and `portEnv
|
|
178
|
+
return invalid("`comms.email.smtp` needs both `hostEnv` and `portEnv`: the environment variables your app reads for its SMTP host and port. humanish sets them to its own loopback listener.");
|
|
179
179
|
}
|
|
180
180
|
if (hostEnv.startsWith("__invalid__") || portEnv.startsWith("__invalid__")) {
|
|
181
181
|
return invalid("`comms.email.smtp.hostEnv` and `portEnv` must be valid env var names.");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { StudyParseFailure, StudyDefaults, StudyExecution, StudyPolicies, StudyReview, StudyScenario } from "../types.js";
|
|
2
|
+
export declare function parseExecution(raw: unknown): {
|
|
3
|
+
ok: true;
|
|
4
|
+
value: StudyExecution | undefined;
|
|
5
|
+
} | StudyParseFailure;
|
|
6
|
+
export declare function parsePersonas(raw: unknown): Record<string, unknown>[] | undefined;
|
|
7
|
+
export declare function parseScenario(raw: unknown): {
|
|
8
|
+
ok: true;
|
|
9
|
+
value: StudyScenario | undefined;
|
|
10
|
+
} | StudyParseFailure;
|
|
11
|
+
export declare function parsePolicies(raw: unknown): {
|
|
12
|
+
ok: true;
|
|
13
|
+
value: StudyPolicies | undefined;
|
|
14
|
+
} | StudyParseFailure;
|
|
15
|
+
export declare function parseReview(raw: unknown): {
|
|
16
|
+
ok: true;
|
|
17
|
+
value: StudyReview | undefined;
|
|
18
|
+
} | StudyParseFailure;
|
|
19
|
+
export declare function parseDefaults(raw: unknown): StudyDefaults | undefined;
|
|
@@ -33,7 +33,7 @@ export function parseExecution(raw) {
|
|
|
33
33
|
}
|
|
34
34
|
if (desktopResult.value)
|
|
35
35
|
execution.desktop = desktopResult.value;
|
|
36
|
-
// Reuse the terminal route's caps parser (same shape, not a fork)
|
|
36
|
+
// Reuse the terminal route's caps parser (same shape, not a fork); a malformed budget is a hard
|
|
37
37
|
// error, never silently dropped (a cap that silently does nothing would be a safety lie).
|
|
38
38
|
const capsResult = parseCaps(raw.caps);
|
|
39
39
|
if (!capsResult.ok) {
|
|
@@ -90,9 +90,9 @@ function parseTerminal(raw) {
|
|
|
90
90
|
const transport = str(raw.transport);
|
|
91
91
|
if (transport !== "exec-stream") {
|
|
92
92
|
// "pty" is deliberately rejected: stdin is disabled, so the capture is a non-interactive
|
|
93
|
-
// exec stream
|
|
94
|
-
//
|
|
95
|
-
return invalid("`execution.terminal.transport` must be exec-stream
|
|
93
|
+
// exec stream, and an interactive-PTY label would overstate the mechanism. True duplex PTY
|
|
94
|
+
// does not ship.
|
|
95
|
+
return invalid("`execution.terminal.transport` must be exec-stream: the route captures the agent's output with stdin closed, and an interactive terminal (pty) is not supported.");
|
|
96
96
|
}
|
|
97
97
|
terminal.transport = transport;
|
|
98
98
|
}
|
|
@@ -103,9 +103,9 @@ function parseTerminal(raw) {
|
|
|
103
103
|
}
|
|
104
104
|
if (stdin === "sent") {
|
|
105
105
|
// Assisted input is forbidden until the interventions ledger + comparability flag + verify
|
|
106
|
-
// check exist (
|
|
107
|
-
// autonomous green proof.
|
|
108
|
-
return invalid("`execution.terminal.stdin: sent` (assisted input) is not supported
|
|
106
|
+
// check exist (the terminal route's stdin rule); shipping it now would let an assisted run
|
|
107
|
+
// pose as autonomous green proof.
|
|
108
|
+
return invalid("`execution.terminal.stdin: sent` (assisted input) is not supported: the route cannot yet mark a run that received typed help as different from an unassisted one. Remove the setting; stdin stays closed.");
|
|
109
109
|
}
|
|
110
110
|
terminal.stdin = stdin;
|
|
111
111
|
}
|
|
@@ -153,13 +153,13 @@ function parseDesktop(raw) {
|
|
|
153
153
|
}
|
|
154
154
|
desktop.browser = browser;
|
|
155
155
|
}
|
|
156
|
-
// A custom E2B desktop template
|
|
157
|
-
// allowlisted (any string is a valid template name/id
|
|
156
|
+
// A custom E2B desktop template name or ID. Trimmed non-empty when present; deliberately not
|
|
157
|
+
// allowlisted (any string is a valid template name/id; over-restricting would reject real
|
|
158
158
|
// adopter images). An explicitly-set but blank/whitespace value is a mistake, not a template.
|
|
159
159
|
if (raw.template !== undefined) {
|
|
160
160
|
const template = str(raw.template);
|
|
161
161
|
if (template === undefined) {
|
|
162
|
-
return invalid("`execution.desktop.template` must be a non-empty E2B desktop template
|
|
162
|
+
return invalid("`execution.desktop.template` must be a non-empty E2B desktop template name or ID when it is set. Any name is accepted.");
|
|
163
163
|
}
|
|
164
164
|
desktop.template = template;
|
|
165
165
|
}
|
|
@@ -247,8 +247,8 @@ export function parseScenario(raw) {
|
|
|
247
247
|
}
|
|
248
248
|
/**
|
|
249
249
|
* Parse `scenario.caps`. Returns a parse failure on a malformed value rather than silently
|
|
250
|
-
* dropping a budget declaration (a cap that silently does nothing would
|
|
251
|
-
*
|
|
250
|
+
* dropping a budget declaration (a cap that silently does nothing would claim protection it
|
|
251
|
+
* does not give). Each cap must be a non-negative finite number.
|
|
252
252
|
*/
|
|
253
253
|
function parseCaps(raw) {
|
|
254
254
|
if (raw === undefined) {
|
|
@@ -339,7 +339,7 @@ function parseReviewScorer(raw) {
|
|
|
339
339
|
return invalid("`review.scorer` must be a mapping with a `ref` path (e.g. { ref: scorers/product.mjs }).");
|
|
340
340
|
const unknownKeys = Object.keys(raw).filter((key) => key !== "ref");
|
|
341
341
|
if (unknownKeys.length > 0) {
|
|
342
|
-
return invalid(
|
|
342
|
+
return invalid(`\`review.scorer\` accepts only \`ref\` (a scorer module path relative to the project), and these fields are unknown: ${unknownKeys.join(", ")}.`);
|
|
343
343
|
}
|
|
344
344
|
const ref = str(raw.ref);
|
|
345
345
|
if (!ref)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { StudyRoute } from "../routing.js";
|
|
2
|
+
import { type StudyParseFailure } from "../types.js";
|
|
3
|
+
export interface StudyDocument {
|
|
4
|
+
readonly route: StudyRoute;
|
|
5
|
+
/** The same document in the humanish.lab.v2 spelling. */
|
|
6
|
+
readonly v2: Record<string, unknown>;
|
|
7
|
+
/** For each expanded participant, the index of the `participants` entry it came from. */
|
|
8
|
+
readonly participantSource: readonly number[];
|
|
9
|
+
}
|
|
10
|
+
type Parsed<T> = {
|
|
11
|
+
ok: true;
|
|
12
|
+
value: T;
|
|
13
|
+
} | StudyParseFailure;
|
|
14
|
+
/** Rewrite a humanish.study.v3 document into humanish.lab.v2, or refuse it. */
|
|
15
|
+
export declare function studyToV2(raw: Record<string, unknown>): Parsed<StudyDocument>;
|
|
16
|
+
/**
|
|
17
|
+
* Spell a v2 parser message the way the study file is written: `actors[0].lanes[3]` becomes the
|
|
18
|
+
* `participants` entry it came from, `execution.caps` and `scenario.caps` become `caps`, and so on.
|
|
19
|
+
*/
|
|
20
|
+
export declare function studySpelling(message: string, route: StudyRoute, participantSource: readonly number[]): string;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// humanish.study.v3, the study format. A study declares its route, one `actor`, its `participants`
|
|
2
|
+
// and one `caps` block. studyToV2 rewrites a v3 document into the v2 spelling, so both formats run
|
|
3
|
+
// through one parser and parse into the same StudyConfig. parseStudy then checks the declared
|
|
4
|
+
// route against the one the config takes, and refuses any field that route does not read.
|
|
5
|
+
import { isRecord } from "../../run/type-guards.js";
|
|
6
|
+
import { findUnknownStudyKey } from "../keys.js";
|
|
7
|
+
import { V2_SCHEMA } from "../types.js";
|
|
8
|
+
import { PARTICIPANT_ID_MAX_CHARS, PARTICIPANT_ID_PATTERN } from "./actors.js";
|
|
9
|
+
import { invalid, posInt } from "./values.js";
|
|
10
|
+
// The values `route:` takes: every route routeOf can return.
|
|
11
|
+
const ROUTES = [
|
|
12
|
+
"preview",
|
|
13
|
+
"computer-use",
|
|
14
|
+
"shared-world",
|
|
15
|
+
"terminal",
|
|
16
|
+
"scripted",
|
|
17
|
+
];
|
|
18
|
+
// The caps keys each route reads. Computer use and shared world stop on estimated model spend;
|
|
19
|
+
// the terminal route checks its spend ledger, product jobs and the command deadline.
|
|
20
|
+
const CAPS_KEYS = {
|
|
21
|
+
preview: [],
|
|
22
|
+
"computer-use": ["maxUsd", "maxTotalUsd"],
|
|
23
|
+
"shared-world": ["maxUsd", "maxTotalUsd"],
|
|
24
|
+
terminal: ["maxUsd", "maxJobs", "maxMinutes"],
|
|
25
|
+
scripted: [],
|
|
26
|
+
};
|
|
27
|
+
/** Rewrite a humanish.study.v3 document into humanish.lab.v2, or refuse it. */
|
|
28
|
+
export function studyToV2(raw) {
|
|
29
|
+
const moved = movedKeyReason(raw);
|
|
30
|
+
if (moved)
|
|
31
|
+
return invalid(moved);
|
|
32
|
+
const unknownKey = findUnknownStudyKey(raw);
|
|
33
|
+
if (unknownKey)
|
|
34
|
+
return invalid(unknownKey);
|
|
35
|
+
const route = ROUTES.find((name) => name === raw.route);
|
|
36
|
+
if (route === undefined) {
|
|
37
|
+
return invalid(`A study needs \`route:\`, one of ${ROUTES.join(", ")}.`);
|
|
38
|
+
}
|
|
39
|
+
if (raw.mode !== undefined && raw.mode !== "dry-run" && raw.mode !== "live") {
|
|
40
|
+
return invalid("`mode` must be `dry-run` or `live`. Leave it out for a dry run.");
|
|
41
|
+
}
|
|
42
|
+
if (!isRecord(raw.actor)) {
|
|
43
|
+
return invalid("A study needs `actor:`, an object with at least `type`.");
|
|
44
|
+
}
|
|
45
|
+
if (raw.scenario !== undefined && typeof raw.scenario !== "string") {
|
|
46
|
+
return invalid("`scenario` is a scenario id or a path to one, as a string.");
|
|
47
|
+
}
|
|
48
|
+
const participants = participantsOf(route, raw.participants);
|
|
49
|
+
if (!participants.ok)
|
|
50
|
+
return participants;
|
|
51
|
+
const surfaces = surfaceCount(route, raw.surfaces);
|
|
52
|
+
if (!surfaces.ok)
|
|
53
|
+
return surfaces;
|
|
54
|
+
const capsReason = capsKeysReason(route, raw.caps);
|
|
55
|
+
if (capsReason)
|
|
56
|
+
return invalid(capsReason);
|
|
57
|
+
// The v2 inert-field table does not list this one, but the terminal planner never reads it.
|
|
58
|
+
if (route === "terminal" && isRecord(raw.execution) && raw.execution.timeoutMs !== undefined) {
|
|
59
|
+
return invalid("route: terminal does not read `execution.timeoutMs`: `caps.maxMinutes` is the command deadline. Remove it.");
|
|
60
|
+
}
|
|
61
|
+
const { count, instruction, entries, source } = participants.value;
|
|
62
|
+
// The v2 actor keys that hold participants.
|
|
63
|
+
const v2Participants = [
|
|
64
|
+
["count", count ?? surfaces.value],
|
|
65
|
+
["laneFocus", instruction === undefined ? undefined : { instruction }],
|
|
66
|
+
["lanes", entries],
|
|
67
|
+
];
|
|
68
|
+
const actor = { ...raw.actor };
|
|
69
|
+
for (const [key, value] of v2Participants)
|
|
70
|
+
if (value !== undefined)
|
|
71
|
+
actor[key] = value;
|
|
72
|
+
const caps = raw.caps;
|
|
73
|
+
const scenario = {};
|
|
74
|
+
if (raw.scenario !== undefined)
|
|
75
|
+
scenario.ref = raw.scenario;
|
|
76
|
+
if (raw.mode !== undefined)
|
|
77
|
+
scenario.mode = raw.mode;
|
|
78
|
+
if (caps !== undefined && route === "terminal")
|
|
79
|
+
scenario.caps = caps;
|
|
80
|
+
const execution = caps !== undefined && route !== "terminal"
|
|
81
|
+
? withKey(raw.execution, "caps", caps)
|
|
82
|
+
: raw.execution;
|
|
83
|
+
const subject = route === "shared-world" ? withKey(raw.subject, "topology", "shared-world") : raw.subject;
|
|
84
|
+
const v2 = { schema: V2_SCHEMA };
|
|
85
|
+
const fields = [
|
|
86
|
+
["id", raw.id],
|
|
87
|
+
["title", raw.title],
|
|
88
|
+
["description", raw.description],
|
|
89
|
+
["subject", subject],
|
|
90
|
+
["actors", [actor]],
|
|
91
|
+
["execution", execution],
|
|
92
|
+
["scenario", Object.keys(scenario).length > 0 ? scenario : undefined],
|
|
93
|
+
["policies", raw.policies],
|
|
94
|
+
["review", raw.review],
|
|
95
|
+
["defaults", raw.defaults],
|
|
96
|
+
["comms", raw.comms],
|
|
97
|
+
];
|
|
98
|
+
for (const [key, value] of fields)
|
|
99
|
+
if (value !== undefined)
|
|
100
|
+
v2[key] = value;
|
|
101
|
+
return { ok: true, value: { route, v2, participantSource: source } };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Spell a v2 parser message the way the study file is written: `actors[0].lanes[3]` becomes the
|
|
105
|
+
* `participants` entry it came from, `execution.caps` and `scenario.caps` become `caps`, and so on.
|
|
106
|
+
*/
|
|
107
|
+
export function studySpelling(message, route, participantSource) {
|
|
108
|
+
return (message
|
|
109
|
+
.replaceAll(/actors\[0\]\.lanes\[(\d+)\]/g, (_match, index) => `participants[${participantSource[Number(index)] ?? index}]`)
|
|
110
|
+
// "an `actors[0].lanes` roster" reads "a `participants` list" in a v3 file.
|
|
111
|
+
.replaceAll(/\ban `actors\[0\]\.(lanes|roster)` roster\b/g, "a `participants` list")
|
|
112
|
+
.replaceAll(/actors\[0\]\.(lanes|roster)\b/g, "participants")
|
|
113
|
+
.replaceAll(/actors\[0\]\.laneFocus(\.instruction)?/g, "participants.instruction")
|
|
114
|
+
.replaceAll(/actors\[0\]\.count\b/g, route === "scripted" ? "surfaces" : "participants")
|
|
115
|
+
.replaceAll("actors[0]", "actor")
|
|
116
|
+
.replaceAll(/\b(execution|scenario)\.caps\b/g, "caps")
|
|
117
|
+
.replaceAll(/\bscenario\.mode\b/g, "mode")
|
|
118
|
+
.replaceAll(/\bscenario\.ref\b/g, "scenario")
|
|
119
|
+
.replaceAll("subject.topology: shared-world", "route: shared-world"));
|
|
120
|
+
}
|
|
121
|
+
// The v2 keys a hand-converted file is most likely to keep, each with where it went.
|
|
122
|
+
function movedKeyReason(raw) {
|
|
123
|
+
if (raw.actors !== undefined) {
|
|
124
|
+
return "A study has one `actor:` object, and its participants go in `participants:`. `actors:` is the humanish.lab.v2 spelling.";
|
|
125
|
+
}
|
|
126
|
+
if (isRecord(raw.actor)) {
|
|
127
|
+
for (const key of ["count", "lanes", "roster", "laneFocus"]) {
|
|
128
|
+
if (raw.actor[key] !== undefined) {
|
|
129
|
+
return `\`actor.${key}\` moved to \`participants:\`: a count, \`{ count, instruction }\`, or a list of participants.`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (isRecord(raw.subject) && raw.subject.topology !== undefined) {
|
|
134
|
+
return "`subject.topology` moved to `route:`. A shared world is `route: shared-world`.";
|
|
135
|
+
}
|
|
136
|
+
if (isRecord(raw.execution) && raw.execution.caps !== undefined) {
|
|
137
|
+
return "`execution.caps` moved to a top-level `caps:` block.";
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
function participantsOf(route, raw) {
|
|
142
|
+
if (raw === undefined) {
|
|
143
|
+
if (route === "shared-world")
|
|
144
|
+
return invalid(listOnly(route));
|
|
145
|
+
return { ok: true, value: { source: [] } };
|
|
146
|
+
}
|
|
147
|
+
switch (route) {
|
|
148
|
+
case "scripted":
|
|
149
|
+
return invalid("route: scripted takes no `participants`. It replays committed steps on `surfaces`: [desktop] or [desktop, mobile].");
|
|
150
|
+
case "terminal":
|
|
151
|
+
return invalid("route: terminal takes no `participants`. It runs one agent.");
|
|
152
|
+
case "preview":
|
|
153
|
+
return typeof raw === "number"
|
|
154
|
+
? countOf(raw)
|
|
155
|
+
: invalid("route: preview takes `participants` as a count, such as `participants: 4`.");
|
|
156
|
+
case "shared-world":
|
|
157
|
+
return Array.isArray(raw) ? entriesOf(raw) : invalid(listOnly(route));
|
|
158
|
+
case "computer-use":
|
|
159
|
+
if (typeof raw === "number")
|
|
160
|
+
return countOf(raw);
|
|
161
|
+
if (Array.isArray(raw))
|
|
162
|
+
return entriesOf(raw);
|
|
163
|
+
return homogeneousOf(raw);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function listOnly(route) {
|
|
167
|
+
return `route: ${route} takes \`participants\` as a list of at least two entries, one per participant in the shared world.`;
|
|
168
|
+
}
|
|
169
|
+
function countOf(raw) {
|
|
170
|
+
const count = posInt(raw);
|
|
171
|
+
if (count === undefined)
|
|
172
|
+
return invalid("`participants` must be a positive integer.");
|
|
173
|
+
return { ok: true, value: { count, source: [] } };
|
|
174
|
+
}
|
|
175
|
+
// `{ count, instruction }`: participants that share one instruction. The count stays a count, so
|
|
176
|
+
// `--count` still overrides it.
|
|
177
|
+
function homogeneousOf(raw) {
|
|
178
|
+
if (!isRecord(raw)) {
|
|
179
|
+
return invalid("`participants` is a count, an object `{ count, instruction }`, or a list of participants.");
|
|
180
|
+
}
|
|
181
|
+
const extra = Object.keys(raw).filter((key) => key !== "count" && key !== "instruction");
|
|
182
|
+
if (extra.length > 0) {
|
|
183
|
+
return invalid(`\`participants\` as an object takes only \`count\` and \`instruction\`, and every participant gets them. To set ${extra.map((key) => `\`${key}\``).join(", ")}, write participants as a list.`);
|
|
184
|
+
}
|
|
185
|
+
const participants = { source: [] };
|
|
186
|
+
if (raw.count !== undefined) {
|
|
187
|
+
const count = typeof raw.count === "number" ? posInt(raw.count) : undefined;
|
|
188
|
+
if (count === undefined)
|
|
189
|
+
return invalid("`participants.count` must be a positive integer.");
|
|
190
|
+
participants.count = count;
|
|
191
|
+
}
|
|
192
|
+
if (raw.instruction !== undefined)
|
|
193
|
+
participants.instruction = raw.instruction;
|
|
194
|
+
return { ok: true, value: participants };
|
|
195
|
+
}
|
|
196
|
+
// A list entry with `count: n` is a group: n participants `<id>-01` to `<id>-NN`, even when n is 1.
|
|
197
|
+
// The v2 parser then checks every entry and the expanded ids for collisions.
|
|
198
|
+
function entriesOf(raw) {
|
|
199
|
+
const entries = [];
|
|
200
|
+
const source = [];
|
|
201
|
+
for (const [index, entry] of raw.entries()) {
|
|
202
|
+
if (!isRecord(entry) || entry.count === undefined) {
|
|
203
|
+
entries.push(entry);
|
|
204
|
+
source.push(index);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const count = typeof entry.count === "number" ? posInt(entry.count) : undefined;
|
|
208
|
+
if (count === undefined) {
|
|
209
|
+
return invalid(`participants[${index}].count must be a positive integer.`);
|
|
210
|
+
}
|
|
211
|
+
const id = entry.id;
|
|
212
|
+
if (typeof id !== "string" ||
|
|
213
|
+
!PARTICIPANT_ID_PATTERN.test(id) ||
|
|
214
|
+
id.length > PARTICIPANT_ID_MAX_CHARS - 3) {
|
|
215
|
+
return invalid(`participants[${index}] has a count, so it needs an \`id\` matching ${PARTICIPANT_ID_PATTERN} of at most ${PARTICIPANT_ID_MAX_CHARS - 3} characters. Its participants are <id>-01 to <id>-${String(count).padStart(2, "0")}.`);
|
|
216
|
+
}
|
|
217
|
+
const shared = { ...entry };
|
|
218
|
+
delete shared.count;
|
|
219
|
+
for (let n = 1; n <= count; n += 1) {
|
|
220
|
+
entries.push({ ...shared, id: `${id}-${String(n).padStart(2, "0")}` });
|
|
221
|
+
source.push(index);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return { ok: true, value: { entries, source } };
|
|
225
|
+
}
|
|
226
|
+
// Scripted surfaces become the v2 count: 1 is desktop, 2 adds mobile.
|
|
227
|
+
function surfaceCount(route, raw) {
|
|
228
|
+
if (raw === undefined)
|
|
229
|
+
return { ok: true, value: undefined };
|
|
230
|
+
if (route !== "scripted") {
|
|
231
|
+
return invalid(`\`surfaces\` belongs to route: scripted. This study's route is ${route}; remove it.`);
|
|
232
|
+
}
|
|
233
|
+
if (Array.isArray(raw) && raw.length === 1 && raw[0] === "desktop")
|
|
234
|
+
return { ok: true, value: 1 };
|
|
235
|
+
if (Array.isArray(raw) && raw.length === 2 && raw[0] === "desktop" && raw[1] === "mobile") {
|
|
236
|
+
return { ok: true, value: 2 };
|
|
237
|
+
}
|
|
238
|
+
return invalid("`surfaces` must be [desktop] or [desktop, mobile].");
|
|
239
|
+
}
|
|
240
|
+
function capsKeysReason(route, raw) {
|
|
241
|
+
if (raw === undefined)
|
|
242
|
+
return undefined;
|
|
243
|
+
const read = CAPS_KEYS[route];
|
|
244
|
+
if (read.length === 0)
|
|
245
|
+
return `route: ${route} reads no \`caps\`; remove the block.`;
|
|
246
|
+
if (!isRecord(raw))
|
|
247
|
+
return undefined;
|
|
248
|
+
const unread = Object.keys(raw).filter((key) => !read.includes(key));
|
|
249
|
+
if (unread.length === 0)
|
|
250
|
+
return undefined;
|
|
251
|
+
return `route: ${route} reads only ${read.map((key) => `caps.${key}`).join(", ")}. Remove ${unread.map((key) => `caps.${key}`).join(", ")}.`;
|
|
252
|
+
}
|
|
253
|
+
// Add one key to a section. A section that is not an object passes through unchanged, so the v2
|
|
254
|
+
// parser refuses it with its own message.
|
|
255
|
+
function withKey(section, key, value) {
|
|
256
|
+
if (section === undefined)
|
|
257
|
+
return { [key]: value };
|
|
258
|
+
return isRecord(section) ? { ...section, [key]: value } : section;
|
|
259
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { StudyParseFailure, StudySubjectState } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Literal non-secret subject env. Real apps need configuration before they will boot: a public base
|
|
4
|
+
* URL, a transport selector, a feature flag, and none of that is secret. Routing it through
|
|
5
|
+
* `subject.env` would force an adopter to carry a private env file just to reproduce a public study.
|
|
6
|
+
*
|
|
7
|
+
* These values are recorded in evidence (they are part of how the subject was configured), so a
|
|
8
|
+
* value that looks like a credential is refused here rather than committed to a public repo.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseEnvValues(raw: unknown): {
|
|
11
|
+
ok: true;
|
|
12
|
+
value?: Record<string, string>;
|
|
13
|
+
} | StudyParseFailure;
|
|
14
|
+
/**
|
|
15
|
+
* Structural parse of `subject.state` into a candidate StudySubjectState. Deliberately keeps
|
|
16
|
+
* unrecognized `when`/`timeoutMs` values in the candidate (instead of silently dropping
|
|
17
|
+
* them) so subjectStateInvalidReason rejects them: a state declaration that silently does
|
|
18
|
+
* less than it says would claim more than its mechanism does.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseState(raw: unknown): {
|
|
21
|
+
ok: true;
|
|
22
|
+
value: StudySubjectState | undefined;
|
|
23
|
+
} | StudyParseFailure;
|
|
24
|
+
/**
|
|
25
|
+
* Semantic validation for `subject.state`, shared by parseStudy and the engine
|
|
26
|
+
* (the route re-enforces it on configs that arrive through runStudyWith). Returns
|
|
27
|
+
* the failure message, or null when the declaration is valid. Reads the candidate
|
|
28
|
+
* defensively: library callers can hand the engine arbitrarily-shaped objects.
|
|
29
|
+
*/
|
|
30
|
+
export declare function subjectStateInvalidReason(state: StudySubjectState, env: readonly string[] | undefined): string | null;
|