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
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
# Contract Schema Index
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
schema constants, parsers, and validators in `src/` are authoritative. Rows
|
|
8
|
-
marked "reserved" name layering intent only; no code emits or validates them
|
|
9
|
-
yet. Do not emit a reserved schema.
|
|
3
|
+
This page maps the shipped contracts; it does not list every command or result envelope. The
|
|
4
|
+
exported types, schema constants, parsers and validators in `src/` are the source of truth. A row
|
|
5
|
+
marked "reserved" names layering intent only: no code emits or validates it yet, so do not emit a
|
|
6
|
+
reserved schema.
|
|
10
7
|
|
|
11
8
|
## Purpose
|
|
12
9
|
|
|
@@ -31,7 +28,7 @@ workflow without leaking private upstream truth into core.
|
|
|
31
28
|
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
|
32
29
|
| Run bundle | `humanish.run-bundle.v1` | `synthetic-run-bundle` |
|
|
33
30
|
| Adapter | `humanish.adapter.v1` | `synthetic-cli-adapter` |
|
|
34
|
-
|
|
|
31
|
+
| Study file | `humanish.study.v3` | `first-run` |
|
|
35
32
|
| Persona | `humanish.persona.v1` | `synthetic-maintainer` |
|
|
36
33
|
| Scenario | `humanish.scenario.v1` | `first-run-smoke` |
|
|
37
34
|
| Actor trace | `humanish.actor-trace.v1` | `synthetic-actor-trace` |
|
|
@@ -55,32 +52,72 @@ workflow without leaking private upstream truth into core.
|
|
|
55
52
|
| Adapter score | `humanish.adapter-score.v1` (`RunBundle.adapterScore`; namespaced; route-specific acceptance semantics) | see Product-Adapter Extension Seam below |
|
|
56
53
|
| Adapter artifact | `humanish.adapter-artifact.v1` (`RunBundle.adapterArtifacts[]`; namespaced; local relative proof references) | see Product-Adapter Extension Seam below |
|
|
57
54
|
| Shared-world evidence | `humanish.shared-world.v1` (additive `RunBundle.sharedWorld` + `RunBundle.attributionClass`; `topologyMode: sequential \| concurrent`) | see Shared-World Evidence below |
|
|
58
|
-
| Comms thread | `humanish.comms-thread.v1` (off-app email/SMS the app sent, captured; a `kind: log` run-dir artifact of
|
|
55
|
+
| Comms thread | `humanish.comms-thread.v1` (off-app email/SMS the app sent, captured; a `kind: log` run-dir artifact of digests only: from/to/subject/link digests + one OTP count, never raw) | see `comms` under Study File |
|
|
59
56
|
| Serve result | `humanish.serve-result.v1` (`src/observer/serve.ts` is authoritative) | none (command result envelope; see Serve Result below) |
|
|
60
57
|
| Serve control plane | reserved (`/_humanish/api/*` answers `501` `HUMANISH_SERVE_CONTROL_PLANE_DISABLED` in v1) | none |
|
|
61
58
|
|
|
62
|
-
##
|
|
63
|
-
|
|
64
|
-
Schema: `humanish.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
A
|
|
59
|
+
## Study File
|
|
60
|
+
|
|
61
|
+
Schema: `humanish.study.v3`. `src/lab/parse/study-v3.ts` rewrites a v3 document into the
|
|
62
|
+
normalized config in `src/lab/types.ts`, which `src/lab/config.ts` parses. A `humanish.lab.v2`
|
|
63
|
+
file still parses in 0.108 with one warning that names `humanish migrate`; 0.109 removes it.
|
|
64
|
+
|
|
65
|
+
A study declares its route and composes code primitives; it is not a hardcoded kind. The
|
|
66
|
+
top-level keys:
|
|
67
|
+
|
|
68
|
+
| Key | Meaning |
|
|
69
|
+
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `schema`, `id`, `title`, `description` | the document's identity |
|
|
71
|
+
| `route` | required: `preview`, `computer-use`, `shared-world`, `terminal` or `scripted` |
|
|
72
|
+
| `mode` | `dry-run` (the default) or `live` |
|
|
73
|
+
| `actor` | who drives it, one object: `type`, `model`, `localAgent`, `persona`, `mission`, `tasks`, `maxOutputTokens`, `reasoningEffort`, `stopWhen`, `dwell` |
|
|
74
|
+
| `participants` | a count, a homogeneous `{ count, instruction }`, or a list of entries (below) |
|
|
75
|
+
| `surfaces` | scripted only: `[desktop]` or `[desktop, mobile]` |
|
|
76
|
+
| `caps` | the route's budget (below) |
|
|
77
|
+
| `scenario` | scripted only: a committed scenario id or path |
|
|
78
|
+
| `subject`, `execution`, `policies`, `review`, `defaults`, `comms` | as below |
|
|
79
|
+
|
|
80
|
+
The parser reads only the declared route's keys. A key that route never reads is an error that
|
|
81
|
+
names the route, such as "route: computer-use does not read scenario. Remove it.", and an
|
|
82
|
+
unknown key is an error that lists the known ones; both are `HUMANISH_STUDY_INVALID`.
|
|
83
|
+
|
|
84
|
+
`participants` takes the forms its route accepts:
|
|
85
|
+
|
|
86
|
+
| Route | Forms | Default |
|
|
87
|
+
| -------------- | ------------------------------------------------------------------------------ | -------------- |
|
|
88
|
+
| `preview` | a count | 4 |
|
|
89
|
+
| `computer-use` | a count, a homogeneous object, or a list (a `local-app` subject takes no list) | 1 |
|
|
90
|
+
| `shared-world` | a list of at least two entries | none: required |
|
|
91
|
+
| `scripted` | none; `surfaces` instead | |
|
|
92
|
+
| `terminal` | none; one agent | |
|
|
93
|
+
|
|
94
|
+
`caps` keeps each route's meaning, and a route refuses the keys it does not read:
|
|
95
|
+
`computer-use` and `shared-world` read `maxUsd` and `maxTotalUsd`; `terminal` reads `maxUsd`,
|
|
96
|
+
`maxJobs` and `maxMinutes`; `preview` and `scripted` take no `caps`.
|
|
97
|
+
|
|
98
|
+
A v2 file maps as follows: `actors[0]` is `actor`; `lanes` is the `participants` list; a
|
|
99
|
+
`roster` group is an entry with `count`; `count` is the count; `count` with `laneFocus` is the
|
|
100
|
+
homogeneous object; `execution.caps` (computer-use, shared-world) or `scenario.caps`
|
|
101
|
+
(terminal) is `caps`; `scenario.mode` is `mode`; `scenario.ref` is `scenario`; and
|
|
102
|
+
`subject.topology: shared-world` is `route: shared-world`. A key the v2 inert-field table
|
|
103
|
+
marks inert for the route is not in v3.
|
|
104
|
+
|
|
105
|
+
The rest of this section names keys by their v3 spelling:
|
|
69
106
|
|
|
70
107
|
- `subject`: what the run acts on: `this-repo`, `clone` (owner/repo slugs,
|
|
71
108
|
optional in-sandbox `serve` + env var names + `state`), `local-tree` (the
|
|
72
109
|
operator's own working tree, packed on the host and provisioned in-sandbox
|
|
73
110
|
in place of a clone; see below), `app-url`
|
|
74
111
|
(loopback unless `policies.allowPublicTargets` declares an owned deployment),
|
|
75
|
-
`local-app` (an already-running
|
|
76
|
-
custom `
|
|
112
|
+
`local-app` (an already-running local dev server driven in-process via a
|
|
113
|
+
custom `ComputerUseExecutor`, no clone and no E2B desktop, always loopback),
|
|
77
114
|
`terminal-product` (a CLI/product a real autonomous terminal agent studies
|
|
78
|
-
from
|
|
115
|
+
from public surfaces only; see below), or `desktop-cli` (a computer-use
|
|
79
116
|
participant using a CLI in a terminal on a hosted desktop). A
|
|
80
117
|
`local-app` subject pairs a computer-use actor with `execution.target: local`
|
|
81
118
|
(or absent) and is library-assisted: the caller supplies
|
|
82
|
-
`
|
|
83
|
-
closed (`
|
|
119
|
+
`RunStudyOptions.inProcess.executor` + `createProvider`; with neither the engine fails
|
|
120
|
+
closed (`HUMANISH_COMPUTER_USE_LOCAL_APP_NO_EXECUTOR`), never a desktop attempt. See
|
|
84
121
|
[`docs/architecture/state-driven-executor.md`](../architecture/state-driven-executor.md);
|
|
85
122
|
- `subject.localTree` (`local-tree` subjects, computer-use route): pack/upload
|
|
86
123
|
knobs for the packed working tree: `exclude[]` (extra archive excludes on
|
|
@@ -93,8 +130,8 @@ A lab is a composition over code primitives, not a hardcoded kind:
|
|
|
93
130
|
`maxArchiveBytes` (upload size cap override; default 256 MiB). Routing requires `execution.target: e2b-desktop` and a
|
|
94
131
|
computer-use actor; `subject.serve`/`env`/`state` apply exactly as they do
|
|
95
132
|
on the clone route (identical install/build/start/state semantics). The
|
|
96
|
-
packed root is the
|
|
97
|
-
(an absolute path in a
|
|
133
|
+
packed root is the study resolution cwd; there is no path field, by design
|
|
134
|
+
(an absolute path in a study file would be a machine-specific,
|
|
98
135
|
unshareable, leak-prone artifact). Enumeration is git-aware when the root
|
|
99
136
|
is a git work tree (`git ls-files --cached --others --exclude-standard`,
|
|
100
137
|
honoring `.gitignore`) or a denylist-only recursive walk otherwise; an
|
|
@@ -104,17 +141,17 @@ A lab is a composition over code primitives, not a hardcoded kind:
|
|
|
104
141
|
`src/subject/local-tree-archive.ts`)
|
|
105
142
|
applies in both modes and is not overridable. The denylist matches names,
|
|
106
143
|
not contents; a secret in a file it does not name packs like any other
|
|
107
|
-
file, so review the pack summary line and use `localTree.exclude`. The
|
|
108
|
-
|
|
144
|
+
file, so review the pack summary line and use `localTree.exclude`. The study packs
|
|
145
|
+
once per run and uploads the identical archive to every fan-out participant. The
|
|
109
146
|
in-sandbox commit refresh clone subjects use is skipped: `.git` is never
|
|
110
147
|
uploaded, so identity comes from the host-side archive digest instead. See
|
|
111
|
-
[`docs/goals/local-tree-subject/goal.md`](https://github.com/danielgwilson/humanish/blob/main/docs/goals/local-tree-subject/goal.md);
|
|
148
|
+
[`docs/history/goals/local-tree-subject/goal.md`](https://github.com/danielgwilson/humanish/blob/main/docs/history/goals/local-tree-subject/goal.md);
|
|
112
149
|
- `subject.product` (terminal-product subjects): the product the agent studies.
|
|
113
|
-
`product.name` is a public-safe token (committed fixtures use a
|
|
150
|
+
`product.name` is a public-safe token (committed fixtures use a neutral mock
|
|
114
151
|
name); `product.publicSurfaces[]` is the list of http(s) URLs (docs, llms.txt,
|
|
115
|
-
skill manifest) that are the
|
|
116
|
-
clone/provision the product, so its provenance is recorded
|
|
117
|
-
|
|
152
|
+
skill manifest) that are the only world the agent sees. The study does not
|
|
153
|
+
clone/provision the product, so its provenance is recorded
|
|
154
|
+
unpinned. `serve`/`clone`/`state`/`repos`/`appUrl` are rejected on a
|
|
118
155
|
terminal-product subject (a field that cannot act on the route is a parse
|
|
119
156
|
error, not silently dropped). See
|
|
120
157
|
[`docs/architecture/terminal-product-route.md`](../architecture/terminal-product-route.md);
|
|
@@ -122,83 +159,80 @@ A lab is a composition over code primitives, not a hardcoded kind:
|
|
|
122
159
|
subject's state story. `state.seed[]` declares ordered, bounded
|
|
123
160
|
seed/migration/fixture steps (`{ name, command, when: before-build |
|
|
124
161
|
before-start | after-ready, timeoutMs }`) executed in-sandbox around the
|
|
125
|
-
serve sequence; `state.external[]` declares env var
|
|
126
|
-
appear in `subject.env`) pointing at state the
|
|
127
|
-
recorded as
|
|
162
|
+
serve sequence; `state.external[]` declares env var names (each must also
|
|
163
|
+
appear in `subject.env`) pointing at state the study does not control,
|
|
164
|
+
recorded as unpinned in provenance. Commands persist in evidence as
|
|
128
165
|
sha256-16 digests only, never as text;
|
|
129
|
-
- `
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
166
|
+
- `actor`: who drives it. On the computer-use, shared-world, scripted and
|
|
167
|
+
terminal routes, `actor.type` is a real dispatch key resolved against the
|
|
168
|
+
actor registry. On the preview route it remains a descriptive label (e.g.
|
|
169
|
+
`synthetic-persona`). The
|
|
133
170
|
`codex-exec` descriptor's direct `runSession` member is a fail-closed
|
|
134
|
-
compatibility entry, not the live runner; the terminal
|
|
171
|
+
compatibility entry, not the live runner; the terminal route
|
|
135
172
|
owns the live sandbox, auth, cap, evidence, and cleanup lifecycle.
|
|
136
|
-
`
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
participant (no E2B to fan out);
|
|
142
|
-
- `
|
|
143
|
-
each `{ id?, actorType?, surface?, caseGroup?, persona?, device?,
|
|
173
|
+
A `participants` count is the preview route's participant count (simCount),
|
|
174
|
+
and on the computer-use **E2B** route the homogeneous fan-out count (N
|
|
175
|
+
identical participants, each its own E2B desktop: separate worlds, cap 16).
|
|
176
|
+
The scripted route's `surfaces` is its surface list (`[desktop]`, the default,
|
|
177
|
+
or `[desktop, mobile]`). The in-process/local-app computer-use route stays
|
|
178
|
+
single participant (no E2B to fan out);
|
|
179
|
+
- a `participants` list (computer-use E2B route): a differentiated fan-out,
|
|
180
|
+
each entry `{ id?, actorType?, surface?, caseGroup?, persona?, device?,
|
|
144
181
|
instruction?, target?, entry?, host?, reasoningEffort?, stopWhen?, dwell? }` becoming one independent E2B desktop (or, on the
|
|
145
182
|
shared-world routes, one participant against the shared plane). `actorType`,
|
|
146
183
|
`surface`, and `caseGroup` are adapter-owned public-safe labels for grouping
|
|
147
184
|
simulated users; they are not core enums, and `actorType` is deliberately
|
|
148
|
-
separate from the execution dispatch key `
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
`lanes[].device` is XOR with a raw `execution.desktop.resolution`. Participant ids
|
|
185
|
+
separate from the execution dispatch key `actor.type`. A list sets the count,
|
|
186
|
+
so `--count` does not apply to it, and each entry's `instruction` is the steer;
|
|
187
|
+
an entry's `device` is XOR with a raw `execution.desktop.resolution`. Participant ids
|
|
152
188
|
default `lane-01`..`lane-NN` (`role-01`..`role-NN` for shared-world participants), must be unique, and name per-participant evidence paths
|
|
153
|
-
(`actors/<streamId>.json`, `screenshots/<laneId>/`). Cap 16 participants.
|
|
154
|
-
routes
|
|
155
|
-
|
|
156
|
-
`
|
|
157
|
-
- `
|
|
189
|
+
(`actors/<streamId>.json`, `screenshots/<laneId>/`). Cap 16 participants. The other
|
|
190
|
+
routes refuse a list (table above). `subject.clone.fanout` is rejected on the
|
|
191
|
+
computer-use, shared-world and scripted routes (declare fan-out with
|
|
192
|
+
`participants`). No current route reads `clone.fanout`;
|
|
193
|
+
- `participants[].target` (app-url × computer-use E2B route only): an
|
|
158
194
|
absolute browser URL that participant opens instead of `subject.appUrl`. This is the
|
|
159
|
-
setup-produced-target handoff for crawler/swarm
|
|
195
|
+
setup-produced-target handoff for crawler/swarm studies: an adapter may start any
|
|
160
196
|
topology it needs, then declare exactly which target each actor should drive.
|
|
161
|
-
If any participant declares `target`, every
|
|
197
|
+
If any participant declares `target`, every entry in the list must declare one.
|
|
162
198
|
Public/non-loopback targets still require `policies.allowPublicTargets: true`.
|
|
163
199
|
`target` is mutually exclusive with `entry`: `target` is an absolute app-url
|
|
164
200
|
browser target; `entry` is a shared-world participant's same-origin path;
|
|
165
|
-
- `
|
|
166
|
-
repeated
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
...), so the runtime and run bundle keep one normalized participant shape. `roster`
|
|
171
|
-
is XOR with explicit `lanes`, homogeneous `count`, and `laneFocus`;
|
|
201
|
+
- a `participants` entry with `count: n` (computer-use E2B route): a group of
|
|
202
|
+
repeated participants. It needs an `id` and the parser expands it to
|
|
203
|
+
`<id>-01` … `<id>-NN`, even when n is 1, before the engine runs (`viewer-01`,
|
|
204
|
+
`viewer-02`, ...), so the runtime and run bundle keep one normalized participant
|
|
205
|
+
shape. Ids are checked for collisions after expansion;
|
|
172
206
|
- `execution.concurrency` (computer-use E2B routes, including shared-world): a
|
|
173
|
-
|
|
207
|
+
cap on participants in flight at once. When omitted, every participant runs
|
|
174
208
|
simultaneously: independent participants resolve it from the final participant count,
|
|
175
209
|
after any `--count` override, and the parser fills `concurrency = participantCount`
|
|
176
|
-
for multi-participant shared-world
|
|
210
|
+
for multi-participant shared-world studies. Total sessions and spend are identical either way; only wall-clock
|
|
177
211
|
and simultaneity differ. Declaring a value below the participant count runs participants in
|
|
178
212
|
waves and emits a warning saying so, because a green waved run is otherwise
|
|
179
|
-
indistinguishable from the all-live run the author meant. Shared-world
|
|
213
|
+
indistinguishable from the all-live run the author meant. Shared-world studies
|
|
180
214
|
need at least 2 (the host and a follower are live together); `concurrency: 1`
|
|
181
215
|
there is refused at parse, since the sequential turn-taking route was removed
|
|
182
216
|
in 0.106.0. The env override
|
|
183
|
-
`HUMANISH_CUA_MAX_CONCURRENCY` may only
|
|
184
|
-
raise concurrent paid desktops
|
|
217
|
+
`HUMANISH_CUA_MAX_CONCURRENCY` may only lower the effective bound, never
|
|
218
|
+
raise concurrent paid desktops, and a lowering is recorded on
|
|
185
219
|
the plan (`envLoweredConcurrencyFrom`). Inert (warned) on other routes.
|
|
186
|
-
`execution.timeoutMs` is the
|
|
220
|
+
`execution.timeoutMs` is the per-participant session budget on this route (semantics
|
|
187
221
|
change: it was the single-session budget pre-fan-out); there is no run-level
|
|
188
222
|
wall clock. `policies.allowPublicTargets` cannot combine with N>1 against one
|
|
189
223
|
implicit public `subject.appUrl` (ambiguous shared-world-ish topology); it may
|
|
190
|
-
combine with N>1 only when
|
|
191
|
-
|
|
192
|
-
routes N>1 against one public target to the
|
|
224
|
+
combine with N>1 only when every `participants` entry declares `target`, or when
|
|
225
|
+
the study declares `route: shared-world`. That
|
|
226
|
+
routes N>1 against one public target to the external-public shared-world plane
|
|
193
227
|
(see Shared-World Evidence below), not separate worlds;
|
|
194
228
|
- `subject.publicTarget: { owner, authorized: true }` (external-public shared-world
|
|
195
|
-
route
|
|
196
|
-
deployment (`source: app-url` + `
|
|
229
|
+
route only): the operator's required ownership attestation when a real public
|
|
230
|
+
deployment (`source: app-url` + `route: shared-world` + `allowPublicTargets` +
|
|
197
231
|
`concurrency > 1`) is used directly as the shared plane. The harness neither
|
|
198
232
|
provisions nor exposes the target, so it cannot attest synthetic data; the operator
|
|
199
|
-
|
|
200
|
-
subjects and inert (warned) on other app-url routes. `
|
|
201
|
-
- `subject.appUrl`: a loopback http(s) URL a computer-use actor drives,
|
|
233
|
+
must attest they own/operate it. Author-trust (unverifiable); rejected on non-app-url
|
|
234
|
+
subjects and inert (warned) on other app-url routes. `participants[].host: true` marks the single designated host participant there;
|
|
235
|
+
- `subject.appUrl`: a loopback http(s) URL a computer-use actor drives, or (on the
|
|
202
236
|
external-public shared-world route) the non-loopback public deployment used as the
|
|
203
237
|
shared plane (with `publicTarget` + `allowPublicTargets`);
|
|
204
238
|
- `execution`: where it runs (`local`, `e2b-desktop`, or `e2b-terminal`), plus
|
|
@@ -210,18 +244,18 @@ dwell? }`. The parser expands it into
|
|
|
210
244
|
computer-use and scripted-browser routes refuse it when a library caller skips
|
|
211
245
|
the parser;
|
|
212
246
|
- `execution.desktop.template` (e2b-desktop computer-use and clone scripted-browser routes): a custom E2B
|
|
213
|
-
desktop
|
|
247
|
+
desktop template (image) name or ID the run launches on, for a subject that
|
|
214
248
|
needs runtimes the stock `desktop` image lacks (e.g. node/bun/a local Postgres
|
|
215
249
|
baked into an adopter-maintained image). Any non-empty string is a valid
|
|
216
250
|
name/id (no allowlist); a blank/whitespace value is rejected. Threaded to the
|
|
217
|
-
SDK's `Sandbox.create(template, opts)` on
|
|
218
|
-
single-participant and fan-out cua participants, and the shared-world subject
|
|
251
|
+
SDK's `Sandbox.create(template, opts)` on every desktop-creating route (the
|
|
252
|
+
single-participant and fan-out cua participants, and the shared-world subject and every actor
|
|
219
253
|
sandbox); when absent the call
|
|
220
254
|
stays the byte-stable `Sandbox.create(opts)` default (the stock template). The
|
|
221
255
|
template actually used is recorded in the run bundle as `desktopTemplate`
|
|
222
256
|
(public-safe, since a template name is not a secret). Inert (warned) on every route
|
|
223
257
|
that creates no desktop, incl. the in-process `local-app` cua route, so it is
|
|
224
|
-
never silently ignored
|
|
258
|
+
never silently ignored. Custom images need the
|
|
225
259
|
Desktop SDK's `xdotool` input support, `mktemp`, and the `C.UTF-8` locale:
|
|
226
260
|
every typed text is written to a private 0600 file and typed with
|
|
227
261
|
`LC_ALL=C.UTF-8 xdotool type --file` in one attempt. Without that locale a
|
|
@@ -238,79 +272,76 @@ dwell? }`. The parser expands it into
|
|
|
238
272
|
when known. Inert (warned) where this route-specific browser launcher is not
|
|
239
273
|
used;
|
|
240
274
|
- `execution.terminal` + `execution.runtimeAuth` (terminal-product route):
|
|
241
|
-
`terminal.transport` is `exec-stream`: captured
|
|
275
|
+
`terminal.transport` is `exec-stream`: captured non-interactive exec output
|
|
242
276
|
(stdin disabled); `pty` is rejected because labeling captured exec output as
|
|
243
|
-
an interactive PTY would overstate the mechanism (
|
|
244
|
-
|
|
277
|
+
an interactive PTY would overstate the mechanism (a duplex PTY
|
|
278
|
+
transport does not ship). `terminal.stdin` defaults to `disabled`
|
|
245
279
|
(`sent`/assisted input is rejected until the interventions ledger + a
|
|
246
280
|
non-comparable marker exist). `runtimeAuth` (`openai-env`, the default, or `openai-egress`)
|
|
247
|
-
declares the agent's runtime-auth channel, recorded as
|
|
281
|
+
declares the agent's runtime-auth channel, recorded as names only. On a live run, the engine
|
|
248
282
|
resolves the registered terminal descriptor and requires
|
|
249
283
|
`keyPlacement: in-sandbox-command-scoped` before creating a sandbox. With `openai-env`
|
|
250
284
|
the key is passed only to the agent command, never to `Sandbox.create` or
|
|
251
285
|
metadata; with `openai-egress` it stays in the host-side E2B header transform
|
|
252
286
|
set at sandbox creation, and the agent gets a placeholder. A dry-run neither
|
|
253
287
|
reads nor injects it;
|
|
254
|
-
- `
|
|
255
|
-
`
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
- `
|
|
288
|
+
- `mode`: `dry-run` (a synthetic bundle, no spend; the default) or `live`. On the
|
|
289
|
+
scripted route `live` gates real browser actuation against the declared app.
|
|
290
|
+
Provider spend stays $0 there by mechanism (no model runs);
|
|
291
|
+
- `scenario` (scripted route, required): a committed scenario id or path
|
|
292
|
+
(`humanish/scenarios/<id>.yaml` or a repo path) whose `browser.steps` are what
|
|
293
|
+
the actor executes, digest-pinned into bundle provenance. Every other route
|
|
294
|
+
refuses it;
|
|
295
|
+
- `caps` on the terminal route: `{ maxUsd, maxJobs, maxMinutes }`,
|
|
262
296
|
all non-negative numbers (0 = no-spend, the default). This budget bounds the
|
|
263
297
|
in-sandbox live key by mechanism: the live key is never exercised without a
|
|
264
298
|
fail-closed cap in force. `maxMinutes` is the
|
|
265
299
|
wall-clock kill; `maxUsd`/`maxJobs` are enforced fail-closed against the cost
|
|
266
|
-
ledger after the session (a run whose
|
|
267
|
-
`
|
|
300
|
+
ledger after the session (a run whose known spend exceeds the cap fails closed,
|
|
301
|
+
`HUMANISH_TERMINAL_CAPS_EXCEEDED`). Core records Codex provider spend as
|
|
268
302
|
unpriced tokens, so without a cost probe no line can trip a positive `maxUsd`:
|
|
269
303
|
a live run refuses `maxUsd > 0` before creating a sandbox
|
|
270
|
-
(`
|
|
304
|
+
(`HUMANISH_TERMINAL_UNPRICED_CAP`) unless the caller passes a `costProbe`
|
|
271
305
|
hook. The sandbox's server-side timeout is the steps before the
|
|
272
306
|
codex command (Node bootstrap, runtime version check, and product setup when
|
|
273
307
|
`subject.product.install` is declared) plus `maxMinutes` plus a 5-minute
|
|
274
308
|
teardown buffer; a live run refuses a `maxMinutes` that would take it past
|
|
275
|
-
E2B's one-hour limit (`
|
|
276
|
-
Terminal Cost Ledger And No-Spend Proof).
|
|
277
|
-
|
|
278
|
-
`scenario.caps.maxUsd` or `scenario.caps.maxTotalUsd` on a computer-use lab
|
|
279
|
-
is a parse error naming the matching `execution.caps` field, which is what
|
|
280
|
-
that route enforces;
|
|
309
|
+
E2B's one-hour limit (`HUMANISH_TERMINAL_CAPS_INVALID`). The no-spend proof is derived from that real ledger, never asserted (see
|
|
310
|
+
Terminal Cost Ledger And No-Spend Proof). `maxTotalUsd` is refused on the
|
|
311
|
+
terminal route;
|
|
281
312
|
- `policies`: `redactRepos`, `redactScreenshots`, `allowPublicTargets`,
|
|
282
313
|
`mediaPermission` (`prompt` | `granted`), and the
|
|
283
314
|
terminal-product credential-boundary booleans `allowPrivateRepoAccess`,
|
|
284
315
|
`allowProviderCredentials`, `allowPaymentCredentials`, `allowGitHubMutation`
|
|
285
|
-
(all
|
|
316
|
+
(all default false, deny-by-default; only the runtime LLM key enters, and
|
|
286
317
|
only command-scoped). The scripted-browser route is loopback-only and rejects
|
|
287
318
|
`redactScreenshots: true` (blur unimplemented there) and
|
|
288
319
|
`allowPublicTargets: true` fail-closed rather than ignoring them.
|
|
289
|
-
- `comms` (
|
|
320
|
+
- `comms` (hosted on the clone/local-tree computer-use route and the
|
|
290
321
|
shared-world getHost plane, or connected to an external catch on
|
|
291
322
|
app-url/operator-provided subjects): off-app
|
|
292
|
-
email the app itself sends, made a persona-driven testable surface.
|
|
323
|
+
email the app itself sends, made a persona-driven testable surface. Study
|
|
293
324
|
configuration rejects `comms.sms` and unknown channel names; message-bus SMS
|
|
294
325
|
types do not imply a supported SMS execution route. SMTP capture is supported
|
|
295
326
|
on provisioned routes with separate worlds and rejected for shared-world studies.
|
|
296
327
|
`comms.email` = `{ kind: fake, injectEnv?, port?, smtp?, recipients?, linkOrigin?, external? }`.
|
|
297
|
-
`injectEnv` is the
|
|
328
|
+
`injectEnv` is the adopter-named env var the app reads for its email-API base
|
|
298
329
|
URL (e.g. `RESEND_API_URL`); the harness sets it to an in-sandbox catch (so it
|
|
299
|
-
is
|
|
330
|
+
is not declared in `subject.env`) that captures the app's sends without touching
|
|
300
331
|
the internet. Verify the app actually reads that variable, because a run whose
|
|
301
332
|
catch captured zero sends warns at teardown for exactly that. `kind` must be
|
|
302
333
|
`fake`; a real inbox names a saved `comms.email.connection` instead; `port` ≤ 65534
|
|
303
334
|
(the catch reserves `port+1` for the read-only inbox listener the shared-world
|
|
304
|
-
route getHost-exposes). `recipients[]` = `{ lane, address? }`:
|
|
335
|
+
route getHost-exposes). `recipients[]` = `{ lane, address? }`: omit the list
|
|
305
336
|
and the parser fills one deterministic address per participant
|
|
306
|
-
(`<laneId>@example.test`) so every participant can do email
|
|
307
|
-
`lane` must be one of the
|
|
308
|
-
`lane-01..lane-NN` under
|
|
337
|
+
(`<laneId>@example.test`) so every participant can do email. When declared, a
|
|
338
|
+
`lane` must be one of the study's real participant ids (list or group ids, or the
|
|
339
|
+
generated `lane-01..lane-NN` under a count). An unknown participant id is a hard parse error
|
|
309
340
|
listing them, zero addressed participants is a hard error, partial coverage warns
|
|
310
341
|
with the uncovered participants. Each addressed participant's actor prompt is extended with
|
|
311
342
|
the full handoff: its address ("enter exactly that"), the inbox URL, and the
|
|
312
343
|
wait steering ("waiting for an email is normal, not a blocker").
|
|
313
|
-
`external`
|
|
344
|
+
`external` switches the funnel to an adopter-hosted catch, which is what
|
|
314
345
|
makes comms work on planes humanish does not provision (app-url /
|
|
315
346
|
operator-provisioned): `{ catchBaseUrl, inboxBaseUrl?, authTokenEnv? }`. The
|
|
316
347
|
operator runs the catch and points their own app's email-API base URL at it.
|
|
@@ -323,19 +354,19 @@ dwell? }`. The parser expands it into
|
|
|
323
354
|
`humanish-comms-catch` marker, so a proxy answering 200 for everything cannot
|
|
324
355
|
pass for a catch), the teardown drain over `GET /deliveries`, and the same
|
|
325
356
|
digest-only evidence. `authTokenEnv` names an env var holding a bearer token
|
|
326
|
-
for the drain read. The
|
|
357
|
+
for the drain read. The name is recorded as evidence; the value never
|
|
327
358
|
persists. A live run refuses a token shorter than 16 characters, or not
|
|
328
359
|
well-formed Unicode, before it probes the catch
|
|
329
|
-
(`
|
|
330
|
-
`
|
|
360
|
+
(`HUMANISH_COMPUTER_USE_COMMS_TOKEN_INVALID`,
|
|
361
|
+
`HUMANISH_SHARED_WORLD_COMMS_TOKEN_INVALID`), as
|
|
331
362
|
`humanish comms catch --token` does, and scrubs the token and its encoded
|
|
332
363
|
forms from every comms warning. Declaring `external` on a harness-provisioned subject warns: two
|
|
333
364
|
catches would exist and the app would point at humanish's own. `linkOrigin`
|
|
334
365
|
is an optional operator-declared origin the app bakes into links when it
|
|
335
366
|
differs from the serve origin; the harness rewrites captured links through it
|
|
336
367
|
so a clicked link resolves to a reachable host. Captured mail is drained into a
|
|
337
|
-
digest-only `humanish.comms-thread.v1` artifact (from/to/subject/link
|
|
338
|
-
an OTP
|
|
368
|
+
digest-only `humanish.comms-thread.v1` artifact (from/to/subject/link digests +
|
|
369
|
+
an OTP count; no raw address/link/code persists). The readable proof a
|
|
339
370
|
persona saw the email is its screenshots of the inbox page. Requires `python3`
|
|
340
371
|
in the subject sandbox (the stock E2B desktop template has it).
|
|
341
372
|
|
|
@@ -361,7 +392,7 @@ Adding an existing name with different settings refuses to overwrite it.
|
|
|
361
392
|
profiles and local key presence/source, not provider authentication or delivery.
|
|
362
393
|
`comms providers --json` returns `humanish.comms-providers.v1` with explicit
|
|
363
394
|
setup/receiving availability. The key store accepts `humanish keys set agentmail`.
|
|
364
|
-
Saving a connection neither modifies a
|
|
395
|
+
Saving a connection neither modifies a study nor creates a provider resource;
|
|
365
396
|
a supported study explicitly selects `comms.email: { connection: agentmail }`.
|
|
366
397
|
Optional `allowedOrigins` lists exact additional HTTP(S) origins; `linkOrigin`
|
|
367
398
|
participates in the existing provisioned-subject origin rewrite. Connection
|
|
@@ -379,15 +410,15 @@ acquisition and cleanup status. It contains no raw addresses, bodies, provider I
|
|
|
379
410
|
or content digests. `run.json` embeds the final projection as `commsReceiving` and
|
|
380
411
|
carries `publication.restrictions: [real-communications]`. Verification keeps the
|
|
381
412
|
run local-only regardless of screenshot redaction. See the
|
|
382
|
-
[receiving contract](
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
`
|
|
386
|
-
|
|
387
|
-
`
|
|
388
|
-
`humanish.run-bundle.v1` in every case.
|
|
389
|
-
|
|
390
|
-
per-participant session budget, worst-case sandbox-minutes), `lanes[]` (
|
|
413
|
+
[receiving contract](https://humanish.dev/docs/email-receiving).
|
|
414
|
+
|
|
415
|
+
A study's result has one schema on every route, `humanish.study-result.v1`
|
|
416
|
+
(`src/run/study-result.ts`), with `route`, `studyId` and, until 0.109, a deprecated `labId`
|
|
417
|
+
holding the same value. Route-specific fields stay on their route's result. `humanish run`
|
|
418
|
+
with no study, `observe`, and a refusal made before a route is picked keep
|
|
419
|
+
`humanish.run-result.v1`. The evidence record stays `humanish.run-bundle.v1` in every case.
|
|
420
|
+
The computer-use result carries `plan` (the pre-flight participant table: concurrency, waves,
|
|
421
|
+
per-participant session budget, worst-case sandbox-minutes), `lanes[]` (always present,
|
|
391
422
|
length 1 at N=1; per-participant status/session/sandbox/subject), and `laneSummary`
|
|
392
423
|
(passed/skipped/harnessErrors/hollow counts). The top-level `session`/`sandbox`
|
|
393
424
|
mirror the first participant and `subject.commit` is unanimity-gated across participants
|
|
@@ -396,14 +427,14 @@ the run bundle is byte-stable with the pre-fan-out output; only the result
|
|
|
396
427
|
projection changed. A fan-out run records a `cua-lab.fanout.plan` bundle event
|
|
397
428
|
(and a `cua-lab.fanout.fail-fast` event when a harness error skips queued participants);
|
|
398
429
|
`ok = observer.ok ∧ every participant ok ∧ no failed adapter or declared-scorer verdict`,
|
|
399
|
-
where a dry-run participant is ok as
|
|
430
|
+
where a dry-run participant is ok as synthetic and a live participant must pass
|
|
400
431
|
`participantPassed` (`src/run/judge.ts`): not skipped, status `passed`, no harness
|
|
401
432
|
error, engaged, and no self-reported blocker.
|
|
402
433
|
|
|
403
434
|
Explicit failed-participant reruns are supported on the CUA fan-out route via
|
|
404
|
-
`humanish
|
|
405
|
-
participant id is a declared `
|
|
406
|
-
The source run must be a live CUA fan-out bundle. humanish creates a
|
|
435
|
+
`humanish run <study> --rerun-failed-from <run-id> [--participants <ids>]`, where a
|
|
436
|
+
participant id is a declared `participants` entry id, or `lane-01`, `lane-02`, … by position.
|
|
437
|
+
The source run must be a live CUA fan-out bundle. humanish creates a new run for
|
|
407
438
|
the selected failed/blocked/timed-out/hollow participants (or explicit participant ids), leaves
|
|
408
439
|
the source verdict unchanged, and records lineage as `run.rerun` plus a
|
|
409
440
|
`cua-lab.fanout.rerun` event: source run id, selected participant ids, and previous participant
|
|
@@ -414,26 +445,28 @@ That field is the judge's status (`judgedStatus` in `src/run/judge.ts`), so a go
|
|
|
414
445
|
session that reported a blocker counts as blocked. A source bundle without `judgedStatus` falls back
|
|
415
446
|
to the stream's trace status, its actor status and completion reason, and a zero-action check.
|
|
416
447
|
|
|
417
|
-
|
|
418
|
-
committed public-safe
|
|
419
|
-
`.humanish/local/
|
|
420
|
-
not
|
|
421
|
-
|
|
448
|
+
Study files are human-authored `.yaml` source under `humanish/studies/*.yaml` for
|
|
449
|
+
committed public-safe studies, or ignored `.humanish/studies/*.yaml` /
|
|
450
|
+
`.humanish/local/studies/*.yaml` for private local dogfood. A v3 key the declared route
|
|
451
|
+
does not read is an error, so a study file never silently claims behavior that did not run.
|
|
452
|
+
A v2 file keeps its inert-field warnings until 0.109 (`humanish study show` shows them).
|
|
422
453
|
|
|
423
|
-
Committed fixture (`humanish/
|
|
454
|
+
Committed fixture (`humanish/studies/first-run.yaml`):
|
|
424
455
|
|
|
425
456
|
```yaml
|
|
426
|
-
schema: humanish.
|
|
457
|
+
schema: humanish.study.v3
|
|
427
458
|
id: first-run
|
|
428
459
|
title: First-run synthetic Observer
|
|
429
|
-
description:
|
|
460
|
+
description: >-
|
|
461
|
+
Writes a preview run: a synthetic run bundle and Observer for four participants. Needs no browser,
|
|
462
|
+
model or keys, and costs nothing. Run it with humanish run first-run.
|
|
463
|
+
route: preview
|
|
464
|
+
mode: dry-run
|
|
430
465
|
subject:
|
|
431
466
|
source: this-repo
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
scenario:
|
|
436
|
-
mode: dry-run
|
|
467
|
+
actor:
|
|
468
|
+
type: synthetic-persona
|
|
469
|
+
participants: 4
|
|
437
470
|
defaults:
|
|
438
471
|
open: true
|
|
439
472
|
```
|
|
@@ -462,38 +495,40 @@ Core-owned fields:
|
|
|
462
495
|
- `artifacts`
|
|
463
496
|
- `review`
|
|
464
497
|
- `feedbackCandidates`
|
|
465
|
-
- `
|
|
466
|
-
`{ id, path?, origin? }`, where `origin` is `committed` (humanish/
|
|
498
|
+
- `study` (optional, additive): which study file produced the run,
|
|
499
|
+
`{ id, path?, origin? }`, where `origin` is `committed` (`humanish/studies/`, or
|
|
500
|
+
`humanish/labs/` for a v2 file),
|
|
467
501
|
`ignored` (a local overlay), or `explicit` (a path the operator passed).
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
`
|
|
472
|
-
`persona.source
|
|
502
|
+
`lab` holds the same value until 0.109 removes it, so a 0.107 reader keeps working.
|
|
503
|
+
Both are absent on bundles written before this contract and on library callers who
|
|
504
|
+
hand a `StudyConfig` directly; such a run has no study. Readers go through
|
|
505
|
+
`studyProvenanceOf` (`src/run/study-provenance.ts`): a valid `study`, else a valid `lab`,
|
|
506
|
+
else `inferLegacyStudyId`, which reads the `persona.source` or `scenario.source`
|
|
507
|
+
convention `study:<id>`, or `lab:<id>` from runs written before 0.108, and nothing else.
|
|
473
508
|
- `subject` (optional, additive): structured subject provenance,
|
|
474
509
|
`{ source: clone | app-url | local-tree, repo?, commit?, archiveSha256?,
|
|
475
510
|
dirty?, envNames?, state }` where `state` is `{ provenance: seeded |
|
|
476
511
|
unpinned | declared-not-run | undeclared | external-public, seed?: [{ name, when,
|
|
477
512
|
commandDigest, ok?, exitCode?, timedOut?, durationMs? }], externalEnvNames?
|
|
478
|
-
}`. Emitted by the computer-use and shared-world
|
|
513
|
+
}`. Emitted by the computer-use and shared-world routes and the clone
|
|
479
514
|
scripted-browser route; absent on pre-existing and other bundles. `repo`/`commit` are clone-route fields; `archiveSha256`
|
|
480
515
|
(64-hex sha256, the local-tree provenance pin) and `dirty` (host git
|
|
481
516
|
porcelain status at pack time) are local-tree-route fields, additive under
|
|
482
517
|
`humanish.run-bundle.v1`: a dirty working tree cannot be commit-pinned, so
|
|
483
518
|
the archive content digest stands in for it. `commandDigest` is the
|
|
484
519
|
sha256-16 of the exact seed command; command text and env values never
|
|
485
|
-
appear. `humanish verify` fails closed when a
|
|
520
|
+
appear. `humanish verify` fails closed when a live `local-tree` bundle carries
|
|
486
521
|
no well-formed `archiveSha256`, in addition to the existing `subject state
|
|
487
522
|
provenance` check.
|
|
488
|
-
- `desktopTemplate` (optional, additive): the custom E2B desktop
|
|
523
|
+
- `desktopTemplate` (optional, additive): the custom E2B desktop template (image)
|
|
489
524
|
the run's sandbox(es) launched on, from `execution.desktop.template`, so the
|
|
490
|
-
evidence shows
|
|
525
|
+
evidence shows which image ran. Present only when a template was configured;
|
|
491
526
|
absent == the stock `desktop` template, so every pre-existing bundle is
|
|
492
527
|
byte-stable. Public-safe (a template name is not a secret).
|
|
493
528
|
- `attributionClass` (optional, additive): `isolated | shared-world`. Absent ==
|
|
494
529
|
`isolated`, so every existing bundle is byte-stable. It is the
|
|
495
|
-
interaction-attribution axis
|
|
496
|
-
evidence classes. Set to `shared-world` by the shared-world
|
|
530
|
+
interaction-attribution axis, independent of the persona-sampling
|
|
531
|
+
evidence classes. Set to `shared-world` by the shared-world route, paired
|
|
497
532
|
with `sharedWorld`.
|
|
498
533
|
- `sharedWorld` (optional, additive): the shared-world evidence block
|
|
499
534
|
(`humanish.shared-world.v1`); see [Shared-World Evidence](#shared-world-evidence)
|
|
@@ -567,43 +602,43 @@ feedbackCandidates: []
|
|
|
567
602
|
|
|
568
603
|
## Shared-World Evidence
|
|
569
604
|
|
|
570
|
-
The shared-world topology
|
|
571
|
-
default: N distinct participants drive
|
|
572
|
-
app + one seeded DB) so their actions interact through shared state. The
|
|
605
|
+
The shared-world topology is the declared override of the `per-lane-worlds`
|
|
606
|
+
default: N distinct participants drive one provisioned, mutable service plane (one
|
|
607
|
+
app + one seeded DB) so their actions interact through shared state. The one
|
|
573
608
|
subject plane is provisioned via `subject.source: clone` (a fresh `git clone`) or
|
|
574
609
|
`subject.source: local-tree` (the operator's own working tree, packed on the host
|
|
575
610
|
and provisioned in-sandbox in place of a clone - see `subject.localTree` above);
|
|
576
611
|
both sources are accepted on the shared-world route.
|
|
577
612
|
|
|
578
|
-
Current runs write only the
|
|
613
|
+
Current runs write only the concurrent shape below. The sequential shape came from the
|
|
579
614
|
sequential shared-world route, which 0.106.0 removed. Only bundles written before
|
|
580
615
|
0.106.0 carry it, and `humanish verify` still checks them
|
|
581
|
-
(`tests/run/legacy-sequential-shared-world.test.ts`). The
|
|
616
|
+
(`tests/run/legacy-sequential-shared-world.test.ts`). The sequential rules below apply
|
|
582
617
|
to those bundles.
|
|
583
618
|
|
|
584
|
-
A shared-world bundle adds
|
|
619
|
+
A shared-world bundle adds two additive, optional fields to `humanish.run-bundle.v1`
|
|
585
620
|
(absent on every other bundle, so they stay byte-stable):
|
|
586
621
|
|
|
587
622
|
- `attributionClass: isolated | shared-world`: a separate attribution axis
|
|
588
|
-
("how well did the run attribute
|
|
623
|
+
("how well did the run attribute interaction?"), distinct from the persona-sampling
|
|
589
624
|
evidence classes ("how representative is the actor?"). Absent == `isolated`.
|
|
590
|
-
- `sharedWorld` (`humanish.shared-world.v1`):
|
|
625
|
+
- `sharedWorld` (`humanish.shared-world.v1`): two variants discriminated by
|
|
591
626
|
`topologyMode: "sequential" | "concurrent"` (`sharedWorldEvidenceFindings` in `src/verify/shared-world.ts` branches on
|
|
592
|
-
it
|
|
627
|
+
it first; unknown/missing or a mismatched shape fails closed). Common fields:
|
|
593
628
|
- `topology: shared-world`
|
|
594
629
|
- `topologyMode: sequential | concurrent`
|
|
595
|
-
- `roleCount`: the
|
|
596
|
-
- `plane: { commit?, seedDigest, envNames, hostDigest?, exposure?, publicOriginDigest?, declaredOriginDigest? }`: the
|
|
630
|
+
- `roleCount`: the declared number of participants.
|
|
631
|
+
- `plane: { commit?, seedDigest, envNames, hostDigest?, exposure?, publicOriginDigest?, declaredOriginDigest? }`: the one
|
|
597
632
|
shared-plane provenance. `seedDigest` is the sha256-16 of the ordered seed-step
|
|
598
|
-
command digests (the seed
|
|
599
|
-
|
|
600
|
-
`publicOriginDigest` is
|
|
633
|
+
command digests (the seed recipe identity, not the runtime state); `envNames` are
|
|
634
|
+
names only. `hostDigest`/`exposure` are concurrent provisioned-getHost-only, and
|
|
635
|
+
`publicOriginDigest` is concurrent external-public-only (below).
|
|
601
636
|
- `planeClass?: provisioned-getHost | external-public` and `lobbyConvergenceDigest?` are
|
|
602
|
-
|
|
637
|
+
concurrent-only (below); both absent on the sequential shape.
|
|
603
638
|
- `attributionLimits: [...]`: the verify-enforced attribution ceiling (the set
|
|
604
639
|
differs per `topologyMode`, below).
|
|
605
640
|
|
|
606
|
-
|
|
641
|
+
Sequential shape (`topologyMode: sequential`; only in bundles written before
|
|
607
642
|
0.106.0):
|
|
608
643
|
- `sequence: [roleId, …]`: the role ids that actually took a turn, in declared order.
|
|
609
644
|
- `skippedTail` (optional, live sequential only): `{ afterRoleId, roles,
|
|
@@ -620,121 +655,121 @@ cause, maxTotalUsd?, estimatedTotalUsd? }`. Each ordered `roles` entry names
|
|
|
620
655
|
timeline that starts `cp-baseline`, alternates checkpoint → turn → checkpoint,
|
|
621
656
|
and ends on a checkpoint:
|
|
622
657
|
- checkpoint = `{ kind: checkpoint, name, digest, deltaFromPrev }`; `digest` is
|
|
623
|
-
sha256-16(scrub+redact(probe stdout)); the record is
|
|
658
|
+
sha256-16(scrub+redact(probe stdout)); the record is digest-only (no
|
|
624
659
|
value-shaped field). `deltaFromPrev` is true when the observed state changed
|
|
625
660
|
across the intervening turn.
|
|
626
661
|
- turn = `{ kind: turn, roleId, simId, streamId, commit?, seedDigest }` references
|
|
627
662
|
a real `RunBundle["simulations"][number]` and `RunBundle["streams"][number]`; carries the
|
|
628
663
|
plane provenance it observed
|
|
629
664
|
(identical across turns by construction; this is the single-plane proof).
|
|
630
|
-
- Sequential `attributionLimits`
|
|
665
|
+
- Sequential `attributionLimits` must contain `sequential-only`, `no-concurrent-races`,
|
|
631
666
|
and `delta-attributed-to-turn-not-action`.
|
|
632
667
|
|
|
633
|
-
|
|
634
|
-
getHost-exposed plane
|
|
635
|
-
- `plane.hostDigest`: sha256-16 of the harness-minted `getHost`
|
|
636
|
-
drove (a first-class provisioned-subject target
|
|
668
|
+
Concurrent shape (`topologyMode: concurrent`: N personas drive one
|
|
669
|
+
getHost-exposed plane at once; no `timeline`/`sequence`):
|
|
670
|
+
- `plane.hostDigest`: sha256-16 of the harness-minted `getHost` origin every actor
|
|
671
|
+
drove (a first-class provisioned-subject target). A digest, not the raw
|
|
637
672
|
URL: a getHost URL embeds the live sandbox id and matches the publish-safety e2b-URL
|
|
638
673
|
redaction, so it never lands raw in a published bundle (the raw tokenless URL is
|
|
639
|
-
surfaced only on the ephemeral
|
|
640
|
-
|
|
641
|
-
- `plane.exposure: synthetic`: the
|
|
674
|
+
surfaced only on the ephemeral study result). The orchestrator confirms the URL is
|
|
675
|
+
tokenless (no authKey) before digesting.
|
|
676
|
+
- `plane.exposure: synthetic`: the required author attestation that the subject behind
|
|
642
677
|
the internet-reachable getHost URL is synthetic seeded data (author-trust + a
|
|
643
|
-
provenance gate,
|
|
678
|
+
provenance gate, not a no-real-data guarantee).
|
|
644
679
|
- `laneWindows: [{ roleId, simId, streamId, startedAt, endedAt, verdict, routeHostDigest,
|
|
645
|
-
commit?, seedDigest }]`: one harness-clocked window per actor;
|
|
680
|
+
commit?, seedDigest }]`: one harness-clocked window per actor; overlapping windows
|
|
646
681
|
prove ≥2 personas were active simultaneously. `routeHostDigest` == `plane.hostDigest`
|
|
647
682
|
(every actor drove exactly the harness-minted host).
|
|
648
683
|
- `stateSeries: [{ timestamp, digest }]`: cadence digests of the shared world under
|
|
649
|
-
load (baseline + periodic + final).
|
|
650
|
-
|
|
684
|
+
load (baseline + periodic + final). Digest-only: the allowed-keys tripwire permits
|
|
685
|
+
only `timestamp` + `digest` (no per-delta→actor field, because causation under concurrency
|
|
651
686
|
is structurally inexpressible).
|
|
652
687
|
- `outcomes: [{ roleId, simId, streamId, status, completionReason?, ok }]`: per-persona
|
|
653
|
-
|
|
654
|
-
- Concurrent `attributionLimits`
|
|
688
|
+
outcome (the "M of N succeeded" headline).
|
|
689
|
+
- Concurrent `attributionLimits` must contain `concurrent`,
|
|
655
690
|
`best-effort-causal-attribution`, `non-deterministic-shared-state`,
|
|
656
691
|
`window-and-snapshot-granularity`, `contention-observed-not-proven-safe`,
|
|
657
|
-
`state-change-not-isolated-to-actors`, and
|
|
692
|
+
`state-change-not-isolated-to-actors`, and must not contain `sequential-only` or
|
|
658
693
|
`no-concurrent-races` (a sequential guarantee on a concurrent run is an overclaim).
|
|
659
694
|
|
|
660
695
|
The `shared-world evidence` check in `humanish verify` is fail-closed (live runs only;
|
|
661
|
-
dry-run contract bundles are skipped). It dispatches on `topologyMode`
|
|
662
|
-
|
|
696
|
+
dry-run contract bundles are skipped). It dispatches on `topologyMode` first.
|
|
697
|
+
Sequential: the timeline must be well-formed (start `cp-baseline`, strictly alternate,
|
|
663
698
|
end on a checkpoint, turn order == sequence, sequence length == roleCount == turn
|
|
664
699
|
count, no `laneWindows`); every turn's simId/streamId resolves; every checkpoint digest
|
|
665
|
-
is sha256-16 with
|
|
666
|
-
mandatory limits are present; and a
|
|
667
|
-
delta-on-pass gate).
|
|
668
|
-
cover exactly roleCount and stateSeries is present; the required limits are present
|
|
669
|
-
`plane.hostDigest` present and every `routeHostDigest` equals it
|
|
670
|
-
`plane.exposure == synthetic`
|
|
700
|
+
is sha256-16 with no value-shaped field; all turns share one plane provenance; the
|
|
701
|
+
mandatory limits are present; and a passed run shows ≥1 checkpoint `deltaFromPrev` (the
|
|
702
|
+
delta-on-pass gate). Concurrent: no `timeline`; laneWindows and outcomes each
|
|
703
|
+
cover exactly roleCount and stateSeries is present; the required limits are present and the forbidden ones absent;
|
|
704
|
+
`plane.hostDigest` present and every `routeHostDigest` equals it;
|
|
705
|
+
`plane.exposure == synthetic` and `subject.state.provenance == seeded` (the
|
|
671
706
|
synthetic-subject gate); stateSeries snapshots are digest-only (allowed-keys tripwire);
|
|
672
|
-
all laneWindows share
|
|
673
|
-
run
|
|
707
|
+
all laneWindows share one plane provenance; and the concurrency-on-pass gate: a passed
|
|
708
|
+
run must show ≥2 overlapping laneWindows and a stateSeries delta whose timestamp is
|
|
674
709
|
AT/AFTER an overlap interval start (otherwise it was not actually concurrent, or the
|
|
675
710
|
world never changed under load). The per-participant no-engagement guard applies to both.
|
|
676
|
-
Checkpoints / stateSeries persist digest-only by
|
|
677
|
-
detector lands.
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
fidelity; the
|
|
681
|
-
|
|
682
|
-
the checkpoint after A strictly precedes B's turn). It
|
|
683
|
-
causation, concurrency/races (sequential-only), or exact-state determinism.
|
|
684
|
-
each persona's own behavior at full fidelity; per-persona
|
|
685
|
-
world ("M of N");
|
|
686
|
-
under load (the stateSeries) with best-effort temporal correlation. It
|
|
711
|
+
Checkpoints / stateSeries persist digest-only by default until the
|
|
712
|
+
[PII/PHI detector](https://github.com/danielgwilson/humanish/issues/108) lands.
|
|
713
|
+
|
|
714
|
+
What the bundle can / cannot claim. Sequential: each role's own behavior at full
|
|
715
|
+
fidelity; the observed system outcome as an ordered digest sequence; and the
|
|
716
|
+
sequenced-interaction proof (role B entered a world already containing role A's mutation;
|
|
717
|
+
the checkpoint after A strictly precedes B's turn). It cannot claim action-granular
|
|
718
|
+
causation, concurrency/races (sequential-only), or exact-state determinism. Concurrent:
|
|
719
|
+
each persona's own behavior at full fidelity; per-persona outcome against the contended
|
|
720
|
+
world ("M of N"); proven concurrency (overlapping windows); and system-state evolution
|
|
721
|
+
under load (the stateSeries) with best-effort temporal correlation. It cannot claim
|
|
687
722
|
strict causal attribution of a delta to an actor (concurrent ⇒ ambiguous), determinism
|
|
688
|
-
of exact state, per-action granularity, or concurrency-
|
|
689
|
-
|
|
723
|
+
of exact state, per-action granularity, or concurrency-safety (races are observed, never
|
|
724
|
+
proven absent). Limits: the deterministic $0 gate proves the plumbing + the
|
|
690
725
|
attribution contract. A kept 2026-06-17 live receipt separately proves one
|
|
691
726
|
bounded three-persona trial against a synthetic plane. Neither the deterministic
|
|
692
727
|
gate nor that receipt proves scale, repeatability, or adopter-harness replacement.
|
|
693
728
|
|
|
694
|
-
### Concurrent plane classes: provisioned
|
|
729
|
+
### Concurrent plane classes: provisioned and external-public
|
|
695
730
|
|
|
696
|
-
The
|
|
731
|
+
The concurrent shape carries a plane-class discriminator, `sharedWorld.planeClass:
|
|
697
732
|
"provisioned-getHost" | "external-public"`. Absent == `provisioned-getHost` (every existing
|
|
698
|
-
concurrent bundle byte-stable). It gates
|
|
733
|
+
concurrent bundle byte-stable). It gates every getHost-specific verify assertion, so a getHost
|
|
699
734
|
claim can never leak onto the external-public class (or vice versa).
|
|
700
735
|
|
|
701
736
|
- `provisioned-getHost`: the historical plane described above: a `clone`/`local-tree` subject the
|
|
702
|
-
harness serves + getHost-exposes in-sandbox. The harness
|
|
737
|
+
harness serves + getHost-exposes in-sandbox. The harness minted the host, so it asserts the
|
|
703
738
|
synthetic-seeded attestation (`plane.exposure: synthetic` + `subject.state.provenance == seeded`),
|
|
704
739
|
the harness-minted host identity (`plane.hostDigest`, every `routeHostDigest == it`), and an
|
|
705
740
|
authoritative in-sandbox checkpoint `stateSeries` with a delta-on-pass.
|
|
706
741
|
|
|
707
|
-
- `external-public`: a real, operator-
|
|
708
|
-
(`subject.source: app-url` + `
|
|
709
|
-
`execution.concurrency > 1`).
|
|
742
|
+
- `external-public`: a real, operator-owned public deployment used directly as the shared plane
|
|
743
|
+
(`subject.source: app-url` + `route: shared-world` + `policies.allowPublicTargets: true` +
|
|
744
|
+
`execution.concurrency > 1`). No getHost, no clone, no subject sandbox, no seed. The subject
|
|
710
745
|
carries the ownership attestation `subject.publicTarget: { owner, authorized: true }` (the
|
|
711
|
-
analog of `exposure: synthetic`: you cannot attest synthetic on a real site, but you
|
|
746
|
+
analog of `exposure: synthetic`: you cannot attest synthetic on a real site, but you must attest
|
|
712
747
|
you own/operate it; author-trust, unverifiable by the harness). Evidence deltas, all
|
|
713
748
|
asserted-absent (never silently dropped):
|
|
714
|
-
- `plane.publicOriginDigest`: sha256-16 of the one
|
|
715
|
-
`laneWindow.routeHostDigest` (computed from that participant's CDP-
|
|
749
|
+
- `plane.publicOriginDigest`: sha256-16 of the one observed origin the participants converged on. Every
|
|
750
|
+
`laneWindow.routeHostDigest` (computed from that participant's CDP-observed final-URL origin) equals it.
|
|
716
751
|
`plane.declaredOriginDigest` records the operator-declared origin as evidence only; a redirect
|
|
717
|
-
can make the two differ. This proves inter-participant
|
|
718
|
-
(
|
|
719
|
-
- `plane.exposure` is
|
|
752
|
+
can make the two differ. This proves inter-participant convergence on one origin, not harness control of the plane
|
|
753
|
+
(weaker than getHost's `hostDigest`, and disclosed).
|
|
754
|
+
- `plane.exposure` is absent (verify fails closed if `synthetic` appears, since that
|
|
720
755
|
attestation would be false on a real site);
|
|
721
|
-
`plane.hostDigest` is
|
|
756
|
+
`plane.hostDigest` is absent (the harness minted no host).
|
|
722
757
|
- `subject.state.provenance == "external-public"` (never `seeded`, `unpinned` or `undeclared`).
|
|
723
|
-
- `stateSeries` is
|
|
724
|
-
is
|
|
725
|
-
|
|
726
|
-
- `lobbyConvergenceDigest` (optional, strong, cheap): sha256-16 of the shared `/lobby/CODE`
|
|
727
|
-
all participants' CDP URLs converged on; present only when every participant converged on
|
|
758
|
+
- `stateSeries` is omitted (Option A): no in-sandbox filesystem to authoritatively digest, so there
|
|
759
|
+
is no authoritative shared-state proof. Concurrency-on-pass is relaxed to temporal co-occupancy
|
|
760
|
+
only (≥2 overlapping `laneWindows`); there is no state-delta requirement.
|
|
761
|
+
- `lobbyConvergenceDigest` (optional, strong, cheap): sha256-16 of the shared `/lobby/CODE` path
|
|
762
|
+
all participants' CDP URLs converged on; present only when every participant converged on one code. Digest-only
|
|
728
763
|
(the raw 6-char code and full URLs are runtime-only and never land).
|
|
729
|
-
- `attributionLimits`
|
|
764
|
+
- `attributionLimits` must contain the concurrent family plus `external-public-plane`,
|
|
730
765
|
`operator-attested-target-not-harness-controlled`, `no-synthetic-attestation`,
|
|
731
|
-
`no-authoritative-shared-state-proof`, `concurrency-by-temporal-co-occupancy-only`; and
|
|
766
|
+
`no-authoritative-shared-state-proof`, `concurrency-by-temporal-co-occupancy-only`; and must not
|
|
732
767
|
contain `sequential-only`, `no-concurrent-races`, or any `seeded`/`synthetic` limit.
|
|
733
768
|
|
|
734
|
-
The getHost synthetic gate is deliberately
|
|
735
|
-
exists because getHost is internet-reachable
|
|
769
|
+
The getHost synthetic gate is deliberately not reachable from the app-url branch, and why: that gate
|
|
770
|
+
exists because getHost is internet-reachable and harness-owned (real data behind a harness-exposed
|
|
736
771
|
URL is the hazard). A public site the harness neither provisioned nor exposed has neither property,
|
|
737
|
-
so the gate's hazard does not exist there. Attribution stays `shared-world` (N participants,
|
|
772
|
+
so the gate's hazard does not exist there. Attribution stays `shared-world` (N participants, one plane); the
|
|
738
773
|
plane-control claim degrades from "harness-controlled" to "operator-attested, observed-only".
|
|
739
774
|
|
|
740
775
|
## Adapter
|
|
@@ -829,7 +864,7 @@ Legacy profiles retain medium defaults. `none_declared`/`none` access needs are
|
|
|
829
864
|
omitted. `keyboard_first` allows pointer fallback; `keyboard_only` does not.
|
|
830
865
|
These are prompt instructions, not evidence that a participant complied.
|
|
831
866
|
|
|
832
|
-
`humanish
|
|
867
|
+
`humanish study show` adds `personas: [{ id, resolved, brief? }]`. A brief contains
|
|
833
868
|
`compilerVersion`, persona-section `text`, its pre-redaction `digest`, `redacted`,
|
|
834
869
|
and an optional source-file `sourceDigest`. It excludes task criteria and runtime
|
|
835
870
|
grants. This is authored context; whether a selected actor consumes it depends on
|
|
@@ -858,7 +893,7 @@ Actors execute or simulate the trial. Actor evidence is the provider-neutral
|
|
|
858
893
|
computer-use cycles, scripted browser steps, and in-sandbox terminal-agent exec
|
|
859
894
|
output all map onto one `ActorTrace`. Registered actors live in
|
|
860
895
|
`src/actors/registry.ts` (`codex-app-server`, `openai-computer-use`,
|
|
861
|
-
`local-agent`, `scripted-browser`, `codex-exec`). A
|
|
896
|
+
`local-agent`, `scripted-browser`, `codex-exec`). A study that names
|
|
862
897
|
`pi-agent-core` or `claude-agent-sdk` fails to parse. There is no
|
|
863
898
|
`humanish.actor.v1`; that name never shipped.
|
|
864
899
|
|
|
@@ -870,7 +905,7 @@ Core-owned fields:
|
|
|
870
905
|
`terminal-exec`)
|
|
871
906
|
- `lane` (`code` | `computer-use` | `scripted-browser` | `terminal`)
|
|
872
907
|
- `persona` (`id`, `traitsApplied`, `promptDigest`)
|
|
873
|
-
- `capabilities.keyPlacement` (`external` | `in-sandbox-command-scoped`):
|
|
908
|
+
- `capabilities.keyPlacement` (`external` | `in-sandbox-command-scoped`): where
|
|
874
909
|
the actor's runtime key lives. It is registry metadata the engine enforces. The
|
|
875
910
|
terminal agent declares `in-sandbox-command-scoped` (the agent-under-test runs
|
|
876
911
|
inside the sandbox); every other actor is `external` (absent === external).
|
|
@@ -881,7 +916,7 @@ Core-owned fields:
|
|
|
881
916
|
meaning the subject failed the script while the harness executed faithfully; and
|
|
882
917
|
`budget_reached`: a computer-use session ended by a limit. The loop
|
|
883
918
|
(`src/actors/computer-use/loop/ending.ts`) emits it when the time budget runs
|
|
884
|
-
out after at least one material action, when `
|
|
919
|
+
out after at least one material action, when `caps.maxUsd` or the
|
|
885
920
|
study's shared spend threshold is crossed, when the adapter reports a local
|
|
886
921
|
admission limit, or when the provider hits its output or context token limit.
|
|
887
922
|
Every `budget_reached` session has status `incomplete`
|
|
@@ -893,24 +928,24 @@ Core-owned fields:
|
|
|
893
928
|
may carry `cacheWriteInput` (tokens billed at the provider's cache-write rate,
|
|
894
929
|
OpenAI 5.6+) and `turns[]` (per provider-request usage, the recorded fact
|
|
895
930
|
long-context tier pricing needs). Both are additive and absent on
|
|
896
|
-
producers that do not report them
|
|
931
|
+
producers that do not report them. Items may
|
|
897
932
|
carry `at` (ISO-8601 recording stamp from the loop's clock) and, on
|
|
898
|
-
click-like `ui_action` items, structured `coord` (`x`/`y`). Both are additive
|
|
899
|
-
|
|
933
|
+
click-like `ui_action` items, structured `coord` (`x`/`y`). Both are additive:
|
|
934
|
+
absent on older bundles and non-stamping producers, and absence means
|
|
900
935
|
"timing/position unrecorded", never zero
|
|
901
|
-
- optional `modelSettings` (`humanish.model-settings.v1
|
|
936
|
+
- optional `modelSettings` (`humanish.model-settings.v1`): how the model was
|
|
902
937
|
asked to run, alongside `ids.model` which says which model it was.
|
|
903
|
-
`reasoningEffort` records the value the request
|
|
904
|
-
including the provider default when a
|
|
938
|
+
`reasoningEffort` records the value the request actually carried,
|
|
939
|
+
including the provider default when a study declared nothing. The resolved
|
|
905
940
|
value is what produced the trace, and recording "unset" would misdescribe
|
|
906
941
|
the run. Absent when a provider declares no settings and on every
|
|
907
942
|
pre-existing bundle; tolerated by verify. It exists because effort was
|
|
908
|
-
unreachable from a
|
|
909
|
-
is part of
|
|
943
|
+
unreachable from a study, which made every run take the default silently. Effort
|
|
944
|
+
is part of who the participant was rather than of how the instrument was tuned
|
|
910
945
|
(docs/principles/actor-fidelity.md), so a trace without it is a result missing
|
|
911
946
|
half its sample description
|
|
912
947
|
- optional `modelSettings.maxOutputTokens`: the declared positive integer
|
|
913
|
-
`
|
|
948
|
+
`actor.maxOutputTokens`, passed to every first-party OpenAI CUA response
|
|
914
949
|
request, including reasoning output. Absent when undeclared. The first request
|
|
915
950
|
of a session and closing reports use `min(maxOutputTokens, 1024)`, or 1024 when
|
|
916
951
|
undeclared; the first request also carries the opening screenshot as an
|
|
@@ -923,17 +958,37 @@ Core-owned fields:
|
|
|
923
958
|
“provider reply cut off by the output limit; asking again” notice. A second
|
|
924
959
|
cut-off reply to the same request ends the session with
|
|
925
960
|
`stopCause: provider_output_limit`.
|
|
926
|
-
- optional `
|
|
961
|
+
- optional `conversation`: how the first-party OpenAI computer-use provider
|
|
962
|
+
carried the conversation between requests. `mode` is `threaded` (the server
|
|
963
|
+
kept it through `previous_response_id`) or `explicit_context` (the server kept
|
|
964
|
+
none). `explicitReason` says why: `configured` (`zeroDataRetention`) or
|
|
965
|
+
`zdr_rejection` (the organization rejected server-side state; `switchedAt` and
|
|
966
|
+
`switchedAtRequest` say when). In `explicit_context` each request carries the
|
|
967
|
+
conversation from the client, with `store: false` and the reasoning returned
|
|
968
|
+
in encrypted form. It carries the opening message, every exchange of a reply
|
|
969
|
+
with the outputs that answered it, and the latest reply. Past an estimated
|
|
970
|
+
64,000 input tokens the opening screenshot is dropped first, then the oldest
|
|
971
|
+
exchanges become lines of a note, sent as an assistant message, that keeps
|
|
972
|
+
their reasoning summaries, messages and actions as text. The two newest
|
|
973
|
+
exchanges are always carried whole, even when they alone pass the budget. `summarizedTurns` counts the earlier exchanges the latest
|
|
974
|
+
`explicit_context` request no longer carried whole: lines of the note, or
|
|
975
|
+
only counted once the note reached its cap.
|
|
976
|
+
`requests[]` has one entry per participant request: its `mode` and, in
|
|
977
|
+
`explicit_context`, `carriedExchanges`, `carriedScreenshots` and
|
|
978
|
+
`estimatedInputTokens`. Billed input per request is in `tokenUsage.turns`. A
|
|
979
|
+
participant that ran in `explicit_context` gets a run warning. Absent on other
|
|
980
|
+
providers and on earlier bundles
|
|
981
|
+
- optional `affordanceUse` (`humanish.affordance-use.v1`): which kind of route this
|
|
927
982
|
actor took (see Affordance Use below)
|
|
928
983
|
- optional `estimatedCost` (`humanish.actor-estimated-cost.v1`): a token-derived
|
|
929
|
-
cost
|
|
930
|
-
It is deliberately a
|
|
931
|
-
`costUsd` is
|
|
932
|
-
`estimatedCost.estimatedCostUsd` is a rate-table multiply, named
|
|
933
|
-
|
|
984
|
+
cost estimate for this participant (see Run Cost Summary And Estimated Actor Cost).
|
|
985
|
+
It is deliberately a different field from `tokenUsage.costUsd`: a bare
|
|
986
|
+
`costUsd` is reserved for a real provider-returned charge, while
|
|
987
|
+
`estimatedCost.estimatedCostUsd` is a rate-table multiply, named as an
|
|
988
|
+
estimate so a reader can never confuse the two. Absent on
|
|
934
989
|
the Codex app-server and scripted routes and on every pre-existing bundle; the
|
|
935
990
|
terminal route records a `null` estimate. A `null`
|
|
936
|
-
`estimatedCostUsd` is
|
|
991
|
+
`estimatedCostUsd` is declared absent (unknown rate / no usage), never 0.
|
|
937
992
|
|
|
938
993
|
Unexpected actor-loop diagnostics live inside `items[]` as
|
|
939
994
|
`kind: notice`, `status: error` rows. They are public-safe evidence, not crash
|
|
@@ -976,12 +1031,12 @@ items: []
|
|
|
976
1031
|
|
|
977
1032
|
Reserved: `humanish.substrate.v1` is named here for layering intent but has
|
|
978
1033
|
never shipped; no code emits or validates it. Substrate truth today lives
|
|
979
|
-
inside run bundles (per-stream transport and status) and
|
|
1034
|
+
inside run bundles (per-stream transport and status) and study execution config
|
|
980
1035
|
(`execution.target: local | e2b-desktop | e2b-terminal`). Do not emit this schema.
|
|
981
1036
|
|
|
982
1037
|
## Serve Result And Reserved Control-Plane Namespace
|
|
983
1038
|
|
|
984
|
-
`humanish
|
|
1039
|
+
`humanish observe --all` reports `humanish.serve-result.v1`. The exported `ServeResult`
|
|
985
1040
|
type and `SERVE_SCHEMA` constant in `src/observer/serve.ts` are authoritative:
|
|
986
1041
|
mode (`loopback | exposed | share-safe-open`), the loopback host/port,
|
|
987
1042
|
`publicUrl`, the `tunnel` provider/url, an `oauth` echo (`provider`,
|
|
@@ -1022,10 +1077,10 @@ discipline** that distinguishes three states and never conflates them:
|
|
|
1022
1077
|
- `usd: null` is **not measured**: no spend signal exists for the category on
|
|
1023
1078
|
this run. `null` is written explicitly (never `undefined`-omitted, never guessed
|
|
1024
1079
|
to `0`). A line with `null` says "this category exists but we did not measure
|
|
1025
|
-
it"; the no-spend proof reports it as unmeasured and does
|
|
1080
|
+
it"; the no-spend proof reports it as unmeasured and does not claim it is zero.
|
|
1026
1081
|
- an **absent** line is **not applicable** (n/a) to the participant or run.
|
|
1027
1082
|
|
|
1028
|
-
`knownTotalUsd` sums
|
|
1083
|
+
`knownTotalUsd` sums only the non-null lines (a `null` line contributes nothing
|
|
1029
1084
|
and is never coerced to `0`); `fullyMeasured` is true only when no line is null.
|
|
1030
1085
|
Core meters only the `provider` line, populated from the actor trace's
|
|
1031
1086
|
`tokenUsage.costUsd` when present (else `null`); `product`/`media`/`payment`
|
|
@@ -1034,7 +1089,7 @@ cost-probe seam. The terminal route records the model as `codex` and does not pi
|
|
|
1034
1089
|
model, so a measured token count stays `usd: null` with `source: unpriced-token-usage`, and
|
|
1035
1090
|
`knownTotalUsd: 0` with `fullyMeasured: false` means no line of this ledger was priced. E2B
|
|
1036
1091
|
time for the terminal sandbox is not a line of this ledger, whose lines are checked against
|
|
1037
|
-
`
|
|
1092
|
+
`caps.maxUsd`. The run bundle's `cost` summary prices it as a `desktop-minutes`
|
|
1038
1093
|
line (see Run Cost Summary And Estimated Actor Cost below).
|
|
1039
1094
|
|
|
1040
1095
|
```yaml
|
|
@@ -1049,10 +1104,10 @@ knownTotalUsd: 0
|
|
|
1049
1104
|
fullyMeasured: false
|
|
1050
1105
|
```
|
|
1051
1106
|
|
|
1052
|
-
The no-spend proof (`humanish.terminal-no-spend-proof.v1`) is
|
|
1107
|
+
The no-spend proof (`humanish.terminal-no-spend-proof.v1`) is derived from the
|
|
1053
1108
|
ledger. It vouches only for what it measured: `knownZeroLines` (proven zero),
|
|
1054
1109
|
`knownNonZeroLines` (break `satisfied`), and `unmeasuredLines` (the `null` lines
|
|
1055
|
-
it explicitly
|
|
1110
|
+
it explicitly cannot vouch for). `satisfied` is true only when every known line
|
|
1056
1111
|
is within `maxUsd` (for a no-spend run, `maxUsd: 0` ⇒ every known line is `0`).
|
|
1057
1112
|
Unmeasured lines never break it, so a ledger whose lines are all `null` is
|
|
1058
1113
|
`satisfied` with nothing proven. The `statement` says which case applies: it
|
|
@@ -1072,9 +1127,9 @@ knownTotalUsd: 0
|
|
|
1072
1127
|
statement: "No-spend proof not established for maxUsd=0: no spend line was measured. …"
|
|
1073
1128
|
```
|
|
1074
1129
|
|
|
1075
|
-
**Full caps enforcement (fail-closed, not advisory).** `
|
|
1076
|
-
is enforced against the ledger: if the observed
|
|
1077
|
-
run fails closed (`
|
|
1130
|
+
**Full caps enforcement (fail-closed, not advisory).** `caps.maxUsd`
|
|
1131
|
+
is enforced against the ledger: if the observed known spend exceeds `maxUsd`, the
|
|
1132
|
+
run fails closed (`HUMANISH_TERMINAL_CAPS_EXCEEDED`); `maxJobs` likewise when
|
|
1078
1133
|
a known job count is present; `maxMinutes` is the wall-clock kill (unchanged).
|
|
1079
1134
|
Unknowns (`null`) never trip a cap (we cannot claim a violation we did not
|
|
1080
1135
|
measure) and never grant a green pass (they surface as unmeasured). `verifyRun`
|
|
@@ -1084,10 +1139,10 @@ proof claims zero on a `null` line, or when known spend exceeds the declared cap
|
|
|
1084
1139
|
## Run Status (identity + liveness index)
|
|
1085
1140
|
|
|
1086
1141
|
`humanish.run-status.v1` is `status.json`, written inside each run directory by
|
|
1087
|
-
every
|
|
1142
|
+
every route at run start, refreshed on a fixed cadence while the run is
|
|
1088
1143
|
alive, and finalized when it ends: `{ schema, runId, state: running |
|
|
1089
|
-
finished | interrupted, mode, lab?, pid, startedAt, updatedAt, completedAt?,
|
|
1090
|
-
signal?, outcome? }`.
|
|
1144
|
+
finished | interrupted, mode, study?, lab?, pid, startedAt, updatedAt, completedAt?,
|
|
1145
|
+
signal?, outcome? }`. `lab` repeats `study` until 0.109.
|
|
1091
1146
|
`outcome` carries the bundle's `verdict`, `participants` and `estimatedCostUsd`
|
|
1092
1147
|
when the run finishes, then the result's `ok` and `execution: { succeeded,
|
|
1093
1148
|
failures: [{ kind, message }], warnings? }` once the Observer has rendered. The
|
|
@@ -1116,26 +1171,26 @@ analysis. It then kills the E2B sandboxes the run's `sandbox-receipts.ndjson`
|
|
|
1116
1171
|
names, writes `reclaim-receipt.json` as `humanish reclaim` does, and exits
|
|
1117
1172
|
128+n. The bundle is not finished: run.json keeps its last live flush, and no
|
|
1118
1173
|
`outcome` is written. A `running` record that stopped refreshing still reads as
|
|
1119
|
-
interrupted once stale. A library caller of `
|
|
1174
|
+
interrupted once stale. A library caller of `runStudy` gets no signal handling. A
|
|
1120
1175
|
humanish older than this field rejects the state and falls back to the bundle's
|
|
1121
1176
|
own liveness.
|
|
1122
1177
|
|
|
1123
|
-
It answers two questions the filesystem could not answer before: **which
|
|
1178
|
+
It answers two questions the filesystem could not answer before: **which study**
|
|
1124
1179
|
a run belongs to, and **whether it is still alive**, including for runs an
|
|
1125
|
-
agent launched (`
|
|
1180
|
+
agent launched (`run --json`) or that were detached, which previously wrote
|
|
1126
1181
|
nothing at all until they completed.
|
|
1127
1182
|
|
|
1128
|
-
It is a
|
|
1183
|
+
It is a derived index, not evidence. `run.json` remains the evidence-of-record;
|
|
1129
1184
|
`verify` never gates on `status.json`, nothing in it is a claim about what a
|
|
1130
1185
|
participant did, and when the two disagree the bundle wins and the record is
|
|
1131
1186
|
rebuildable from it. A `running` record whose `updatedAt` is older than three
|
|
1132
|
-
touch intervals is
|
|
1187
|
+
touch intervals is interrupted, not alive. A dropped connection or a killed
|
|
1133
1188
|
terminal leaves exactly that shape, so the record reads as interrupted
|
|
1134
1189
|
(`classifyRunStatus` is the one shared definition). Fields are
|
|
1135
1190
|
public-safe by construction: no hostname and no user paths, because a run
|
|
1136
1191
|
directory may be shared.
|
|
1137
1192
|
|
|
1138
|
-
## Run Index, Run Detail, And The Terminal Surface
|
|
1193
|
+
## Run Index, Run Detail, And The Terminal Surface
|
|
1139
1194
|
|
|
1140
1195
|
Three derived projections that exist so a surface can list, classify and watch
|
|
1141
1196
|
runs without opening evidence for all of them. None is authoritative: `run.json`
|
|
@@ -1145,17 +1200,17 @@ them is a claim about what a participant did.
|
|
|
1145
1200
|
`humanish.run-index.v1` is the listing projection. One entry per run, read
|
|
1146
1201
|
cheapest-source-first: the `status.json` record, else the bundle, else the run
|
|
1147
1202
|
directory alone. `{ runId, derivedFrom: status | bundle | directory, liveness,
|
|
1148
|
-
mode?, pid?,
|
|
1203
|
+
mode?, pid?, study?, startedAt?, updatedAt?, completedAt?, verdict?,
|
|
1149
1204
|
participants?, estimatedCostUsd?, durationMs? }`. The point is cost: walking
|
|
1150
1205
|
every run tree and parsing every bundle measured 167ms on a 25-run project,
|
|
1151
1206
|
against 16ms cold and 2.8ms warm here, which is what makes a surface that
|
|
1152
1207
|
refreshes on a cadence affordable. `derivedFrom` is reported so a surprising row
|
|
1153
1208
|
can be traced to the file it came from. A run with receipts and no outcome is
|
|
1154
1209
|
`interrupted` (the shape a dropped connection leaves), and so is an
|
|
1155
|
-
|
|
1210
|
+
in-progress bundle reached without a status record: there is no freshness to
|
|
1156
1211
|
judge, and nothing in it says the run finished.
|
|
1157
1212
|
|
|
1158
|
-
`humanish.run-detail.v1` is the watching projection, for
|
|
1213
|
+
`humanish.run-detail.v1` is the watching projection, for one run. Who is in it
|
|
1159
1214
|
and what they are thinking: `{ runId, participants: [{ id, label, personaId?,
|
|
1160
1215
|
traits, status?, completionReason?, thought?, turns?, actions?, thoughts?,
|
|
1161
1216
|
estimatedCostUsd? }], observerPath?, automaticAnalysis? }`. It reads the actor trace from
|
|
@@ -1163,7 +1218,7 @@ estimatedCostUsd? }], observerPath?, automaticAnalysis? }`. It reads the actor t
|
|
|
1163
1218
|
finished, preferring the live one, so a single screen renders a run the whole
|
|
1164
1219
|
way through. A reasoning item still being written is skipped rather than quoted
|
|
1165
1220
|
half-finished, and the text is carried verbatim: a surface may wrap it, and
|
|
1166
|
-
nothing paraphrases it. Unlike the index this
|
|
1221
|
+
nothing paraphrases it. Unlike the index this does open the bundle, which is affordable
|
|
1167
1222
|
only because it is asked for the one run being watched.
|
|
1168
1223
|
|
|
1169
1224
|
`humanish.tui-result.v1` is what `humanish tui` emits when it refuses:
|
|
@@ -1172,23 +1227,23 @@ only because it is asked for the one run being watched.
|
|
|
1172
1227
|
`HUMANISH_TUI_BUNDLE_MISSING`. `HUMANISH_TUI_AGENT_SESSION` is the one a TTY
|
|
1173
1228
|
check could not catch: `codex exec` allocates a PTY for the commands it runs, so
|
|
1174
1229
|
both streams are terminals and the surface used to open. A study watched an
|
|
1175
|
-
agent move through the
|
|
1176
|
-
(humanish/
|
|
1230
|
+
agent move through the studies list and start a run it did not mean to start
|
|
1231
|
+
(humanish/studies/handed-a-human-surface.yaml). The refusal names the environment variable
|
|
1177
1232
|
that identified the runner, so the reader can check the claim, and `--force` is
|
|
1178
1233
|
the escape for a person who really is at that keyboard. Every other
|
|
1179
1234
|
command is built for an agent to drive; this one takes the screen and waits for
|
|
1180
1235
|
a person, so a non-interactive stdin or stdout fails closed naming the commands
|
|
1181
|
-
that
|
|
1236
|
+
that do answer the question rather than rendering escape codes into a pipe.
|
|
1182
1237
|
|
|
1183
1238
|
## Run Cost Summary And Estimated Actor Cost
|
|
1184
1239
|
|
|
1185
|
-
The computer-use (CUA) route surfaces an
|
|
1240
|
+
The computer-use (CUA) route surfaces an advisory, additive cost estimate. It is
|
|
1186
1241
|
never authoritative: every dollar figure is a rate-table multiply, labeled
|
|
1187
|
-
"estimated (rates as of `<date>`)", and is
|
|
1188
|
-
|
|
1242
|
+
"estimated (rates as of `<date>`)", and is never presented as a provider charge.
|
|
1243
|
+
Three new `.v1` schema tags ship, all additive and optional so
|
|
1189
1244
|
`humanish.run-bundle.v1` stays v1 and every pre-existing bundle is byte-stable:
|
|
1190
1245
|
|
|
1191
|
-
- `humanish.pricing.v1`: the
|
|
1246
|
+
- `humanish.pricing.v1`: the operator-editable rate table in `src/run/pricing.ts`:
|
|
1192
1247
|
dated per-model input/output USD-per-token rates and E2B CPU/GiB-second
|
|
1193
1248
|
rates, each with a public pricing-page `source` and an `asOf`
|
|
1194
1249
|
date. A prominent banner says these are estimates to update when providers
|
|
@@ -1198,9 +1253,9 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
|
|
|
1198
1253
|
request; priced exactly only from the trace's per-request `turns` ledger;
|
|
1199
1254
|
totals alone never re-tier, which errs toward under-estimating). Some
|
|
1200
1255
|
entries are `placeholder: true` stand-ins (the legacy desktop helper's
|
|
1201
|
-
8-vCPU/8-GiB planning assumption); an operator
|
|
1256
|
+
8-vCPU/8-GiB planning assumption); an operator must confirm them before trusting
|
|
1202
1257
|
the magnitude; the flag propagates into every estimate so a stand-in is never
|
|
1203
|
-
mistaken for a live rate. An
|
|
1258
|
+
mistaken for a live rate. An unknown model/desktop rate is declared absent
|
|
1204
1259
|
(`estimatedCostUsd: null` + a `reason`), never guessed.
|
|
1205
1260
|
- `humanish.actor-estimated-cost.v1` (`ActorTrace.estimatedCost`): one participant's
|
|
1206
1261
|
token-derived model cost, with `estimatedCostUsd` (or `null` + `reason`
|
|
@@ -1209,7 +1264,7 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
|
|
|
1209
1264
|
arrived as turn totals, such as Codex `turn.completed`, priced at the base tier because no single
|
|
1210
1265
|
request's size is known; the run summary's `model-tokens` line carries the same `basis`.
|
|
1211
1266
|
- `humanish.run-cost-summary.v1` (`RunBundle.cost`): the sum of every participant's
|
|
1212
|
-
`model-tokens` lines
|
|
1267
|
+
`model-tokens` lines plus `desktop-minutes` lines. New independent CUA runs
|
|
1213
1268
|
price each owned desktop separately; concurrent shared-world runs add one line per participant
|
|
1214
1269
|
and a `laneId: subject` desktop line for a provisioned plane; older single aggregate
|
|
1215
1270
|
lines remain readable.
|
|
@@ -1222,23 +1277,23 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
|
|
|
1222
1277
|
Live terminal-product runs emit the same summary: one `desktop-minutes` line for
|
|
1223
1278
|
the E2B shell sandbox (its acquired-to-cleanup span and `e2b.getInfo` size) and
|
|
1224
1279
|
one `model-tokens` line for the Codex participant, priced from the model the route passed
|
|
1225
|
-
to Codex (`gpt-5.6-sol` unless the
|
|
1280
|
+
to Codex (`gpt-5.6-sol` unless the study declares one) with `basis:
|
|
1226
1281
|
aggregated_turns_base_rate`, or `null` with `no_token_usage`. Older terminal bundles record
|
|
1227
1282
|
`no_rate_for_model` (model `codex`). The terminal trace records the same `estimatedCost`. The sandbox line is not part of the
|
|
1228
|
-
terminal cost ledger, whose lines are checked against `
|
|
1283
|
+
terminal cost ledger, whose lines are checked against `caps.maxUsd`.
|
|
1229
1284
|
|
|
1230
|
-
The summary follows the
|
|
1231
|
-
`estimatedTotalUsd` sums
|
|
1232
|
-
|
|
1285
|
+
The summary follows the same null discipline as the terminal cost ledger above.
|
|
1286
|
+
`estimatedTotalUsd` sums only the non-null `breakdown` lines and is `null` iff
|
|
1287
|
+
every line is null (never coerced to `0`); a present-but-unpriceable line stays
|
|
1233
1288
|
in `breakdown` with `estimatedCostUsd: null` + a `reason` (it records that we
|
|
1234
1289
|
tried and could not price it) and contributes nothing. `fullyEstimated` is
|
|
1235
1290
|
`false` when any applicable line is null (the total is then a lower bound). A spend-free
|
|
1236
1291
|
live run has no lines and records `estimatedTotalUsd: 0` with `ratesAsOf: null`; verify
|
|
1237
1292
|
asks for a rates date only when a line is priced.
|
|
1238
1293
|
`placeholder` is true when any contributing rate is a stand-in; `ratesAsOf` is
|
|
1239
|
-
the
|
|
1240
|
-
as its stalest input, so
|
|
1241
|
-
keeps its own true `asOf`). `desktopMinutes` is a
|
|
1294
|
+
the min (oldest) `asOf` across contributing rates. An aggregate is only as fresh
|
|
1295
|
+
as its stalest input, so max would overclaim freshness (each `breakdown` line
|
|
1296
|
+
keeps its own true `asOf`). `desktopMinutes` is a host-side
|
|
1242
1297
|
acquired-handle→cleanup span, excluding allocation/startup before handle acquisition.
|
|
1243
1298
|
It approximates E2B's server-side billed lifetime. Plan fees, credits, and negotiated
|
|
1244
1299
|
prices are excluded; unknown remaining lifetime makes `fullyEstimated` false.
|
|
@@ -1272,17 +1327,17 @@ desktopMinutes: 1
|
|
|
1272
1327
|
note: "Estimated 11.60167 USD total…"
|
|
1273
1328
|
```
|
|
1274
1329
|
|
|
1275
|
-
`verifyRun` asserts
|
|
1330
|
+
`verifyRun` asserts labeling and provenance, never magnitude. Absence passes
|
|
1276
1331
|
(fail-open on display): a bundle with no cost, a null estimate that carries a
|
|
1277
|
-
`reason` and a null `ratesAsOf`, or a participant without `estimatedCost` verifies fine. A
|
|
1332
|
+
`reason` and a null `ratesAsOf`, or a participant without `estimatedCost` verifies fine. A claimed number fails closed when it lacks its
|
|
1278
1333
|
`ratesAsOf` date or `source`, or when `estimatedTotalUsd` does not equal the
|
|
1279
1334
|
rounded sum of its non-null lines (a null line coerced to 0 is a mechanism
|
|
1280
1335
|
mismatch). A correctly-labeled huge estimate still passes. Cost is neither a
|
|
1281
1336
|
secret nor a share-blocker: its magnitude never affects `shareSafety`. A labeling
|
|
1282
1337
|
failure fails verify, which blocks sharing like any other failed check.
|
|
1283
1338
|
|
|
1284
|
-
**Computer-use spend thresholds.** `
|
|
1285
|
-
to each participant. `
|
|
1339
|
+
**Computer-use spend thresholds.** `caps.maxUsd` applies independently
|
|
1340
|
+
to each participant. `caps.maxTotalUsd` shares an estimated model-spend ledger
|
|
1286
1341
|
across the study's participants. Either, both, or neither may be declared. Without a
|
|
1287
1342
|
shared threshold, N participants each have their own `maxUsd`; N × `maxUsd` is the sum
|
|
1288
1343
|
of those thresholds, not a guaranteed total spending ceiling.
|
|
@@ -1326,10 +1381,10 @@ threshold is not proof of task completion.
|
|
|
1326
1381
|
An absent threshold is uncapped. A zero threshold can still permit a paid model
|
|
1327
1382
|
request before reported usage trips it; `maxUsd: 0` is not a no-provider-call
|
|
1328
1383
|
mode. Use the keyless `humanish run first-run` preview or an explicit
|
|
1329
|
-
`humanish
|
|
1384
|
+
`humanish run <study> --dry-run` for a path without provider calls. A declared
|
|
1330
1385
|
threshold on a model `src/run/pricing.ts` cannot price is refused at preflight
|
|
1331
|
-
(`
|
|
1332
|
-
shared-world
|
|
1386
|
+
(`HUMANISH_COMPUTER_USE_UNPRICED_CAP`, or `HUMANISH_SHARED_WORLD_UNPRICED_CAP` on a
|
|
1387
|
+
shared-world study) before sandbox allocation. This rate-availability
|
|
1333
1388
|
check is separate from the post-response spend check.
|
|
1334
1389
|
|
|
1335
1390
|
Library callers can make a capped session stricter by composing it from the loop and the default
|
|
@@ -1370,19 +1425,19 @@ original interruption cause first, with any missing usage still recorded as
|
|
|
1370
1425
|
unknown.
|
|
1371
1426
|
|
|
1372
1427
|
These computer-use rules do not replace the terminal route's separate
|
|
1373
|
-
`
|
|
1428
|
+
`caps` cost-ledger and product-spend rules described above.
|
|
1374
1429
|
|
|
1375
1430
|
## Affordance Use
|
|
1376
1431
|
|
|
1377
|
-
`humanish.affordance-use.v1` (additive `ActorTrace.affordanceUse`) records
|
|
1378
|
-
|
|
1432
|
+
`humanish.affordance-use.v1` (additive `ActorTrace.affordanceUse`) records which
|
|
1433
|
+
kind of route an actor took, per dispatched action, as counts by class:
|
|
1379
1434
|
|
|
1380
1435
|
| Class | What it covers |
|
|
1381
1436
|
| ------------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
1382
1437
|
| `pointer` | click, double-click, drag, scroll: interaction with what is rendered |
|
|
1383
1438
|
| `keyboard` | typed text and key presses into the page |
|
|
1384
1439
|
| `speech` | spoken input from a `speak` action |
|
|
1385
|
-
| `url-navigation` | typing a URL (the `nav` subset); a
|
|
1440
|
+
| `url-navigation` | typing a URL (the `nav` subset); a human affordance, see below |
|
|
1386
1441
|
| `script-execution` | a `javascript:` or `data:` URL; not a human affordance |
|
|
1387
1442
|
| `devtools` | developer tooling opened by keyboard chord |
|
|
1388
1443
|
| `browser-internal` | `chrome://`, `about:`, `view-source:`, `devtools:`, `file:`, `edge:`, `brave:` (the browser, not the product) |
|
|
@@ -1393,45 +1448,49 @@ rate; `shortcutTotal` rolls up `script-execution` + `devtools` +
|
|
|
1393
1448
|
`browser-internal`, and a value of `0` is a meaningful result rather than an
|
|
1394
1449
|
absence.
|
|
1395
1450
|
|
|
1396
|
-
Direct URL navigation is deliberately its
|
|
1451
|
+
Direct URL navigation is deliberately its own class and is grouped with the
|
|
1397
1452
|
naturalistic classes, not with script execution: `load(url)` appears in 99.4% of
|
|
1398
1453
|
2,337 real human web demonstrations, so address-bar use is ordinary human
|
|
1399
1454
|
behavior and classifying it as a shortcut would make an ordinary human participant look
|
|
1400
1455
|
unfaithful. See [`docs/principles/actor-fidelity.md`](../principles/actor-fidelity.md)
|
|
1401
1456
|
for the evidence and the scoping of what a fidelity claim can mean.
|
|
1402
1457
|
|
|
1403
|
-
The record carries a
|
|
1458
|
+
The record carries a class and at most a scheme-shaped signal (`javascript:`,
|
|
1404
1459
|
`https:`, a devtools chord), never the typed text, which can be a password, a
|
|
1405
1460
|
session token, or an identifying URL path. Classification runs at dispatch
|
|
1406
1461
|
because the text exists only there: the trace's own action label deliberately
|
|
1407
1462
|
renders `type [N chars]`.
|
|
1408
1463
|
|
|
1409
|
-
The harness states
|
|
1464
|
+
The harness states no verdict about a class. Whether an affordance invalidates a
|
|
1410
1465
|
study depends on the population that study declares, which is product semantics
|
|
1411
1466
|
and belongs to the adopter's scorer. That scorer already receives the full trace,
|
|
1412
1467
|
so `affordanceUse` needs no extra wiring to reach it. Present on computer-use participants
|
|
1413
1468
|
that dispatched at least one action; absent elsewhere and on every pre-existing
|
|
1414
1469
|
bundle, and its absence is tolerated by verify.
|
|
1415
1470
|
|
|
1416
|
-
## Library options
|
|
1417
|
-
|
|
1418
|
-
`
|
|
1419
|
-
refusal comes back in the route's own result envelope, with no run directory, receipt or
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
|
1426
|
-
|
|
|
1427
|
-
| `
|
|
1428
|
-
| `
|
|
1429
|
-
| `
|
|
1430
|
-
| `
|
|
1431
|
-
| `
|
|
1432
|
-
| `
|
|
1433
|
-
|
|
1434
|
-
`
|
|
1471
|
+
## Library options
|
|
1472
|
+
|
|
1473
|
+
`runStudy(config, options)` checks its `RunStudyOptions` against the study's route before anything
|
|
1474
|
+
runs. A refusal comes back in the route's own result envelope, with no run directory, receipt or
|
|
1475
|
+
sandbox. The 0.107 names (`runLab`, `RunLabOptions`, `parseLabConfig`, `LAB_CONFIG_SCHEMA`,
|
|
1476
|
+
`LabConfig`, the `Lab*` result and event types, the nine `Cua*` loop types, `CuaAdmissionLimitError`
|
|
1477
|
+
and `BrowserLabScoringContext`) are deprecated aliases with the same value or class until 0.109
|
|
1478
|
+
(`src/library-aliases.ts`).
|
|
1479
|
+
|
|
1480
|
+
| Option | What it does | Routes |
|
|
1481
|
+
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1482
|
+
| `env` | Keys and subject env for the run. Defaults to `process.env` | all |
|
|
1483
|
+
| `scorer` | An `AdapterScorerModule` (`score`, `deriveFeedback`, and `deriveArtifacts` on browser routes) over the finished evidence. A scorer written for one context passes through `browserScorer` or `terminalScorer` | computer use, shared world, terminal |
|
|
1484
|
+
| `createProvider(ctx)` | The participant's brain. `ctx` is `ProviderContext`: `config`, `participant { id, index, count }` and `executor` | computer use: E2B, local VM or in process |
|
|
1485
|
+
| `inProcess.executor({ config, appUrl })` | Drives an `app-url` or `local-app` subject in process, one participant. Requires `createProvider` | computer use |
|
|
1486
|
+
| `prepareDesktop(desktop, target)` | Runs on an E2B sandbox before provisioning. `target` is `{ kind: "subject" }` or `{ kind: "participant", participant }` | computer use on E2B (each participant); shared world (provisioned: the subject, then each participant; external-public: each participant); scripted clone (the subject) |
|
|
1487
|
+
| `onEvent(event)` | Observes `StudyEvent`s, below | all |
|
|
1488
|
+
| `onStream(event)` | Awaited. `ready` after a participant's live stream starts; its `url` carries an auth key and must never be persisted. `ended` after the participant's sandbox is gone. A rejected `ready` becomes a run warning; an `ended` rejection is swallowed. Each event carries `participantId`, `recordId` (the id of this participant's entry in `run.json` `simulations[]`, such as `sim-001`, which `streams[].simId` also holds) and `streamId`; `ready` adds `sandboxId` and `url`. `simId` is the older name for `recordId`: its first read prints a `DeprecationWarning` with code `HUMANISH_STREAM_EVENT_FIELD_DEPRECATED`, and the next minor removes it. | computer use and shared world on E2B |
|
|
1489
|
+
| `analysisSignal` | Cancels post-run analysis only | routes with post-run analysis |
|
|
1490
|
+
| `onObserverReady(observer)` | Awaited before any participant desktop exists | live computer use; live shared world, where the provisioned subject sandbox is already billed |
|
|
1491
|
+
| `rerun.participantIds` | Participants to rerun from `rerun.sourceRunId` | computer use |
|
|
1492
|
+
|
|
1493
|
+
`StudyEvent` has four types:
|
|
1435
1494
|
|
|
1436
1495
|
- `plan`: the participants (`id`, `persona`, `device`, `instructionDigest`) before any sandbox
|
|
1437
1496
|
or provider call. Computer use only; the other routes gain it later.
|
|
@@ -1446,16 +1505,16 @@ throw or a rejected promise becomes a redacted run warning.
|
|
|
1446
1505
|
|
|
1447
1506
|
Refusals:
|
|
1448
1507
|
|
|
1449
|
-
- `
|
|
1450
|
-
a preview or scripted
|
|
1508
|
+
- `HUMANISH_STUDY_OPTION_UNSUPPORTED`: the route cannot honor the option, for example `scorer` on
|
|
1509
|
+
a preview or scripted study, `createProvider` off computer use, or `prepareDesktop` where no E2B
|
|
1451
1510
|
desktop exists. The message names the option and the route. The same code refuses a field
|
|
1452
|
-
`
|
|
1511
|
+
`RunStudyOptions` no longer has (below); that message names the field and where its job went.
|
|
1453
1512
|
|
|
1454
1513
|
`rerun.laneIds`, the older name of `rerun.participantIds`, was removed in 0.107.0. A caller that
|
|
1455
|
-
still passes it is refused with `
|
|
1514
|
+
still passes it is refused with `HUMANISH_STUDY_OPTION_UNSUPPORTED`; use `rerun.participantIds`.
|
|
1456
1515
|
|
|
1457
1516
|
The route hook bags (`cuaHooks`, `scriptedHooks`, `terminalHooks`, `sharedWorldHooks`,
|
|
1458
|
-
`automaticAnalysis`) were removed from `
|
|
1517
|
+
`automaticAnalysis`) were removed from `RunStudyOptions`, with the hook bag types and the four route
|
|
1459
1518
|
runners that took them (`runCuaActorLab`, `runScriptedBrowserLab`, `runTerminalProductLab`,
|
|
1460
1519
|
`runConcurrentSharedWorld`). `lab` and `scorerProvenance` are set only by the humanish CLI. A
|
|
1461
1520
|
JavaScript caller that passes any of these fields is refused before anything runs. Where each
|
|
@@ -1481,25 +1540,25 @@ route-specific verdict rules in the next section.
|
|
|
1481
1540
|
0.107.0 also removed these exports from `humanish`. A removed function no longer warns; an import
|
|
1482
1541
|
of it fails.
|
|
1483
1542
|
|
|
1484
|
-
| Removed export | Use
|
|
1485
|
-
| ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1486
|
-
| `runDryRun`, `RunOptions`, `RunResult` | `
|
|
1487
|
-
| `runCuaActorSession` | `runComputerUseLoop` with `createOpenAiResponsesProvider({ singleDispatch: true })`, `redaction: defaultRedactionHooks` and `now: Date.now`, which `runCuaActorSession` defaulted (the composition above). A caller that passed `desktop` and `executorOptions` writes its own `
|
|
1488
|
-
| `CuaActorLabResult`, `ScriptedBrowserLabResult`, `TerminalProductLabResult`, `ConcurrentSharedWorldLabResult` | `
|
|
1489
|
-
| `SubjectPhaseEvent` | the `subject-phase` `
|
|
1490
|
-
| `
|
|
1491
|
-
| `selectLabBackend`, the `routesTo*` predicates, `actorResolvesToTerminal` | `routeOf(config)`
|
|
1492
|
-
| `resolveLabDryRun` | `
|
|
1493
|
-
| `cuaLaneCount` | the `plan` `
|
|
1494
|
-
| `resolveSeatUrl` | `
|
|
1495
|
-
| `cuaLaneValidationReason`, `sharedWorldValidationReason`, `concurrentSharedWorldValidationReason`, `externalPublicSharedWorldValidationReason` | `
|
|
1496
|
-
| `MAX_CUA_LANES` | none; `
|
|
1543
|
+
| Removed export | Use |
|
|
1544
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1545
|
+
| `runDryRun`, `RunOptions`, `RunResult` | `runStudy(config, options)` on a `this-repo` study; its result is `StudyResult<"preview">` |
|
|
1546
|
+
| `runCuaActorSession` | `runComputerUseLoop` with `createOpenAiResponsesProvider({ singleDispatch: true })`, `redaction: defaultRedactionHooks` and `now: Date.now`, which `runCuaActorSession` defaulted (the composition above). A caller that passed `desktop` and `executorOptions` writes its own `ComputerUseExecutor`: the E2B desktop executor is not exported |
|
|
1547
|
+
| `CuaActorLabResult`, `ScriptedBrowserLabResult`, `TerminalProductLabResult`, `ConcurrentSharedWorldLabResult` | `StudyResult<"computer-use">`, `StudyResult<"scripted">`, `StudyResult<"terminal">`, `StudyResult<"shared-world">` |
|
|
1548
|
+
| `SubjectPhaseEvent` | the `subject-phase` `StudyEvent` through `onEvent`. Its `type` is `"subject-phase"`; the phase that was `type` is `name`, and `target` is `{ kind: "subject" }` or `{ kind: "participant", participant }`. `message`, `at`, `ok` and `durationMs` are unchanged |
|
|
1549
|
+
| `StudyOutcome.backend`, `LabBackend` | `outcome.route` |
|
|
1550
|
+
| `selectLabBackend`, the `routesTo*` predicates, `actorResolvesToTerminal` | `routeOf(config)` |
|
|
1551
|
+
| `resolveLabDryRun` | `RunStudyOptions.dryRun`; without it, `scenario.mode` decides |
|
|
1552
|
+
| `cuaLaneCount` | the `plan` `StudyEvent`, which lists every participant |
|
|
1553
|
+
| `resolveSeatUrl` | `parseStudy`, which refuses a participant entry that is not same-origin loopback; build the URL with `new URL(entry, serveUrl)` |
|
|
1554
|
+
| `cuaLaneValidationReason`, `sharedWorldValidationReason`, `concurrentSharedWorldValidationReason`, `externalPublicSharedWorldValidationReason` | `parseStudy`, which returns the same reason |
|
|
1555
|
+
| `MAX_CUA_LANES` | none; `parseStudy` refuses a roster of more than 16 participants |
|
|
1497
1556
|
|
|
1498
1557
|
## Product-Adapter Extension Seam
|
|
1499
1558
|
|
|
1500
1559
|
The terminal-product and browser/computer-use routes let an adopter attach
|
|
1501
|
-
product-specific scoring + feedback as a
|
|
1502
|
-
core. The seam is `
|
|
1560
|
+
product-specific scoring + feedback as a thin in-repo extension without forking
|
|
1561
|
+
core. The seam is `RunStudyOptions.scorer`, an `AdapterScorerModule`, for terminal-product,
|
|
1503
1562
|
computer-use and shared-world runs; the CLI loads the same module with `--scorer`. This is never a built-in product scorer (the adopter's scorecard
|
|
1504
1563
|
lives in the adopter's repo).
|
|
1505
1564
|
|
|
@@ -1516,8 +1575,8 @@ status, score, summary, data? }`. Core never reads `data`, where the adopter's
|
|
|
1516
1575
|
feedback-candidate shape; product-specific concepts (public CLI/product command
|
|
1517
1576
|
observed, hosted product success-or-blocker, feedback id/draft, media/job/asset
|
|
1518
1577
|
ids, explicit no-media/no-provider-spend proof, defection/friction risk) are
|
|
1519
|
-
recorded
|
|
1520
|
-
validates the
|
|
1578
|
+
recorded only under `adapter: { namespace, data }`, never as core enums. Core
|
|
1579
|
+
validates the shape (a non-empty `namespace` + a `data` record); the keys inside
|
|
1521
1580
|
`data` are the adapter's.
|
|
1522
1581
|
- **Adapter artifacts** (`humanish.adapter-artifact.v1`,
|
|
1523
1582
|
`RunBundle.adapterArtifacts[]`). A namespaced list of local relative artifact
|
|
@@ -1563,7 +1622,7 @@ Acceptance semantics are route-specific:
|
|
|
1563
1622
|
|
|
1564
1623
|
- Terminal-product runs keep the mission-based `review` verdict unchanged for a
|
|
1565
1624
|
library `scorer`; the adapter score is additive. A config-declared scorer
|
|
1566
|
-
(`--scorer` or the
|
|
1625
|
+
(`--scorer` or the study's `review.scorer.ref`) that returns `status: fail`,
|
|
1567
1626
|
throws, or returns a malformed score turns a pass-like verdict into `fail` and
|
|
1568
1627
|
fails the run result.
|
|
1569
1628
|
- Browser/computer-use runs treat `adapterScore.status: fail` as product-red:
|
|
@@ -1574,18 +1633,18 @@ Acceptance semantics are route-specific:
|
|
|
1574
1633
|
evidence.
|
|
1575
1634
|
|
|
1576
1635
|
The `e2b-terminal` substrate is added to `RunFeedbackCandidate.substrate` so a
|
|
1577
|
-
terminal-agent candidate names
|
|
1578
|
-
the existing `e2b-desktop` substrate. The routes invoke hooks over
|
|
1636
|
+
terminal-agent candidate names the substrate it ran on; browser candidates use
|
|
1637
|
+
the existing `e2b-desktop` substrate. The routes invoke hooks over fully-assembled,
|
|
1579
1638
|
redacted evidence (`TerminalProductScoringContext` or
|
|
1580
|
-
`
|
|
1639
|
+
`BrowserScoringContext`: `bundle`, runtime-only `runDir`, run identifiers,
|
|
1581
1640
|
actor and route metadata; all exported public types), scrub+redact returned
|
|
1582
|
-
payloads, and
|
|
1641
|
+
payloads, and drop any malformed score, candidate, or artifact reference with a
|
|
1583
1642
|
warning so a bad extension never poisons a verifiable bundle. On the terminal
|
|
1584
|
-
route the context also carries `transcript`: the
|
|
1643
|
+
route the context also carries `transcript`: the full normalized session
|
|
1585
1644
|
transcript, source-scrubbed then shape-redacted and byte-identical to the
|
|
1586
1645
|
persisted `terminal-transcript.txt` (the trace's `transcriptTail` is a ~2KB
|
|
1587
1646
|
projection of it), so an adopter rubric can find command-tier evidence anywhere
|
|
1588
|
-
in the session
|
|
1647
|
+
in the session, including outside the tail window. Default behavior
|
|
1589
1648
|
(no hook) is unchanged. `verifyRun` re-checks the surviving shapes fail-closed,
|
|
1590
1649
|
including existence for referenced adapter artifacts.
|
|
1591
1650
|
|
|
@@ -1671,10 +1730,10 @@ bundlePath: .humanish/runs/synthetic-run-bundle-2026-06-02t10-00-00-000z-proof/r
|
|
|
1671
1730
|
checks:
|
|
1672
1731
|
- name: run.json exists
|
|
1673
1732
|
ok: true
|
|
1674
|
-
message: run.json present
|
|
1733
|
+
message: run.json is present
|
|
1675
1734
|
- name: redaction passed
|
|
1676
1735
|
ok: true
|
|
1677
|
-
message: redaction
|
|
1736
|
+
message: redaction passed
|
|
1678
1737
|
shareSafety:
|
|
1679
1738
|
status: share_ready
|
|
1680
1739
|
reasons: []
|