humanish 0.106.1 → 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 +38 -32
- package/CHANGELOG.md +28 -0
- package/CONTEXT.md +51 -41
- package/README.md +152 -174
- 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-client.js +2 -1
- package/dist/actors/codex/app-server-trace.d.ts +0 -1
- package/dist/actors/codex/app-server-trace.js +1 -3
- package/dist/actors/codex/app-server-ui.js +1 -1
- package/dist/actors/codex/app-server.js +2 -1
- package/dist/actors/codex/codex-admission.d.ts +91 -0
- package/dist/actors/codex/codex-admission.js +150 -0
- package/dist/actors/codex/protocol-compat.d.ts +27 -0
- package/dist/actors/codex/protocol-compat.js +357 -0
- package/dist/actors/codex/protocol-contract.d.ts +61 -0
- package/dist/actors/codex/protocol-contract.js +471 -0
- package/dist/actors/codex/restricted-executable.d.ts +28 -4
- package/dist/actors/codex/restricted-executable.js +104 -29
- package/dist/actors/codex/restricted-launch.d.ts +9 -2
- package/dist/actors/codex/restricted-launch.js +43 -4
- package/dist/actors/codex/restricted-notifications.d.ts +101 -0
- package/dist/actors/codex/restricted-notifications.js +277 -0
- package/dist/actors/codex/restricted-participant.d.ts +30 -0
- package/dist/actors/codex/restricted-participant.js +66 -6
- package/dist/actors/codex/restricted-policy.d.ts +11 -3
- package/dist/actors/codex/restricted-policy.js +2 -2
- package/dist/actors/codex/restricted-session.d.ts +16 -3
- package/dist/actors/codex/restricted-session.js +60 -22
- package/dist/actors/codex/restricted-transport.d.ts +29 -0
- package/dist/actors/codex/restricted-transport.js +139 -27
- package/dist/actors/codex/restricted-turn.d.ts +19 -4
- package/dist/actors/codex/restricted-turn.js +43 -30
- package/dist/actors/computer-use/actor.d.ts +17 -17
- package/dist/actors/computer-use/actor.js +6 -6
- 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 +15 -13
- package/dist/actors/computer-use/loop/ending.d.ts +7 -7
- package/dist/actors/computer-use/loop/ending.js +9 -9
- 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 +2 -2
- package/dist/actors/computer-use/loop/spend.js +4 -4
- package/dist/actors/computer-use/loop/trace.js +11 -10
- package/dist/actors/computer-use/loop/types.d.ts +37 -26
- package/dist/actors/computer-use/loop/usage.js +2 -2
- package/dist/actors/computer-use/loop.js +4 -4
- package/dist/actors/computer-use/openai-context.d.ts +37 -0
- package/dist/actors/computer-use/openai-context.js +214 -0
- package/dist/actors/computer-use/openai-provider.d.ts +12 -9
- package/dist/actors/computer-use/openai-provider.js +47 -36
- 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 +86 -49
- 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/journey.js +15 -4
- package/dist/actors/scripted-browser/steps.js +19 -1
- package/dist/actors/scripted-browser/types.d.ts +12 -2
- package/dist/actors/stop-conditions.d.ts +1 -1
- package/dist/actors/stop-conditions.js +1 -3
- package/dist/analysis/automatic-completion.d.ts +13 -8
- package/dist/analysis/automatic-completion.js +14 -8
- package/dist/analysis/automatic-config.d.ts +1 -1
- package/dist/analysis/automatic-config.js +2 -2
- package/dist/analysis/automatic.d.ts +10 -4
- package/dist/analysis/automatic.js +17 -13
- package/dist/analysis/codex-config.d.ts +6 -2
- package/dist/analysis/codex-config.js +22 -14
- package/dist/analysis/diagnostics.d.ts +23 -0
- package/dist/analysis/diagnostics.js +112 -0
- package/dist/analysis/evidence-sources.js +2 -2
- package/dist/analysis/evidence.js +6 -6
- package/dist/analysis/execute.d.ts +7 -1
- package/dist/analysis/execute.js +39 -12
- package/dist/analysis/job.d.ts +8 -5
- package/dist/analysis/job.js +15 -14
- package/dist/analysis/load.d.ts +1 -1
- package/dist/analysis/load.js +5 -5
- package/dist/analysis/provider.d.ts +9 -1
- package/dist/analysis/restricted-codex.d.ts +2 -0
- package/dist/analysis/restricted-codex.js +10 -1
- package/dist/analysis/service.d.ts +2 -0
- package/dist/analysis/service.js +35 -16
- package/dist/analysis/sharing.d.ts +2 -2
- package/dist/analysis/sharing.js +5 -5
- package/dist/analysis/store-executions.d.ts +5 -5
- package/dist/analysis/store-executions.js +12 -12
- package/dist/analysis/store.d.ts +4 -4
- package/dist/analysis/store.js +13 -13
- package/dist/cli/commands/analysis-signals.d.ts +2 -1
- package/dist/cli/commands/analysis-signals.js +4 -1
- package/dist/cli/commands/analyze.js +23 -18
- package/dist/cli/commands/codex.js +9 -9
- package/dist/cli/commands/comms.js +62 -34
- 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.d.ts +0 -2
- package/dist/cli/commands/lab-route-computer-use.js +32 -21
- package/dist/cli/commands/lab-route-preview.d.ts +1 -3
- package/dist/cli/commands/lab-route-preview.js +7 -7
- package/dist/cli/commands/lab-route-run.d.ts +6 -4
- package/dist/cli/commands/lab-route-run.js +6 -2
- package/dist/cli/commands/lab-route-scripted.d.ts +0 -2
- package/dist/cli/commands/lab-route-scripted.js +0 -1
- package/dist/cli/commands/lab-route-shared-world.d.ts +0 -2
- package/dist/cli/commands/lab-route-shared-world.js +3 -4
- package/dist/cli/commands/lab-route-terminal.d.ts +0 -2
- package/dist/cli/commands/lab-route-terminal.js +0 -1
- package/dist/cli/commands/lab-run.js +84 -54
- package/dist/cli/commands/lab-scorer.d.ts +2 -4
- 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 +168 -107
- package/dist/cli/commands/run-command.d.ts +10 -0
- package/dist/cli/commands/run-command.js +85 -0
- package/dist/cli/commands/run-signals.d.ts +30 -0
- package/dist/cli/commands/run-signals.js +146 -0
- package/dist/cli/commands/runs.js +137 -136
- 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 +64 -77
- package/dist/cli/deprecations.d.ts +18 -0
- package/dist/cli/deprecations.js +48 -0
- package/dist/cli/doctor.d.ts +18 -5
- package/dist/cli/doctor.js +111 -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 +46 -10
- package/dist/cli/io.js +109 -30
- 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 +112 -43
- package/dist/cli/telemetry.d.ts +17 -17
- package/dist/cli/telemetry.js +49 -41
- package/dist/cli.js +1 -1
- package/dist/comms/capture-surface.d.ts +14 -13
- package/dist/comms/capture-surface.js +23 -22
- package/dist/comms/catch-host.d.ts +8 -8
- package/dist/comms/catch-host.js +14 -14
- package/dist/comms/connections.js +4 -4
- package/dist/comms/email-catch.d.ts +3 -3
- package/dist/comms/email-catch.js +8 -8
- package/dist/comms/external-evidence.d.ts +27 -0
- package/dist/comms/external-evidence.js +93 -0
- 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/lease-store.js +7 -7
- 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 +4 -4
- package/dist/evidence/browser-hygiene.d.ts +1 -1
- package/dist/evidence/browser-hygiene.js +1 -1
- 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 +5 -5
- package/dist/evidence/secret-scrub.d.ts +9 -0
- package/dist/evidence/secret-scrub.js +96 -0
- package/dist/feedback/draft.js +41 -85
- package/dist/feedback/export-bundle.js +5 -5
- package/dist/feedback/export.d.ts +5 -1
- package/dist/feedback/export.js +16 -17
- package/dist/feedback/feedback.d.ts +3 -3
- 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 +14 -15
- package/dist/index.js +10 -11
- package/dist/keys/key-resolution.d.ts +7 -5
- package/dist/keys/key-resolution.js +40 -38
- package/dist/lab/adapter-extension.d.ts +35 -13
- package/dist/lab/adapter-extension.js +50 -134
- package/dist/lab/adapter-scorer-loader.d.ts +32 -12
- package/dist/lab/adapter-scorer-loader.js +56 -32
- package/dist/lab/composition-rules.js +58 -58
- package/dist/lab/config.js +85 -43
- package/dist/lab/deprecated-fields.d.ts +10 -0
- package/dist/lab/deprecated-fields.js +24 -0
- package/dist/lab/device-presets.d.ts +10 -10
- package/dist/lab/device-presets.js +11 -11
- package/dist/lab/discover.d.ts +13 -9
- package/dist/lab/discover.js +118 -60
- package/dist/lab/doctor.d.ts +53 -11
- package/dist/lab/doctor.js +193 -47
- package/dist/lab/init-plan.js +1 -3
- 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 +43 -9
- package/dist/lab/lab-deps.d.ts +80 -0
- package/dist/lab/lab-deps.js +4 -0
- package/dist/lab/parse/actors.js +17 -16
- package/dist/lab/parse/comms.js +8 -7
- package/dist/lab/parse/execution.js +15 -14
- 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 +18 -17
- package/dist/lab/parse/subject.js +30 -29
- package/dist/lab/parse/values.d.ts +1 -2
- package/dist/lab/parse/values.js +3 -6
- package/dist/lab/persona-resolve.d.ts +7 -7
- package/dist/lab/persona-resolve.js +8 -8
- package/dist/lab/persona.js +1 -3
- package/dist/lab/plan-base.d.ts +0 -2
- package/dist/lab/plan-base.js +6 -4
- package/dist/lab/plan-participants.d.ts +7 -7
- package/dist/lab/plan-participants.js +2 -2
- package/dist/lab/plan-types.d.ts +26 -8
- package/dist/lab/plan.d.ts +4 -18
- package/dist/lab/plan.js +24 -79
- package/dist/lab/preflight-probes.js +11 -11
- package/dist/lab/preflight-result.d.ts +2 -1
- package/dist/lab/preflight-result.js +5 -6
- package/dist/lab/preflight.d.ts +7 -8
- package/dist/lab/preflight.js +6 -6
- package/dist/lab/requirements.d.ts +20 -0
- package/dist/lab/requirements.js +54 -0
- package/dist/lab/route-inputs.d.ts +23 -12
- package/dist/lab/route-inputs.js +48 -51
- package/dist/lab/routing.d.ts +42 -41
- package/dist/lab/routing.js +66 -68
- package/dist/lab/run-lab-events.d.ts +3 -11
- package/dist/lab/run-lab-events.js +2 -16
- package/dist/lab/run-lab-homes.d.ts +76 -0
- package/dist/lab/run-lab-homes.js +7 -0
- package/dist/lab/run-lab-options.d.ts +12 -66
- package/dist/lab/run-lab-options.js +58 -285
- package/dist/lab/summary.d.ts +15 -9
- package/dist/lab/summary.js +24 -17
- package/dist/lab/tasks.d.ts +15 -15
- package/dist/lab/tasks.js +14 -14
- package/dist/lab/types.d.ts +201 -195
- package/dist/lab/types.js +2 -0
- package/dist/lab/validation.d.ts +21 -21
- package/dist/lab/validation.js +61 -59
- package/dist/lab/warnings.d.ts +10 -0
- package/dist/lab/warnings.js +70 -41
- package/dist/library-aliases.d.ts +56 -0
- package/dist/library-aliases.js +22 -0
- package/dist/observer/artifact.d.ts +2 -2
- package/dist/observer/artifact.js +9 -9
- package/dist/observer/data.d.ts +6 -6
- package/dist/observer/data.js +7 -6
- 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 +12 -7
- package/dist/observer/run-routes.d.ts +4 -4
- package/dist/observer/run-routes.js +6 -6
- package/dist/observer/serve.d.ts +17 -0
- package/dist/observer/serve.js +41 -6
- 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 +24 -17
- package/dist/routes/computer-use/bundle.d.ts +4 -11
- package/dist/routes/computer-use/bundle.js +12 -18
- package/dist/routes/computer-use/diagnostics.d.ts +1 -1
- package/dist/routes/computer-use/diagnostics.js +1 -1
- package/dist/routes/computer-use/e2b-desktop/comms.d.ts +11 -11
- package/dist/routes/computer-use/e2b-desktop/comms.js +24 -27
- package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +2 -2
- package/dist/routes/computer-use/e2b-desktop/desktop.js +8 -11
- package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +16 -15
- package/dist/routes/computer-use/e2b-desktop/fidelity.js +20 -19
- package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +3 -3
- package/dist/routes/computer-use/e2b-desktop/prepare.js +40 -43
- package/dist/routes/computer-use/e2b-desktop/start.d.ts +1 -1
- package/dist/routes/computer-use/e2b-desktop/start.js +32 -30
- package/dist/routes/computer-use/e2b-desktop/state.d.ts +5 -4
- package/dist/routes/computer-use/e2b-desktop/state.js +5 -3
- package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +1 -1
- package/dist/routes/computer-use/e2b-desktop/teardown.js +33 -20
- package/dist/routes/computer-use/external-comms.d.ts +4 -4
- package/dist/routes/computer-use/external-comms.js +19 -43
- package/dist/routes/computer-use/fanout-bundle.d.ts +3 -3
- package/dist/routes/computer-use/fanout-bundle.js +38 -42
- package/dist/routes/computer-use/{fanout-lanes.js → fanout-records.js} +33 -28
- package/dist/routes/computer-use/in-process-desktop.d.ts +3 -10
- package/dist/routes/computer-use/in-process-desktop.js +3 -9
- package/dist/routes/computer-use/live-flush.d.ts +7 -5
- package/dist/routes/computer-use/live-flush.js +20 -28
- package/dist/routes/computer-use/{run-lanes.d.ts → live-phase.d.ts} +4 -2
- package/dist/routes/computer-use/{run-lanes.js → live-phase.js} +17 -16
- package/dist/routes/computer-use/local-tree-pack.d.ts +3 -3
- package/dist/routes/computer-use/local-tree-pack.js +4 -5
- package/dist/routes/computer-use/local-vm.d.ts +11 -8
- package/dist/routes/computer-use/local-vm.js +40 -67
- package/dist/routes/computer-use/participant-desktop.d.ts +8 -23
- package/dist/routes/computer-use/participant-desktop.js +5 -25
- package/dist/routes/computer-use/{lanes.d.ts → participant-execution.d.ts} +11 -11
- package/dist/routes/computer-use/{lanes.js → participant-execution.js} +39 -26
- package/dist/routes/computer-use/participant-facts.d.ts +7 -1
- package/dist/routes/computer-use/participant-facts.js +12 -1
- package/dist/routes/computer-use/participant-model.d.ts +25 -11
- package/dist/routes/computer-use/participant-model.js +76 -34
- package/dist/routes/computer-use/participant-prompt.d.ts +13 -13
- package/dist/routes/computer-use/participant-prompt.js +8 -8
- package/dist/routes/computer-use/participant-runs.d.ts +11 -11
- package/dist/routes/computer-use/participant-runs.js +18 -18
- package/dist/routes/computer-use/plan.d.ts +21 -29
- package/dist/routes/computer-use/plan.js +70 -78
- package/dist/routes/computer-use/preflight.d.ts +3 -5
- package/dist/routes/computer-use/preflight.js +40 -36
- package/dist/routes/computer-use/rerun-selection.js +11 -7
- package/dist/routes/computer-use/result.d.ts +1 -1
- package/dist/routes/computer-use/result.js +48 -38
- package/dist/routes/computer-use/route.d.ts +5 -5
- package/dist/routes/computer-use/route.js +36 -33
- package/dist/routes/computer-use/self-report.d.ts +8 -8
- package/dist/routes/computer-use/self-report.js +33 -33
- package/dist/routes/computer-use/setup.d.ts +9 -15
- package/dist/routes/computer-use/setup.js +106 -83
- package/dist/routes/computer-use/single-bundle.d.ts +13 -16
- package/dist/routes/computer-use/single-bundle.js +46 -53
- package/dist/routes/computer-use/subject-projection.d.ts +6 -8
- package/dist/routes/computer-use/subject-projection.js +24 -23
- package/dist/routes/computer-use/types.d.ts +195 -238
- package/dist/routes/computer-use/types.js +21 -13
- package/dist/routes/preview.d.ts +8 -2
- package/dist/routes/preview.js +8 -5
- package/dist/routes/scripted/bundle.d.ts +5 -8
- package/dist/routes/scripted/bundle.js +23 -27
- package/dist/routes/scripted/plan.d.ts +6 -8
- package/dist/routes/scripted/plan.js +15 -13
- package/dist/routes/scripted/result.d.ts +4 -1
- package/dist/routes/scripted/result.js +15 -11
- package/dist/routes/scripted/route.js +15 -17
- package/dist/routes/scripted/scenario.d.ts +1 -1
- package/dist/routes/scripted/scenario.js +3 -3
- package/dist/routes/scripted/setup.d.ts +3 -2
- package/dist/routes/scripted/setup.js +27 -26
- package/dist/routes/scripted/subject.d.ts +8 -3
- package/dist/routes/scripted/subject.js +10 -6
- package/dist/routes/scripted/surface-records.js +4 -4
- package/dist/routes/scripted/surface-sessions.d.ts +2 -2
- package/dist/routes/scripted/surface-sessions.js +4 -4
- package/dist/routes/scripted/types.d.ts +21 -40
- package/dist/routes/scripted/types.js +2 -3
- package/dist/routes/shared-world/{seat-records.js → bundle-records.js} +17 -16
- package/dist/routes/shared-world/bundle.d.ts +7 -7
- package/dist/routes/shared-world/bundle.js +60 -62
- package/dist/routes/shared-world/checkpoints.d.ts +5 -5
- package/dist/routes/shared-world/checkpoints.js +5 -5
- package/dist/routes/shared-world/comms.d.ts +8 -7
- package/dist/routes/shared-world/comms.js +38 -51
- package/dist/routes/shared-world/external-public.d.ts +6 -5
- package/dist/routes/shared-world/external-public.js +28 -25
- package/dist/routes/shared-world/handoff.d.ts +10 -9
- package/dist/routes/shared-world/handoff.js +89 -81
- package/dist/routes/shared-world/lobby-code.d.ts +7 -7
- package/dist/routes/shared-world/lobby-code.js +22 -22
- package/dist/routes/shared-world/{seats.d.ts → participant-specs.d.ts} +15 -12
- package/dist/routes/shared-world/{seats.js → participant-specs.js} +65 -63
- package/dist/routes/shared-world/plan.d.ts +2 -6
- package/dist/routes/shared-world/plan.js +8 -9
- package/dist/routes/shared-world/provenance.d.ts +4 -4
- package/dist/routes/shared-world/provenance.js +4 -4
- package/dist/routes/shared-world/provisioned.d.ts +3 -2
- package/dist/routes/shared-world/provisioned.js +55 -48
- package/dist/routes/shared-world/result.js +47 -36
- package/dist/routes/shared-world/route.js +54 -57
- package/dist/routes/shared-world/setup.d.ts +6 -5
- package/dist/routes/shared-world/setup.js +16 -18
- package/dist/routes/shared-world/types.d.ts +70 -141
- package/dist/routes/shared-world/types.js +5 -5
- package/dist/routes/terminal/adapter.d.ts +10 -10
- package/dist/routes/terminal/adapter.js +32 -129
- package/dist/routes/terminal/bundle.d.ts +10 -15
- package/dist/routes/terminal/bundle.js +43 -52
- package/dist/routes/terminal/credentials.d.ts +4 -4
- package/dist/routes/terminal/credentials.js +20 -20
- package/dist/routes/terminal/dry-run.d.ts +1 -1
- package/dist/routes/terminal/dry-run.js +13 -15
- package/dist/routes/terminal/encoding.d.ts +1 -1
- package/dist/routes/terminal/encoding.js +4 -4
- package/dist/routes/terminal/ledger.d.ts +8 -8
- package/dist/routes/terminal/ledger.js +22 -22
- package/dist/routes/terminal/live-finish.d.ts +1 -2
- package/dist/routes/terminal/live-finish.js +24 -23
- package/dist/routes/terminal/live-sandbox.d.ts +3 -2
- package/dist/routes/terminal/live-sandbox.js +31 -32
- package/dist/routes/terminal/participant-text.d.ts +23 -0
- package/dist/routes/terminal/participant-text.js +165 -0
- package/dist/routes/terminal/persona.js +1 -1
- package/dist/routes/terminal/plan.d.ts +2 -6
- package/dist/routes/terminal/plan.js +23 -19
- package/dist/routes/terminal/recorder.d.ts +6 -1
- package/dist/routes/terminal/recorder.js +12 -4
- package/dist/routes/terminal/result.js +12 -12
- package/dist/routes/terminal/route.js +30 -31
- package/dist/routes/terminal/runtime.d.ts +3 -2
- package/dist/routes/terminal/runtime.js +3 -3
- package/dist/routes/terminal/sandbox.d.ts +4 -4
- package/dist/routes/terminal/sandbox.js +10 -10
- package/dist/routes/terminal/session.js +22 -19
- package/dist/routes/terminal/token-usage.d.ts +1 -1
- package/dist/routes/terminal/token-usage.js +10 -11
- package/dist/routes/terminal/trace.d.ts +7 -10
- package/dist/routes/terminal/trace.js +11 -66
- package/dist/routes/terminal/types.d.ts +104 -114
- package/dist/routes/terminal/types.js +12 -7
- package/dist/run/active-runs.d.ts +13 -0
- package/dist/run/active-runs.js +12 -0
- package/dist/run/bundle-shape.d.ts +2 -1
- package/dist/run/bundle-shape.js +7 -7
- package/dist/run/bundle.d.ts +99 -92
- package/dist/run/bundle.js +10 -8
- package/dist/run/cost-summary.js +6 -5
- package/dist/run/costs.d.ts +6 -6
- package/dist/run/costs.js +17 -11
- package/dist/run/detail.d.ts +9 -9
- package/dist/run/detail.js +10 -10
- package/dist/run/dry-run-inputs.js +1 -1
- package/dist/run/dry-run.js +23 -30
- package/dist/run/{study-files.d.ts → evidence-files.d.ts} +4 -4
- package/dist/run/{study-files.js → evidence-files.js} +5 -5
- package/dist/run/feedback-shape.js +1 -1
- package/dist/run/judge.d.ts +34 -16
- package/dist/run/judge.js +41 -14
- package/dist/run/locate.d.ts +2 -0
- package/dist/run/locate.js +3 -1
- package/dist/run/outcomes.d.ts +3 -3
- package/dist/run/outcomes.js +3 -3
- package/dist/run/participant-records.d.ts +2 -2
- package/dist/run/participant-records.js +2 -2
- package/dist/run/paths.d.ts +1 -1
- package/dist/run/paths.js +2 -2
- package/dist/run/pricing.d.ts +30 -15
- package/dist/run/pricing.js +49 -30
- package/dist/run/projection.d.ts +27 -27
- package/dist/run/projection.js +23 -23
- package/dist/run/reclaim.d.ts +6 -0
- package/dist/run/reclaim.js +15 -5
- package/dist/run/results.d.ts +5 -5
- package/dist/run/run-index.d.ts +9 -7
- package/dist/run/run-index.js +24 -25
- 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 +7 -4
- package/dist/run/run.js +10 -6
- package/dist/run/sandbox-receipts.d.ts +5 -5
- package/dist/run/sandbox-receipts.js +6 -6
- package/dist/run/shared-world-evidence.d.ts +67 -66
- package/dist/run/shared-world-evidence.js +4 -4
- package/dist/run/shared-world-shape.d.ts +3 -3
- package/dist/run/shared-world-shape.js +16 -16
- package/dist/run/stats.d.ts +17 -11
- package/dist/run/stats.js +124 -66
- package/dist/run/status.d.ts +21 -32
- package/dist/run/status.js +34 -33
- package/dist/run/stored-runs.js +2 -1
- package/dist/run/stream-shape.js +1 -1
- package/dist/run/streams.d.ts +29 -22
- 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/terminal-contract.d.ts +5 -5
- package/dist/run/terminal-contract.js +4 -4
- package/dist/run/text.d.ts +2 -0
- package/dist/run/text.js +4 -0
- package/dist/run/transient-comms-secrets.d.ts +5 -0
- package/dist/run/transient-comms-secrets.js +12 -0
- package/dist/run-lab.d.ts +40 -38
- package/dist/run-lab.js +42 -40
- 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 +2 -2
- package/dist/subject/desktop-cli.js +3 -3
- package/dist/subject/local-tree-archive.d.ts +1 -1
- package/dist/subject/local-tree-archive.js +5 -5
- package/dist/subject/local-tree.d.ts +3 -3
- package/dist/subject/local-tree.js +1 -1
- package/dist/subject/node-bootstrap.d.ts +1 -1
- package/dist/subject/node-bootstrap.js +4 -4
- package/dist/subject/runtime.js +10 -11
- package/dist/subject/serve.d.ts +1 -1
- package/dist/subject/serve.js +4 -4
- package/dist/subject/state.d.ts +1 -1
- package/dist/subject/steps.d.ts +8 -8
- package/dist/subject/steps.js +7 -7
- 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 +11 -11
- package/dist/substrates/e2b/cdp-probe.js +15 -18
- package/dist/substrates/e2b/desktop-browser.d.ts +5 -5
- package/dist/substrates/e2b/desktop-browser.js +12 -12
- package/dist/substrates/e2b/desktop-cdp.d.ts +15 -15
- package/dist/substrates/e2b/desktop-cdp.js +11 -11
- 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 +6 -6
- package/dist/substrates/e2b/desktop-geometry.js +9 -9
- package/dist/substrates/e2b/desktop-media.d.ts +1 -1
- package/dist/substrates/e2b/desktop-media.js +3 -3
- 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 +2 -2
- package/dist/substrates/e2b/sdk.d.ts +11 -11
- package/dist/substrates/e2b/sdk.js +8 -8
- package/dist/substrates/local/runtime-host.d.ts +2 -0
- package/dist/substrates/local/runtime-host.js +6 -0
- package/dist/tui/actions.d.ts +8 -6
- package/dist/tui/actions.js +31 -22
- 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 +83 -83
- package/dist/verify/actor.d.ts +14 -14
- package/dist/verify/actor.js +45 -45
- package/dist/verify/artifacts.d.ts +3 -3
- package/dist/verify/artifacts.js +6 -6
- package/dist/verify/context-growth.d.ts +9 -0
- package/dist/verify/context-growth.js +62 -0
- package/dist/verify/costs.d.ts +7 -7
- package/dist/verify/costs.js +9 -9
- package/dist/verify/liveness.d.ts +1 -1
- package/dist/verify/liveness.js +4 -1
- package/dist/verify/rerun.d.ts +1 -1
- package/dist/verify/rerun.js +1 -1
- 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 +18 -18
- package/dist/verify/subject.d.ts +4 -4
- package/dist/verify/subject.js +18 -18
- package/dist/verify/verify.js +98 -49
- package/docs/README.md +26 -33
- package/docs/architecture/actor-contract.md +105 -98
- package/docs/architecture/desktop-sessions.md +14 -14
- package/docs/architecture/external-public-shared-world.md +66 -59
- package/docs/architecture/github-feedback-loop.md +1 -4
- package/docs/architecture/guest-desktop.md +2 -2
- package/docs/architecture/observer.md +37 -37
- package/docs/architecture/restricted-codex-analysis.md +70 -30
- package/docs/architecture/serve.md +34 -37
- package/docs/architecture/state-driven-executor.md +55 -59
- package/docs/architecture/task-protocol-support.md +7 -9
- package/docs/architecture/{terminal-product-lane.md → terminal-product-route.md} +83 -115
- 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 +54 -51
- package/docs/contracts/schemas.md +505 -390
- package/docs/contracts/study-analysis.md +26 -2
- 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/decisions/0004-participant-desktops-stay-in-the-route.md +2 -1
- 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 +8 -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 +63 -60
- package/skills/humanish/agents/openai.yaml +1 -1
- package/AGENTS.md +0 -99
- package/CONTRIBUTING.md +0 -148
- package/dist/actors/codex/qualified-versions.d.ts +0 -45
- package/dist/actors/codex/qualified-versions.js +0 -87
- package/dist/cli/commands/lab.d.ts +0 -3
- package/dist/cli/commands/lab.js +0 -193
- package/dist/deprecated.d.ts +0 -90
- package/dist/deprecated.js +0 -106
- package/dist/lab/bag-overrides.d.ts +0 -18
- package/dist/lab/bag-overrides.js +0 -121
- package/dist/observer/static.d.ts +0 -39
- package/dist/observer/static.js +0 -369
- package/dist/routes/computer-use/legacy-lane-spec.d.ts +0 -62
- package/dist/routes/computer-use/legacy-lane-spec.js +0 -39
- 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 -294
- 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/routes/computer-use/{fanout-lanes.d.ts → fanout-records.d.ts} +0 -0
- /package/dist/routes/shared-world/{seat-records.d.ts → bundle-records.d.ts} +0 -0
|
@@ -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;
|
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
// the structural parse of `subject.state`, and subjectStateInvalidReason, the semantic check
|
|
3
3
|
// parseLabConfig and the computer-use route share.
|
|
4
4
|
import { containsSensitive } from "../../evidence/redaction.js";
|
|
5
|
-
import { ENV_NAME_PATTERN, invalid,
|
|
5
|
+
import { ENV_NAME_PATTERN, invalid, posInt, str, strList } from "./values.js";
|
|
6
|
+
import { isRecord } from "../../run/type-guards.js";
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
-
* URL, a transport selector, a feature flag
|
|
8
|
+
* Literal non-secret subject env. Real apps need configuration before they will boot: a public base
|
|
9
|
+
* URL, a transport selector, a feature flag, and none of that is secret. Routing it through
|
|
9
10
|
* `subject.env` would force an adopter to carry a private env file just to reproduce a public study.
|
|
10
11
|
*
|
|
11
|
-
* These values
|
|
12
|
+
* These values are recorded in evidence (they are part of how the subject was configured), so a
|
|
12
13
|
* value that looks like a credential is refused here rather than committed to a public repo.
|
|
13
14
|
*/
|
|
14
15
|
export function parseEnvValues(raw) {
|
|
15
16
|
if (raw === undefined)
|
|
16
17
|
return { ok: true };
|
|
17
18
|
if (!isRecord(raw)) {
|
|
18
|
-
return invalid("`subject.envValues` must
|
|
19
|
+
return invalid("`subject.envValues` must map environment variable names to literal values that are not secret.");
|
|
19
20
|
}
|
|
20
21
|
const envValues = {};
|
|
21
22
|
for (const [name, rawValue] of Object.entries(raw)) {
|
|
22
23
|
if (!ENV_NAME_PATTERN.test(name)) {
|
|
23
|
-
return invalid(`subject.envValues keys must be
|
|
24
|
+
return invalid(`subject.envValues keys must be environment variable names like NEXT_PUBLIC_APP_URL, and "${name}" is not one.`);
|
|
24
25
|
}
|
|
25
26
|
const value = typeof rawValue === "number" || typeof rawValue === "boolean"
|
|
26
27
|
? String(rawValue)
|
|
@@ -29,9 +30,9 @@ export function parseEnvValues(raw) {
|
|
|
29
30
|
return invalid(`\`subject.envValues.${name}\` must be a string, number, or boolean.`);
|
|
30
31
|
}
|
|
31
32
|
// Reuse the redaction module's own detector rather than inventing a second opinion about what
|
|
32
|
-
// a secret looks like
|
|
33
|
+
// a secret looks like; the two must never disagree about the same string.
|
|
33
34
|
if (containsSensitive(value)) {
|
|
34
|
-
return invalid(`\`subject.envValues.${name}\` looks like a secret or a local path, and
|
|
35
|
+
return invalid(`\`subject.envValues.${name}\` looks like a secret or a local path, and envValues are committed with the study and recorded in evidence. List the name in \`subject.env\` instead: its value then comes from your environment and is never stored.`);
|
|
35
36
|
}
|
|
36
37
|
envValues[name] = value;
|
|
37
38
|
}
|
|
@@ -40,8 +41,8 @@ export function parseEnvValues(raw) {
|
|
|
40
41
|
/**
|
|
41
42
|
* Structural parse of `subject.state` into a candidate LabSubjectState. Deliberately keeps
|
|
42
43
|
* unrecognized `when`/`timeoutMs` values in the candidate (instead of silently dropping
|
|
43
|
-
* them) so subjectStateInvalidReason rejects them
|
|
44
|
-
* less than it says would
|
|
44
|
+
* them) so subjectStateInvalidReason rejects them: a state declaration that silently does
|
|
45
|
+
* less than it says would claim more than its mechanism does.
|
|
45
46
|
*/
|
|
46
47
|
export function parseState(raw) {
|
|
47
48
|
if (raw === undefined) {
|
|
@@ -67,7 +68,7 @@ export function parseState(raw) {
|
|
|
67
68
|
if (raw.external !== undefined) {
|
|
68
69
|
const external = strList(raw.external);
|
|
69
70
|
if (!external) {
|
|
70
|
-
return invalid("`subject.state.external` must be a non-empty list of
|
|
71
|
+
return invalid("`subject.state.external` must be a non-empty list of environment variable names when it is set.");
|
|
71
72
|
}
|
|
72
73
|
state.external = external;
|
|
73
74
|
}
|
|
@@ -78,7 +79,7 @@ export function parseState(raw) {
|
|
|
78
79
|
state.checkpoint = raw.checkpoint.map((probe) => ({
|
|
79
80
|
name: typeof probe.name === "string" ? probe.name.trim() : "",
|
|
80
81
|
command: typeof probe.command === "string" ? probe.command.trim() : "",
|
|
81
|
-
// Preserve the redact list verbatim (literal secret values may contain commas, so do
|
|
82
|
+
// Preserve the redact list verbatim (literal secret values may contain commas, so do not
|
|
82
83
|
// run it through the comma-splitting strList); subjectStateInvalidReason validates the shape.
|
|
83
84
|
...(probe.redact === undefined ? {} : { redact: probe.redact }),
|
|
84
85
|
}));
|
|
@@ -86,7 +87,7 @@ export function parseState(raw) {
|
|
|
86
87
|
return { ok: true, value: state };
|
|
87
88
|
}
|
|
88
89
|
// The step name interpolates into in-sandbox script/status/log paths (`subject-state-<name>`);
|
|
89
|
-
// the
|
|
90
|
+
// so the shape is strict, exactly like the repo slug.
|
|
90
91
|
const STATE_STEP_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
91
92
|
const STATE_STEP_NAME_MAX_CHARS = 40;
|
|
92
93
|
const STATE_STEP_WHENS = [
|
|
@@ -96,9 +97,9 @@ const STATE_STEP_WHENS = [
|
|
|
96
97
|
];
|
|
97
98
|
/**
|
|
98
99
|
* Semantic validation for `subject.state`, shared by parseLabConfig and the engine
|
|
99
|
-
* (
|
|
100
|
+
* (the route re-enforces it on configs that arrive through runLab). Returns
|
|
100
101
|
* the failure message, or null when the declaration is valid. Reads the candidate
|
|
101
|
-
* defensively
|
|
102
|
+
* defensively: library callers can hand the engine arbitrarily-shaped objects.
|
|
102
103
|
*/
|
|
103
104
|
export function subjectStateInvalidReason(state, env) {
|
|
104
105
|
const seed = state.seed;
|
|
@@ -139,11 +140,11 @@ export function subjectStateInvalidReason(state, env) {
|
|
|
139
140
|
}
|
|
140
141
|
if (external !== undefined) {
|
|
141
142
|
if (!Array.isArray(external) || external.length === 0) {
|
|
142
|
-
return "`subject.state.external` must be a non-empty list of
|
|
143
|
+
return "`subject.state.external` must be a non-empty list of environment variable names when it is set.";
|
|
143
144
|
}
|
|
144
145
|
for (const name of external) {
|
|
145
146
|
if (typeof name !== "string" || !ENV_NAME_PATTERN.test(name)) {
|
|
146
|
-
return "subject.state.external entries must be
|
|
147
|
+
return "subject.state.external entries must be environment variable names like DATABASE_URL. Their values come from your environment and are never stored.";
|
|
147
148
|
}
|
|
148
149
|
if (!env?.includes(name)) {
|
|
149
150
|
return "subject.state.external names must also be declared in subject.env (the declaration must name a provisioned channel).";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeExtraExcludeEntry } from "../../subject/local-tree-archive.js";
|
|
2
|
-
import { ENV_NAME_PATTERN, invalid,
|
|
2
|
+
import { ENV_NAME_PATTERN, invalid, posInt, str, strList } from "./values.js";
|
|
3
3
|
import { parseEnvValues, parseState, subjectStateInvalidReason } from "./subject-state.js";
|
|
4
|
+
import { isRecord } from "../../run/type-guards.js";
|
|
4
5
|
export function parseSubject(raw) {
|
|
5
6
|
if (!isRecord(raw)) {
|
|
6
7
|
return invalid("Lab `subject` is required and must be an object.");
|
|
@@ -16,9 +17,9 @@ export function parseSubject(raw) {
|
|
|
16
17
|
return invalid("`subject.source` must be one of: this-repo, clone, app-url, local-app, terminal-product, desktop-cli, local-tree.");
|
|
17
18
|
}
|
|
18
19
|
const subject = { source };
|
|
19
|
-
// topology is enum-validated everywhere; its
|
|
20
|
+
// topology is enum-validated everywhere; its semantics (shared-world requires clone × e2b-desktop
|
|
20
21
|
// × a ≥2 roster) are enforced in the shared-world cross-validation below, and a set-but-unconsumed
|
|
21
|
-
// topology warns as inert off the shared-world route
|
|
22
|
+
// topology warns as inert off the shared-world route.
|
|
22
23
|
if (raw.topology !== undefined) {
|
|
23
24
|
const topology = str(raw.topology);
|
|
24
25
|
if (topology !== "per-lane-worlds" && topology !== "shared-world") {
|
|
@@ -26,12 +27,12 @@ export function parseSubject(raw) {
|
|
|
26
27
|
}
|
|
27
28
|
subject.topology = topology;
|
|
28
29
|
}
|
|
29
|
-
// exposure is enum-validated everywhere; it is
|
|
30
|
+
// exposure is enum-validated everywhere; it is required on the concurrent shared-world route (the
|
|
30
31
|
// getHost synthetic-subject attestation) and warns inert elsewhere.
|
|
31
32
|
if (raw.exposure !== undefined) {
|
|
32
33
|
const exposure = str(raw.exposure);
|
|
33
34
|
if (exposure !== "synthetic") {
|
|
34
|
-
return invalid("`subject.exposure` must be `synthetic
|
|
35
|
+
return invalid("`subject.exposure` must be `synthetic`: your statement that the subject served on a public sandbox URL holds only synthetic seeded data.");
|
|
35
36
|
}
|
|
36
37
|
subject.exposure = exposure;
|
|
37
38
|
}
|
|
@@ -56,15 +57,15 @@ export function parseSubject(raw) {
|
|
|
56
57
|
}
|
|
57
58
|
/** The first field set on a source it cannot act on, in the order parseSubject has always checked. */
|
|
58
59
|
function misplacedFieldFailure(source, raw) {
|
|
59
|
-
// `product` is terminal-product-only; reject it elsewhere
|
|
60
|
-
//
|
|
60
|
+
// `product` is terminal-product-only; reject it elsewhere. A field that cannot act on this
|
|
61
|
+
// route is a parse error and is never silently dropped.
|
|
61
62
|
if (source !== "terminal-product" && source !== "desktop-cli" && raw.product !== undefined) {
|
|
62
63
|
return invalid("`subject.product` applies only to terminal-product and desktop-cli subjects (the CLI a participant studies from public surfaces).");
|
|
63
64
|
}
|
|
64
|
-
// appUrl is app-url/local-app-only; a terminal-product subject drives
|
|
65
|
-
// single loopback app
|
|
65
|
+
// appUrl is app-url/local-app-only; a terminal-product subject drives public surfaces instead of a
|
|
66
|
+
// single loopback app, so reject appUrl on it.
|
|
66
67
|
if (source === "terminal-product" && raw.appUrl !== undefined) {
|
|
67
|
-
return invalid("`subject.appUrl` does not apply to terminal-product
|
|
68
|
+
return invalid("`subject.appUrl` does not apply to a terminal-product subject: its agent works from the product's public pages. List them in `subject.product.publicSurfaces` instead.");
|
|
68
69
|
}
|
|
69
70
|
// serve/env/state are shared between clone (cloned app) and local-tree (packed working
|
|
70
71
|
// tree): both routes serve a subject in-sandbox with the same install/build/start/url +
|
|
@@ -78,7 +79,7 @@ function misplacedFieldFailure(source, raw) {
|
|
|
78
79
|
if (source !== "clone" && source !== "local-tree" && raw.state !== undefined) {
|
|
79
80
|
return invalid("`subject.state` applies only to clone subjects or local-tree subjects (the lab seeds the state it serves).");
|
|
80
81
|
}
|
|
81
|
-
// repos/clone are clone-
|
|
82
|
+
// repos/clone are clone-only (a fresh-clone subject's git inputs). local-tree packs the
|
|
82
83
|
// resolution cwd itself, so it has no repo slug to clone and gets its own precise reasons
|
|
83
84
|
// rather than falling through to the generic clone-only message below.
|
|
84
85
|
if (source === "local-tree" && raw.repos !== undefined) {
|
|
@@ -88,19 +89,19 @@ function misplacedFieldFailure(source, raw) {
|
|
|
88
89
|
return invalid("`subject.clone` does not apply to local-tree subjects. Declare `subject.localTree` instead (keep/exclude/maxArchiveBytes).");
|
|
89
90
|
}
|
|
90
91
|
// Rejected, never silently dropped, on app-url/local-app/this-repo/terminal-product subjects
|
|
91
|
-
// too
|
|
92
|
+
// too: a field that cannot act on this route is a parse error.
|
|
92
93
|
if (source !== "clone" && raw.repos !== undefined) {
|
|
93
94
|
return invalid("`subject.repos` applies only to clone subjects (the owner/repo slugs to clone).");
|
|
94
95
|
}
|
|
95
96
|
if (source !== "clone" && raw.clone !== undefined) {
|
|
96
97
|
return invalid("`subject.clone` applies only to clone subjects (clone depth/fanout/keep).");
|
|
97
98
|
}
|
|
98
|
-
// localTree is local-tree-
|
|
99
|
+
// localTree is local-tree-only (pack/upload knobs for the packed working tree).
|
|
99
100
|
if (source !== "local-tree" && raw.localTree !== undefined) {
|
|
100
101
|
return invalid("`subject.localTree` applies only to local-tree subjects (keep/exclude/maxArchiveBytes for packing the working tree).");
|
|
101
102
|
}
|
|
102
|
-
// publicTarget is app-url-
|
|
103
|
-
// meaningless without a real public deployment as the plane
|
|
103
|
+
// publicTarget is app-url-only (the external-public shared-world ownership attestation). It is
|
|
104
|
+
// meaningless without a real public deployment as the plane, so reject it elsewhere.
|
|
104
105
|
if (source !== "app-url" && raw.publicTarget !== undefined) {
|
|
105
106
|
return invalid("`subject.publicTarget` applies only to app-url subjects on the external-public shared-world route (the operator's ownership attestation for a real public deployment used directly as the shared plane).");
|
|
106
107
|
}
|
|
@@ -124,7 +125,7 @@ function parseCloneSubject(raw, subject) {
|
|
|
124
125
|
}
|
|
125
126
|
function parseLocalTreeSubject(raw, subject) {
|
|
126
127
|
// A local-tree subject exists to be packed and served; there is no other way to boot it, so
|
|
127
|
-
// serve is
|
|
128
|
+
// serve is required here. Clone subjects get the same requirement from each route's checks in
|
|
128
129
|
// parseLabConfig.
|
|
129
130
|
if (raw.serve === undefined) {
|
|
130
131
|
return invalid("`subject.serve` is required when source is local-tree: a local-tree subject exists to be packed and served, so declare install/build/start/url exactly like the clone route.");
|
|
@@ -156,11 +157,11 @@ function parseServedApp(raw, subject) {
|
|
|
156
157
|
if (raw.env !== undefined) {
|
|
157
158
|
const env = strList(raw.env);
|
|
158
159
|
if (!env || env.length === 0) {
|
|
159
|
-
return invalid("`subject.env` must be a non-empty list of
|
|
160
|
+
return invalid("`subject.env` must be a non-empty list of environment variable names when it is set.");
|
|
160
161
|
}
|
|
161
162
|
const badName = env.find((name) => !ENV_NAME_PATTERN.test(name));
|
|
162
163
|
if (badName) {
|
|
163
|
-
return invalid(`subject.env entries must be
|
|
164
|
+
return invalid(`subject.env entries must be environment variable names like DATABASE_URL, and "${badName}" is not one. Their values come from your environment and are never stored.`);
|
|
164
165
|
}
|
|
165
166
|
subject.env = env;
|
|
166
167
|
}
|
|
@@ -191,11 +192,11 @@ function parseAppSubject(raw, source, subject) {
|
|
|
191
192
|
}
|
|
192
193
|
// app-url: shape-only here; the loopback-vs-public-target gate is applied in the
|
|
193
194
|
// cross-validation block below, where policies.allowPublicTargets is available.
|
|
194
|
-
// local-app: an in-process local dev server
|
|
195
|
+
// local-app: an in-process local dev server, always loopback (no public-target option),
|
|
195
196
|
// so the loopback wall is enforced right here at parse.
|
|
196
197
|
if (source === "local-app") {
|
|
197
198
|
if (!isLoopbackUrl(appUrl)) {
|
|
198
|
-
return invalid("`subject.appUrl` must be a loopback URL (127.0.0.1
|
|
199
|
+
return invalid("`subject.appUrl` must be a loopback URL (127.0.0.1 or localhost) on a local-app subject: humanish drives your running dev server from this process, so it cannot reach a public URL.");
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
else if (!isHttpUrl(appUrl)) {
|
|
@@ -203,7 +204,7 @@ function parseAppSubject(raw, source, subject) {
|
|
|
203
204
|
}
|
|
204
205
|
subject.appUrl = appUrl;
|
|
205
206
|
// publicTarget (external-public shared-world ownership attestation) is app-url-only. Shape it
|
|
206
|
-
// here; its
|
|
207
|
+
// here; its required-on-that-route semantics live in externalPublicSharedWorldValidationReason.
|
|
207
208
|
if (source === "app-url" && raw.publicTarget !== undefined) {
|
|
208
209
|
const publicTargetResult = parsePublicTarget(raw.publicTarget);
|
|
209
210
|
if (!publicTargetResult.ok) {
|
|
@@ -225,22 +226,22 @@ function parseProductSubject(raw, subject) {
|
|
|
225
226
|
// "example-operator/lobby-trivia" or a bare org name). Slash allowed for the owner/repo convention.
|
|
226
227
|
export const PUBLIC_TARGET_OWNER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_./-]*$/;
|
|
227
228
|
/** Parse the external-public shared-world ownership attestation ({ owner, authorized: true }). The
|
|
228
|
-
* harness cannot verify ownership
|
|
229
|
+
* harness cannot verify ownership: this is author-trust, and the evidence class says so. */
|
|
229
230
|
function parsePublicTarget(raw) {
|
|
230
231
|
if (!isRecord(raw)) {
|
|
231
|
-
return invalid("`subject.publicTarget` must be an object
|
|
232
|
+
return invalid("`subject.publicTarget` must be an object, `{ owner, authorized: true }`, declaring that you own or operate the public deployment.");
|
|
232
233
|
}
|
|
233
234
|
const owner = str(raw.owner);
|
|
234
235
|
if (!owner || !PUBLIC_TARGET_OWNER_PATTERN.test(owner)) {
|
|
235
236
|
return invalid("`subject.publicTarget.owner` must be a public-safe operator/repo label (e.g. owner/repo); it is recorded in evidence, so it must carry no secret.");
|
|
236
237
|
}
|
|
237
238
|
if (raw.authorized !== true) {
|
|
238
|
-
return invalid("`subject.publicTarget.authorized` must be true
|
|
239
|
+
return invalid("`subject.publicTarget.authorized` must be true to declare that you own or operate the public deployment. humanish cannot check ownership, so the study states it.");
|
|
239
240
|
}
|
|
240
241
|
return { ok: true, value: { owner, authorized: true } };
|
|
241
242
|
}
|
|
242
243
|
// The product name interpolates into evidence labels and the composed prompt; the public-safe
|
|
243
|
-
// token shape is the same
|
|
244
|
+
// token shape is the same strict constraint as a lab id.
|
|
244
245
|
const PRODUCT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
|
|
245
246
|
function parseProduct(raw) {
|
|
246
247
|
if (!isRecord(raw)) {
|
|
@@ -262,7 +263,7 @@ function parseProduct(raw) {
|
|
|
262
263
|
if (upload.startsWith("/") ||
|
|
263
264
|
/^[A-Za-z]:/.test(upload) ||
|
|
264
265
|
upload.split(/[\\/]/).includes("..")) {
|
|
265
|
-
return invalid("`subject.product.upload` must stay inside the project
|
|
266
|
+
return invalid("`subject.product.upload` must stay inside the project: use a relative path with no `..` segments.");
|
|
266
267
|
}
|
|
267
268
|
}
|
|
268
269
|
const install = str(raw.install);
|
|
@@ -288,7 +289,7 @@ function parseProduct(raw) {
|
|
|
288
289
|
},
|
|
289
290
|
};
|
|
290
291
|
}
|
|
291
|
-
// Public-safe stance: a computer-use actor's
|
|
292
|
+
// Public-safe stance: a computer-use actor's entry URL is always an app the lab owner runs on
|
|
292
293
|
// loopback (inside the sandbox), never an arbitrary public site. (The constraint binds the
|
|
293
294
|
// entry point; a navigation watchdog for mid-session escapes is a later slice.) Exported so
|
|
294
295
|
// the engine re-enforces the same boundary on configs that arrive through the library API.
|
|
@@ -329,7 +330,7 @@ function parseServe(raw) {
|
|
|
329
330
|
}
|
|
330
331
|
const url = str(raw.url);
|
|
331
332
|
if (!url || !isLoopbackUrl(url)) {
|
|
332
|
-
return invalid("`subject.serve.url` must be a loopback http(s) URL (127.0.0.1 or localhost)
|
|
333
|
+
return invalid("`subject.serve.url` must be a loopback http(s) URL (127.0.0.1 or localhost), because the app runs inside the sandbox.");
|
|
333
334
|
}
|
|
334
335
|
const serve = { start, url };
|
|
335
336
|
const install = str(raw.install);
|
|
@@ -367,7 +368,7 @@ function parseClone(raw) {
|
|
|
367
368
|
/**
|
|
368
369
|
* Structural parse of `subject.localTree`, mirroring parseClone. Unlike parseClone (which
|
|
369
370
|
* silently drops an out-of-range depth/fanout), an invalid exclude/maxArchiveBytes value is
|
|
370
|
-
*
|
|
371
|
+
* rejected, never silently dropped: a caller who typed an empty exclude entry or a non-positive
|
|
371
372
|
* maxArchiveBytes almost certainly meant something, and the archive-size cap is a safety knob,
|
|
372
373
|
* not a cosmetic default.
|
|
373
374
|
*/
|
|
@@ -2,7 +2,6 @@ import type { LabConfigParseFailure } from "../types.js";
|
|
|
2
2
|
export declare const REPO_SLUG_PATTERN: RegExp;
|
|
3
3
|
export declare const ENV_NAME_PATTERN: RegExp;
|
|
4
4
|
export declare function invalid(message: string): LabConfigParseFailure;
|
|
5
|
-
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
6
5
|
export declare function str(value: unknown): string | undefined;
|
|
7
6
|
export declare function optionalStr(key: string, value: unknown): Record<string, string>;
|
|
8
7
|
export declare function strList(value: unknown): string[] | undefined;
|
|
@@ -12,6 +11,6 @@ export declare function strList(value: unknown): string[] | undefined;
|
|
|
12
11
|
*/
|
|
13
12
|
export declare function readPositiveInt(value: string | undefined, fallback: number): number;
|
|
14
13
|
export declare function posInt(value: unknown): number | undefined;
|
|
15
|
-
/** A non-negative finite number (0 allowed
|
|
14
|
+
/** A non-negative finite number (0 allowed; caps default to 0 = no-spend). Accepts a numeric
|
|
16
15
|
* string too, since YAML scalars can arrive as strings. */
|
|
17
16
|
export declare function nonNegNumber(value: unknown): number | undefined;
|
package/dist/lab/parse/values.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
// The slug interpolates into an in-sandbox shell command
|
|
1
|
+
// The slug interpolates into an in-sandbox shell command, so its shape is strict.
|
|
2
2
|
export const REPO_SLUG_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
3
3
|
export const ENV_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
4
4
|
export function invalid(message) {
|
|
5
|
-
return { ok: false, error: { code: "
|
|
6
|
-
}
|
|
7
|
-
export function isRecord(value) {
|
|
8
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5
|
+
return { ok: false, error: { code: "HUMANISH_STUDY_INVALID", message } };
|
|
9
6
|
}
|
|
10
7
|
export function str(value) {
|
|
11
8
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
@@ -51,7 +48,7 @@ export function posInt(value) {
|
|
|
51
48
|
}
|
|
52
49
|
return undefined;
|
|
53
50
|
}
|
|
54
|
-
/** A non-negative finite number (0 allowed
|
|
51
|
+
/** A non-negative finite number (0 allowed; caps default to 0 = no-spend). Accepts a numeric
|
|
55
52
|
* string too, since YAML scalars can arrive as strings. */
|
|
56
53
|
export function nonNegNumber(value) {
|
|
57
54
|
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|