humanish 0.107.0 → 0.108.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 +22 -17
- package/CHANGELOG.md +14 -0
- package/CONTEXT.md +51 -44
- package/README.md +145 -172
- 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-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 +8 -6
- 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 +2 -2
- package/dist/actors/codex/restricted-session.js +10 -5
- package/dist/actors/computer-use/actor.d.ts +16 -16
- package/dist/actors/computer-use/actor.js +2 -2
- package/dist/actors/computer-use/admission-limit.d.ts +1 -1
- package/dist/actors/computer-use/admission-limit.js +1 -1
- package/dist/actors/computer-use/loop/actions.d.ts +3 -3
- package/dist/actors/computer-use/loop/actions.js +10 -10
- 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/ending.d.ts +5 -5
- package/dist/actors/computer-use/loop/ending.js +7 -7
- package/dist/actors/computer-use/loop/observation.d.ts +2 -2
- package/dist/actors/computer-use/loop/observation.js +9 -9
- package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
- package/dist/actors/computer-use/loop/provider-call.js +1 -1
- package/dist/actors/computer-use/loop/session.js +3 -3
- 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 +9 -8
- package/dist/actors/computer-use/loop/types.d.ts +25 -23
- package/dist/actors/computer-use/loop/usage.js +2 -2
- 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 +11 -8
- package/dist/actors/computer-use/openai-provider.js +45 -34
- 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/contract.d.ts +78 -43
- package/dist/actors/contract.js +29 -23
- package/dist/actors/local-agent/claude-session.d.ts +1 -1
- package/dist/actors/local-agent/claude-session.js +10 -9
- package/dist/actors/local-agent/cli.d.ts +8 -6
- 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 +6 -6
- package/dist/actors/registry.d.ts +10 -10
- package/dist/actors/registry.js +18 -17
- package/dist/actors/scripted-browser/actor.d.ts +8 -8
- package/dist/actors/scripted-browser/actor.js +16 -16
- 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/codex-config.d.ts +6 -2
- package/dist/analysis/codex-config.js +22 -14
- package/dist/analysis/service.js +5 -5
- package/dist/cli/commands/analyze.js +19 -16
- package/dist/cli/commands/codex.js +9 -9
- package/dist/cli/commands/comms.js +54 -33
- package/dist/cli/commands/feedback.js +20 -21
- package/dist/cli/commands/lab-format.d.ts +5 -4
- package/dist/cli/commands/lab-format.js +35 -23
- package/dist/cli/commands/lab-route-computer-use.js +6 -7
- package/dist/cli/commands/lab-route-preview.js +4 -3
- package/dist/cli/commands/lab-route-shared-world.js +3 -3
- package/dist/cli/commands/lab-run.js +10 -11
- package/dist/cli/commands/lab-scorer.d.ts +2 -2
- package/dist/cli/commands/lab-scorer.js +1 -1
- package/dist/cli/commands/migrate.d.ts +4 -0
- package/dist/cli/commands/migrate.js +65 -0
- package/dist/cli/commands/observe.js +147 -108
- 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 +137 -138
- package/dist/cli/commands/setup.d.ts +2 -2
- package/dist/cli/commands/setup.js +26 -21
- package/dist/cli/commands/study.d.ts +3 -0
- package/dist/cli/commands/study.js +182 -0
- package/dist/cli/commands/tui.d.ts +4 -4
- package/dist/cli/commands/tui.js +21 -21
- package/dist/cli/commands/watch.js +60 -75
- package/dist/cli/deprecations.d.ts +18 -0
- package/dist/cli/deprecations.js +48 -0
- package/dist/cli/doctor.d.ts +15 -5
- package/dist/cli/doctor.js +103 -48
- package/dist/cli/first-run-path.d.ts +12 -10
- package/dist/cli/first-run-path.js +30 -28
- package/dist/cli/io.d.ts +41 -6
- package/dist/cli/io.js +97 -22
- package/dist/cli/observer-follow.d.ts +2 -2
- package/dist/cli/observer-follow.js +5 -6
- package/dist/cli/orientation.d.ts +6 -3
- package/dist/cli/orientation.js +43 -39
- 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 +109 -40
- package/dist/cli/telemetry.d.ts +15 -15
- package/dist/cli/telemetry.js +48 -40
- 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 +14 -14
- package/dist/comms/email-catch.d.ts +3 -3
- package/dist/comms/email-catch.js +8 -8
- package/dist/comms/external-evidence.js +2 -2
- package/dist/comms/extract.js +2 -2
- package/dist/comms/fake-inbox.d.ts +2 -2
- package/dist/comms/fake-inbox.js +2 -2
- package/dist/comms/receiving-render.d.ts +1 -1
- package/dist/comms/receiving-render.js +2 -2
- 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 +36 -37
- package/dist/comms/sandbox-catch.js +34 -35
- package/dist/comms/setup.js +42 -8
- 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 +36 -78
- package/dist/feedback/export-bundle.js +1 -1
- package/dist/feedback/export.d.ts +5 -1
- package/dist/feedback/export.js +7 -8
- package/dist/feedback/feedback.d.ts +2 -2
- package/dist/feedback/feedback.js +21 -3
- package/dist/guest/chromium-text.js +2 -2
- package/dist/guest/desktop-native.d.ts +1 -1
- package/dist/guest/runtime.js +1 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.js +8 -6
- package/dist/keys/key-resolution.d.ts +5 -5
- package/dist/keys/key-resolution.js +23 -23
- package/dist/lab/adapter-extension.d.ts +7 -7
- package/dist/lab/adapter-extension.js +13 -36
- package/dist/lab/adapter-scorer-loader.d.ts +7 -7
- package/dist/lab/adapter-scorer-loader.js +32 -32
- package/dist/lab/composition-rules.js +42 -42
- package/dist/lab/config.js +65 -25
- package/dist/lab/deprecated-fields.d.ts +10 -0
- package/dist/lab/deprecated-fields.js +24 -0
- package/dist/lab/device-presets.d.ts +8 -8
- package/dist/lab/device-presets.js +11 -11
- package/dist/lab/discover.d.ts +13 -9
- package/dist/lab/discover.js +117 -57
- package/dist/lab/doctor.d.ts +10 -3
- package/dist/lab/doctor.js +47 -9
- package/dist/lab/init-templates.js +147 -147
- package/dist/lab/init.d.ts +1 -1
- package/dist/lab/init.js +41 -9
- package/dist/lab/keys.d.ts +2 -0
- package/dist/lab/keys.js +42 -8
- package/dist/lab/lab-deps.d.ts +7 -12
- package/dist/lab/parse/actors.js +9 -9
- package/dist/lab/parse/comms.js +5 -5
- package/dist/lab/parse/execution.js +13 -13
- package/dist/lab/parse/study-v3.d.ts +23 -0
- package/dist/lab/parse/study-v3.js +259 -0
- package/dist/lab/parse/subject-state.d.ts +7 -7
- package/dist/lab/parse/subject-state.js +16 -16
- package/dist/lab/parse/subject.js +28 -28
- package/dist/lab/parse/values.d.ts +1 -1
- package/dist/lab/parse/values.js +3 -3
- package/dist/lab/persona-resolve.d.ts +4 -4
- package/dist/lab/persona-resolve.js +4 -4
- package/dist/lab/plan-participants.d.ts +2 -2
- package/dist/lab/plan-types.d.ts +4 -4
- package/dist/lab/plan.d.ts +1 -7
- package/dist/lab/plan.js +10 -41
- package/dist/lab/preflight-probes.js +11 -11
- package/dist/lab/preflight-result.d.ts +2 -1
- package/dist/lab/preflight-result.js +5 -4
- package/dist/lab/preflight.d.ts +7 -5
- package/dist/lab/preflight.js +5 -5
- package/dist/lab/routing.d.ts +31 -25
- package/dist/lab/routing.js +60 -29
- package/dist/lab/run-lab-events.d.ts +0 -3
- package/dist/lab/run-lab-events.js +0 -6
- package/dist/lab/run-lab-homes.d.ts +22 -10
- package/dist/lab/run-lab-homes.js +5 -1
- package/dist/lab/run-lab-options.d.ts +1 -1
- package/dist/lab/run-lab-options.js +16 -13
- package/dist/lab/summary.d.ts +7 -6
- package/dist/lab/summary.js +10 -10
- package/dist/lab/tasks.d.ts +15 -15
- package/dist/lab/tasks.js +14 -14
- package/dist/lab/types.d.ts +101 -95
- package/dist/lab/types.js +2 -0
- package/dist/lab/validation.d.ts +13 -13
- package/dist/lab/validation.js +44 -42
- package/dist/lab/warnings.d.ts +10 -0
- package/dist/lab/warnings.js +61 -33
- package/dist/library-aliases.d.ts +56 -0
- package/dist/library-aliases.js +22 -0
- package/dist/observer/artifact.d.ts +1 -1
- package/dist/observer/artifact.js +6 -6
- 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 +1 -1
- package/dist/observer/library.js +4 -2
- package/dist/observer/listen.js +5 -4
- package/dist/observer/render.d.ts +1 -1
- package/dist/observer/render.js +10 -5
- package/dist/observer/run-routes.d.ts +3 -3
- package/dist/observer/run-routes.js +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 +15 -15
- package/dist/routes/computer-use/bundle.js +3 -3
- package/dist/routes/computer-use/e2b-desktop/comms.d.ts +1 -1
- package/dist/routes/computer-use/e2b-desktop/comms.js +14 -14
- package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -4
- package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +4 -3
- package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -10
- package/dist/routes/computer-use/e2b-desktop/prepare.js +7 -7
- package/dist/routes/computer-use/e2b-desktop/start.js +9 -8
- package/dist/routes/computer-use/e2b-desktop/teardown.js +5 -4
- package/dist/routes/computer-use/external-comms.d.ts +1 -1
- package/dist/routes/computer-use/external-comms.js +1 -1
- package/dist/routes/computer-use/fanout-bundle.js +18 -18
- package/dist/routes/computer-use/fanout-records.js +10 -11
- package/dist/routes/computer-use/live-flush.d.ts +1 -1
- package/dist/routes/computer-use/live-flush.js +6 -6
- package/dist/routes/computer-use/live-phase.d.ts +4 -2
- package/dist/routes/computer-use/live-phase.js +6 -6
- package/dist/routes/computer-use/local-vm.js +1 -1
- package/dist/routes/computer-use/participant-desktop.d.ts +4 -4
- package/dist/routes/computer-use/participant-desktop.js +4 -4
- package/dist/routes/computer-use/participant-execution.d.ts +4 -4
- package/dist/routes/computer-use/participant-execution.js +9 -9
- package/dist/routes/computer-use/participant-model.d.ts +6 -0
- package/dist/routes/computer-use/participant-model.js +31 -13
- package/dist/routes/computer-use/participant-prompt.d.ts +11 -11
- package/dist/routes/computer-use/participant-prompt.js +6 -6
- package/dist/routes/computer-use/participant-runs.d.ts +7 -7
- package/dist/routes/computer-use/participant-runs.js +13 -13
- package/dist/routes/computer-use/plan.js +22 -22
- package/dist/routes/computer-use/preflight.js +21 -21
- package/dist/routes/computer-use/rerun-selection.js +1 -1
- package/dist/routes/computer-use/result.js +11 -11
- package/dist/routes/computer-use/route.d.ts +2 -1
- package/dist/routes/computer-use/route.js +15 -14
- package/dist/routes/computer-use/self-report.d.ts +7 -7
- package/dist/routes/computer-use/self-report.js +31 -31
- package/dist/routes/computer-use/setup.d.ts +1 -1
- package/dist/routes/computer-use/setup.js +17 -18
- package/dist/routes/computer-use/single-bundle.d.ts +7 -7
- package/dist/routes/computer-use/single-bundle.js +28 -30
- package/dist/routes/computer-use/subject-projection.d.ts +1 -1
- package/dist/routes/computer-use/subject-projection.js +6 -6
- package/dist/routes/computer-use/types.d.ts +40 -42
- package/dist/routes/computer-use/types.js +10 -10
- package/dist/routes/preview.d.ts +8 -2
- package/dist/routes/preview.js +8 -4
- package/dist/routes/scripted/bundle.d.ts +2 -2
- package/dist/routes/scripted/bundle.js +13 -12
- package/dist/routes/scripted/plan.d.ts +1 -1
- package/dist/routes/scripted/plan.js +9 -9
- package/dist/routes/scripted/result.js +5 -5
- package/dist/routes/scripted/route.js +5 -6
- package/dist/routes/scripted/scenario.js +2 -2
- package/dist/routes/scripted/setup.js +7 -7
- package/dist/routes/scripted/subject.js +1 -1
- package/dist/routes/scripted/surface-records.js +3 -3
- package/dist/routes/scripted/types.d.ts +3 -5
- package/dist/routes/scripted/types.js +1 -1
- package/dist/routes/shared-world/bundle-records.js +9 -8
- package/dist/routes/shared-world/bundle.d.ts +5 -5
- package/dist/routes/shared-world/bundle.js +40 -38
- package/dist/routes/shared-world/checkpoints.d.ts +1 -1
- package/dist/routes/shared-world/checkpoints.js +1 -1
- package/dist/routes/shared-world/comms.d.ts +5 -5
- package/dist/routes/shared-world/comms.js +14 -14
- 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 +7 -6
- package/dist/routes/shared-world/participant-specs.js +31 -24
- package/dist/routes/shared-world/plan.js +7 -7
- 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.js +14 -13
- package/dist/routes/shared-world/result.js +20 -21
- package/dist/routes/shared-world/route.js +23 -22
- package/dist/routes/shared-world/setup.d.ts +3 -3
- package/dist/routes/shared-world/setup.js +4 -4
- package/dist/routes/shared-world/types.d.ts +30 -28
- package/dist/routes/shared-world/types.js +4 -4
- package/dist/routes/terminal/adapter.d.ts +3 -3
- package/dist/routes/terminal/adapter.js +5 -5
- package/dist/routes/terminal/bundle.d.ts +3 -3
- package/dist/routes/terminal/bundle.js +22 -21
- package/dist/routes/terminal/credentials.d.ts +4 -4
- package/dist/routes/terminal/credentials.js +7 -7
- package/dist/routes/terminal/dry-run.js +4 -4
- package/dist/routes/terminal/encoding.js +2 -2
- package/dist/routes/terminal/ledger.d.ts +3 -3
- package/dist/routes/terminal/ledger.js +14 -14
- package/dist/routes/terminal/live-finish.js +3 -3
- package/dist/routes/terminal/live-sandbox.js +12 -13
- package/dist/routes/terminal/persona.js +1 -1
- package/dist/routes/terminal/plan.js +11 -11
- package/dist/routes/terminal/recorder.js +2 -2
- package/dist/routes/terminal/result.js +8 -8
- package/dist/routes/terminal/route.js +6 -7
- package/dist/routes/terminal/sandbox.d.ts +1 -1
- package/dist/routes/terminal/sandbox.js +1 -1
- package/dist/routes/terminal/session.js +8 -7
- 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 +2 -2
- package/dist/routes/terminal/trace.js +2 -2
- package/dist/routes/terminal/types.d.ts +26 -28
- package/dist/routes/terminal/types.js +3 -3
- package/dist/run/bundle-shape.js +3 -3
- package/dist/run/bundle.d.ts +46 -46
- package/dist/run/bundle.js +2 -1
- package/dist/run/cost-summary.js +3 -3
- package/dist/run/costs.js +7 -1
- package/dist/run/detail.d.ts +6 -6
- package/dist/run/detail.js +4 -4
- package/dist/run/dry-run-inputs.js +1 -1
- package/dist/run/dry-run.js +18 -18
- package/dist/run/judge.d.ts +19 -13
- package/dist/run/judge.js +18 -8
- package/dist/run/paths.d.ts +1 -1
- package/dist/run/paths.js +1 -1
- package/dist/run/pricing.d.ts +11 -6
- package/dist/run/pricing.js +20 -13
- package/dist/run/projection.d.ts +14 -14
- package/dist/run/projection.js +15 -15
- package/dist/run/reclaim.js +2 -2
- package/dist/run/results.d.ts +5 -5
- package/dist/run/run-index.d.ts +8 -6
- package/dist/run/run-index.js +15 -16
- 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 +5 -4
- package/dist/run/run.js +1 -1
- 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 +36 -35
- 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 +6 -6
- package/dist/run/stats.d.ts +12 -6
- package/dist/run/stats.js +116 -58
- package/dist/run/status.d.ts +8 -29
- package/dist/run/status.js +5 -27
- package/dist/run/stored-runs.js +2 -1
- package/dist/run/stream-shape.js +1 -1
- package/dist/run/streams.d.ts +6 -6
- package/dist/run/study-provenance.d.ts +47 -0
- package/dist/run/study-provenance.js +63 -0
- package/dist/run/study-result.d.ts +16 -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-lab.d.ts +7 -7
- package/dist/run-lab.js +1 -3
- package/dist/study/convert.d.ts +33 -0
- package/dist/study/convert.js +432 -0
- 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 +43 -0
- 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/subject/clone.d.ts +1 -1
- package/dist/subject/desktop-cli.d.ts +1 -1
- package/dist/subject/desktop-cli.js +2 -2
- package/dist/subject/local-tree-archive.js +1 -1
- package/dist/subject/local-tree.d.ts +1 -1
- package/dist/subject/node-bootstrap.js +1 -1
- package/dist/subject/runtime.js +8 -9
- package/dist/subject/serve.d.ts +1 -1
- package/dist/subject/serve.js +3 -3
- package/dist/subject/state.d.ts +1 -1
- package/dist/subject/steps.d.ts +5 -5
- package/dist/subject/steps.js +4 -4
- package/dist/substrates/command-failure.d.ts +3 -3
- package/dist/substrates/command-failure.js +3 -3
- 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 +2 -2
- package/dist/substrates/e2b/desktop-browser.js +8 -8
- package/dist/substrates/e2b/desktop-cdp.d.ts +9 -9
- package/dist/substrates/e2b/desktop-cdp.js +8 -8
- package/dist/substrates/e2b/desktop-executor.d.ts +1 -1
- package/dist/substrates/e2b/desktop-executor.js +1 -1
- package/dist/substrates/e2b/desktop-geometry.d.ts +3 -3
- package/dist/substrates/e2b/desktop-geometry.js +7 -7
- package/dist/substrates/e2b/desktop-media.d.ts +1 -1
- 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/sandbox.d.ts +1 -1
- package/dist/substrates/e2b/sandbox.js +1 -1
- package/dist/substrates/e2b/sdk.d.ts +11 -11
- package/dist/substrates/e2b/sdk.js +6 -6
- package/dist/tui/actions.d.ts +1 -1
- package/dist/tui/actions.js +13 -13
- package/dist/tui/contract.d.ts +16 -16
- package/dist/tui/contract.js +10 -10
- package/dist/tui/launch.d.ts +4 -4
- package/dist/tui/launch.js +18 -19
- package/dist/tui/project.js +5 -5
- package/dist/tui-app.js +82 -82
- 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 +4 -4
- package/dist/verify/subject.js +18 -18
- package/dist/verify/verify.js +92 -43
- package/docs/README.md +25 -32
- package/docs/architecture/actor-contract.md +56 -87
- package/docs/architecture/desktop-sessions.md +7 -7
- package/docs/architecture/external-public-shared-world.md +61 -59
- package/docs/architecture/github-feedback-loop.md +1 -4
- 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 +5 -5
- package/docs/architecture/terminal-product-route.md +67 -107
- package/docs/contracts/README.md +24 -0
- package/docs/contracts/adapter-admission.md +3 -3
- package/docs/contracts/adapter-fixtures.md +2 -5
- 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 +45 -44
- package/docs/contracts/schemas.md +421 -362
- package/docs/contracts/study-analysis.md +3 -3
- package/docs/contracts/study-costs.md +11 -7
- 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/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/package.json +16 -17
- package/skills/humanish/SKILL.md +60 -58
- 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.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/observer/static.d.ts +0 -39
- package/dist/observer/static.js +0 -369
- 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/run-lab.d.ts
CHANGED
|
@@ -6,12 +6,12 @@ import type { RunLabDriving, RunLabHomes } from "./lab/run-lab-homes.js";
|
|
|
6
6
|
import { type LabConfig } from "./lab/types.js";
|
|
7
7
|
import type { ObserverResult } from "./observer/render.js";
|
|
8
8
|
import { type CuaActorLabResult } from "./routes/computer-use/types.js";
|
|
9
|
+
import { type PreviewStudyResult } from "./routes/preview.js";
|
|
9
10
|
import { type ScriptedBrowserLabResult } from "./routes/scripted/types.js";
|
|
10
11
|
import { type ConcurrentSharedWorldLabResult } from "./routes/shared-world/types.js";
|
|
11
12
|
import { type TerminalProductLabResult } from "./routes/terminal/types.js";
|
|
12
13
|
import { type RunScorerProvenance } from "./run/bundle.js";
|
|
13
|
-
import {
|
|
14
|
-
import { type RunLabProvenance } from "./run/status.js";
|
|
14
|
+
import type { RunStudyProvenance } from "./run/study-provenance.js";
|
|
15
15
|
/**
|
|
16
16
|
* Runs a lab on its route. The options are checked against the route and mapped into the route's
|
|
17
17
|
* hook bags before anything runs. Each route closes the run it started on every exit through its
|
|
@@ -65,13 +65,13 @@ interface RunLabBase {
|
|
|
65
65
|
export type InternalRunLabOptions = RunLabOptions & RunLabInternals;
|
|
66
66
|
interface RunLabInternals {
|
|
67
67
|
/**
|
|
68
|
-
* Which manifest this run came from
|
|
68
|
+
* Which manifest this run came from. planLab puts it on the plan, and the route reads
|
|
69
69
|
* plan.lab for the run's status record and bundle. Absent for a library caller that passes a
|
|
70
70
|
* LabConfig directly; that run records no lab.
|
|
71
71
|
*/
|
|
72
|
-
lab?:
|
|
72
|
+
lab?: RunStudyProvenance;
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
74
|
+
* Config-declared scorer provenance, forwarded alongside the
|
|
75
75
|
* loaded scorer. Its presence is the "declared" marker the terminal route reads to flip a
|
|
76
76
|
* status:"fail" verdict; the browser routes stamp it as evidence (they already flip). Core-computed
|
|
77
77
|
* (path + digest), never adopter-supplied; absent for library callers.
|
|
@@ -85,7 +85,7 @@ interface RouteOutcome<R extends LabRoute, T> {
|
|
|
85
85
|
route: R;
|
|
86
86
|
result: T;
|
|
87
87
|
}
|
|
88
|
-
export type LabOutcome = RouteOutcome<"preview",
|
|
88
|
+
export type LabOutcome = RouteOutcome<"preview", PreviewStudyResult> | RouteOutcome<"computer-use", CuaActorLabResult> | RouteOutcome<"scripted", ScriptedBrowserLabResult> | RouteOutcome<"terminal", TerminalProductLabResult> | RouteOutcome<"shared-world", ConcurrentSharedWorldLabResult>;
|
|
89
89
|
/**
|
|
90
90
|
* A plan past its route's local checks that need no scorer: the refusal they returned, or the
|
|
91
91
|
* route's run, which takes a scorer loaded after them.
|
|
@@ -103,7 +103,7 @@ export type AdmittedPlan<R extends LabRoute = LabRoute> = {
|
|
|
103
103
|
};
|
|
104
104
|
/** The result of a run on route `R`, the `result` of that route's `LabOutcome`. */
|
|
105
105
|
export type LabResult<R extends LabRoute = LabRoute> = {
|
|
106
|
-
preview:
|
|
106
|
+
preview: PreviewStudyResult;
|
|
107
107
|
"computer-use": CuaActorLabResult;
|
|
108
108
|
scripted: ScriptedBrowserLabResult;
|
|
109
109
|
terminal: TerminalProductLabResult;
|
package/dist/run-lab.js
CHANGED
|
@@ -18,8 +18,6 @@ import {} from "./routes/shared-world/types.js";
|
|
|
18
18
|
import { admitTerminalPlan, terminalLabRefusal } from "./routes/terminal/route.js";
|
|
19
19
|
import {} from "./routes/terminal/types.js";
|
|
20
20
|
import {} from "./run/bundle.js";
|
|
21
|
-
import {} from "./run/results.js";
|
|
22
|
-
import {} from "./run/status.js";
|
|
23
21
|
import { isLocalBrowserLab, localBrowserDefaults } from "./substrates/local/runtime-config.js";
|
|
24
22
|
/**
|
|
25
23
|
* Runs a lab on its route. The options are checked against the route and mapped into the route's
|
|
@@ -126,7 +124,7 @@ async function refusalOutcome(config, options, refusal, deps, emit) {
|
|
|
126
124
|
case "preview":
|
|
127
125
|
return {
|
|
128
126
|
route: "preview",
|
|
129
|
-
result: previewLabRefusal(options.cwd, refusal),
|
|
127
|
+
result: previewLabRefusal(options.cwd, config.id, refusal),
|
|
130
128
|
};
|
|
131
129
|
case "computer-use":
|
|
132
130
|
return {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { StudyRoute } from "../lab/parse/study-v3.js";
|
|
2
|
+
/** A key the conversion moved, by its v2 path and its v3 path. */
|
|
3
|
+
export interface MovedKey {
|
|
4
|
+
readonly from: string;
|
|
5
|
+
readonly to: string;
|
|
6
|
+
}
|
|
7
|
+
/** A key the conversion dropped because the study's route never reads it. */
|
|
8
|
+
export interface DroppedKey {
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
/** The comments that sat on the dropped key, which the v3 file no longer has. */
|
|
12
|
+
readonly comments?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A v2 file converted to v3. */
|
|
15
|
+
interface StudyConversion {
|
|
16
|
+
readonly text: string;
|
|
17
|
+
readonly route: StudyRoute;
|
|
18
|
+
readonly moved: readonly MovedKey[];
|
|
19
|
+
readonly dropped: readonly DroppedKey[];
|
|
20
|
+
}
|
|
21
|
+
/** The conversion, or why the file cannot be converted as it stands. */
|
|
22
|
+
export type StudyConversionResult = {
|
|
23
|
+
readonly ok: true;
|
|
24
|
+
readonly conversion: StudyConversion;
|
|
25
|
+
} | {
|
|
26
|
+
readonly ok: false;
|
|
27
|
+
readonly reason: string;
|
|
28
|
+
};
|
|
29
|
+
/** Whether a parsed YAML value is a v3 study file. */
|
|
30
|
+
export declare function isStudyV3(raw: unknown): boolean;
|
|
31
|
+
/** Convert one v2 file's text. `cwd` is the project the plan check plans in. */
|
|
32
|
+
export declare function convertStudyText(text: string, cwd: string): StudyConversionResult;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// Convert a humanish.lab.v2 study file to humanish.study.v3. It edits the parsed YAML document in
|
|
2
|
+
// place and moves each pair node, so the comments on a moved key move with it. The result lists
|
|
3
|
+
// every key it moved and every key it dropped. It refuses a file whose v3 form would parse or plan
|
|
4
|
+
// differently from the v2 source with the dropped keys removed.
|
|
5
|
+
import { isAlias, isMap, isScalar, isSeq, LineCounter, Pair, parseDocument, Scalar, visit, YAMLMap, YAMLSeq, } from "yaml";
|
|
6
|
+
import { parseLabConfig } from "../lab/config.js";
|
|
7
|
+
import { focusOf } from "../lab/parse/actors.js";
|
|
8
|
+
import { posInt } from "../lab/parse/values.js";
|
|
9
|
+
import { planLab } from "../lab/plan.js";
|
|
10
|
+
import { routeOf } from "../lab/routing.js";
|
|
11
|
+
import { LAB_CONFIG_SCHEMA, STUDY_SCHEMA } from "../lab/types.js";
|
|
12
|
+
import { inertFieldPaths } from "../lab/warnings.js";
|
|
13
|
+
import { deleteNodeFieldPath, deletePlainFieldPath, pairIndex, readFieldPath, } from "./field-paths.js";
|
|
14
|
+
function isPlainRecord(value) {
|
|
15
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
function refuse(reason) {
|
|
18
|
+
return { ok: false, reason };
|
|
19
|
+
}
|
|
20
|
+
/** Whether a parsed YAML value is a v3 study file. */
|
|
21
|
+
export function isStudyV3(raw) {
|
|
22
|
+
return isPlainRecord(raw) && raw.schema === STUDY_SCHEMA;
|
|
23
|
+
}
|
|
24
|
+
/** Convert one v2 file's text. `cwd` is the project the plan check plans in. */
|
|
25
|
+
export function convertStudyText(text, cwd) {
|
|
26
|
+
const lineCounter = new LineCounter();
|
|
27
|
+
const doc = parseDocument(text, { lineCounter });
|
|
28
|
+
if (doc.errors.length > 0)
|
|
29
|
+
return refuse(`it is not valid YAML: ${doc.errors[0].message}`);
|
|
30
|
+
const anchorLine = firstAnchorLine(doc, lineCounter);
|
|
31
|
+
if (anchorLine !== undefined) {
|
|
32
|
+
return refuse(`line ${anchorLine} uses a YAML anchor or alias. Write the value out in full, then migrate.`);
|
|
33
|
+
}
|
|
34
|
+
const raw = doc.toJS();
|
|
35
|
+
if (!isPlainRecord(raw) || !isMap(doc.contents))
|
|
36
|
+
return refuse("it is not a YAML mapping.");
|
|
37
|
+
if (raw.schema !== LAB_CONFIG_SCHEMA)
|
|
38
|
+
return refuse(`its schema is not ${LAB_CONFIG_SCHEMA}.`);
|
|
39
|
+
const source = parseLabConfig(raw);
|
|
40
|
+
if (!source.ok)
|
|
41
|
+
return refuse(`it does not parse: ${source.error.message}`);
|
|
42
|
+
const route = routeOf(source.config);
|
|
43
|
+
const actorRaw = Array.isArray(raw.actors) ? raw.actors[0] : undefined;
|
|
44
|
+
if (route === "terminal" && isPlainRecord(actorRaw)) {
|
|
45
|
+
for (const key of ["count", "laneFocus"]) {
|
|
46
|
+
if (actorRaw[key] !== undefined) {
|
|
47
|
+
return refuse(`actors[0].${key} does nothing on the terminal route, and a v3 terminal study has no participants. Remove it, then migrate.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const root = doc.contents;
|
|
52
|
+
const dropped = [];
|
|
53
|
+
for (const path of droppedPaths(source.config, route, raw)) {
|
|
54
|
+
const value = readFieldPath(raw, path);
|
|
55
|
+
const pairs = deleteNodeFieldPath(root, path);
|
|
56
|
+
if (value === undefined || pairs.length === 0)
|
|
57
|
+
continue;
|
|
58
|
+
const comments = pairComments(pairs);
|
|
59
|
+
dropped.push({ path, value, ...(comments === undefined ? {} : { comments }) });
|
|
60
|
+
}
|
|
61
|
+
const moved = [];
|
|
62
|
+
// v2 reads some laneFocus values as no focus at all ({}, a valueless key, valueless fields).
|
|
63
|
+
const focused = focusOf(source.config.actors[0]) !== undefined;
|
|
64
|
+
const converted = convertDocument(root, route, focused, moved);
|
|
65
|
+
if (!converted.ok)
|
|
66
|
+
return converted;
|
|
67
|
+
const out = doc.toString({ lineWidth: foldWidth(text), flowCollectionPadding: false });
|
|
68
|
+
// Fail closed: a comment the conversion did not carry over or report stops it.
|
|
69
|
+
const lost = lostComment(text, out, dropped);
|
|
70
|
+
if (lost !== undefined)
|
|
71
|
+
return refuse(`it would lose the comment "${lost}".`);
|
|
72
|
+
const check = samePlans(raw, dropped.map((key) => key.path), out, cwd);
|
|
73
|
+
if (!check.ok)
|
|
74
|
+
return check;
|
|
75
|
+
return { ok: true, conversion: { text: out, route, moved, dropped } };
|
|
76
|
+
}
|
|
77
|
+
// Every comment line in a YAML text, trimmed.
|
|
78
|
+
function commentLinesOf(text) {
|
|
79
|
+
const doc = parseDocument(text);
|
|
80
|
+
const found = [doc.commentBefore ?? "", doc.comment ?? ""];
|
|
81
|
+
visit(doc, (_key, node) => {
|
|
82
|
+
// An implicit empty value, as `id` in `{id, instruction: x}`, is a null node.
|
|
83
|
+
if (typeof node !== "object" || node === null)
|
|
84
|
+
return;
|
|
85
|
+
const commented = node;
|
|
86
|
+
found.push(commented.commentBefore ?? "", commented.comment ?? "");
|
|
87
|
+
});
|
|
88
|
+
return found
|
|
89
|
+
.flatMap((comment) => comment.split("\n"))
|
|
90
|
+
.map((line) => line.trim())
|
|
91
|
+
.filter(Boolean);
|
|
92
|
+
}
|
|
93
|
+
// The first comment line of the source that is neither in the output nor in a dropped key's report.
|
|
94
|
+
function lostComment(source, output, dropped) {
|
|
95
|
+
const kept = new Map();
|
|
96
|
+
const reported = dropped.flatMap((key) => (key.comments ?? "").split("\n"));
|
|
97
|
+
for (const line of [...commentLinesOf(output), ...reported.map((line) => line.trim())])
|
|
98
|
+
if (line)
|
|
99
|
+
kept.set(line, (kept.get(line) ?? 0) + 1);
|
|
100
|
+
for (const line of commentLinesOf(source)) {
|
|
101
|
+
const left = kept.get(line) ?? 0;
|
|
102
|
+
if (left === 0)
|
|
103
|
+
return line;
|
|
104
|
+
kept.set(line, left - 1);
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
// The inert-field table's paths for the route, plus terminal `execution.timeoutMs`, which no
|
|
109
|
+
// terminal code reads. Parents come before their children, so a child under a dropped parent is
|
|
110
|
+
// reported with the parent.
|
|
111
|
+
function droppedPaths(config, route, raw) {
|
|
112
|
+
const paths = inertFieldPaths(config);
|
|
113
|
+
if (route === "terminal" && readFieldPath(raw, "execution.timeoutMs") !== undefined)
|
|
114
|
+
paths.push("execution.timeoutMs");
|
|
115
|
+
const depth = (path) => path.split(".").length;
|
|
116
|
+
return [...new Set(paths)].sort((left, right) => depth(left) - depth(right));
|
|
117
|
+
}
|
|
118
|
+
// Folded text is folded again on output. Most of a file's long lines end near the width its author
|
|
119
|
+
// folded at, so the 90th percentile of their lengths stands for it, between 80 and 120.
|
|
120
|
+
function foldWidth(text) {
|
|
121
|
+
const long = text
|
|
122
|
+
.split("\n")
|
|
123
|
+
.map((line) => line.length)
|
|
124
|
+
.filter((length) => length > 60)
|
|
125
|
+
.sort((left, right) => left - right);
|
|
126
|
+
if (long.length === 0)
|
|
127
|
+
return 80;
|
|
128
|
+
const width = long[Math.min(long.length - 1, Math.floor(long.length * 0.9))];
|
|
129
|
+
return Math.min(120, Math.max(80, width));
|
|
130
|
+
}
|
|
131
|
+
function firstAnchorLine(doc, lineCounter) {
|
|
132
|
+
let line;
|
|
133
|
+
visit(doc, (_key, node) => {
|
|
134
|
+
const anchored = isAlias(node) ||
|
|
135
|
+
(typeof node === "object" &&
|
|
136
|
+
node !== null &&
|
|
137
|
+
"anchor" in node &&
|
|
138
|
+
Boolean(node.anchor));
|
|
139
|
+
if (!anchored)
|
|
140
|
+
return undefined;
|
|
141
|
+
const offset = node.range?.[0] ?? 0;
|
|
142
|
+
line = lineCounter.linePos(offset).line;
|
|
143
|
+
return visit.BREAK;
|
|
144
|
+
});
|
|
145
|
+
return line;
|
|
146
|
+
}
|
|
147
|
+
// Every comment on a node and everything under it.
|
|
148
|
+
function subtreeComments(node, found) {
|
|
149
|
+
if (typeof node !== "object" || node === null)
|
|
150
|
+
return;
|
|
151
|
+
const commented = node;
|
|
152
|
+
if (commented.commentBefore)
|
|
153
|
+
found.push(commented.commentBefore);
|
|
154
|
+
if (commented.comment)
|
|
155
|
+
found.push(commented.comment);
|
|
156
|
+
if (isMap(node) || isSeq(node)) {
|
|
157
|
+
for (const item of node.items) {
|
|
158
|
+
if (item instanceof Pair) {
|
|
159
|
+
subtreeComments(item.key, found);
|
|
160
|
+
subtreeComments(item.value, found);
|
|
161
|
+
}
|
|
162
|
+
else
|
|
163
|
+
subtreeComments(item, found);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function pairComments(pairs) {
|
|
168
|
+
const found = [];
|
|
169
|
+
for (const pair of pairs) {
|
|
170
|
+
subtreeComments(pair.key, found);
|
|
171
|
+
subtreeComments(pair.value, found);
|
|
172
|
+
}
|
|
173
|
+
const text = found
|
|
174
|
+
.flatMap((comment) => comment.split("\n"))
|
|
175
|
+
.map((line) => line.trim())
|
|
176
|
+
.filter(Boolean)
|
|
177
|
+
.join("\n");
|
|
178
|
+
return text.length > 0 ? text : undefined;
|
|
179
|
+
}
|
|
180
|
+
function keyNode(pair) {
|
|
181
|
+
return pair.key;
|
|
182
|
+
}
|
|
183
|
+
function rename(pair, key) {
|
|
184
|
+
keyNode(pair).value = key;
|
|
185
|
+
}
|
|
186
|
+
function addCommentBefore(pair, comment) {
|
|
187
|
+
if (!comment)
|
|
188
|
+
return;
|
|
189
|
+
const key = keyNode(pair);
|
|
190
|
+
key.commentBefore = key.commentBefore ? `${comment}\n${key.commentBefore}` : comment;
|
|
191
|
+
}
|
|
192
|
+
// Remove `key` from a mapping. A comment the mapping holds above its first key belongs to that key.
|
|
193
|
+
function take(map, key) {
|
|
194
|
+
if (!isMap(map))
|
|
195
|
+
return undefined;
|
|
196
|
+
const index = pairIndex(map, key);
|
|
197
|
+
if (index < 0)
|
|
198
|
+
return undefined;
|
|
199
|
+
const [pair] = map.items.splice(index, 1);
|
|
200
|
+
if (index === 0 && map.commentBefore) {
|
|
201
|
+
addCommentBefore(pair, map.commentBefore);
|
|
202
|
+
map.commentBefore = null;
|
|
203
|
+
}
|
|
204
|
+
return pair;
|
|
205
|
+
}
|
|
206
|
+
function insertAfter(map, anchors, ...pairs) {
|
|
207
|
+
const anchor = anchors.map((key) => pairIndex(map, key)).find((index) => index >= 0);
|
|
208
|
+
map.items.splice(anchor === undefined ? 0 : anchor + 1, 0, ...pairs);
|
|
209
|
+
}
|
|
210
|
+
// A removed section's trailing comment goes above the pair that followed the section, which is
|
|
211
|
+
// where it sat in the file, or to the end of the file when nothing followed.
|
|
212
|
+
function keepTrailingComment(root, next, comment) {
|
|
213
|
+
if (!comment)
|
|
214
|
+
return;
|
|
215
|
+
if (next !== undefined)
|
|
216
|
+
addCommentBefore(next, comment);
|
|
217
|
+
else
|
|
218
|
+
root.comment = root.comment ? `${root.comment}\n${comment}` : comment;
|
|
219
|
+
}
|
|
220
|
+
// Remove a key from a top-level section, and the section with it once it is empty. The section's
|
|
221
|
+
// own comments move to the promoted key.
|
|
222
|
+
function promote(root, section, key) {
|
|
223
|
+
const index = pairIndex(root, section);
|
|
224
|
+
if (index < 0)
|
|
225
|
+
return undefined;
|
|
226
|
+
const container = root.items[index];
|
|
227
|
+
const pair = take(container.value, key);
|
|
228
|
+
if (pair === undefined)
|
|
229
|
+
return undefined;
|
|
230
|
+
if (isMap(container.value) && container.value.items.length === 0) {
|
|
231
|
+
const next = root.items[index + 1];
|
|
232
|
+
root.items.splice(index, 1);
|
|
233
|
+
addCommentBefore(pair, keyNode(container).commentBefore);
|
|
234
|
+
keepTrailingComment(root, next, container.value.comment);
|
|
235
|
+
}
|
|
236
|
+
return pair;
|
|
237
|
+
}
|
|
238
|
+
function convertDocument(root, route, focused, moved) {
|
|
239
|
+
const schema = root.items[pairIndex(root, "schema")];
|
|
240
|
+
if (schema && isScalar(schema.value))
|
|
241
|
+
schema.value.value = STUDY_SCHEMA;
|
|
242
|
+
// actors[0] becomes actor, at the same position.
|
|
243
|
+
const actorsPair = root.items[pairIndex(root, "actors")];
|
|
244
|
+
if (!actorsPair || !isSeq(actorsPair.value) || !isMap(actorsPair.value.items[0]))
|
|
245
|
+
return refuse("its actors list does not hold one actor mapping.");
|
|
246
|
+
const list = actorsPair.value;
|
|
247
|
+
const actor = list.items[0];
|
|
248
|
+
if (list.commentBefore)
|
|
249
|
+
actor.commentBefore = [list.commentBefore, actor.commentBefore].filter(Boolean).join("\n");
|
|
250
|
+
if (list.comment)
|
|
251
|
+
actor.comment = [actor.comment, list.comment].filter(Boolean).join("\n");
|
|
252
|
+
rename(actorsPair, "actor");
|
|
253
|
+
actorsPair.value = actor;
|
|
254
|
+
moved.push({ from: "actors[0]", to: "actor" });
|
|
255
|
+
let after = "actor";
|
|
256
|
+
const participants = participantsPair(actor, route, focused, moved);
|
|
257
|
+
if (participants !== undefined) {
|
|
258
|
+
insertAfter(root, [after], participants);
|
|
259
|
+
after = String(keyNode(participants).value);
|
|
260
|
+
}
|
|
261
|
+
const capsSection = route === "terminal" ? "scenario" : "execution";
|
|
262
|
+
const caps = promote(root, capsSection, "caps");
|
|
263
|
+
if (caps !== undefined) {
|
|
264
|
+
insertAfter(root, [after], caps);
|
|
265
|
+
moved.push({ from: `${capsSection}.caps`, to: "caps" });
|
|
266
|
+
}
|
|
267
|
+
// scenario.ref becomes scenario, a string, where scenario was; scenario.mode becomes mode.
|
|
268
|
+
let mode;
|
|
269
|
+
const scenarioIndex = pairIndex(root, "scenario");
|
|
270
|
+
if (scenarioIndex >= 0) {
|
|
271
|
+
const scenario = root.items[scenarioIndex];
|
|
272
|
+
mode = take(scenario.value, "mode");
|
|
273
|
+
const ref = take(scenario.value, "ref");
|
|
274
|
+
if (isMap(scenario.value) && scenario.value.items.length > 0) {
|
|
275
|
+
return refuse(`scenario still holds ${scenario.value.items.map((pair) => String(keyNode(pair).value)).join(", ")} after conversion.`);
|
|
276
|
+
}
|
|
277
|
+
const next = root.items[scenarioIndex + 1];
|
|
278
|
+
root.items.splice(scenarioIndex, 1);
|
|
279
|
+
const promoted = mode ?? ref;
|
|
280
|
+
// A comment on the `scenario:` line sits on the section's value, a mapping or an empty scalar,
|
|
281
|
+
// when nothing took it.
|
|
282
|
+
const value = scenario.value;
|
|
283
|
+
const note = [keyNode(scenario).commentBefore, value?.commentBefore].filter(Boolean).join("\n");
|
|
284
|
+
if (promoted !== undefined)
|
|
285
|
+
addCommentBefore(promoted, note);
|
|
286
|
+
else
|
|
287
|
+
keepTrailingComment(root, next, note);
|
|
288
|
+
keepTrailingComment(root, next, value?.comment);
|
|
289
|
+
if (ref !== undefined) {
|
|
290
|
+
rename(ref, "scenario");
|
|
291
|
+
root.items.splice(scenarioIndex, 0, ref);
|
|
292
|
+
moved.push({ from: "scenario.ref", to: "scenario" });
|
|
293
|
+
}
|
|
294
|
+
if (mode !== undefined)
|
|
295
|
+
moved.push({ from: "scenario.mode", to: "mode" });
|
|
296
|
+
}
|
|
297
|
+
const routePair = new Pair(new Scalar("route"), new Scalar(route));
|
|
298
|
+
// subject.topology says shared-world; route says it now, and takes its comments.
|
|
299
|
+
const subject = root.items[pairIndex(root, "subject")];
|
|
300
|
+
const topology = route === "shared-world" && subject ? take(subject.value, "topology") : undefined;
|
|
301
|
+
if (topology !== undefined) {
|
|
302
|
+
addCommentBefore(routePair, keyNode(topology).commentBefore);
|
|
303
|
+
const value = topology.value;
|
|
304
|
+
addCommentBefore(routePair, value?.commentBefore);
|
|
305
|
+
if (value?.comment)
|
|
306
|
+
routePair.value.comment = value.comment;
|
|
307
|
+
moved.push({ from: "subject.topology", to: "route" });
|
|
308
|
+
}
|
|
309
|
+
insertAfter(root, ["description", "title", "id"], ...[routePair, mode].filter((pair) => pair !== undefined));
|
|
310
|
+
return { ok: true };
|
|
311
|
+
}
|
|
312
|
+
// The participant keys leave the actor for `participants`, or `surfaces` on the scripted route.
|
|
313
|
+
function participantsPair(actor, route, focused, moved) {
|
|
314
|
+
const count = take(actor, "count");
|
|
315
|
+
const entries = take(actor, "lanes");
|
|
316
|
+
const roster = take(actor, "roster");
|
|
317
|
+
let focus = take(actor, "laneFocus");
|
|
318
|
+
// A `laneFocus` v2 reads as no focus converts as if absent. Every comment in it goes above the
|
|
319
|
+
// count, or to the end of the actor.
|
|
320
|
+
if (focus !== undefined && !focused) {
|
|
321
|
+
const found = [];
|
|
322
|
+
subtreeComments(focus.key, found);
|
|
323
|
+
subtreeComments(focus.value, found);
|
|
324
|
+
const notes = found.join("\n");
|
|
325
|
+
if (count !== undefined)
|
|
326
|
+
addCommentBefore(count, notes);
|
|
327
|
+
else if (notes)
|
|
328
|
+
actor.comment = [actor.comment, notes].filter(Boolean).join("\n");
|
|
329
|
+
focus = undefined;
|
|
330
|
+
}
|
|
331
|
+
if (route === "scripted") {
|
|
332
|
+
if (count === undefined)
|
|
333
|
+
return undefined;
|
|
334
|
+
const surfaces = new YAMLSeq();
|
|
335
|
+
surfaces.flow = true;
|
|
336
|
+
const two = isScalar(count.value) && Number(count.value.value) === 2;
|
|
337
|
+
surfaces.items.push(new Scalar("desktop"), ...(two ? [new Scalar("mobile")] : []));
|
|
338
|
+
if (isScalar(count.value) && count.value.comment)
|
|
339
|
+
surfaces.comment = count.value.comment;
|
|
340
|
+
if (isScalar(count.value))
|
|
341
|
+
addCommentBefore(count, count.value.commentBefore);
|
|
342
|
+
rename(count, "surfaces");
|
|
343
|
+
count.value = surfaces;
|
|
344
|
+
moved.push({ from: "actors[0].count", to: "surfaces" });
|
|
345
|
+
return count;
|
|
346
|
+
}
|
|
347
|
+
const list = entries ?? roster;
|
|
348
|
+
if (list !== undefined) {
|
|
349
|
+
if (isSeq(list.value))
|
|
350
|
+
for (const item of list.value.items)
|
|
351
|
+
if (isMap(item))
|
|
352
|
+
numericCount(item.items[pairIndex(item, "count")]);
|
|
353
|
+
rename(list, "participants");
|
|
354
|
+
moved.push({ from: `actors[0].${entries ? "lanes" : "roster"}`, to: "participants" });
|
|
355
|
+
return list;
|
|
356
|
+
}
|
|
357
|
+
if (focus !== undefined) {
|
|
358
|
+
const instruction = take(focus.value, "instruction");
|
|
359
|
+
const homogeneous = new YAMLMap();
|
|
360
|
+
// laneFocus's mapping, or the empty scalar a valueless `laneFocus:` holds, carries its comments.
|
|
361
|
+
const focusValue = focus.value;
|
|
362
|
+
if (focusValue?.comment)
|
|
363
|
+
homogeneous.comment = focusValue.comment;
|
|
364
|
+
if (focusValue?.commentBefore)
|
|
365
|
+
homogeneous.commentBefore = focusValue.commentBefore;
|
|
366
|
+
// Fields v2 read as unset (`id: null`) go with the old mapping; their comments stay.
|
|
367
|
+
const rest = [];
|
|
368
|
+
if (isMap(focus.value))
|
|
369
|
+
for (const item of focus.value.items) {
|
|
370
|
+
subtreeComments(item.key, rest);
|
|
371
|
+
subtreeComments(item.value, rest);
|
|
372
|
+
}
|
|
373
|
+
if (rest.length > 0)
|
|
374
|
+
homogeneous.comment = [homogeneous.comment, ...rest].filter(Boolean).join("\n");
|
|
375
|
+
numericCount(count);
|
|
376
|
+
if (count !== undefined)
|
|
377
|
+
homogeneous.items.push(count);
|
|
378
|
+
if (instruction !== undefined)
|
|
379
|
+
homogeneous.items.push(instruction);
|
|
380
|
+
rename(focus, "participants");
|
|
381
|
+
focus.value = homogeneous;
|
|
382
|
+
moved.push({ from: "actors[0].laneFocus", to: "participants" });
|
|
383
|
+
if (count !== undefined)
|
|
384
|
+
moved.push({ from: "actors[0].count", to: "participants.count" });
|
|
385
|
+
return focus;
|
|
386
|
+
}
|
|
387
|
+
if (count !== undefined) {
|
|
388
|
+
numericCount(count);
|
|
389
|
+
rename(count, "participants");
|
|
390
|
+
moved.push({ from: "actors[0].count", to: "participants" });
|
|
391
|
+
return count;
|
|
392
|
+
}
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
// v2 reads a count written as a digit string, such as "2"; v3 takes a number, so write the number.
|
|
396
|
+
function numericCount(pair) {
|
|
397
|
+
if (pair === undefined || !isScalar(pair.value) || typeof pair.value.value !== "string")
|
|
398
|
+
return;
|
|
399
|
+
const count = posInt(pair.value.value);
|
|
400
|
+
if (count === undefined)
|
|
401
|
+
return;
|
|
402
|
+
pair.value.value = count;
|
|
403
|
+
pair.value.type = Scalar.PLAIN;
|
|
404
|
+
}
|
|
405
|
+
// JSON drops undefined fields, so two configs built through different branches compare by value.
|
|
406
|
+
function plain(value) {
|
|
407
|
+
return JSON.parse(JSON.stringify(value));
|
|
408
|
+
}
|
|
409
|
+
function samePlans(raw, dropped, text, cwd) {
|
|
410
|
+
const projected = structuredClone(raw);
|
|
411
|
+
for (const path of dropped)
|
|
412
|
+
deletePlainFieldPath(projected, path);
|
|
413
|
+
const before = parseLabConfig(projected);
|
|
414
|
+
if (!before.ok) {
|
|
415
|
+
return refuse(`it does not parse once the unread keys are dropped: ${before.error.message}`);
|
|
416
|
+
}
|
|
417
|
+
const after = parseLabConfig(parseDocument(text).toJS());
|
|
418
|
+
if (!after.ok)
|
|
419
|
+
return refuse(`its v3 form does not parse: ${after.error.message}`);
|
|
420
|
+
if (JSON.stringify(plain({ ...after.config, schema: LAB_CONFIG_SCHEMA })) !==
|
|
421
|
+
JSON.stringify(plain(before.config))) {
|
|
422
|
+
return refuse("its v3 form parses to a different study.");
|
|
423
|
+
}
|
|
424
|
+
for (const dryRun of [true, false]) {
|
|
425
|
+
const v2 = plain(planLab(before.config, { cwd, dryRun }));
|
|
426
|
+
const v3 = plain(planLab(after.config, { cwd, dryRun }));
|
|
427
|
+
if (JSON.stringify(v2) !== JSON.stringify(v3)) {
|
|
428
|
+
return refuse(`its v3 form plans a different ${dryRun ? "dry" : "live"} run.`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return { ok: true };
|
|
432
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Pair, type YAMLMap } from "yaml";
|
|
2
|
+
/** The value at `path`, or for a `[]` segment the values found in each item; undefined if none. */
|
|
3
|
+
export declare function readFieldPath(root: unknown, path: string): unknown;
|
|
4
|
+
/** Delete `path` from a parsed object. Returns whether anything was deleted. */
|
|
5
|
+
export declare function deletePlainFieldPath(root: unknown, path: string): boolean;
|
|
6
|
+
/** The index of `key`'s pair in a YAML mapping, or -1. */
|
|
7
|
+
export declare function pairIndex(map: YAMLMap, key: string): number;
|
|
8
|
+
/**
|
|
9
|
+
* Delete `path` from a YAML document's root mapping. Returns the deleted pairs, including the
|
|
10
|
+
* mappings a deletion left empty, so the caller can report their comments.
|
|
11
|
+
*/
|
|
12
|
+
export declare function deleteNodeFieldPath(root: unknown, path: string): Pair[];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Dotted field paths such as `execution.timeoutMs` or `actors[0].lanes[].entry`, read and deleted
|
|
2
|
+
// on a parsed object and on a YAML document node. The parsed participant list's key also matches
|
|
3
|
+
// a v2 `roster`, which the parser expands into that list. A mapping a deletion leaves empty is deleted with it.
|
|
4
|
+
import { isMap, isScalar, isSeq } from "yaml";
|
|
5
|
+
function segments(path) {
|
|
6
|
+
return path.split(".").map((part) => {
|
|
7
|
+
const match = /^([^[\]]+)(?:\[(\d*)\])?$/.exec(part);
|
|
8
|
+
if (!match)
|
|
9
|
+
throw new Error(`Unreadable field path: ${path}`);
|
|
10
|
+
const [, key, index] = match;
|
|
11
|
+
if (index === undefined)
|
|
12
|
+
return { key: key };
|
|
13
|
+
return { key: key, index: index === "" ? "all" : Number(index) };
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function isPlainRecord(value) {
|
|
17
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
// `lanes` names the participant list after parsing; a v2 file may spell it `roster`.
|
|
20
|
+
function plainKey(record, key) {
|
|
21
|
+
return key === "lanes" && !(key in record) && "roster" in record ? "roster" : key;
|
|
22
|
+
}
|
|
23
|
+
function itemsAt(list, index) {
|
|
24
|
+
if (index === "all")
|
|
25
|
+
return [...list];
|
|
26
|
+
const item = list[index];
|
|
27
|
+
return item === undefined ? [] : [item];
|
|
28
|
+
}
|
|
29
|
+
/** The value at `path`, or for a `[]` segment the values found in each item; undefined if none. */
|
|
30
|
+
export function readFieldPath(root, path) {
|
|
31
|
+
const read = (value, rest) => {
|
|
32
|
+
const [segment, ...tail] = rest;
|
|
33
|
+
if (segment === undefined)
|
|
34
|
+
return [value];
|
|
35
|
+
if (!isPlainRecord(value))
|
|
36
|
+
return [];
|
|
37
|
+
const child = value[plainKey(value, segment.key)];
|
|
38
|
+
if (child === undefined)
|
|
39
|
+
return [];
|
|
40
|
+
if (segment.index === undefined)
|
|
41
|
+
return read(child, tail);
|
|
42
|
+
if (!Array.isArray(child))
|
|
43
|
+
return [];
|
|
44
|
+
return itemsAt(child, segment.index).flatMap((item) => read(item, tail));
|
|
45
|
+
};
|
|
46
|
+
const parsed = segments(path);
|
|
47
|
+
const found = read(root, parsed);
|
|
48
|
+
if (found.length === 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
return parsed.some((segment) => segment.index === "all") ? found : found[0];
|
|
51
|
+
}
|
|
52
|
+
/** Delete `path` from a parsed object. Returns whether anything was deleted. */
|
|
53
|
+
export function deletePlainFieldPath(root, path) {
|
|
54
|
+
const remove = (value, rest) => {
|
|
55
|
+
const [segment, ...tail] = rest;
|
|
56
|
+
if (segment === undefined || !isPlainRecord(value))
|
|
57
|
+
return false;
|
|
58
|
+
const key = plainKey(value, segment.key);
|
|
59
|
+
if (!(key in value))
|
|
60
|
+
return false;
|
|
61
|
+
const child = value[key];
|
|
62
|
+
let removed;
|
|
63
|
+
if (segment.index === undefined) {
|
|
64
|
+
if (tail.length === 0) {
|
|
65
|
+
delete value[key];
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
removed = remove(child, tail);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
if (!Array.isArray(child))
|
|
72
|
+
return false;
|
|
73
|
+
removed = itemsAt(child, segment.index).reduce((any, item) => remove(item, tail) || any, false);
|
|
74
|
+
}
|
|
75
|
+
if (removed && isPlainRecord(child) && Object.keys(child).length === 0)
|
|
76
|
+
delete value[key];
|
|
77
|
+
return removed;
|
|
78
|
+
};
|
|
79
|
+
return remove(root, segments(path));
|
|
80
|
+
}
|
|
81
|
+
function mapKey(map, key) {
|
|
82
|
+
const has = (name) => map.items.some((pair) => isScalar(pair.key) && pair.key.value === name);
|
|
83
|
+
return key === "lanes" && !has(key) && has("roster") ? "roster" : key;
|
|
84
|
+
}
|
|
85
|
+
/** The index of `key`'s pair in a YAML mapping, or -1. */
|
|
86
|
+
export function pairIndex(map, key) {
|
|
87
|
+
return map.items.findIndex((pair) => isScalar(pair.key) && pair.key.value === key);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Delete `path` from a YAML document's root mapping. Returns the deleted pairs, including the
|
|
91
|
+
* mappings a deletion left empty, so the caller can report their comments.
|
|
92
|
+
*/
|
|
93
|
+
export function deleteNodeFieldPath(root, path) {
|
|
94
|
+
const remove = (node, rest) => {
|
|
95
|
+
const [segment, ...tail] = rest;
|
|
96
|
+
if (segment === undefined || !isMap(node))
|
|
97
|
+
return [];
|
|
98
|
+
const index = pairIndex(node, mapKey(node, segment.key));
|
|
99
|
+
if (index < 0)
|
|
100
|
+
return [];
|
|
101
|
+
const pair = node.items[index];
|
|
102
|
+
let removed;
|
|
103
|
+
if (segment.index === undefined) {
|
|
104
|
+
if (tail.length === 0) {
|
|
105
|
+
// A mapping holds the comment above its first key; it goes with that key.
|
|
106
|
+
if (index === 0 && node.commentBefore && isScalar(pair.key)) {
|
|
107
|
+
pair.key.commentBefore = [node.commentBefore, pair.key.commentBefore]
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.join("\n");
|
|
110
|
+
node.commentBefore = null;
|
|
111
|
+
}
|
|
112
|
+
return node.items.splice(index, 1);
|
|
113
|
+
}
|
|
114
|
+
removed = remove(pair.value, tail);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
if (!isSeq(pair.value))
|
|
118
|
+
return [];
|
|
119
|
+
removed = itemsAt(pair.value.items, segment.index).flatMap((item) => remove(item, tail));
|
|
120
|
+
}
|
|
121
|
+
if (removed.length > 0 && isMap(pair.value) && pair.value.items.length === 0)
|
|
122
|
+
removed.push(...node.items.splice(index, 1));
|
|
123
|
+
return removed;
|
|
124
|
+
};
|
|
125
|
+
return remove(root, segments(path));
|
|
126
|
+
}
|