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/lab/keys.js
CHANGED
|
@@ -93,7 +93,10 @@ const ACTOR = {
|
|
|
93
93
|
count: true,
|
|
94
94
|
lanes: PARTICIPANT_ENTRY,
|
|
95
95
|
// Roster groups are participants with a count; the parser expands them into `lanes[]`.
|
|
96
|
-
roster: {
|
|
96
|
+
roster: {
|
|
97
|
+
...PARTICIPANT_ENTRY,
|
|
98
|
+
count: true,
|
|
99
|
+
},
|
|
97
100
|
persona: true,
|
|
98
101
|
mission: true,
|
|
99
102
|
model: true,
|
|
@@ -200,14 +203,45 @@ const LAB_KEYS = {
|
|
|
200
203
|
defaults: { open: true },
|
|
201
204
|
comms: { email: EMAIL },
|
|
202
205
|
};
|
|
206
|
+
function without(shape, ...keys) {
|
|
207
|
+
return Object.fromEntries(Object.entries(shape).filter(([key]) => !keys.includes(key)));
|
|
208
|
+
}
|
|
209
|
+
// humanish.study.v3. The route is declared, so `subject.topology` goes. The participant keys leave
|
|
210
|
+
// the actor for `participants`, and `caps` is one top-level block. Top-level `personas` and
|
|
211
|
+
// `scenario.inline` are read by no route, and `scenario` is a string.
|
|
212
|
+
const STUDY_KEYS = {
|
|
213
|
+
schema: true,
|
|
214
|
+
id: true,
|
|
215
|
+
title: true,
|
|
216
|
+
description: true,
|
|
217
|
+
route: true,
|
|
218
|
+
mode: true,
|
|
219
|
+
subject: without(SUBJECT, "topology"),
|
|
220
|
+
actor: without(ACTOR, "count", "lanes", "roster", "laneFocus"),
|
|
221
|
+
// A count, `{ count, instruction }` or a list of entries. studyToV2 checks which form the route
|
|
222
|
+
// takes; an entry with a count is a group.
|
|
223
|
+
participants: { ...PARTICIPANT_ENTRY, count: true },
|
|
224
|
+
surfaces: true,
|
|
225
|
+
caps: CAPS,
|
|
226
|
+
execution: without(EXECUTION, "caps"),
|
|
227
|
+
scenario: true,
|
|
228
|
+
policies: LAB_KEYS.policies,
|
|
229
|
+
review: LAB_KEYS.review,
|
|
230
|
+
defaults: LAB_KEYS.defaults,
|
|
231
|
+
comms: LAB_KEYS.comms,
|
|
232
|
+
};
|
|
203
233
|
/** The first key in `raw` that LAB_KEYS does not list, as an error message; undefined if none. */
|
|
204
234
|
export function findUnknownLabKey(raw) {
|
|
205
|
-
return walk(raw, LAB_KEYS, "");
|
|
235
|
+
return walk(raw, LAB_KEYS, "", "lab");
|
|
236
|
+
}
|
|
237
|
+
/** The first key in a humanish.study.v3 document that the format does not have; undefined if none. */
|
|
238
|
+
export function findUnknownStudyKey(raw) {
|
|
239
|
+
return walk(raw, STUDY_KEYS, "", "study");
|
|
206
240
|
}
|
|
207
|
-
function walk(value, shape, path) {
|
|
241
|
+
function walk(value, shape, path, noun) {
|
|
208
242
|
if (Array.isArray(value)) {
|
|
209
243
|
for (const [index, item] of value.entries()) {
|
|
210
|
-
const found = walk(item, shape, `${path}[${index}]
|
|
244
|
+
const found = walk(item, shape, `${path}[${index}]`, noun);
|
|
211
245
|
if (found)
|
|
212
246
|
return found;
|
|
213
247
|
}
|
|
@@ -220,23 +254,23 @@ function walk(value, shape, path) {
|
|
|
220
254
|
// Object.hasOwn: a key like `constructor` must not match Object.prototype.
|
|
221
255
|
const unknown = Object.keys(record).filter((key) => !Object.hasOwn(shape, key));
|
|
222
256
|
if (unknown.length > 0)
|
|
223
|
-
return unknownKeysMessage(path, unknown, known);
|
|
257
|
+
return unknownKeysMessage(path, unknown, known, noun);
|
|
224
258
|
for (const [key, child] of Object.entries(record)) {
|
|
225
259
|
const expected = shape[key];
|
|
226
260
|
if (expected !== true && expected !== undefined) {
|
|
227
|
-
const found = walk(child, expected, path ? `${path}.${key}` : key);
|
|
261
|
+
const found = walk(child, expected, path ? `${path}.${key}` : key, noun);
|
|
228
262
|
if (found)
|
|
229
263
|
return found;
|
|
230
264
|
}
|
|
231
265
|
}
|
|
232
266
|
return undefined;
|
|
233
267
|
}
|
|
234
|
-
function unknownKeysMessage(path, unknown, known) {
|
|
268
|
+
function unknownKeysMessage(path, unknown, known, noun) {
|
|
235
269
|
const named = unknown.map((key) => {
|
|
236
270
|
const suggestion = closest(key, known);
|
|
237
271
|
return suggestion ? `${key} (did you mean \`${suggestion}\`?)` : key;
|
|
238
272
|
});
|
|
239
|
-
return `Unknown
|
|
273
|
+
return `Unknown ${noun} field(s)${path ? ` in \`${path}\`` : ""}: ${named.join(", ")}. Known fields: ${known.join(", ")}.`;
|
|
240
274
|
}
|
|
241
275
|
// The known key within edit distance 2, or one that differs only in case.
|
|
242
276
|
function closest(key, known) {
|
package/dist/lab/lab-deps.d.ts
CHANGED
|
@@ -9,12 +9,7 @@ import type { DetachedTimers } from "../substrates/detached.js";
|
|
|
9
9
|
import type { E2BDesktopModule } from "../substrates/e2b/sdk.js";
|
|
10
10
|
import type { LocalTreeArchive } from "../subject/local-tree-archive.js";
|
|
11
11
|
import type { SubjectPhaseEvent } from "../subject/steps.js";
|
|
12
|
-
|
|
13
|
-
export interface PhaseParticipant {
|
|
14
|
-
readonly laneId: string;
|
|
15
|
-
readonly laneIndex: number;
|
|
16
|
-
readonly laneCount: number;
|
|
17
|
-
}
|
|
12
|
+
import type { ParticipantRef } from "./run-lab-events.js";
|
|
18
13
|
export interface LabDeps {
|
|
19
14
|
/** Loads the E2B SDK. Defaults to loadE2BDesktopModule. */
|
|
20
15
|
readonly desktopModule?: () => Promise<E2BDesktopModule>;
|
|
@@ -29,10 +24,10 @@ export interface LabDeps {
|
|
|
29
24
|
* computer-use phase names its participant; a shared-world plane's names none. onEvent still
|
|
30
25
|
* receives every phase.
|
|
31
26
|
*/
|
|
32
|
-
readonly subjectPhaseSink?: (event: SubjectPhaseEvent, participant?:
|
|
27
|
+
readonly subjectPhaseSink?: (event: SubjectPhaseEvent, participant?: ParticipantRef) => void;
|
|
33
28
|
/**
|
|
34
|
-
* Shared world: runs each
|
|
35
|
-
* reads whether it is set: a custom runner cannot enforce actors[0].maxOutputTokens.
|
|
29
|
+
* Shared world: runs each participant's computer-use session in place of the actor's own. The
|
|
30
|
+
* planner reads whether it is set: a custom runner cannot enforce actors[0].maxOutputTokens.
|
|
36
31
|
*/
|
|
37
32
|
readonly runSession?: (options: CuaActorSessionOptions) => Promise<CuaLoopResult>;
|
|
38
33
|
/**
|
|
@@ -50,13 +45,13 @@ export interface LabDeps {
|
|
|
50
45
|
/** Shared world: the background stateSeries prober's cadence in milliseconds. Defaults to 1000. */
|
|
51
46
|
readonly proberCadenceMs?: number;
|
|
52
47
|
/**
|
|
53
|
-
* Shared world, external-public plane: how long the host
|
|
48
|
+
* Shared world, external-public plane: how long the host has to surface a /lobby/CODE URL
|
|
54
49
|
* before the run fails closed. Defaults to 120000, capped by execution.timeoutMs.
|
|
55
50
|
*/
|
|
56
51
|
readonly handoffDeadlineMs?: number;
|
|
57
52
|
/**
|
|
58
|
-
* Shared world, external-public plane: reads a /lobby/CODE off a
|
|
59
|
-
* the single-frame OpenAI read.
|
|
53
|
+
* Shared world, external-public plane: reads a /lobby/CODE off a participant's screenshot.
|
|
54
|
+
* Defaults to the single-frame OpenAI read.
|
|
60
55
|
*/
|
|
61
56
|
readonly readLobbyCodeFromFrame?: (frame: Buffer, apiKey: string) => Promise<string | undefined>;
|
|
62
57
|
/**
|
package/dist/lab/parse/actors.js
CHANGED
|
@@ -22,8 +22,8 @@ export function focusOf(actor) {
|
|
|
22
22
|
// Actor ids humanish no longer registers. Rejecting them at parse keeps a lab 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(),
|
|
@@ -57,10 +57,10 @@ export function parseActors(raw) {
|
|
|
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}").`);
|
|
@@ -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;
|
package/dist/lab/parse/comms.js
CHANGED
|
@@ -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.");
|
|
@@ -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,23 @@
|
|
|
1
|
+
import { type LabConfigParseFailure } from "../types.js";
|
|
2
|
+
declare const ROUTES: readonly ["preview", "computer-use", "shared-world", "terminal", "scripted"];
|
|
3
|
+
/** A route a study declares. */
|
|
4
|
+
export type StudyRoute = (typeof ROUTES)[number];
|
|
5
|
+
export interface StudyDocument {
|
|
6
|
+
readonly route: StudyRoute;
|
|
7
|
+
/** The same document in the humanish.lab.v2 spelling. */
|
|
8
|
+
readonly v2: Record<string, unknown>;
|
|
9
|
+
/** For each expanded participant, the index of the `participants` entry it came from. */
|
|
10
|
+
readonly participantSource: readonly number[];
|
|
11
|
+
}
|
|
12
|
+
type Parsed<T> = {
|
|
13
|
+
ok: true;
|
|
14
|
+
value: T;
|
|
15
|
+
} | LabConfigParseFailure;
|
|
16
|
+
/** Rewrite a humanish.study.v3 document into humanish.lab.v2, or refuse it. */
|
|
17
|
+
export declare function studyToV2(raw: Record<string, unknown>): Parsed<StudyDocument>;
|
|
18
|
+
/**
|
|
19
|
+
* Spell a v2 parser message the way the study file is written: `actors[0].lanes[3]` becomes the
|
|
20
|
+
* `participants` entry it came from, `execution.caps` and `scenario.caps` become `caps`, and so on.
|
|
21
|
+
*/
|
|
22
|
+
export declare function studySpelling(message: string, route: StudyRoute, participantSource: readonly number[]): string;
|
|
23
|
+
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 LabConfig. parseLabConfig 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 { LAB_CONFIG_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: LAB_CONFIG_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
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { LabConfigParseFailure, LabSubjectState } from "../types.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* URL, a transport selector, a feature flag
|
|
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
5
|
* `subject.env` would force an adopter to carry a private env file just to reproduce a public study.
|
|
6
6
|
*
|
|
7
|
-
* These values
|
|
7
|
+
* These values are recorded in evidence (they are part of how the subject was configured), so a
|
|
8
8
|
* value that looks like a credential is refused here rather than committed to a public repo.
|
|
9
9
|
*/
|
|
10
10
|
export declare function parseEnvValues(raw: unknown): {
|
|
@@ -14,8 +14,8 @@ export declare function parseEnvValues(raw: unknown): {
|
|
|
14
14
|
/**
|
|
15
15
|
* Structural parse of `subject.state` into a candidate LabSubjectState. Deliberately keeps
|
|
16
16
|
* unrecognized `when`/`timeoutMs` values in the candidate (instead of silently dropping
|
|
17
|
-
* them) so subjectStateInvalidReason rejects them
|
|
18
|
-
* less than it says would
|
|
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
19
|
*/
|
|
20
20
|
export declare function parseState(raw: unknown): {
|
|
21
21
|
ok: true;
|
|
@@ -23,8 +23,8 @@ export declare function parseState(raw: unknown): {
|
|
|
23
23
|
} | LabConfigParseFailure;
|
|
24
24
|
/**
|
|
25
25
|
* Semantic validation for `subject.state`, shared by parseLabConfig and the engine
|
|
26
|
-
* (
|
|
26
|
+
* (the route re-enforces it on configs that arrive through runLab). Returns
|
|
27
27
|
* the failure message, or null when the declaration is valid. Reads the candidate
|
|
28
|
-
* defensively
|
|
28
|
+
* defensively: library callers can hand the engine arbitrarily-shaped objects.
|
|
29
29
|
*/
|
|
30
30
|
export declare function subjectStateInvalidReason(state: LabSubjectState, env: readonly string[] | undefined): string | null;
|