humanish 0.106.0 → 0.107.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/AGENTS.md +4 -2
- package/ARCHITECTURE.md +17 -16
- package/CHANGELOG.md +24 -0
- package/CONTEXT.md +42 -39
- package/CONTRIBUTING.md +8 -6
- package/README.md +8 -3
- package/dist/actors/codex/app-server-client.js +2 -1
- package/dist/actors/codex/app-server-trace.d.ts +0 -1
- package/dist/actors/codex/app-server-trace.js +1 -3
- package/dist/actors/codex/app-server.js +2 -1
- package/dist/actors/codex/protocol-compat.d.ts +27 -0
- package/dist/actors/codex/protocol-compat.js +357 -0
- package/dist/actors/codex/protocol-contract.d.ts +61 -0
- package/dist/actors/codex/protocol-contract.js +471 -0
- package/dist/actors/codex/qualified-versions.d.ts +31 -4
- package/dist/actors/codex/qualified-versions.js +51 -6
- package/dist/actors/codex/restricted-executable.d.ts +24 -0
- package/dist/actors/codex/restricted-executable.js +97 -22
- package/dist/actors/codex/restricted-launch.d.ts +7 -1
- package/dist/actors/codex/restricted-launch.js +42 -3
- package/dist/actors/codex/restricted-notifications.d.ts +101 -0
- package/dist/actors/codex/restricted-notifications.js +277 -0
- package/dist/actors/codex/restricted-participant.d.ts +30 -0
- package/dist/actors/codex/restricted-participant.js +60 -2
- package/dist/actors/codex/restricted-policy.d.ts +9 -1
- package/dist/actors/codex/restricted-session.d.ts +14 -1
- package/dist/actors/codex/restricted-session.js +50 -17
- package/dist/actors/codex/restricted-transport.d.ts +29 -0
- package/dist/actors/codex/restricted-transport.js +139 -27
- package/dist/actors/codex/restricted-turn.d.ts +19 -4
- package/dist/actors/codex/restricted-turn.js +43 -30
- package/dist/actors/computer-use/actor.d.ts +1 -1
- package/dist/actors/computer-use/actor.js +4 -4
- package/dist/actors/computer-use/loop/backstop.js +1 -1
- package/dist/actors/computer-use/loop/ending.d.ts +2 -2
- package/dist/actors/computer-use/loop/ending.js +2 -2
- package/dist/actors/computer-use/loop/spend.d.ts +1 -1
- package/dist/actors/computer-use/loop/spend.js +3 -3
- package/dist/actors/computer-use/loop/trace.js +2 -2
- package/dist/actors/computer-use/loop/types.d.ts +14 -5
- package/dist/actors/computer-use/loop.js +4 -4
- package/dist/actors/computer-use/openai-provider.d.ts +1 -1
- package/dist/actors/computer-use/openai-provider.js +2 -2
- package/dist/actors/contract.d.ts +10 -8
- package/dist/actors/contract.js +7 -1
- package/dist/actors/local-agent/cli.js +1 -1
- package/dist/actors/scripted-browser/actor.d.ts +2 -2
- package/dist/actors/scripted-browser/journey.js +15 -4
- package/dist/actors/scripted-browser/steps.js +18 -0
- package/dist/actors/scripted-browser/types.d.ts +11 -1
- package/dist/actors/stop-conditions.js +1 -3
- package/dist/analysis/automatic-completion.d.ts +13 -8
- package/dist/analysis/automatic-completion.js +14 -8
- package/dist/analysis/automatic-config.d.ts +1 -1
- package/dist/analysis/automatic-config.js +2 -2
- package/dist/analysis/automatic.d.ts +10 -4
- package/dist/analysis/automatic.js +17 -13
- package/dist/analysis/diagnostics.d.ts +23 -0
- package/dist/analysis/diagnostics.js +112 -0
- package/dist/analysis/evidence-sources.js +2 -2
- package/dist/analysis/evidence.js +6 -6
- package/dist/analysis/execute.d.ts +7 -1
- package/dist/analysis/execute.js +39 -12
- package/dist/analysis/job.d.ts +8 -5
- package/dist/analysis/job.js +15 -14
- package/dist/analysis/load.d.ts +1 -1
- package/dist/analysis/load.js +5 -5
- package/dist/analysis/provider.d.ts +9 -1
- package/dist/analysis/restricted-codex.d.ts +2 -0
- package/dist/analysis/restricted-codex.js +10 -1
- package/dist/analysis/service.d.ts +2 -0
- package/dist/analysis/service.js +31 -12
- package/dist/analysis/sharing.d.ts +2 -2
- package/dist/analysis/sharing.js +5 -5
- package/dist/analysis/store-executions.d.ts +5 -5
- package/dist/analysis/store-executions.js +12 -12
- package/dist/analysis/store.d.ts +4 -4
- package/dist/analysis/store.js +13 -13
- package/dist/cli/commands/analysis-signals.d.ts +2 -1
- package/dist/cli/commands/analysis-signals.js +4 -1
- package/dist/cli/commands/analyze.js +4 -2
- package/dist/cli/commands/comms.js +8 -1
- package/dist/cli/commands/lab-route-computer-use.d.ts +0 -2
- package/dist/cli/commands/lab-route-computer-use.js +26 -14
- package/dist/cli/commands/lab-route-preview.d.ts +1 -3
- package/dist/cli/commands/lab-route-preview.js +3 -4
- package/dist/cli/commands/lab-route-run.d.ts +6 -4
- package/dist/cli/commands/lab-route-run.js +6 -2
- package/dist/cli/commands/lab-route-scripted.d.ts +0 -2
- package/dist/cli/commands/lab-route-scripted.js +0 -1
- package/dist/cli/commands/lab-route-shared-world.d.ts +0 -2
- package/dist/cli/commands/lab-route-shared-world.js +0 -1
- package/dist/cli/commands/lab-route-terminal.d.ts +0 -2
- package/dist/cli/commands/lab-route-terminal.js +0 -1
- package/dist/cli/commands/lab-run.js +76 -45
- package/dist/cli/commands/lab-scorer.d.ts +0 -2
- package/dist/cli/commands/lab.js +12 -6
- package/dist/cli/commands/observe.js +22 -0
- package/dist/cli/commands/run-signals.d.ts +30 -0
- package/dist/cli/commands/run-signals.js +146 -0
- package/dist/cli/commands/runs.js +10 -8
- package/dist/cli/commands/watch.js +12 -10
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/doctor.js +10 -2
- package/dist/cli/io.d.ts +5 -4
- package/dist/cli/io.js +12 -8
- package/dist/cli/program.js +3 -3
- package/dist/cli/renamed-options.d.ts +11 -0
- package/dist/cli/renamed-options.js +18 -0
- package/dist/cli/telemetry.d.ts +2 -2
- package/dist/cli/telemetry.js +1 -1
- package/dist/comms/capture-surface.d.ts +1 -1
- package/dist/comms/catch-host.d.ts +1 -1
- package/dist/comms/connections.js +4 -4
- package/dist/comms/external-evidence.d.ts +27 -0
- package/dist/comms/external-evidence.js +93 -0
- package/dist/comms/lease-store.js +7 -7
- package/dist/comms/types.js +1 -1
- package/dist/evidence/browser-hygiene.d.ts +1 -1
- package/dist/evidence/browser-hygiene.js +1 -1
- package/dist/evidence/redaction.js +1 -1
- package/dist/evidence/secret-scrub.d.ts +9 -0
- package/dist/evidence/secret-scrub.js +96 -0
- package/dist/feedback/draft.js +7 -9
- package/dist/feedback/export-bundle.js +4 -4
- package/dist/feedback/export.js +9 -9
- package/dist/feedback/feedback.d.ts +1 -1
- package/dist/index.d.ts +3 -6
- package/dist/index.js +3 -6
- package/dist/keys/key-resolution.d.ts +3 -1
- package/dist/keys/key-resolution.js +18 -16
- package/dist/lab/adapter-extension.d.ts +28 -6
- package/dist/lab/adapter-extension.js +65 -126
- package/dist/lab/adapter-scorer-loader.d.ts +25 -5
- package/dist/lab/adapter-scorer-loader.js +24 -0
- package/dist/lab/composition-rules.d.ts +2 -0
- package/dist/lab/composition-rules.js +35 -26
- package/dist/lab/config.js +28 -26
- package/dist/lab/device-presets.d.ts +2 -2
- package/dist/lab/discover.js +1 -3
- package/dist/lab/doctor.d.ts +44 -9
- package/dist/lab/doctor.js +149 -41
- package/dist/lab/init-plan.js +1 -3
- package/dist/lab/keys.js +1 -1
- package/dist/lab/lab-deps.d.ts +85 -0
- package/dist/lab/lab-deps.js +4 -0
- package/dist/lab/parse/actors.js +8 -7
- package/dist/lab/parse/comms.js +3 -2
- package/dist/lab/parse/execution.js +3 -2
- package/dist/lab/parse/subject-state.js +2 -1
- package/dist/lab/parse/subject.js +2 -1
- package/dist/lab/parse/values.d.ts +0 -1
- package/dist/lab/parse/values.js +0 -3
- package/dist/lab/persona-resolve.d.ts +3 -3
- package/dist/lab/persona-resolve.js +4 -4
- package/dist/lab/persona.js +1 -3
- package/dist/lab/plan-base.d.ts +6 -2
- package/dist/lab/plan-base.js +22 -7
- package/dist/lab/plan-participants.d.ts +5 -5
- package/dist/lab/plan-participants.js +2 -2
- package/dist/lab/plan-types.d.ts +33 -7
- package/dist/lab/plan.d.ts +3 -11
- package/dist/lab/plan.js +18 -42
- package/dist/lab/preflight-result.js +0 -2
- package/dist/lab/preflight.d.ts +0 -3
- package/dist/lab/preflight.js +1 -1
- package/dist/lab/requirements.d.ts +20 -0
- package/dist/lab/requirements.js +54 -0
- package/dist/lab/route-inputs.d.ts +23 -12
- package/dist/lab/route-inputs.js +48 -51
- package/dist/lab/routing.d.ts +19 -24
- package/dist/lab/routing.js +14 -47
- package/dist/lab/run-lab-events.d.ts +6 -11
- package/dist/lab/run-lab-events.js +3 -11
- package/dist/lab/run-lab-homes.d.ts +64 -0
- package/dist/lab/run-lab-homes.js +3 -0
- package/dist/lab/run-lab-options.d.ts +12 -66
- package/dist/lab/run-lab-options.js +45 -275
- package/dist/lab/summary.d.ts +9 -4
- package/dist/lab/summary.js +15 -8
- package/dist/lab/types.d.ts +132 -132
- package/dist/lab/validation.d.ts +15 -15
- package/dist/lab/validation.js +28 -28
- package/dist/lab/warnings.js +20 -19
- package/dist/observer/artifact.d.ts +1 -1
- package/dist/observer/artifact.js +3 -3
- package/dist/observer/data.js +2 -2
- package/dist/observer/render.js +2 -2
- package/dist/observer/run-routes.d.ts +1 -1
- package/dist/observer/run-routes.js +5 -5
- package/dist/observer/serve.d.ts +17 -0
- package/dist/observer/serve.js +34 -3
- package/dist/observer-app.html +1 -1
- package/dist/routes/computer-use/bundle-parts.d.ts +1 -1
- package/dist/routes/computer-use/bundle-parts.js +10 -3
- package/dist/routes/computer-use/bundle.d.ts +4 -11
- package/dist/routes/computer-use/bundle.js +9 -15
- package/dist/routes/computer-use/diagnostics.d.ts +1 -1
- package/dist/routes/computer-use/diagnostics.js +1 -1
- package/dist/routes/computer-use/e2b-desktop/comms.d.ts +10 -10
- package/dist/routes/computer-use/e2b-desktop/comms.js +11 -14
- package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +2 -2
- package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -7
- package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +13 -13
- package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -11
- package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +3 -3
- package/dist/routes/computer-use/e2b-desktop/prepare.js +33 -36
- package/dist/routes/computer-use/e2b-desktop/start.d.ts +1 -1
- package/dist/routes/computer-use/e2b-desktop/start.js +24 -23
- package/dist/routes/computer-use/e2b-desktop/state.d.ts +5 -4
- package/dist/routes/computer-use/e2b-desktop/state.js +5 -3
- package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +1 -1
- package/dist/routes/computer-use/e2b-desktop/teardown.js +29 -17
- package/dist/routes/computer-use/external-comms.d.ts +4 -4
- package/dist/routes/computer-use/external-comms.js +19 -43
- package/dist/routes/computer-use/fanout-bundle.d.ts +3 -3
- package/dist/routes/computer-use/fanout-bundle.js +25 -29
- package/dist/routes/computer-use/{fanout-lanes.js → fanout-records.js} +25 -19
- package/dist/routes/computer-use/in-process-desktop.d.ts +3 -10
- package/dist/routes/computer-use/in-process-desktop.js +3 -9
- package/dist/routes/computer-use/live-flush.d.ts +7 -5
- package/dist/routes/computer-use/live-flush.js +19 -27
- package/dist/routes/computer-use/{run-lanes.js → live-phase.js} +14 -13
- package/dist/routes/computer-use/local-tree-pack.d.ts +3 -3
- package/dist/routes/computer-use/local-tree-pack.js +4 -5
- package/dist/routes/computer-use/local-vm.d.ts +11 -8
- package/dist/routes/computer-use/local-vm.js +39 -66
- package/dist/routes/computer-use/participant-desktop.d.ts +8 -23
- package/dist/routes/computer-use/participant-desktop.js +5 -25
- package/dist/routes/computer-use/{lanes.d.ts → participant-execution.d.ts} +10 -10
- package/dist/routes/computer-use/{lanes.js → participant-execution.js} +35 -22
- package/dist/routes/computer-use/participant-facts.d.ts +7 -1
- package/dist/routes/computer-use/participant-facts.js +12 -1
- package/dist/routes/computer-use/participant-model.d.ts +19 -11
- package/dist/routes/computer-use/participant-model.js +58 -33
- package/dist/routes/computer-use/participant-prompt.d.ts +3 -3
- package/dist/routes/computer-use/participant-prompt.js +4 -4
- package/dist/routes/computer-use/participant-runs.d.ts +6 -6
- package/dist/routes/computer-use/participant-runs.js +8 -8
- package/dist/routes/computer-use/plan.d.ts +21 -29
- package/dist/routes/computer-use/plan.js +59 -65
- package/dist/routes/computer-use/preflight.d.ts +6 -11
- package/dist/routes/computer-use/preflight.js +26 -18
- package/dist/routes/computer-use/rerun-selection.js +10 -6
- package/dist/routes/computer-use/result.d.ts +1 -1
- package/dist/routes/computer-use/result.js +40 -30
- package/dist/routes/computer-use/route.d.ts +3 -4
- package/dist/routes/computer-use/route.js +21 -19
- package/dist/routes/computer-use/self-report.d.ts +2 -2
- package/dist/routes/computer-use/self-report.js +4 -4
- package/dist/routes/computer-use/setup.d.ts +8 -17
- package/dist/routes/computer-use/setup.js +98 -86
- package/dist/routes/computer-use/single-bundle.d.ts +8 -11
- package/dist/routes/computer-use/single-bundle.js +20 -25
- package/dist/routes/computer-use/subject-projection.d.ts +5 -7
- package/dist/routes/computer-use/subject-projection.js +20 -19
- package/dist/routes/computer-use/types.d.ts +169 -211
- package/dist/routes/computer-use/types.js +13 -6
- package/dist/routes/preview.js +0 -1
- package/dist/routes/scripted/bundle.d.ts +3 -6
- package/dist/routes/scripted/bundle.js +11 -16
- package/dist/routes/scripted/plan.d.ts +5 -7
- package/dist/routes/scripted/plan.js +6 -4
- package/dist/routes/scripted/result.d.ts +4 -1
- package/dist/routes/scripted/result.js +10 -6
- package/dist/routes/scripted/route.js +10 -11
- package/dist/routes/scripted/scenario.d.ts +1 -1
- package/dist/routes/scripted/scenario.js +1 -1
- package/dist/routes/scripted/setup.d.ts +3 -2
- package/dist/routes/scripted/setup.js +22 -21
- package/dist/routes/scripted/subject.d.ts +8 -3
- package/dist/routes/scripted/subject.js +11 -6
- package/dist/routes/scripted/surface-records.js +1 -1
- package/dist/routes/scripted/surface-sessions.d.ts +2 -2
- package/dist/routes/scripted/surface-sessions.js +4 -4
- package/dist/routes/scripted/types.d.ts +19 -36
- package/dist/routes/scripted/types.js +1 -2
- package/dist/routes/shared-world/{seat-records.js → bundle-records.js} +8 -8
- package/dist/routes/shared-world/bundle.d.ts +2 -2
- package/dist/routes/shared-world/bundle.js +22 -26
- package/dist/routes/shared-world/checkpoints.d.ts +4 -4
- package/dist/routes/shared-world/checkpoints.js +4 -4
- package/dist/routes/shared-world/comms.d.ts +5 -4
- package/dist/routes/shared-world/comms.js +28 -41
- package/dist/routes/shared-world/external-public.d.ts +5 -5
- package/dist/routes/shared-world/external-public.js +19 -20
- package/dist/routes/shared-world/handoff.d.ts +4 -4
- package/dist/routes/shared-world/handoff.js +50 -50
- package/dist/routes/shared-world/lobby-code.d.ts +5 -5
- package/dist/routes/shared-world/lobby-code.js +13 -13
- package/dist/routes/shared-world/{seats.d.ts → participant-specs.d.ts} +8 -6
- package/dist/routes/shared-world/{seats.js → participant-specs.js} +46 -51
- package/dist/routes/shared-world/plan.d.ts +2 -6
- package/dist/routes/shared-world/plan.js +1 -2
- package/dist/routes/shared-world/provenance.d.ts +4 -4
- package/dist/routes/shared-world/provenance.js +4 -4
- package/dist/routes/shared-world/provisioned.d.ts +3 -2
- package/dist/routes/shared-world/provisioned.js +48 -41
- package/dist/routes/shared-world/result.js +31 -19
- package/dist/routes/shared-world/route.js +37 -41
- package/dist/routes/shared-world/setup.d.ts +5 -4
- package/dist/routes/shared-world/setup.js +12 -14
- package/dist/routes/shared-world/types.d.ts +53 -126
- package/dist/routes/shared-world/types.js +2 -2
- package/dist/routes/terminal/adapter.d.ts +8 -8
- package/dist/routes/terminal/adapter.js +28 -125
- package/dist/routes/terminal/bundle.d.ts +7 -12
- package/dist/routes/terminal/bundle.js +24 -34
- package/dist/routes/terminal/credentials.d.ts +2 -2
- package/dist/routes/terminal/credentials.js +16 -16
- package/dist/routes/terminal/dry-run.d.ts +1 -1
- package/dist/routes/terminal/dry-run.js +9 -11
- package/dist/routes/terminal/encoding.d.ts +1 -1
- package/dist/routes/terminal/encoding.js +2 -2
- package/dist/routes/terminal/ledger.d.ts +6 -6
- package/dist/routes/terminal/ledger.js +9 -9
- package/dist/routes/terminal/live-finish.d.ts +1 -2
- package/dist/routes/terminal/live-finish.js +21 -20
- package/dist/routes/terminal/live-sandbox.d.ts +4 -2
- package/dist/routes/terminal/live-sandbox.js +25 -21
- package/dist/routes/terminal/participant-text.d.ts +23 -0
- package/dist/routes/terminal/participant-text.js +165 -0
- package/dist/routes/terminal/plan.d.ts +2 -6
- package/dist/routes/terminal/plan.js +13 -9
- package/dist/routes/terminal/recorder.d.ts +6 -1
- package/dist/routes/terminal/recorder.js +12 -4
- package/dist/routes/terminal/result.js +4 -4
- package/dist/routes/terminal/route.js +26 -26
- package/dist/routes/terminal/runtime.d.ts +3 -2
- package/dist/routes/terminal/runtime.js +3 -3
- package/dist/routes/terminal/sandbox.d.ts +5 -3
- package/dist/routes/terminal/sandbox.js +13 -16
- package/dist/routes/terminal/session.js +16 -14
- package/dist/routes/terminal/token-usage.js +5 -5
- package/dist/routes/terminal/trace.d.ts +5 -8
- package/dist/routes/terminal/trace.js +9 -64
- package/dist/routes/terminal/types.d.ts +85 -93
- package/dist/routes/terminal/types.js +9 -4
- package/dist/run/active-runs.d.ts +13 -0
- package/dist/run/active-runs.js +12 -0
- package/dist/run/bundle-shape.d.ts +2 -1
- package/dist/run/bundle-shape.js +4 -4
- package/dist/run/bundle.d.ts +63 -56
- package/dist/run/bundle.js +9 -8
- package/dist/run/cost-summary.js +3 -2
- package/dist/run/costs.d.ts +6 -6
- package/dist/run/costs.js +10 -10
- package/dist/run/detail.d.ts +5 -5
- package/dist/run/detail.js +6 -6
- package/dist/run/dry-run.js +5 -12
- package/dist/run/{study-files.d.ts → evidence-files.d.ts} +4 -4
- package/dist/run/{study-files.js → evidence-files.js} +5 -5
- package/dist/run/feedback-shape.js +1 -1
- package/dist/run/judge.d.ts +16 -4
- package/dist/run/judge.js +24 -7
- package/dist/run/locate.d.ts +2 -0
- package/dist/run/locate.js +3 -1
- package/dist/run/outcomes.d.ts +3 -3
- package/dist/run/outcomes.js +3 -3
- package/dist/run/participant-records.d.ts +2 -2
- package/dist/run/participant-records.js +2 -2
- package/dist/run/paths.js +1 -1
- package/dist/run/pricing.d.ts +19 -9
- package/dist/run/pricing.js +30 -18
- package/dist/run/projection.d.ts +13 -13
- package/dist/run/projection.js +8 -8
- package/dist/run/reclaim.d.ts +7 -1
- package/dist/run/reclaim.js +24 -4
- package/dist/run/results.d.ts +1 -1
- package/dist/run/run-index.d.ts +1 -1
- package/dist/run/run-index.js +11 -11
- package/dist/run/run.d.ts +4 -2
- package/dist/run/run.js +9 -5
- package/dist/run/sandbox-receipts.d.ts +4 -4
- package/dist/run/sandbox-receipts.js +4 -4
- package/dist/run/shared-world-evidence.d.ts +47 -47
- package/dist/run/shared-world-shape.d.ts +3 -3
- package/dist/run/shared-world-shape.js +11 -11
- package/dist/run/stats.d.ts +5 -5
- package/dist/run/stats.js +8 -8
- package/dist/run/status.d.ts +16 -6
- package/dist/run/status.js +31 -8
- package/dist/run/streams.d.ts +26 -19
- package/dist/run/terminal-contract.d.ts +5 -5
- package/dist/run/terminal-contract.js +4 -4
- package/dist/run/transient-comms-secrets.d.ts +5 -0
- package/dist/run/transient-comms-secrets.js +12 -0
- package/dist/run-lab.d.ts +36 -34
- package/dist/run-lab.js +41 -37
- package/dist/subject/desktop-cli.d.ts +1 -1
- package/dist/subject/desktop-cli.js +1 -1
- package/dist/subject/local-tree-archive.d.ts +1 -1
- package/dist/subject/local-tree-archive.js +4 -4
- package/dist/subject/local-tree.d.ts +2 -2
- package/dist/subject/local-tree.js +1 -1
- package/dist/subject/node-bootstrap.d.ts +1 -1
- package/dist/subject/node-bootstrap.js +4 -4
- package/dist/subject/runtime.js +5 -5
- package/dist/subject/serve.js +1 -1
- package/dist/subject/steps.d.ts +4 -4
- package/dist/subject/steps.js +5 -5
- package/dist/substrates/desktop-session.d.ts +2 -0
- package/dist/substrates/e2b/cdp-probe.d.ts +2 -2
- package/dist/substrates/e2b/cdp-probe.js +1 -1
- package/dist/substrates/e2b/desktop-browser.d.ts +3 -3
- package/dist/substrates/e2b/desktop-browser.js +4 -4
- package/dist/substrates/e2b/desktop-cdp.d.ts +7 -7
- package/dist/substrates/e2b/desktop-cdp.js +4 -4
- package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
- package/dist/substrates/e2b/desktop-geometry.js +3 -3
- package/dist/substrates/e2b/desktop-media.js +2 -2
- package/dist/substrates/e2b/sandbox.d.ts +2 -0
- package/dist/substrates/e2b/sandbox.js +22 -8
- package/dist/substrates/e2b/sdk.d.ts +12 -1
- package/dist/substrates/e2b/sdk.js +30 -9
- package/dist/substrates/local/runtime-host.d.ts +2 -0
- package/dist/substrates/local/runtime-host.js +6 -0
- package/dist/tui/actions.d.ts +7 -5
- package/dist/tui/actions.js +18 -9
- package/dist/tui-app.js +42 -42
- package/dist/verify/actor.d.ts +10 -10
- package/dist/verify/actor.js +15 -15
- package/dist/verify/artifacts.d.ts +1 -1
- package/dist/verify/artifacts.js +3 -3
- package/dist/verify/costs.d.ts +2 -2
- package/dist/verify/costs.js +2 -2
- package/dist/verify/liveness.d.ts +1 -1
- package/dist/verify/liveness.js +4 -1
- package/dist/verify/rerun.d.ts +1 -1
- package/dist/verify/rerun.js +1 -1
- package/dist/verify/shared-world.js +2 -2
- package/dist/verify/verify.js +6 -6
- package/docs/README.md +1 -1
- package/docs/architecture/actor-contract.md +60 -22
- package/docs/architecture/desktop-sessions.md +9 -9
- package/docs/architecture/external-public-shared-world.md +5 -0
- package/docs/architecture/observer.md +4 -4
- package/docs/architecture/restricted-codex-analysis.md +40 -4
- package/docs/architecture/state-driven-executor.md +4 -5
- package/docs/architecture/task-protocol-support.md +3 -5
- package/docs/architecture/{terminal-product-lane.md → terminal-product-route.md} +24 -16
- package/docs/contracts/run-bundle.md +10 -8
- package/docs/contracts/schemas.md +109 -53
- package/docs/contracts/study-analysis.md +24 -0
- package/docs/decisions/0004-participant-desktops-stay-in-the-route.md +2 -1
- package/docs/product/automatic-analysis.md +1 -0
- package/docs/ramp/README.md +2 -1
- package/package.json +4 -4
- package/skills/humanish/SKILL.md +3 -2
- package/skills/humanish/agents/openai.yaml +1 -1
- package/dist/deprecated.d.ts +0 -90
- package/dist/deprecated.js +0 -106
- package/dist/lab/bag-overrides.d.ts +0 -18
- package/dist/lab/bag-overrides.js +0 -121
- package/dist/routes/computer-use/legacy-lane-spec.d.ts +0 -62
- package/dist/routes/computer-use/legacy-lane-spec.js +0 -39
- /package/dist/routes/computer-use/{fanout-lanes.d.ts → fanout-records.d.ts} +0 -0
- /package/dist/routes/computer-use/{run-lanes.d.ts → live-phase.d.ts} +0 -0
- /package/dist/routes/shared-world/{seat-records.d.ts → bundle-records.d.ts} +0 -0
|
@@ -52,6 +52,24 @@ export async function executeBrowserPersonaStep(args) {
|
|
|
52
52
|
await target.click({ timeout: stepTimeoutMs });
|
|
53
53
|
await args.page.waitForTimeout(300);
|
|
54
54
|
}
|
|
55
|
+
else if (args.step.action === "press") {
|
|
56
|
+
if (!args.step.key) {
|
|
57
|
+
throw new Error(`${args.step.id} press step is missing key`);
|
|
58
|
+
}
|
|
59
|
+
// With a selector, Playwright focuses that element first; without one, the key goes to
|
|
60
|
+
// whatever has focus, such as the input a fill step just typed into.
|
|
61
|
+
if (args.step.selector) {
|
|
62
|
+
const target = args.page.locator(args.step.selector).first();
|
|
63
|
+
if ((await target.count()) === 0) {
|
|
64
|
+
throw new Error(`${args.step.id} press step found no target`);
|
|
65
|
+
}
|
|
66
|
+
await target.press(args.step.key, { timeout: stepTimeoutMs });
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
await args.page.keyboard.press(args.step.key);
|
|
70
|
+
}
|
|
71
|
+
await args.page.waitForTimeout(300);
|
|
72
|
+
}
|
|
55
73
|
else if (args.step.action === "assertText" || args.step.action === "waitForText") {
|
|
56
74
|
const expectedText = args.step.expectation?.text ?? args.step.value;
|
|
57
75
|
if (!expectedText) {
|
|
@@ -6,6 +6,10 @@ export interface ScriptedLocatorLike {
|
|
|
6
6
|
click(options?: {
|
|
7
7
|
timeout?: number;
|
|
8
8
|
}): Promise<void>;
|
|
9
|
+
/** Focuses the element and presses the key, as Playwright's Locator.press does. */
|
|
10
|
+
press(key: string, options?: {
|
|
11
|
+
timeout?: number;
|
|
12
|
+
}): Promise<void>;
|
|
9
13
|
count(): Promise<number>;
|
|
10
14
|
waitFor(options?: {
|
|
11
15
|
state?: "visible";
|
|
@@ -21,6 +25,10 @@ export interface ScriptedPageLike {
|
|
|
21
25
|
timeout?: number;
|
|
22
26
|
}): Promise<unknown>;
|
|
23
27
|
locator(selector: string): ScriptedLocatorLike;
|
|
28
|
+
/** Presses a key on whatever element has focus. */
|
|
29
|
+
keyboard: {
|
|
30
|
+
press(key: string): Promise<void>;
|
|
31
|
+
};
|
|
24
32
|
waitForTimeout(ms: number): Promise<void>;
|
|
25
33
|
waitForFunction(fn: string, arg: unknown, options?: {
|
|
26
34
|
timeout?: number;
|
|
@@ -83,7 +91,7 @@ export interface BrowserSurfaceCapture {
|
|
|
83
91
|
surface: BrowserSurface;
|
|
84
92
|
tracePath: string;
|
|
85
93
|
}
|
|
86
|
-
export type BrowserPersonaAction = "goto" | "click" | "fill" | "assertText" | "waitForText" | "waitForSelector";
|
|
94
|
+
export type BrowserPersonaAction = "goto" | "click" | "press" | "fill" | "assertText" | "waitForText" | "waitForSelector";
|
|
87
95
|
export interface BrowserPersonaAssertionCapture {
|
|
88
96
|
id: string;
|
|
89
97
|
reason: string;
|
|
@@ -122,6 +130,8 @@ export interface BrowserPersonaStepManifest {
|
|
|
122
130
|
path?: string;
|
|
123
131
|
selector?: string;
|
|
124
132
|
value?: string;
|
|
133
|
+
/** A press step's key, in Playwright's key syntax ("Enter", "Tab", "Control+A"). */
|
|
134
|
+
key?: string;
|
|
125
135
|
}
|
|
126
136
|
export interface BrowserPersonaJourney {
|
|
127
137
|
goal: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord } from "../run/type-guards.js";
|
|
1
2
|
export function evaluateStopWhen(stopWhen, observation) {
|
|
2
3
|
if (!stopWhen)
|
|
3
4
|
return undefined;
|
|
@@ -60,6 +61,3 @@ function valueAtPath(source, path) {
|
|
|
60
61
|
}
|
|
61
62
|
return current;
|
|
62
63
|
}
|
|
63
|
-
function isRecord(value) {
|
|
64
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
65
|
-
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { FinishedRun } from "../run/run.js";
|
|
2
2
|
import type { AnalysisConfig } from "./types.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { LabDeps } from "../lab/lab-deps.js";
|
|
4
|
+
import type { LabEvent } from "../lab/run-lab-events.js";
|
|
5
|
+
import { type AutomaticAnalysisDeps } from "./automatic.js";
|
|
4
6
|
import type { AutomaticAnalysisOutcome } from "./job.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
deps?:
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/** What a route's input gives its analysis: the test's runner and deps, the signal and onEvent. */
|
|
8
|
+
export interface AnalysisInput {
|
|
9
|
+
readonly deps?: Pick<LabDeps, "analysis">;
|
|
10
|
+
/** Cancels the analysis only. */
|
|
11
|
+
readonly analysisSignal?: AbortSignal;
|
|
12
|
+
/** Receives analysis-started and analysis-finished. */
|
|
13
|
+
readonly emit?: (event: LabEvent) => void;
|
|
11
14
|
}
|
|
12
15
|
export interface AutomaticAnalysisResult {
|
|
13
16
|
automaticAnalysis?: AutomaticAnalysisOutcome;
|
|
@@ -23,11 +26,13 @@ export declare function completeAutomaticAnalysis<T extends {
|
|
|
23
26
|
cwd: string;
|
|
24
27
|
runId: string;
|
|
25
28
|
dryRun: boolean;
|
|
26
|
-
}>(result: T, finished: FinishedRun | undefined, config: AnalysisConfig | undefined,
|
|
29
|
+
}>(result: T, finished: FinishedRun | undefined, config: AnalysisConfig | undefined, input: AnalysisInput | undefined, { trigger, preferLargerOutput, refusal, }?: {
|
|
27
30
|
/** "default" when the lab declared no analysis; its missing-key skip is not a failure. */
|
|
28
31
|
trigger?: "default" | "explicit";
|
|
29
32
|
/** The lab omitted an output limit, so a larger one may be used within the admission budget. */
|
|
30
33
|
preferLargerOutput?: boolean;
|
|
34
|
+
/** The route's reason not to analyze this run; see AutomaticAnalysisDeps.refusal. */
|
|
35
|
+
refusal?: AutomaticAnalysisDeps["refusal"];
|
|
31
36
|
}): Promise<T & AutomaticAnalysisResult>;
|
|
32
37
|
/**
|
|
33
38
|
* True when an analysis the lab never declared was refused because its conservative estimate is
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { physicalCwdOf, validatePreparedRunRootIdentity } from "../run/paths.js";
|
|
2
2
|
import { FinishedRun } from "../run/run.js";
|
|
3
|
-
import {
|
|
3
|
+
import { runAutomaticAnalysis } from "./automatic.js";
|
|
4
4
|
/**
|
|
5
5
|
* One post-completion boundary shared by all live recording producers. `finished` is the token
|
|
6
6
|
* the producer's run scope issued when the run published its final bundle; without it, or when it
|
|
7
7
|
* names another run than the result, there is no source to analyze.
|
|
8
8
|
*/
|
|
9
|
-
export async function completeAutomaticAnalysis(result, finished, config,
|
|
9
|
+
export async function completeAutomaticAnalysis(result, finished, config, input, { trigger = "explicit", preferLargerOutput = false, refusal, } = {}) {
|
|
10
10
|
if (config === undefined)
|
|
11
11
|
return result;
|
|
12
12
|
const origin = trigger === "default" ? { automaticAnalysisTrigger: trigger } : {};
|
|
@@ -26,9 +26,12 @@ export async function completeAutomaticAnalysis(result, finished, config, hooks,
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
const prepared = finished.paths;
|
|
29
|
-
|
|
29
|
+
const seams = input?.deps?.analysis;
|
|
30
|
+
const signal = input?.analysisSignal;
|
|
31
|
+
let started = false;
|
|
30
32
|
try {
|
|
31
|
-
|
|
33
|
+
input?.emit?.({ type: "analysis-started" });
|
|
34
|
+
started = true;
|
|
32
35
|
try {
|
|
33
36
|
await validatePreparedRunRootIdentity(prepared);
|
|
34
37
|
}
|
|
@@ -40,8 +43,10 @@ export async function completeAutomaticAnalysis(result, finished, config, hooks,
|
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
const sourceCwd = physicalCwdOf(prepared);
|
|
43
|
-
const automaticAnalysis = await (
|
|
44
|
-
...
|
|
46
|
+
const automaticAnalysis = await (seams?.run ?? runAutomaticAnalysis)(sourceCwd, finished.runId, config, {
|
|
47
|
+
...seams?.deps,
|
|
48
|
+
...(signal === undefined ? {} : { signal }),
|
|
49
|
+
...(refusal === undefined ? {} : { refusal }),
|
|
45
50
|
preferLargerOutput,
|
|
46
51
|
...(trigger === "default" ? { defaultRequest: true } : {}),
|
|
47
52
|
expectedRun: prepared,
|
|
@@ -58,10 +63,11 @@ export async function completeAutomaticAnalysis(result, finished, config, hooks,
|
|
|
58
63
|
}
|
|
59
64
|
finally {
|
|
60
65
|
try {
|
|
61
|
-
|
|
66
|
+
if (started)
|
|
67
|
+
input?.emit?.({ type: "analysis-finished" });
|
|
62
68
|
}
|
|
63
69
|
catch {
|
|
64
|
-
/*
|
|
70
|
+
/* A report cannot erase accounting or the completed recording. */
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
}
|
|
@@ -19,7 +19,7 @@ export type LabAnalysis = LabAnalysisSettings & ({
|
|
|
19
19
|
});
|
|
20
20
|
/** The spend cap on an analysis the lab did not declare. */
|
|
21
21
|
export declare const DEFAULT_ANALYSIS_MAX_COST_USD = 3;
|
|
22
|
-
/** Called for parsed manifests
|
|
22
|
+
/** Called for parsed manifests and direct library configs, before participant execution. */
|
|
23
23
|
export declare function resolveAutomaticAnalysis(raw: unknown): {
|
|
24
24
|
ok: true;
|
|
25
25
|
config: AnalysisConfig | undefined;
|
|
@@ -24,7 +24,7 @@ const SENSITIVE_QUESTION = {
|
|
|
24
24
|
};
|
|
25
25
|
/** The spend cap on an analysis the lab did not declare. */
|
|
26
26
|
export const DEFAULT_ANALYSIS_MAX_COST_USD = 3;
|
|
27
|
-
/** Called for parsed manifests
|
|
27
|
+
/** Called for parsed manifests and direct library configs, before participant execution. */
|
|
28
28
|
export function resolveAutomaticAnalysis(raw) {
|
|
29
29
|
if (raw === false)
|
|
30
30
|
return { ok: true, config: undefined };
|
|
@@ -131,5 +131,5 @@ export function automaticAnalysisBudget(raw, route) {
|
|
|
131
131
|
export function formatAutomaticAnalysisBudget(budget) {
|
|
132
132
|
if (budget.provider === "codex")
|
|
133
133
|
return `After live runs: Codex account analysis · ${budget.model} · separate restricted analyst with remote inference. Account limits apply; dollar cost and output-token ceiling are unknown. Set review.analysis: false to disable.`;
|
|
134
|
-
return `After live runs: ${budget.trigger} analysis · ${budget.model} ·
|
|
134
|
+
return `After live runs: ${budget.trigger} analysis · ${budget.model} · refused before it starts if its estimate is over $${budget.maxCostUsd}; this is not a billing cap. Set review.analysis: false to disable.`;
|
|
135
135
|
}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { type AnalyzeDeps } from "./service.js";
|
|
2
|
-
import { type AutomaticAnalysisView, type AutomaticAnalysisOutcome, type AutomaticAnalysisCancellation } from "./job.js";
|
|
2
|
+
import { type AutomaticAnalysisView, type AutomaticAnalysisOutcome, type AutomaticAnalysisRefusal, type AutomaticAnalysisCancellation } from "./job.js";
|
|
3
3
|
import { type AnalysisConfig } from "./types.js";
|
|
4
4
|
export type { AutomaticAnalysisView, AutomaticAnalysisOutcome, AutomaticAnalysisCancellation, } from "./job.js";
|
|
5
5
|
export type AutomaticAnalysisDeps = Omit<AnalyzeDeps, "analysisId" | "beforeDispatch"> & {
|
|
6
6
|
/** A missing default key records a skip before admission, preserving a successful recording. */
|
|
7
7
|
defaultRequest?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* A route's reason not to analyze this run, such as a local study whose cleanup is unconfirmed.
|
|
10
|
+
* It is checked after the job is claimed, so the skip is recorded where the CLI, the TUI and the
|
|
11
|
+
* Observer read it.
|
|
12
|
+
*/
|
|
13
|
+
refusal?: () => AutomaticAnalysisRefusal | undefined;
|
|
8
14
|
/** Expand an omitted output limit only within the existing admission budget. */
|
|
9
15
|
preferLargerOutput?: boolean;
|
|
10
16
|
};
|
|
11
17
|
/** Small read-only TUI/CLI projection. Neither this nor Observer can resume a job. */
|
|
12
|
-
export declare function
|
|
13
|
-
export declare function
|
|
18
|
+
export declare function readAutomaticAnalysis(cwd: string, runId: string): Promise<AutomaticAnalysisView | undefined>;
|
|
19
|
+
export declare function requestAutomaticAnalysisCancellation(cwd: string, runId: string): Promise<AutomaticAnalysisCancellation>;
|
|
14
20
|
/** One permanent claim per completed run, consumed even when preparation/cancellation fails.
|
|
15
21
|
* Only the original producer calls this after all recording writes return.
|
|
16
22
|
* No file reader, restart recovery, Observer poll or export calls this function. */
|
|
17
|
-
export declare function
|
|
23
|
+
export declare function runAutomaticAnalysis(cwdInput: string, runId: string, configInput: AnalysisConfig, deps?: AutomaticAnalysisDeps): Promise<AutomaticAnalysisOutcome>;
|
|
@@ -5,10 +5,10 @@ import { refreshObserver, analyzeRun, readCompletedAnalysisSource, resolveAnalys
|
|
|
5
5
|
import { preferLargerAnalysisOutput, ANALYSIS_PROMPT_VERSION } from "./execute.js";
|
|
6
6
|
import { captureEvidence } from "./evidence.js";
|
|
7
7
|
import { hashAnalysisValue } from "./validation.js";
|
|
8
|
-
import {
|
|
8
|
+
import { claimAutomaticAnalysis, readAutomaticAnalysisPrepared, requestAutomaticAnalysisCancellationPrepared, } from "./job.js";
|
|
9
9
|
import { ANALYSIS_ID_PATTERN } from "./types.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { readAnalysisVersion } from "./store.js";
|
|
11
|
+
import { readAnalysisExecution } from "./store-executions.js";
|
|
12
12
|
import { physicalCwdOf, resolvePhysicalCwd } from "../run/paths.js";
|
|
13
13
|
const CANCELLATION_POLL_MS = 250;
|
|
14
14
|
// Well inside AUTOMATIC_ANALYSIS_STALE_MS, so a live owner never reads as stale.
|
|
@@ -26,18 +26,18 @@ function hasParticipantEvidence(bundle) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
/** Small read-only TUI/CLI projection. Neither this nor Observer can resume a job. */
|
|
29
|
-
export async function
|
|
29
|
+
export async function readAutomaticAnalysis(cwd, runId) {
|
|
30
30
|
if (!exactId(runId))
|
|
31
31
|
return undefined;
|
|
32
32
|
const prepared = await resolveRunPath(await resolvePhysicalCwd(cwd), runId).catch(() => null);
|
|
33
|
-
return prepared ?
|
|
33
|
+
return prepared ? readAutomaticAnalysisPrepared(prepared) : undefined;
|
|
34
34
|
}
|
|
35
|
-
export async function
|
|
35
|
+
export async function requestAutomaticAnalysisCancellation(cwd, runId) {
|
|
36
36
|
if (!exactId(runId))
|
|
37
37
|
return { requested: false, reason: "AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE" };
|
|
38
38
|
const prepared = await resolveRunPath(await resolvePhysicalCwd(cwd), runId).catch(() => null);
|
|
39
39
|
return prepared
|
|
40
|
-
?
|
|
40
|
+
? requestAutomaticAnalysisCancellationPrepared(prepared)
|
|
41
41
|
: { requested: false, reason: "AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE" };
|
|
42
42
|
}
|
|
43
43
|
/** The job reason for an analysis that reached a status. */
|
|
@@ -114,8 +114,8 @@ async function persistOutcome(job, prepared, outcome) {
|
|
|
114
114
|
const analysisId = outcome.result?.analysisId;
|
|
115
115
|
const [entry, receipt] = analysisId
|
|
116
116
|
? await Promise.all([
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
readAnalysisVersion(prepared, analysisId),
|
|
118
|
+
readAnalysisExecution(prepared, analysisId),
|
|
119
119
|
])
|
|
120
120
|
: [null, null];
|
|
121
121
|
await job.update({
|
|
@@ -131,7 +131,7 @@ async function persistOutcome(job, prepared, outcome) {
|
|
|
131
131
|
}),
|
|
132
132
|
...(entry?.analysis ? { analysisSha256: hashAnalysisValue(entry.analysis) } : {}),
|
|
133
133
|
});
|
|
134
|
-
const persisted = await
|
|
134
|
+
const persisted = await readAutomaticAnalysisPrepared(prepared);
|
|
135
135
|
return persisted?.state === "unknown"
|
|
136
136
|
? { ...outcome, state: "unknown", reason: persisted.reason }
|
|
137
137
|
: outcome;
|
|
@@ -143,7 +143,7 @@ async function persistOutcome(job, prepared, outcome) {
|
|
|
143
143
|
/** One permanent claim per completed run, consumed even when preparation/cancellation fails.
|
|
144
144
|
* Only the original producer calls this after all recording writes return.
|
|
145
145
|
* No file reader, restart recovery, Observer poll or export calls this function. */
|
|
146
|
-
export async function
|
|
146
|
+
export async function runAutomaticAnalysis(cwdInput, runId, configInput, deps = {}) {
|
|
147
147
|
if (!exactId(runId))
|
|
148
148
|
return skipped("AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE");
|
|
149
149
|
let cwd = path.resolve(cwdInput);
|
|
@@ -162,7 +162,7 @@ export async function runAutomaticStudyAnalysis(cwdInput, runId, configInput, de
|
|
|
162
162
|
config = await (await import("./restricted-codex.js")).bindCodexAnalysisCliVersion(config, deps.detectCodexCliVersion);
|
|
163
163
|
let job;
|
|
164
164
|
try {
|
|
165
|
-
const claimed = await
|
|
165
|
+
const claimed = await claimAutomaticAnalysis(prepared, {
|
|
166
166
|
configDigest: hashAnalysisValue(config),
|
|
167
167
|
promptVersion: ANALYSIS_PROMPT_VERSION,
|
|
168
168
|
});
|
|
@@ -211,7 +211,11 @@ export async function runAutomaticStudyAnalysis(cwdInput, runId, configInput, de
|
|
|
211
211
|
try {
|
|
212
212
|
await poll();
|
|
213
213
|
const missingKey = config.provider !== "codex" && !hasKey;
|
|
214
|
-
|
|
214
|
+
const refused = deps.refusal?.();
|
|
215
|
+
if (refused !== undefined) {
|
|
216
|
+
outcome = skipped(refused);
|
|
217
|
+
}
|
|
218
|
+
else if (deps.defaultRequest === true && (missingKey || !participantEvidence)) {
|
|
215
219
|
outcome = signal.aborted
|
|
216
220
|
? { state: "cancelled", reason: "AUTOMATIC_ANALYSIS_CANCELLED" }
|
|
217
221
|
: skipped(missingKey
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** How many rejected outputs are kept across all runs; older ones are removed on each write. */
|
|
2
|
+
export declare const MAX_ANALYSIS_DIAGNOSTICS = 20;
|
|
3
|
+
export interface RejectedAnalysisOutput {
|
|
4
|
+
/** The allowlisted code the analysis artifact records. */
|
|
5
|
+
error: string;
|
|
6
|
+
/** Every failed validation rule, when validation ran. */
|
|
7
|
+
errors: string[];
|
|
8
|
+
/** The response after the narrative scrub when validation ran, else the parsed response. */
|
|
9
|
+
output: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface RejectedAnalysisRecord {
|
|
12
|
+
runId: string;
|
|
13
|
+
analysisId: string;
|
|
14
|
+
model: string;
|
|
15
|
+
promptVersion: string;
|
|
16
|
+
}
|
|
17
|
+
/** Internal fault-injection seam for tests. */
|
|
18
|
+
export interface AnalysisDiagnosticsHooks {
|
|
19
|
+
/** Runs after pruning has listed the records and before it removes any. */
|
|
20
|
+
beforeRemove?: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** Write one rejected output and prune to the newest MAX_ANALYSIS_DIAGNOSTICS. Returns its path relative to cwd. */
|
|
23
|
+
export declare function keepRejectedAnalysisOutput(cwd: string, record: RejectedAnalysisRecord, rejected: RejectedAnalysisOutput, hooks?: AnalysisDiagnosticsHooks): Promise<string>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { lstat, readdir, rmdir, unlink } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { decodeEscapes } from "../evidence/encoded-text.js";
|
|
4
|
+
import { redactText } from "../evidence/redaction.js";
|
|
5
|
+
import { assertPreparedSelectedOutputDirectory, assertSafeOutputPathSegment, bindExistingManagedHumanishOutputDirectory, prepareManagedHumanishOutputDirectory, writeContainedOutputFile, } from "../run/contained-output.js";
|
|
6
|
+
import { scrubTransientCommsText, transientCommsEncodedScrub, } from "../run/transient-comms-secrets.js";
|
|
7
|
+
import { ANALYSIS_ID_PATTERN } from "./types.js";
|
|
8
|
+
// A rejected analyst response is kept for diagnosis under .humanish/analysis-diagnostics/<run>/,
|
|
9
|
+
// outside every run directory. Export, verify and the Observer read run directories only, so the
|
|
10
|
+
// file never reaches a bundle, an export or a served page. The analysis artifact itself still
|
|
11
|
+
// records only the allowlisted code.
|
|
12
|
+
const ANALYSIS_DIAGNOSTICS_DIRECTORY = "analysis-diagnostics";
|
|
13
|
+
const ANALYSIS_DIAGNOSTIC_SCHEMA = "humanish.analysis-rejected-output.v1";
|
|
14
|
+
/** How many rejected outputs are kept across all runs; older ones are removed on each write. */
|
|
15
|
+
export const MAX_ANALYSIS_DIAGNOSTICS = 20;
|
|
16
|
+
const REDACTED = "[REDACTED_SECRET]";
|
|
17
|
+
const markers = (text) => text.split(REDACTED).length - 1;
|
|
18
|
+
/**
|
|
19
|
+
* Scrub every string, key and scalar in the output. The encoded scrub finds a known value written
|
|
20
|
+
* percent-encoded, escaped or base64-encoded and returns decoded text; a string where it finds
|
|
21
|
+
* nothing keeps its original spelling, so an escape in a rejected quote stays visible, and still
|
|
22
|
+
* gets the literal scrub. A number or boolean equal to a known value becomes the marker.
|
|
23
|
+
*/
|
|
24
|
+
function scrubber() {
|
|
25
|
+
const encoded = transientCommsEncodedScrub();
|
|
26
|
+
const text = (value) => {
|
|
27
|
+
const found = encoded(value);
|
|
28
|
+
return redactText(markers(found) > markers(decodeEscapes(value)) ? found : scrubTransientCommsText(value));
|
|
29
|
+
};
|
|
30
|
+
const scrub = (value) => {
|
|
31
|
+
if (typeof value === "string")
|
|
32
|
+
return text(value);
|
|
33
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
34
|
+
return text(String(value)) === String(value) ? value : REDACTED;
|
|
35
|
+
if (Array.isArray(value))
|
|
36
|
+
return value.map(scrub);
|
|
37
|
+
if (value !== null && typeof value === "object")
|
|
38
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [text(key), scrub(entry)]));
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
41
|
+
return scrub;
|
|
42
|
+
}
|
|
43
|
+
/** Write one rejected output and prune to the newest MAX_ANALYSIS_DIAGNOSTICS. Returns its path relative to cwd. */
|
|
44
|
+
export async function keepRejectedAnalysisOutput(cwd, record, rejected, hooks = {}) {
|
|
45
|
+
assertSafeOutputPathSegment(record.runId, "Run id");
|
|
46
|
+
if (!ANALYSIS_ID_PATTERN.test(record.analysisId))
|
|
47
|
+
throw new Error("Unsafe analysis id.");
|
|
48
|
+
const root = await prepareManagedHumanishOutputDirectory(cwd, ANALYSIS_DIAGNOSTICS_DIRECTORY, record.runId);
|
|
49
|
+
const file = `${record.analysisId}.json`;
|
|
50
|
+
const document = {
|
|
51
|
+
schema: ANALYSIS_DIAGNOSTIC_SCHEMA,
|
|
52
|
+
...record,
|
|
53
|
+
recordedAt: new Date().toISOString(),
|
|
54
|
+
error: rejected.error,
|
|
55
|
+
errors: rejected.errors,
|
|
56
|
+
output: scrubber()(rejected.output),
|
|
57
|
+
};
|
|
58
|
+
await writeContainedOutputFile(root, file, `${JSON.stringify(document, null, 2)}\n`, "utf8");
|
|
59
|
+
// A failed prune leaves extra files that the next write removes.
|
|
60
|
+
await pruneAnalysisDiagnostics(cwd, hooks).catch(() => undefined);
|
|
61
|
+
return path.join(".humanish", ANALYSIS_DIAGNOSTICS_DIRECTORY, record.runId, file);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Remove one record, or with no name one emptied run directory, only while the directory is still
|
|
65
|
+
* the one listed: its path resolves to the same physical directory with the same identity, so a
|
|
66
|
+
* directory swapped for a symlink after listing is refused. The record must be a regular file.
|
|
67
|
+
*/
|
|
68
|
+
async function removeListed(root, name) {
|
|
69
|
+
try {
|
|
70
|
+
await assertPreparedSelectedOutputDirectory(root);
|
|
71
|
+
if (name === undefined) {
|
|
72
|
+
await rmdir(root.physicalPath);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const file = path.join(root.physicalPath, name);
|
|
76
|
+
if (!(await lstat(file)).isFile())
|
|
77
|
+
return;
|
|
78
|
+
await unlink(file);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// A changed directory, a vanished file or a directory that still holds a file is left alone.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function pruneAnalysisDiagnostics(cwd, hooks) {
|
|
85
|
+
const parent = await bindExistingManagedHumanishOutputDirectory(cwd, ANALYSIS_DIAGNOSTICS_DIRECTORY);
|
|
86
|
+
if (!parent)
|
|
87
|
+
return;
|
|
88
|
+
const files = [];
|
|
89
|
+
for (const run of await readdir(parent.physicalPath, { withFileTypes: true })) {
|
|
90
|
+
if (!run.isDirectory())
|
|
91
|
+
continue;
|
|
92
|
+
const root = await bindExistingManagedHumanishOutputDirectory(cwd, ANALYSIS_DIAGNOSTICS_DIRECTORY, run.name);
|
|
93
|
+
if (!root)
|
|
94
|
+
continue;
|
|
95
|
+
for (const entry of await readdir(root.physicalPath, { withFileTypes: true })) {
|
|
96
|
+
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
97
|
+
continue;
|
|
98
|
+
const modified = (await lstat(path.join(root.physicalPath, entry.name))).mtimeMs;
|
|
99
|
+
files.push({ root, name: entry.name, modified });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
files.sort((a, b) => b.modified - a.modified ||
|
|
103
|
+
path.join(b.root.physicalPath, b.name).localeCompare(path.join(a.root.physicalPath, a.name)));
|
|
104
|
+
const removed = files.slice(MAX_ANALYSIS_DIAGNOSTICS);
|
|
105
|
+
if (removed.length === 0)
|
|
106
|
+
return;
|
|
107
|
+
await hooks.beforeRemove?.();
|
|
108
|
+
for (const { root, name } of removed)
|
|
109
|
+
await removeListed(root, name);
|
|
110
|
+
for (const root of new Set(removed.map((entry) => entry.root)))
|
|
111
|
+
await removeListed(root);
|
|
112
|
+
}
|
|
@@ -3,7 +3,7 @@ import { eventRecordIdOf, recordIdOf, streamParticipantIdOf } from "../run/parti
|
|
|
3
3
|
import { cuaGoalSource } from "../actors/goal-source.js";
|
|
4
4
|
import { isRecord } from "../run/type-guards.js";
|
|
5
5
|
import { ACTION_CAPTURE_VERSION, } from "./types.js";
|
|
6
|
-
import { decodesToPlainRelativePath,
|
|
6
|
+
import { decodesToPlainRelativePath, isEvidencePath } from "../run/evidence-files.js";
|
|
7
7
|
// How a run bundle becomes analysis sources: each participant's recorded provenance and assignment,
|
|
8
8
|
// the evidence entries its trace offers, and the order in which entries are admitted under the
|
|
9
9
|
// count and byte budgets. captureEvidence selects from these, and validateAnalysisEvidence
|
|
@@ -127,7 +127,7 @@ export function sourceEntries(bundle, stream, captureVersion) {
|
|
|
127
127
|
const capturePath = isCaptureItem(item, captureVersion) &&
|
|
128
128
|
isRecord(item.screenshotRef) &&
|
|
129
129
|
typeof item.screenshotRef.path === "string" &&
|
|
130
|
-
|
|
130
|
+
isEvidencePath(item.screenshotRef.path)
|
|
131
131
|
? item.screenshotRef.path
|
|
132
132
|
: null;
|
|
133
133
|
if (frameIds.has(item.id))
|
|
@@ -7,7 +7,7 @@ import { RUN_BUNDLE_FILE } from "../run/bundle.js";
|
|
|
7
7
|
import { ACTION_CAPTURE_VERSION, } from "./types.js";
|
|
8
8
|
import { digestAnalysisInput, hashAnalysisValue, validateAnalysisInputMetadata, } from "./validation.js";
|
|
9
9
|
import { boundedText, fairShares, hasUnmappedCaptures, participantAssignment, participantSource, sourceEntries, sourceOrder, } from "./evidence-sources.js";
|
|
10
|
-
import {
|
|
10
|
+
import { readBoundedFile, readBoundedFileResult } from "../run/evidence-files.js";
|
|
11
11
|
export const EVIDENCE_LIMITS = Object.freeze({
|
|
12
12
|
participants: 16,
|
|
13
13
|
evidence: 800,
|
|
@@ -96,7 +96,7 @@ export async function captureEvidence(prepared, bundleBytes, requested = {}) {
|
|
|
96
96
|
}
|
|
97
97
|
if (bundleBytes.length > limits.sourceBytes)
|
|
98
98
|
throw new Error("ANALYSIS_SOURCE_TOO_LARGE");
|
|
99
|
-
const current = await
|
|
99
|
+
const current = await readBoundedFile(prepared, RUN_BUNDLE_FILE, limits.sourceBytes);
|
|
100
100
|
if (!current || !current.equals(bundleBytes))
|
|
101
101
|
throw new Error("ANALYSIS_SOURCE_CHANGED");
|
|
102
102
|
const bundle = parseSource(prepared, bundleBytes);
|
|
@@ -164,7 +164,7 @@ export async function captureEvidence(prepared, bundleBytes, requested = {}) {
|
|
|
164
164
|
};
|
|
165
165
|
result.inputDigest = digestAnalysisInput(result);
|
|
166
166
|
validateAnalysisInputMetadata(result);
|
|
167
|
-
const after = await
|
|
167
|
+
const after = await readBoundedFile(prepared, RUN_BUNDLE_FILE, limits.sourceBytes);
|
|
168
168
|
if (!after || !after.equals(bundleBytes))
|
|
169
169
|
throw new Error("ANALYSIS_SOURCE_CHANGED");
|
|
170
170
|
return result;
|
|
@@ -229,7 +229,7 @@ async function selectCaptures(prepared, packings, sel, omissions) {
|
|
|
229
229
|
packing.attempts++;
|
|
230
230
|
sel.attemptedReads++;
|
|
231
231
|
const source = sel.captureOrders[index][packing.captureCursor];
|
|
232
|
-
const result = await
|
|
232
|
+
const result = await readBoundedFileResult(prepared, source.capturePath, allowance);
|
|
233
233
|
if (result.state === "limit" &&
|
|
234
234
|
result.size <= BigInt(limits.imageBytes) &&
|
|
235
235
|
result.size <= BigInt(limits.totalImageBytes - sel.imageBytes) &&
|
|
@@ -458,7 +458,7 @@ export async function validateAnalysisEvidence(prepared, artifact, bundleBytes)
|
|
|
458
458
|
throw new Error("ANALYSIS_CAPTURE_REFERENCE_INVALID");
|
|
459
459
|
let hash = checkedCaptures.get(source.capturePath);
|
|
460
460
|
if (hash === undefined) {
|
|
461
|
-
const bytes = await
|
|
461
|
+
const bytes = await readBoundedFile(prepared, source.capturePath, EVIDENCE_LIMITS.imageBytes);
|
|
462
462
|
if (!bytes || screenshotEvidenceError(source.capturePath, bytes) !== null)
|
|
463
463
|
throw new Error("ANALYSIS_CAPTURE_UNAVAILABLE");
|
|
464
464
|
checkedImageBytes += bytes.length;
|
|
@@ -476,7 +476,7 @@ export async function validateAnalysisEvidence(prepared, artifact, bundleBytes)
|
|
|
476
476
|
artifact.evidence.length) {
|
|
477
477
|
throw new Error("ANALYSIS_COVERAGE_INCOMPLETE");
|
|
478
478
|
}
|
|
479
|
-
const current = await
|
|
479
|
+
const current = await readBoundedFile(prepared, RUN_BUNDLE_FILE, EVIDENCE_LIMITS.sourceBytes);
|
|
480
480
|
if (!current?.equals(bundleBytes))
|
|
481
481
|
throw new Error("ANALYSIS_SOURCE_CHANGED");
|
|
482
482
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AnalysisArtifact, type AnalysisConfig, type AnalysisInput } from "./types.js";
|
|
2
2
|
import { type AnalysisFetch, type AnalysisProvider } from "./provider.js";
|
|
3
|
+
import type { RejectedAnalysisOutput } from "./diagnostics.js";
|
|
3
4
|
export declare const ANALYSIS_PROMPT_VERSION = "study-evidence-6";
|
|
4
5
|
export declare const isSupportedAnalysisModel: (model: string) => boolean;
|
|
5
6
|
/** The OpenAI output limit when a lab omits maxOutputTokens, and the most it may ask for. */
|
|
@@ -21,7 +22,7 @@ export interface AnalysisProgress {
|
|
|
21
22
|
status?: AnalysisArtifact["status"];
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
|
-
* Local admission
|
|
25
|
+
* Local admission estimate, not a provider-enforced billed-spend guarantee. No token-count API
|
|
25
26
|
* call, credential, or network access. One UTF-8 byte/token for all text/schema plus framing is
|
|
26
27
|
* intentionally conservative. Each high-detail image is priced from its PNG size by the vision
|
|
27
28
|
* guide's patch formula (see image-tokens.ts), or at the 3,000-token ceiling when its size or the
|
|
@@ -46,6 +47,11 @@ interface RunAnalysisOptions {
|
|
|
46
47
|
/** Set by automatic analysis to its job attempt id, claimed before any provider call. */
|
|
47
48
|
analysisId?: string;
|
|
48
49
|
beforeDispatch?: (context: AnalysisDispatchContext) => Promise<void>;
|
|
50
|
+
/** Receives the provider's run warnings, such as Codex notification methods humanish does not know. */
|
|
51
|
+
warnings?: string[];
|
|
52
|
+
/** Receives a completed response that failed validation, for local diagnosis only. The
|
|
53
|
+
* artifact still records only the allowlisted code. */
|
|
54
|
+
onRejectedOutput?: (rejected: RejectedAnalysisOutput) => void;
|
|
49
55
|
}
|
|
50
56
|
/** Explicit invocation or an opted-in post-run owner; Observer readers never call this. */
|
|
51
57
|
export declare function runAnalysis(input: AnalysisInput, config: AnalysisConfig, options: RunAnalysisOptions): Promise<AnalysisArtifact>;
|