humanish 0.107.0 → 0.109.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 +73 -69
- package/CHANGELOG.md +27 -0
- package/CONTEXT.md +51 -44
- package/README.md +148 -175
- 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-trace.js +2 -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 +20 -18
- 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 +3 -3
- package/dist/actors/codex/restricted-session.js +10 -5
- package/dist/actors/computer-use/actor.d.ts +18 -18
- package/dist/actors/computer-use/actor.js +4 -4
- package/dist/actors/computer-use/admission-limit.d.ts +3 -3
- package/dist/actors/computer-use/admission-limit.js +4 -4
- package/dist/actors/computer-use/executor-error.d.ts +2 -2
- package/dist/actors/computer-use/executor-error.js +3 -3
- package/dist/actors/computer-use/loop/actions.d.ts +3 -3
- package/dist/actors/computer-use/loop/actions.js +15 -15
- 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/debrief.js +2 -2
- package/dist/actors/computer-use/loop/ending.d.ts +6 -6
- package/dist/actors/computer-use/loop/ending.js +18 -17
- package/dist/actors/computer-use/loop/observation.d.ts +2 -2
- package/dist/actors/computer-use/loop/observation.js +15 -14
- package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
- package/dist/actors/computer-use/loop/provider-call.js +12 -12
- package/dist/actors/computer-use/loop/session.d.ts +4 -4
- package/dist/actors/computer-use/loop/session.js +4 -4
- 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 +11 -9
- package/dist/actors/computer-use/loop/types.d.ts +47 -45
- package/dist/actors/computer-use/loop/usage.d.ts +2 -2
- package/dist/actors/computer-use/loop/usage.js +2 -2
- package/dist/actors/computer-use/loop.d.ts +6 -1
- package/dist/actors/computer-use/loop.js +26 -4
- package/dist/actors/computer-use/openai-context.d.ts +37 -0
- package/dist/actors/computer-use/openai-context.js +214 -0
- package/dist/actors/computer-use/openai-provider.d.ts +13 -10
- package/dist/actors/computer-use/openai-provider.js +52 -41
- 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/computer-use/provider-error.d.ts +4 -4
- package/dist/actors/computer-use/provider-error.js +6 -6
- package/dist/actors/contract.d.ts +82 -47
- package/dist/actors/contract.js +30 -24
- package/dist/actors/local-agent/claude-session.d.ts +1 -1
- package/dist/actors/local-agent/claude-session.js +13 -12
- package/dist/actors/local-agent/cli.d.ts +9 -7
- 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 +8 -8
- package/dist/actors/registry.d.ts +12 -12
- package/dist/actors/registry.js +21 -20
- package/dist/actors/scripted-browser/actor.d.ts +10 -10
- package/dist/actors/scripted-browser/actor.js +17 -17
- 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/automatic-completion.d.ts +7 -7
- package/dist/analysis/automatic-completion.js +1 -1
- package/dist/analysis/automatic-config.d.ts +6 -6
- package/dist/analysis/automatic-config.js +1 -1
- package/dist/analysis/codex-config.d.ts +6 -2
- package/dist/analysis/codex-config.js +22 -14
- package/dist/analysis/execute.d.ts +1 -1
- package/dist/analysis/execute.js +1 -1
- package/dist/analysis/service.js +5 -5
- package/dist/analysis/types.d.ts +6 -0
- package/dist/browser-control/client.js +16 -16
- package/dist/browser-control/dispatcher.js +4 -4
- package/dist/browser-control/protocol.js +14 -14
- package/dist/browser-control/recording-transfer.js +4 -4
- package/dist/browser-control/transport.js +7 -7
- package/dist/cli/commands/analysis-signals.d.ts +2 -2
- package/dist/cli/commands/analyze.js +19 -16
- package/dist/cli/commands/codex.js +9 -9
- package/dist/cli/commands/comms.js +48 -35
- package/dist/cli/commands/feedback.js +20 -21
- package/dist/cli/commands/migrate.d.ts +4 -0
- package/dist/cli/commands/migrate.js +65 -0
- package/dist/cli/commands/observe.d.ts +0 -1
- package/dist/cli/commands/observe.js +136 -112
- 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 +136 -139
- package/dist/cli/commands/setup.d.ts +2 -2
- package/dist/cli/commands/setup.js +27 -24
- package/dist/cli/commands/study-format.d.ts +9 -0
- package/dist/cli/commands/{lab-format.js → study-format.js} +39 -27
- package/dist/cli/commands/{lab-route-computer-use.d.ts → study-route-computer-use.d.ts} +6 -6
- package/dist/cli/commands/{lab-route-computer-use.js → study-route-computer-use.js} +17 -18
- package/dist/cli/commands/{lab-route-open.d.ts → study-route-open.d.ts} +4 -4
- package/dist/cli/commands/{lab-route-open.js → study-route-open.js} +2 -2
- package/dist/cli/commands/{lab-route-preview.d.ts → study-route-preview.d.ts} +7 -7
- package/dist/cli/commands/{lab-route-preview.js → study-route-preview.js} +7 -6
- package/dist/cli/commands/study-route-run.d.ts +19 -0
- package/dist/cli/commands/{lab-route-run.js → study-route-run.js} +6 -6
- package/dist/cli/commands/study-route-scripted.d.ts +17 -0
- package/dist/cli/commands/{lab-route-scripted.js → study-route-scripted.js} +4 -4
- package/dist/cli/commands/{lab-route-shared-world.d.ts → study-route-shared-world.d.ts} +5 -5
- package/dist/cli/commands/{lab-route-shared-world.js → study-route-shared-world.js} +10 -10
- package/dist/cli/commands/study-route-terminal.d.ts +17 -0
- package/dist/cli/commands/{lab-route-terminal.js → study-route-terminal.js} +4 -4
- package/dist/cli/commands/study-run.d.ts +9 -0
- package/dist/cli/commands/{lab-run.js → study-run.js} +44 -45
- package/dist/cli/commands/{lab-scorer.d.ts → study-scorer.d.ts} +7 -7
- package/dist/cli/commands/{lab-scorer.js → study-scorer.js} +2 -2
- package/dist/cli/commands/study.d.ts +3 -0
- package/dist/cli/commands/study.js +146 -0
- package/dist/cli/commands/tui.d.ts +4 -4
- package/dist/cli/commands/tui.js +31 -31
- package/dist/cli/commands/watch.js +71 -121
- package/dist/cli/deprecations.d.ts +6 -0
- package/dist/cli/deprecations.js +18 -0
- package/dist/cli/doctor.d.ts +18 -8
- package/dist/cli/doctor.js +114 -59
- package/dist/cli/first-run-path.d.ts +15 -13
- package/dist/cli/first-run-path.js +35 -33
- package/dist/cli/io.d.ts +44 -9
- package/dist/cli/io.js +98 -23
- package/dist/cli/observer-follow.d.ts +3 -3
- package/dist/cli/observer-follow.js +6 -7
- package/dist/cli/orientation.d.ts +10 -7
- package/dist/cli/orientation.js +89 -47
- 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 +111 -43
- package/dist/cli/telemetry.d.ts +16 -16
- package/dist/cli/telemetry.js +46 -44
- 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 +17 -17
- package/dist/comms/connections.d.ts +1 -1
- package/dist/comms/connections.js +2 -2
- package/dist/comms/email-catch.d.ts +3 -3
- package/dist/comms/email-catch.js +8 -8
- package/dist/comms/external-evidence.d.ts +5 -5
- package/dist/comms/external-evidence.js +5 -4
- package/dist/comms/extract.js +2 -2
- package/dist/comms/fake-inbox.d.ts +3 -3
- package/dist/comms/fake-inbox.js +3 -3
- package/dist/comms/receiving-render.d.ts +1 -1
- package/dist/comms/receiving-render.js +2 -2
- package/dist/comms/receiving-runtime.d.ts +4 -4
- 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 +38 -39
- package/dist/comms/sandbox-catch.js +36 -37
- package/dist/comms/setup.d.ts +2 -2
- package/dist/comms/setup.js +56 -25
- 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 +38 -80
- package/dist/feedback/export-bundle.js +1 -1
- package/dist/feedback/export.d.ts +5 -1
- package/dist/feedback/export.js +10 -10
- package/dist/feedback/feedback.d.ts +2 -2
- package/dist/feedback/feedback.js +21 -3
- package/dist/feedback/proof.d.ts +1 -1
- package/dist/feedback/proof.js +2 -2
- package/dist/guest/bootstrap.js +6 -6
- package/dist/guest/browser-tools.js +8 -8
- package/dist/guest/chromium-text.js +19 -19
- package/dist/guest/desktop-executor.d.ts +2 -2
- package/dist/guest/desktop-executor.js +20 -20
- package/dist/guest/desktop-keys.js +4 -4
- package/dist/guest/desktop-media.d.ts +2 -2
- package/dist/guest/desktop-media.js +16 -16
- package/dist/guest/desktop-native.d.ts +1 -1
- package/dist/guest/desktop-native.js +10 -10
- package/dist/guest/runtime-desktop.js +2 -2
- package/dist/guest/runtime-processes.d.ts +2 -2
- package/dist/guest/runtime-processes.js +2 -2
- package/dist/guest/runtime.js +6 -6
- package/dist/index.d.ts +14 -14
- package/dist/index.js +8 -8
- package/dist/keys/key-resolution.d.ts +5 -5
- package/dist/keys/key-resolution.js +23 -23
- package/dist/observer/artifact.d.ts +2 -2
- package/dist/observer/artifact.js +7 -7
- 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 +3 -1
- package/dist/observer/library.js +8 -3
- package/dist/observer/listen.js +5 -4
- package/dist/observer/render.d.ts +1 -1
- package/dist/observer/render.js +17 -6
- package/dist/observer/run-routes.d.ts +4 -3
- package/dist/observer/run-routes.js +38 -13
- package/dist/observer/serve.d.ts +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 +18 -17
- package/dist/routes/computer-use/bundle.d.ts +2 -2
- package/dist/routes/computer-use/bundle.js +10 -9
- package/dist/routes/computer-use/e2b-desktop/comms.d.ts +6 -6
- package/dist/routes/computer-use/e2b-desktop/comms.js +18 -17
- package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -4
- package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +5 -4
- package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -10
- package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +1 -1
- package/dist/routes/computer-use/e2b-desktop/prepare.js +10 -10
- package/dist/routes/computer-use/e2b-desktop/start.js +7 -7
- package/dist/routes/computer-use/e2b-desktop/state.d.ts +2 -2
- package/dist/routes/computer-use/e2b-desktop/teardown.js +2 -2
- package/dist/routes/computer-use/external-comms.d.ts +4 -4
- package/dist/routes/computer-use/external-comms.js +1 -1
- package/dist/routes/computer-use/fanout-bundle.js +27 -26
- package/dist/routes/computer-use/fanout-records.js +12 -13
- package/dist/routes/computer-use/live-flush.d.ts +4 -4
- package/dist/routes/computer-use/live-flush.js +6 -6
- package/dist/routes/computer-use/live-phase.d.ts +5 -4
- package/dist/routes/computer-use/live-phase.js +8 -8
- package/dist/routes/computer-use/local-tree-pack.d.ts +5 -5
- package/dist/routes/computer-use/local-tree-pack.js +1 -1
- package/dist/routes/computer-use/local-vm.d.ts +8 -8
- package/dist/routes/computer-use/local-vm.js +3 -3
- package/dist/routes/computer-use/participant-desktop.d.ts +7 -7
- package/dist/routes/computer-use/participant-desktop.js +5 -5
- package/dist/routes/computer-use/participant-execution.d.ts +4 -4
- package/dist/routes/computer-use/participant-execution.js +10 -10
- package/dist/routes/computer-use/participant-model.d.ts +6 -0
- package/dist/routes/computer-use/participant-model.js +33 -15
- package/dist/routes/computer-use/participant-prompt.d.ts +15 -15
- package/dist/routes/computer-use/participant-prompt.js +9 -9
- package/dist/routes/computer-use/participant-runs.d.ts +16 -16
- package/dist/routes/computer-use/participant-runs.js +26 -25
- package/dist/routes/computer-use/plan.d.ts +12 -12
- package/dist/routes/computer-use/plan.js +42 -42
- package/dist/routes/computer-use/preflight.d.ts +5 -5
- package/dist/routes/computer-use/preflight.js +25 -25
- package/dist/routes/computer-use/rerun-selection.d.ts +1 -1
- package/dist/routes/computer-use/rerun-selection.js +4 -3
- package/dist/routes/computer-use/result.d.ts +4 -4
- package/dist/routes/computer-use/result.js +22 -21
- package/dist/routes/computer-use/route.d.ts +12 -11
- package/dist/routes/computer-use/route.js +31 -30
- package/dist/routes/computer-use/self-report.d.ts +7 -7
- package/dist/routes/computer-use/self-report.js +32 -32
- package/dist/routes/computer-use/setup.d.ts +23 -23
- package/dist/routes/computer-use/setup.js +28 -29
- package/dist/routes/computer-use/single-bundle.d.ts +11 -11
- package/dist/routes/computer-use/single-bundle.js +40 -41
- package/dist/routes/computer-use/subject-projection.d.ts +4 -4
- package/dist/routes/computer-use/subject-projection.js +6 -6
- package/dist/routes/computer-use/types.d.ts +71 -73
- package/dist/routes/computer-use/types.js +12 -12
- package/dist/routes/preview.d.ts +13 -7
- package/dist/routes/preview.js +11 -7
- package/dist/routes/scripted/bundle.d.ts +7 -7
- package/dist/routes/scripted/bundle.js +20 -18
- package/dist/routes/scripted/plan.d.ts +8 -8
- package/dist/routes/scripted/plan.js +13 -13
- package/dist/routes/scripted/result.d.ts +3 -3
- package/dist/routes/scripted/result.js +13 -13
- package/dist/routes/scripted/route.d.ts +9 -9
- package/dist/routes/scripted/route.js +15 -16
- package/dist/routes/scripted/scenario.d.ts +1 -1
- package/dist/routes/scripted/scenario.js +5 -5
- package/dist/routes/scripted/setup.d.ts +6 -6
- package/dist/routes/scripted/setup.js +8 -8
- package/dist/routes/scripted/subject.d.ts +5 -5
- package/dist/routes/scripted/subject.js +4 -4
- package/dist/routes/scripted/surface-records.d.ts +1 -1
- package/dist/routes/scripted/surface-records.js +4 -4
- package/dist/routes/scripted/surface-sessions.d.ts +3 -3
- package/dist/routes/scripted/types.d.ts +18 -20
- package/dist/routes/scripted/types.js +2 -2
- package/dist/routes/shared-world/bundle-records.js +11 -10
- package/dist/routes/shared-world/bundle.d.ts +5 -5
- package/dist/routes/shared-world/bundle.js +46 -43
- package/dist/routes/shared-world/checkpoints.d.ts +4 -4
- package/dist/routes/shared-world/checkpoints.js +2 -2
- package/dist/routes/shared-world/comms.d.ts +14 -14
- package/dist/routes/shared-world/comms.js +19 -18
- 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 +10 -9
- package/dist/routes/shared-world/participant-specs.js +38 -31
- package/dist/routes/shared-world/plan.d.ts +9 -9
- package/dist/routes/shared-world/plan.js +16 -16
- 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.d.ts +8 -8
- package/dist/routes/shared-world/provisioned.js +18 -17
- package/dist/routes/shared-world/result.d.ts +6 -6
- package/dist/routes/shared-world/result.js +29 -29
- package/dist/routes/shared-world/route.d.ts +11 -11
- package/dist/routes/shared-world/route.js +40 -39
- package/dist/routes/shared-world/setup.d.ts +16 -16
- package/dist/routes/shared-world/setup.js +23 -23
- package/dist/routes/shared-world/types.d.ts +55 -53
- package/dist/routes/shared-world/types.js +4 -4
- package/dist/routes/terminal/adapter.d.ts +5 -5
- package/dist/routes/terminal/adapter.js +9 -9
- package/dist/routes/terminal/bundle.d.ts +13 -13
- package/dist/routes/terminal/bundle.js +28 -27
- package/dist/routes/terminal/credentials.d.ts +8 -8
- package/dist/routes/terminal/credentials.js +12 -12
- package/dist/routes/terminal/dry-run.d.ts +4 -4
- package/dist/routes/terminal/dry-run.js +12 -12
- package/dist/routes/terminal/encoding.js +2 -2
- package/dist/routes/terminal/ledger.d.ts +5 -5
- package/dist/routes/terminal/ledger.js +14 -14
- package/dist/routes/terminal/lifetime.d.ts +1 -1
- package/dist/routes/terminal/lifetime.js +1 -1
- package/dist/routes/terminal/live-finish.d.ts +2 -2
- package/dist/routes/terminal/live-finish.js +10 -10
- package/dist/routes/terminal/live-sandbox.d.ts +2 -2
- package/dist/routes/terminal/live-sandbox.js +15 -15
- package/dist/routes/terminal/participant-text.js +2 -1
- package/dist/routes/terminal/persona.d.ts +2 -2
- package/dist/routes/terminal/persona.js +3 -3
- package/dist/routes/terminal/plan.d.ts +7 -7
- package/dist/routes/terminal/plan.js +15 -15
- package/dist/routes/terminal/recorder.js +2 -2
- package/dist/routes/terminal/result.d.ts +5 -5
- package/dist/routes/terminal/result.js +14 -14
- package/dist/routes/terminal/route.d.ts +9 -9
- package/dist/routes/terminal/route.js +19 -20
- package/dist/routes/terminal/sandbox.d.ts +1 -1
- package/dist/routes/terminal/sandbox.js +1 -1
- package/dist/routes/terminal/session.d.ts +5 -5
- package/dist/routes/terminal/session.js +14 -13
- 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 +4 -4
- package/dist/routes/terminal/trace.js +2 -2
- package/dist/routes/terminal/types.d.ts +48 -50
- package/dist/routes/terminal/types.js +5 -5
- package/dist/run/bundle-shape.js +3 -3
- package/dist/run/bundle.d.ts +49 -49
- package/dist/run/bundle.js +3 -2
- package/dist/run/cost-summary.js +3 -3
- package/dist/run/costs.d.ts +23 -0
- package/dist/run/costs.js +79 -44
- package/dist/run/detail.d.ts +6 -6
- package/dist/run/detail.js +4 -4
- package/dist/run/dry-run-inputs.d.ts +1 -1
- package/dist/run/dry-run-inputs.js +2 -2
- package/dist/run/dry-run.js +20 -20
- package/dist/run/judge.d.ts +19 -13
- package/dist/run/judge.js +18 -8
- package/dist/run/outcomes.d.ts +1 -1
- package/dist/run/participant.d.ts +1 -1
- package/dist/run/paths.d.ts +1 -1
- package/dist/run/paths.js +1 -1
- package/dist/run/preflight-receipts.js +1 -1
- package/dist/run/pricing.d.ts +11 -6
- package/dist/run/pricing.js +22 -15
- package/dist/run/projection.d.ts +46 -46
- package/dist/run/projection.js +57 -57
- package/dist/run/reclaim.d.ts +1 -1
- package/dist/run/reclaim.js +3 -3
- package/dist/run/results.d.ts +5 -5
- package/dist/run/run-cost.d.ts +53 -0
- package/dist/run/run-cost.js +105 -0
- package/dist/run/run-index-analysis.d.ts +15 -0
- package/dist/run/run-index-analysis.js +77 -0
- package/dist/run/run-index.d.ts +19 -7
- package/dist/run/run-index.js +77 -64
- 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 +6 -5
- package/dist/run/run.js +3 -3
- 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 +37 -36
- 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 +7 -7
- package/dist/run/stats.d.ts +14 -8
- package/dist/run/stats.js +121 -63
- package/dist/run/status.d.ts +16 -29
- package/dist/run/status.js +10 -29
- package/dist/run/stored-runs.js +2 -1
- package/dist/run/stream-shape.js +1 -1
- package/dist/run/streams.d.ts +7 -7
- package/dist/run/study-provenance.d.ts +49 -0
- package/dist/run/study-provenance.js +66 -0
- package/dist/run/study-result.d.ts +11 -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-study.d.ts +112 -0
- package/dist/{run-lab.js → run-study.js} +52 -47
- package/dist/{lab → study}/adapter-extension.d.ts +15 -27
- package/dist/{lab → study}/adapter-extension.js +10 -45
- package/dist/{lab → study}/adapter-scorer-loader.d.ts +17 -17
- package/dist/{lab → study}/adapter-scorer-loader.js +34 -34
- package/dist/study/composition-rules.d.ts +7 -0
- package/dist/{lab → study}/composition-rules.js +53 -53
- package/dist/study/config.d.ts +15 -0
- package/dist/{lab → study}/config.js +98 -41
- package/dist/study/convert.d.ts +33 -0
- package/dist/study/convert.js +432 -0
- package/dist/{lab → study}/device-presets.d.ts +10 -10
- package/dist/{lab → study}/device-presets.js +11 -11
- package/dist/study/discover.d.ts +75 -0
- package/dist/{lab → study}/discover.js +134 -95
- package/dist/{lab → study}/doctor.d.ts +19 -12
- package/dist/{lab → study}/doctor.js +58 -20
- 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 +44 -0
- package/dist/{lab → study}/init-templates.d.ts +2 -2
- package/dist/study/init-templates.js +421 -0
- package/dist/{lab → study}/init.d.ts +2 -2
- package/dist/{lab → study}/init.js +46 -11
- package/dist/study/keys.d.ts +4 -0
- package/dist/{lab → study}/keys.js +46 -12
- 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/{lab → study}/parse/actors.d.ts +5 -5
- package/dist/{lab → study}/parse/actors.js +14 -14
- package/dist/study/parse/comms.d.ts +12 -0
- package/dist/{lab → study}/parse/comms.js +5 -5
- package/dist/study/parse/execution.d.ts +19 -0
- package/dist/{lab → study}/parse/execution.js +13 -13
- package/dist/study/parse/study-v3.d.ts +21 -0
- package/dist/study/parse/study-v3.js +259 -0
- package/dist/study/parse/subject-state.d.ts +30 -0
- package/dist/{lab → study}/parse/subject-state.js +19 -19
- package/dist/{lab → study}/parse/subject.d.ts +3 -3
- package/dist/{lab → study}/parse/subject.js +34 -34
- package/dist/{lab → study}/parse/values.d.ts +3 -3
- package/dist/{lab → study}/parse/values.js +3 -3
- package/dist/{lab → study}/persona-resolve.d.ts +8 -8
- package/dist/{lab → study}/persona-resolve.js +6 -6
- package/dist/{lab → study}/plan-base.d.ts +7 -7
- package/dist/{lab → study}/plan-base.js +1 -1
- package/dist/{lab → study}/plan-participants.d.ts +12 -12
- package/dist/{lab → study}/plan-participants.js +4 -4
- package/dist/{lab → study}/plan-types.d.ts +31 -31
- package/dist/{lab → study}/plan-types.js +2 -2
- package/dist/study/plan.d.ts +13 -0
- package/dist/study/plan.js +101 -0
- package/dist/study/preflight-probes.d.ts +3 -0
- package/dist/{lab → study}/preflight-probes.js +20 -19
- package/dist/study/preflight-result.d.ts +8 -0
- package/dist/{lab → study}/preflight-result.js +5 -4
- package/dist/study/preflight.d.ts +94 -0
- package/dist/{lab → study}/preflight.js +12 -12
- package/dist/{lab → study}/requirements.d.ts +3 -3
- package/dist/{lab → study}/requirements.js +1 -1
- package/dist/study/route-inputs.d.ts +28 -0
- package/dist/{lab → study}/route-inputs.js +1 -1
- package/dist/{lab → study}/routing.d.ts +39 -33
- package/dist/{lab → study}/routing.js +60 -29
- package/dist/{lab/run-lab-events.d.ts → study/run-study-events.d.ts} +10 -13
- package/dist/{lab/run-lab-events.js → study/run-study-events.js} +3 -9
- package/dist/{lab/run-lab-homes.d.ts → study/run-study-homes.d.ts} +21 -18
- package/dist/{lab/run-lab-homes.js → study/run-study-homes.js} +1 -1
- package/dist/study/run-study-options.d.ts +28 -0
- package/dist/{lab/run-lab-options.js → study/run-study-options.js} +29 -25
- package/dist/{lab/lab-deps.d.ts → study/study-deps.d.ts} +8 -13
- package/dist/{lab/lab-deps.js → study/study-deps.js} +2 -2
- package/dist/{lab → study}/summary.d.ts +16 -15
- package/dist/{lab → study}/summary.js +21 -21
- package/dist/{lab → study}/tasks.d.ts +18 -18
- package/dist/{lab → study}/tasks.js +14 -14
- package/dist/{lab → study}/types.d.ts +202 -196
- package/dist/{lab → study}/types.js +3 -1
- package/dist/{lab → study}/validation.d.ts +27 -27
- package/dist/{lab → study}/validation.js +53 -51
- package/dist/study/warnings.d.ts +12 -0
- package/dist/{lab → study}/warnings.js +62 -34
- package/dist/subject/clone.d.ts +7 -7
- package/dist/subject/clone.js +2 -2
- package/dist/subject/desktop-cli.d.ts +1 -1
- package/dist/subject/desktop-cli.js +2 -2
- package/dist/subject/local-tree-archive.d.ts +2 -2
- package/dist/subject/local-tree-archive.js +4 -4
- package/dist/subject/local-tree.d.ts +4 -4
- package/dist/subject/node-bootstrap.js +1 -1
- package/dist/subject/runtime.js +9 -10
- package/dist/subject/serve.d.ts +5 -5
- package/dist/subject/serve.js +3 -3
- package/dist/subject/state.d.ts +4 -4
- package/dist/subject/state.js +1 -1
- package/dist/subject/steps.d.ts +6 -6
- package/dist/subject/steps.js +5 -5
- package/dist/substrates/command-failure.d.ts +3 -3
- package/dist/substrates/command-failure.js +3 -3
- package/dist/substrates/desktop-session.js +2 -2
- 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 +6 -6
- package/dist/substrates/e2b/desktop-browser.js +9 -9
- package/dist/substrates/e2b/desktop-cdp.d.ts +10 -10
- package/dist/substrates/e2b/desktop-cdp.js +9 -9
- package/dist/substrates/e2b/desktop-executor.d.ts +2 -2
- package/dist/substrates/e2b/desktop-executor.js +13 -13
- package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
- package/dist/substrates/e2b/desktop-geometry.js +8 -8
- package/dist/substrates/e2b/desktop-media.d.ts +4 -4
- 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/lifetime.js +1 -1
- package/dist/substrates/e2b/sandbox.d.ts +4 -4
- package/dist/substrates/e2b/sandbox.js +2 -2
- package/dist/substrates/e2b/sdk.d.ts +12 -12
- package/dist/substrates/e2b/sdk.js +7 -7
- package/dist/substrates/local/runtime-config.d.ts +4 -4
- package/dist/substrates/local/runtime-config.js +4 -4
- package/dist/tui/actions.d.ts +1 -1
- package/dist/tui/actions.js +13 -13
- package/dist/tui/contract.d.ts +23 -23
- package/dist/tui/contract.js +11 -11
- package/dist/tui/launch.d.ts +7 -7
- package/dist/tui/launch.js +36 -31
- package/dist/tui/project.js +5 -5
- package/dist/tui-app.js +139 -139
- 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 +5 -5
- package/dist/verify/subject.js +19 -19
- package/dist/verify/verify.js +94 -44
- package/docs/README.md +25 -32
- package/docs/architecture/actor-contract.md +60 -91
- package/docs/architecture/browser-control.md +1 -1
- package/docs/architecture/desktop-sessions.md +7 -7
- package/docs/architecture/external-public-shared-world.md +68 -65
- package/docs/architecture/github-feedback-loop.md +2 -5
- 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 +6 -6
- package/docs/architecture/terminal-product-route.md +70 -110
- package/docs/contracts/README.md +24 -0
- package/docs/contracts/adapter-admission.md +3 -3
- package/docs/contracts/adapter-fixtures.md +4 -7
- 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 +42 -44
- package/docs/contracts/schemas.md +446 -368
- package/docs/contracts/study-analysis.md +7 -4
- package/docs/contracts/study-costs.md +28 -9
- 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/README.md +1 -1
- 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/examples/scorer/scorer.mjs +3 -3
- package/package.json +16 -17
- package/skills/humanish/SKILL.md +64 -60
- package/skills/humanish/agents/openai.yaml +1 -1
- package/AGENTS.md +0 -99
- package/CONTRIBUTING.md +0 -148
- package/dist/actors/codex/qualified-versions.d.ts +0 -72
- package/dist/actors/codex/qualified-versions.js +0 -132
- package/dist/cli/commands/lab-format.d.ts +0 -8
- package/dist/cli/commands/lab-route-run.d.ts +0 -19
- package/dist/cli/commands/lab-route-scripted.d.ts +0 -17
- package/dist/cli/commands/lab-route-terminal.d.ts +0 -17
- package/dist/cli/commands/lab-run.d.ts +0 -9
- 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/lab/composition-rules.d.ts +0 -7
- package/dist/lab/config.d.ts +0 -7
- package/dist/lab/discover.d.ts +0 -65
- package/dist/lab/init-templates.js +0 -416
- package/dist/lab/keys.d.ts +0 -2
- package/dist/lab/parse/comms.d.ts +0 -12
- package/dist/lab/parse/execution.d.ts +0 -19
- package/dist/lab/parse/subject-state.d.ts +0 -30
- package/dist/lab/plan.d.ts +0 -19
- package/dist/lab/plan.js +0 -132
- package/dist/lab/preflight-probes.d.ts +0 -3
- package/dist/lab/preflight-result.d.ts +0 -7
- package/dist/lab/preflight.d.ts +0 -92
- package/dist/lab/route-inputs.d.ts +0 -28
- package/dist/lab/run-lab-options.d.ts +0 -28
- package/dist/lab/warnings.d.ts +0 -2
- package/dist/observer/static.d.ts +0 -39
- package/dist/observer/static.js +0 -369
- package/dist/run-lab.d.ts +0 -112
- package/docs/architecture/comms-inbox.md +0 -122
- package/docs/architecture/desktop-recording.md +0 -80
- package/docs/architecture/local-browser-runtime.md +0 -178
- package/docs/architecture/observer-review.md +0 -144
- package/docs/architecture/participant-media.md +0 -90
- package/docs/architecture/project-layout.md +0 -62
- package/docs/architecture/real-email-receiving.md +0 -136
- package/docs/assets/humanish-drawdb-hero.png +0 -0
- package/docs/assets/humanish-observer-hero.png +0 -0
- package/docs/goals/current.md +0 -207
- package/docs/product/lobby-trivia-3player-external-public.md +0 -77
- package/docs/product/open-source-install-experience.md +0 -240
- package/docs/ramp/README.md +0 -295
- package/docs/release/open-source-readiness.md +0 -222
- package/docs/release/public-readiness-standard.md +0 -210
- package/docs/roadmap/world-class-open-source-v0.md +0 -311
- /package/dist/{lab → study}/init-paths.d.ts +0 -0
- /package/dist/{lab → study}/init-paths.js +0 -0
- /package/dist/{lab → study}/init-plan.d.ts +0 -0
- /package/dist/{lab → study}/init-plan.js +0 -0
- /package/dist/{lab → study}/participant-assignment.d.ts +0 -0
- /package/dist/{lab → study}/participant-assignment.js +0 -0
- /package/dist/{lab → study}/persona.d.ts +0 -0
- /package/dist/{lab → study}/persona.js +0 -0
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
// directory) and runs with python3. sandbox-catch.ts deploys and drains it; this module holds only
|
|
3
3
|
// its source, so the Python can be read on its own.
|
|
4
4
|
/**
|
|
5
|
-
* The self-contained in-sandbox capture server
|
|
6
|
-
* stock E2B desktop template ships python3 but
|
|
5
|
+
* The self-contained in-sandbox capture server: a plain **python3** script (stdlib only), because the
|
|
6
|
+
* stock E2B desktop template ships python3 but not node, and the co-located catcher must run in a
|
|
7
7
|
* runtime the sandbox guarantees (the precedented choice: LocalStack is a python catcher the app points
|
|
8
8
|
* at; you pick the runtime the environment has). It runs on the sandbox's own python3, imports nothing
|
|
9
|
-
* from humanish.
|
|
10
|
-
* and returns a plausible provider success
|
|
9
|
+
* from humanish. Deliberately dumb: it records each POST verbatim as an NDJSON line `{t, path, body}`
|
|
10
|
+
* and returns a plausible provider success; all normalization/profile parsing happens host-side on the
|
|
11
11
|
* drained lines, so the typed, tested profiles stay in one place. It also serves the host-rendered inbox
|
|
12
12
|
* surface statically at /inbox + /api/inbox (with a script-forbidding CSP). argv: <port> <deliveriesFile>
|
|
13
|
-
* <servedDir> [inboxPort]. The capture listener binds 127.0.0.1 (loopback)
|
|
13
|
+
* <servedDir> [inboxPort]. The capture listener binds 127.0.0.1 (loopback): the app under test reaches
|
|
14
14
|
* it in-sandbox, nothing on the internet can inject a fake send. When an [inboxPort] is given (the
|
|
15
|
-
* shared-world route, where the persona lives in a
|
|
15
|
+
* shared-world route, where the persona lives in a different sandbox), it also starts a read-only inbox
|
|
16
16
|
* listener on 0.0.0.0:<inboxPort> so getHost can proxy the persona's inbox reads to it; that listener
|
|
17
17
|
* serves GET only (POST → 405). The CUA same-sandbox route omits it and stays loopback-only.
|
|
18
18
|
*/
|
|
@@ -11,8 +11,8 @@ export declare const DEFAULT_SANDBOX_CATCH_PORT = 8025;
|
|
|
11
11
|
export interface DeployCommsCatchOptions {
|
|
12
12
|
/** Fixed loopback port the catch listens on (default 8025). Must be free inside the sandbox. */
|
|
13
13
|
port?: number;
|
|
14
|
-
/** Optional
|
|
15
|
-
*
|
|
14
|
+
/** Optional second fixed port for a read-only inbox listener bound to 0.0.0.0, so a persona in a
|
|
15
|
+
* different sandbox can reach the inbox surface via getHost (the shared-world route). Omit on the
|
|
16
16
|
* CUA same-sandbox route (loopback is enough). Must differ from `port` and be free in the sandbox. */
|
|
17
17
|
inboxPort?: number;
|
|
18
18
|
/** In-sandbox working dir for the script + NDJSON (default /tmp/humanish-comms). */
|
|
@@ -30,20 +30,20 @@ export interface DeployCommsCatchOptions {
|
|
|
30
30
|
}
|
|
31
31
|
export interface DeployedCommsCatch {
|
|
32
32
|
port: number;
|
|
33
|
-
/** Inject
|
|
33
|
+
/** Inject this as the app's email-API base URL (e.g. RESEND_API_URL): the sandbox's own loopback. */
|
|
34
34
|
baseUrl: string;
|
|
35
35
|
deliveriesPath: string;
|
|
36
|
-
/** In-sandbox dir the
|
|
36
|
+
/** In-sandbox dir the host renders the persona-facing inbox-surface files into (via writeInboxSurface);
|
|
37
37
|
* the catch serves them at /inbox and /api/inbox. */
|
|
38
38
|
surfaceDir: string;
|
|
39
|
-
/** The 0.0.0.0 read-only inbox port, when one was requested
|
|
39
|
+
/** The 0.0.0.0 read-only inbox port, when one was requested; getHost-expose this to give a
|
|
40
40
|
* different-sandbox persona a reachable inbox URL. Absent on the loopback-only (CUA) route. */
|
|
41
41
|
inboxPort?: number;
|
|
42
42
|
/** The loopback SMTP port, when one was requested. Point the app's SMTP host/port env at
|
|
43
|
-
* 127.0.0.1 and
|
|
43
|
+
* 127.0.0.1 and this port. */
|
|
44
44
|
smtpPort?: number;
|
|
45
|
-
/** Whether the catch's /health returned
|
|
46
|
-
* treat `ready === false` as fatal (do not inject baseUrl into a dead catch
|
|
45
|
+
/** Whether the catch's /health returned humanish's service marker within the readiness budget. Callers must
|
|
46
|
+
* treat `ready === false` as fatal (do not inject baseUrl into a dead catch: the app's sends would
|
|
47
47
|
* silently fail with nothing captured). */
|
|
48
48
|
ready: boolean;
|
|
49
49
|
}
|
|
@@ -54,8 +54,8 @@ export interface RawCapturedSend {
|
|
|
54
54
|
t: number;
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* Write + launch the in-sandbox catch (detached), then probe it ready. Call
|
|
58
|
-
* is created and
|
|
57
|
+
* Write + launch the in-sandbox catch (detached), then probe it ready. Call after the subject sandbox
|
|
58
|
+
* is created and before the subject app's serve.start, so the base URL resolves at the app's boot.
|
|
59
59
|
*/
|
|
60
60
|
export declare function deployCommsCatch(shell: Shell, options?: DeployCommsCatchOptions): Promise<DeployedCommsCatch>;
|
|
61
61
|
/**
|
|
@@ -67,26 +67,25 @@ export declare function drainCommsCatch(shell: Shell, deployed: Pick<DeployedCom
|
|
|
67
67
|
cursor: number;
|
|
68
68
|
}>;
|
|
69
69
|
/**
|
|
70
|
-
* Parse an append-only deliveries NDJSON blob into raw sends. Split out of drainCommsCatch
|
|
71
|
-
* the
|
|
70
|
+
* Parse an append-only deliveries NDJSON blob into raw sends. Split out of drainCommsCatch so
|
|
71
|
+
* the same parsing serves a sandbox we own (read over the E2B command channel) and a catch running on
|
|
72
72
|
* a plane we do not own (read from the local filesystem by `humanish comms catch`).
|
|
73
73
|
*
|
|
74
|
-
* A file that does not end in a newline may have a
|
|
74
|
+
* A file that does not end in a newline may have a partial last line when a reader races an append of a
|
|
75
75
|
* large body. Dropping it is never lossy: the script only ever emits valid JSON lines, so an incomplete
|
|
76
76
|
* line re-reads complete on the next pass.
|
|
77
77
|
*/
|
|
78
78
|
export declare function parseDeliveriesNdjson(text: string): RawCapturedSend[];
|
|
79
79
|
/**
|
|
80
|
-
* The distinct `to` addresses the captured mail was actually sent to, parsed with the
|
|
81
|
-
* that route it. A
|
|
82
|
-
* so it discovers them from the mail itself
|
|
83
|
-
* a technically-healthy catch rendering an empty inbox forever
|
|
84
|
-
* about.
|
|
80
|
+
* The distinct `to` addresses the captured mail was actually sent to, parsed with the same profiles
|
|
81
|
+
* that route it. A run knows its recipients from the declared roster; a standalone catch does not,
|
|
82
|
+
* so it discovers them from the mail itself. Otherwise an operator who forgot to name an address gets
|
|
83
|
+
* a technically-healthy catch rendering an empty inbox forever: a false green.
|
|
85
84
|
*/
|
|
86
85
|
export declare function capturedRecipientAddresses(sends: readonly RawCapturedSend[], profiles?: EmailSendProfile[]): string[];
|
|
87
86
|
/**
|
|
88
|
-
* Route raw sends into a
|
|
89
|
-
* of refreshInboxSurface
|
|
87
|
+
* Route raw sends into a fresh FakeInbox and return the deduped, delivery-ordered messages. Split out
|
|
88
|
+
* of refreshInboxSurface so the rendering pipeline is shared by every transport; the freshness
|
|
90
89
|
* is what makes a full rebuild idempotent (a send is never routed twice, so no duplicate emails).
|
|
91
90
|
*/
|
|
92
91
|
export declare function inboxMessagesFrom(sends: readonly RawCapturedSend[], recipients: readonly InboxSurfaceRecipient[]): Promise<CommsMessage[]>;
|
|
@@ -98,7 +97,7 @@ export declare function inboxMessagesFrom(sends: readonly RawCapturedSend[], rec
|
|
|
98
97
|
export declare function routeCapturedSends(sends: RawCapturedSend[], channel: CommsChannel, profiles?: EmailSendProfile[]): Promise<number>;
|
|
99
98
|
/** Outcome of a whole-run comms collect. `artifact` is present only when captured mail matched a
|
|
100
99
|
* provisioned inbox; `captured > 0 && artifact === undefined` means the app sent mail to an address
|
|
101
|
-
* no declared recipient covers (captured but unevidenced)
|
|
100
|
+
* no declared recipient covers (captured but unevidenced); the caller should surface that, not drop
|
|
102
101
|
* it silently. */
|
|
103
102
|
export interface CommsThreadCollection {
|
|
104
103
|
artifact?: CommsThreadArtifact;
|
|
@@ -110,8 +109,8 @@ export interface CommsThreadCollection {
|
|
|
110
109
|
/**
|
|
111
110
|
* End of a run's comms funnel: drain everything the in-sandbox catch captured, route it into the
|
|
112
111
|
* host `channel`, poll the provisioned `inboxes`, and build the digest-only thread artifact. The
|
|
113
|
-
* `artifact` is omitted when nothing was captured
|
|
114
|
-
* file would be a false claim of a delivered thread)
|
|
112
|
+
* `artifact` is omitted when nothing was captured or nothing matched a provisioned inbox (an empty
|
|
113
|
+
* file would be a false claim of a delivered thread), but `captured`/`matched` are always reported so
|
|
115
114
|
* the caller can warn on captured-but-unevidenced mail rather than lose it silently. Composes the
|
|
116
115
|
* tested drain/route/build pieces so the CUA and shared-world routes collect evidence identically. The
|
|
117
116
|
* full NDJSON is drained from cursor 0 (a whole-run collect), so it is idempotent to call once at
|
|
@@ -126,9 +125,9 @@ export declare function collectCommsThread(args: {
|
|
|
126
125
|
profiles?: EmailSendProfile[];
|
|
127
126
|
requestTimeoutMs?: number;
|
|
128
127
|
}): Promise<CommsThreadCollection>;
|
|
129
|
-
/** An adopter-hosted catch: humanish never provisioned it, so it is addressed over HTTP
|
|
128
|
+
/** An adopter-hosted catch: humanish never provisioned it, so it is addressed over HTTP. */
|
|
130
129
|
export interface ExternalCommsCatch {
|
|
131
|
-
/** Base URL of the catch the
|
|
130
|
+
/** Base URL of the catch the adopter runs (its POST capture endpoint and GET /deliveries). */
|
|
132
131
|
catchBaseUrl: string;
|
|
133
132
|
/** Base URL the persona opens to read mail. Defaults to catchBaseUrl (same server serves /inbox). */
|
|
134
133
|
inboxBaseUrl?: string;
|
|
@@ -138,9 +137,9 @@ export interface ExternalCommsCatch {
|
|
|
138
137
|
/** The URL a persona is told to open to read its mail on an adopter-hosted plane. */
|
|
139
138
|
export declare function externalInboxUrl(external: ExternalCommsCatch): string;
|
|
140
139
|
/**
|
|
141
|
-
* Probe an adopter-hosted catch the way the in-sandbox one is probed: assert
|
|
142
|
-
* /health, not merely any 2xx
|
|
143
|
-
* 200 for anything, and a comms
|
|
140
|
+
* Probe an adopter-hosted catch the way the in-sandbox one is probed: assert humanish's service marker in
|
|
141
|
+
* /health, not merely any 2xx: an adopter's reverse proxy or a captive portal will happily return
|
|
142
|
+
* 200 for anything, and a comms study whose catch is not actually there collects nothing while
|
|
144
143
|
* looking fine. Fail-closed callers treat `false` as a hard stop before spending on a run.
|
|
145
144
|
*/
|
|
146
145
|
export declare function externalCatchHealthy(external: ExternalCommsCatch, options?: {
|
|
@@ -161,8 +160,8 @@ export declare function drainExternalCommsCatch(external: ExternalCommsCatch, cu
|
|
|
161
160
|
}>;
|
|
162
161
|
/**
|
|
163
162
|
* The adopter-hosted analogue of collectCommsThread: drain over HTTP, route into the host inbox bus,
|
|
164
|
-
* and build the
|
|
165
|
-
* on who hosted the catch
|
|
163
|
+
* and build the same digest-only humanish.comms-thread.v1 artifact. Evidence shape does not depend
|
|
164
|
+
* on who hosted the catch; only the transport does.
|
|
166
165
|
*/
|
|
167
166
|
export declare function collectExternalCommsThread(args: {
|
|
168
167
|
external: ExternalCommsCatch;
|
|
@@ -173,10 +172,10 @@ export declare function collectExternalCommsThread(args: {
|
|
|
173
172
|
fetchFn?: typeof fetch;
|
|
174
173
|
}): Promise<CommsThreadCollection>;
|
|
175
174
|
/**
|
|
176
|
-
* Render the persona-facing inbox surface (host-side, typed
|
|
177
|
-
* into the sandbox's served dir, so the catch serves a
|
|
175
|
+
* Render the persona-facing inbox surface (host-side, typed; see capture-surface.ts) and write the files
|
|
176
|
+
* into the sandbox's served dir, so the catch serves a live inbox the persona opens and clicks. Creates
|
|
178
177
|
* the nested route dirs first; overwrites idempotently, so call it whenever the message set changes
|
|
179
|
-
* (e.g. after a mid-run drain). Returns the number of files written. Raw content is written
|
|
178
|
+
* (e.g. after a mid-run drain). Returns the number of files written. Raw content is written into the
|
|
180
179
|
* sandbox only (runtime-only, served to the in-sandbox browser); nothing here persists to the bundle.
|
|
181
180
|
*/
|
|
182
181
|
export declare function writeInboxSurface(shell: Shell, surfaceDir: string, messages: CommsMessage[], options?: InboxRenderOptions & {
|
|
@@ -189,17 +188,17 @@ export interface InboxSurfaceRecipient {
|
|
|
189
188
|
address: string;
|
|
190
189
|
}
|
|
191
190
|
/**
|
|
192
|
-
* One mid-run inbox-surface refresh cycle:
|
|
193
|
-
* into a
|
|
191
|
+
* One mid-run inbox-surface refresh cycle: full rebuild from the append-only NDJSON (drain from cursor 0)
|
|
192
|
+
* into a fresh FakeInbox each call, provisioning the declared `recipients`, then (re)render the surface
|
|
194
193
|
* so the persona sees new mail while the session is live. Returns the total captured-send `count` + whether
|
|
195
194
|
* it rendered.
|
|
196
195
|
*
|
|
197
|
-
* The full rebuild is deliberate
|
|
198
|
-
*
|
|
196
|
+
* The full rebuild is deliberate because it is idempotent and retry-safe: a transient writeInboxSurface failure
|
|
197
|
+
* propagates (the caller retries next tick without advancing its `sinceCount`), and because each rebuild
|
|
199
198
|
* starts from a clean channel, a send is never routed twice, so the persona never sees duplicate emails.
|
|
200
|
-
* Pass `sinceCount` (the last
|
|
199
|
+
* Pass `sinceCount` (the last successfully-rendered send count) to skip the (N-file) render when nothing
|
|
201
200
|
* new has arrived. This is independent of the teardown collectCommsThread drain (its own fresh channel,
|
|
202
|
-
* also cursor 0)
|
|
201
|
+
* also cursor 0), so no evidence is lost or altered. The NDJSON is small for a run, so re-reading it is cheap.
|
|
203
202
|
*/
|
|
204
203
|
export declare function refreshInboxSurface(args: {
|
|
205
204
|
shell: Shell;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// Deploy the vendor-neutral email catch
|
|
2
|
-
// to the host bus
|
|
3
|
-
// which a sandboxed app cannot reach
|
|
1
|
+
// Deploy the vendor-neutral email catch inside the subject E2B sandbox and bridge captured sends back
|
|
2
|
+
// to the host bus. The host `startEmailCatchServer` binds the host's loopback,
|
|
3
|
+
// which a sandboxed app cannot reach: `127.0.0.1:PORT` from the app is the sandbox's loopback. So the
|
|
4
4
|
// listener must live in the sandbox: we write a tiny self-contained capture server (no deps, no host
|
|
5
5
|
// import) into the sandbox, launch it detached (the same substrate that serves the subject app), and
|
|
6
6
|
// each poll `cat` its append-only NDJSON of captured sends back to the host, where the real profiles
|
|
7
|
-
// parse them and route into the CommsChannel. A
|
|
7
|
+
// parse them and route into the CommsChannel. A fixed loopback port is chosen up front so the app's
|
|
8
8
|
// injected base-URL env (`http://127.0.0.1:<port>`) is known before the sandbox is created.
|
|
9
9
|
import { FakeInbox } from "./fake-inbox.js";
|
|
10
10
|
import { buildCommsThreadArtifact } from "./thread-evidence.js";
|
|
@@ -18,7 +18,7 @@ import { runOrThrow, shellQuote } from "../substrates/shell.js";
|
|
|
18
18
|
* unlikely to collide with a subject app; override via config if it does. */
|
|
19
19
|
export const DEFAULT_SANDBOX_CATCH_PORT = 8025;
|
|
20
20
|
const DEFAULT_CATCH_DIR = "/tmp/humanish-comms";
|
|
21
|
-
/** Readiness probe that asserts
|
|
21
|
+
/** Readiness probe that asserts humanish's service marker in the /health body (a bare 2xx is not enough), so a
|
|
22
22
|
* process squatting on the fixed port cannot produce a false "ready" while the app's sends bypass us. */
|
|
23
23
|
async function catchHealthy(shell, port, options) {
|
|
24
24
|
const now = options.now ?? Date.now;
|
|
@@ -38,8 +38,8 @@ async function catchHealthy(shell, port, options) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
-
* Write + launch the in-sandbox catch (detached), then probe it ready. Call
|
|
42
|
-
* is created and
|
|
41
|
+
* Write + launch the in-sandbox catch (detached), then probe it ready. Call after the subject sandbox
|
|
42
|
+
* is created and before the subject app's serve.start, so the base URL resolves at the app's boot.
|
|
43
43
|
*/
|
|
44
44
|
export async function deployCommsCatch(shell, options = {}) {
|
|
45
45
|
// Validate the port to an integer before it reaches the shell command (defense-in-depth: a future
|
|
@@ -93,8 +93,8 @@ export async function deployCommsCatch(shell, options = {}) {
|
|
|
93
93
|
...options.timers,
|
|
94
94
|
};
|
|
95
95
|
const ready = await catchHealthy(shell, port, probe);
|
|
96
|
-
// Confirm the read-only inbox listener bound too (loopback-reachable at its own port), when requested
|
|
97
|
-
//
|
|
96
|
+
// Confirm the read-only inbox listener bound too (loopback-reachable at its own port), when requested.
|
|
97
|
+
// Without it a getHost-exposed inbox would 502. Fail closed by folding it into `ready`.
|
|
98
98
|
const inboxReady = inboxPort === undefined ? true : await catchHealthy(shell, inboxPort, probe);
|
|
99
99
|
return {
|
|
100
100
|
port,
|
|
@@ -114,8 +114,8 @@ export async function drainCommsCatch(shell, deployed, cursor = 0, requestTimeou
|
|
|
114
114
|
const result = await runOrThrow(shell, `cat ${shellQuote(deployed.deliveriesPath)} 2>/dev/null || true`, { requestTimeoutMs });
|
|
115
115
|
const stdout = result.stdout;
|
|
116
116
|
let lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
117
|
-
// If the file doesn't end in a newline, the last line may be a
|
|
118
|
-
// an in-sandbox append of a large body). Drop it and don't advance the cursor past it
|
|
117
|
+
// If the file doesn't end in a newline, the last line may be a partial append (the host `cat` raced
|
|
118
|
+
// an in-sandbox append of a large body). Drop it and don't advance the cursor past it; it re-reads
|
|
119
119
|
// complete on the next poll, so a captured send is never lost to the race (the script only ever emits
|
|
120
120
|
// valid JSON, so an incomplete line is the only cause of a parse miss).
|
|
121
121
|
if (!stdout.endsWith("\n") && lines.length > 0)
|
|
@@ -139,11 +139,11 @@ export async function drainCommsCatch(shell, deployed, cursor = 0, requestTimeou
|
|
|
139
139
|
return { sends, cursor: lines.length };
|
|
140
140
|
}
|
|
141
141
|
/**
|
|
142
|
-
* Parse an append-only deliveries NDJSON blob into raw sends. Split out of drainCommsCatch
|
|
143
|
-
* the
|
|
142
|
+
* Parse an append-only deliveries NDJSON blob into raw sends. Split out of drainCommsCatch so
|
|
143
|
+
* the same parsing serves a sandbox we own (read over the E2B command channel) and a catch running on
|
|
144
144
|
* a plane we do not own (read from the local filesystem by `humanish comms catch`).
|
|
145
145
|
*
|
|
146
|
-
* A file that does not end in a newline may have a
|
|
146
|
+
* A file that does not end in a newline may have a partial last line when a reader races an append of a
|
|
147
147
|
* large body. Dropping it is never lossy: the script only ever emits valid JSON lines, so an incomplete
|
|
148
148
|
* line re-reads complete on the next pass.
|
|
149
149
|
*/
|
|
@@ -170,11 +170,10 @@ export function parseDeliveriesNdjson(text) {
|
|
|
170
170
|
return sends;
|
|
171
171
|
}
|
|
172
172
|
/**
|
|
173
|
-
* The distinct `to` addresses the captured mail was actually sent to, parsed with the
|
|
174
|
-
* that route it. A
|
|
175
|
-
* so it discovers them from the mail itself
|
|
176
|
-
* a technically-healthy catch rendering an empty inbox forever
|
|
177
|
-
* about.
|
|
173
|
+
* The distinct `to` addresses the captured mail was actually sent to, parsed with the same profiles
|
|
174
|
+
* that route it. A run knows its recipients from the declared roster; a standalone catch does not,
|
|
175
|
+
* so it discovers them from the mail itself. Otherwise an operator who forgot to name an address gets
|
|
176
|
+
* a technically-healthy catch rendering an empty inbox forever: a false green.
|
|
178
177
|
*/
|
|
179
178
|
export function capturedRecipientAddresses(sends, profiles = DEFAULT_EMAIL_PROFILES) {
|
|
180
179
|
const addresses = new Set();
|
|
@@ -199,8 +198,8 @@ export function capturedRecipientAddresses(sends, profiles = DEFAULT_EMAIL_PROFI
|
|
|
199
198
|
return [...addresses];
|
|
200
199
|
}
|
|
201
200
|
/**
|
|
202
|
-
* Route raw sends into a
|
|
203
|
-
* of refreshInboxSurface
|
|
201
|
+
* Route raw sends into a fresh FakeInbox and return the deduped, delivery-ordered messages. Split out
|
|
202
|
+
* of refreshInboxSurface so the rendering pipeline is shared by every transport; the freshness
|
|
204
203
|
* is what makes a full rebuild idempotent (a send is never routed twice, so no duplicate emails).
|
|
205
204
|
*/
|
|
206
205
|
export async function inboxMessagesFrom(sends, recipients) {
|
|
@@ -256,8 +255,8 @@ export async function routeCapturedSends(sends, channel, profiles = DEFAULT_EMAI
|
|
|
256
255
|
/**
|
|
257
256
|
* End of a run's comms funnel: drain everything the in-sandbox catch captured, route it into the
|
|
258
257
|
* host `channel`, poll the provisioned `inboxes`, and build the digest-only thread artifact. The
|
|
259
|
-
* `artifact` is omitted when nothing was captured
|
|
260
|
-
* file would be a false claim of a delivered thread)
|
|
258
|
+
* `artifact` is omitted when nothing was captured or nothing matched a provisioned inbox (an empty
|
|
259
|
+
* file would be a false claim of a delivered thread), but `captured`/`matched` are always reported so
|
|
261
260
|
* the caller can warn on captured-but-unevidenced mail rather than lose it silently. Composes the
|
|
262
261
|
* tested drain/route/build pieces so the CUA and shared-world routes collect evidence identically. The
|
|
263
262
|
* full NDJSON is drained from cursor 0 (a whole-run collect), so it is idempotent to call once at
|
|
@@ -297,9 +296,9 @@ export function externalInboxUrl(external) {
|
|
|
297
296
|
return `${baseOf(external.inboxBaseUrl ?? external.catchBaseUrl)}/inbox`;
|
|
298
297
|
}
|
|
299
298
|
/**
|
|
300
|
-
* Probe an adopter-hosted catch the way the in-sandbox one is probed: assert
|
|
301
|
-
* /health, not merely any 2xx
|
|
302
|
-
* 200 for anything, and a comms
|
|
299
|
+
* Probe an adopter-hosted catch the way the in-sandbox one is probed: assert humanish's service marker in
|
|
300
|
+
* /health, not merely any 2xx: an adopter's reverse proxy or a captive portal will happily return
|
|
301
|
+
* 200 for anything, and a comms study whose catch is not actually there collects nothing while
|
|
303
302
|
* looking fine. Fail-closed callers treat `false` as a hard stop before spending on a run.
|
|
304
303
|
*/
|
|
305
304
|
export async function externalCatchHealthy(external, options = {}) {
|
|
@@ -365,8 +364,8 @@ export async function drainExternalCommsCatch(external, cursor = 0, options = {}
|
|
|
365
364
|
}
|
|
366
365
|
/**
|
|
367
366
|
* The adopter-hosted analogue of collectCommsThread: drain over HTTP, route into the host inbox bus,
|
|
368
|
-
* and build the
|
|
369
|
-
* on who hosted the catch
|
|
367
|
+
* and build the same digest-only humanish.comms-thread.v1 artifact. Evidence shape does not depend
|
|
368
|
+
* on who hosted the catch; only the transport does.
|
|
370
369
|
*/
|
|
371
370
|
export async function collectExternalCommsThread(args) {
|
|
372
371
|
const drainOptions = {
|
|
@@ -397,10 +396,10 @@ export async function collectExternalCommsThread(args) {
|
|
|
397
396
|
};
|
|
398
397
|
}
|
|
399
398
|
/**
|
|
400
|
-
* Render the persona-facing inbox surface (host-side, typed
|
|
401
|
-
* into the sandbox's served dir, so the catch serves a
|
|
399
|
+
* Render the persona-facing inbox surface (host-side, typed; see capture-surface.ts) and write the files
|
|
400
|
+
* into the sandbox's served dir, so the catch serves a live inbox the persona opens and clicks. Creates
|
|
402
401
|
* the nested route dirs first; overwrites idempotently, so call it whenever the message set changes
|
|
403
|
-
* (e.g. after a mid-run drain). Returns the number of files written. Raw content is written
|
|
402
|
+
* (e.g. after a mid-run drain). Returns the number of files written. Raw content is written into the
|
|
404
403
|
* sandbox only (runtime-only, served to the in-sandbox browser); nothing here persists to the bundle.
|
|
405
404
|
*/
|
|
406
405
|
export async function writeInboxSurface(shell, surfaceDir, messages, options = {}) {
|
|
@@ -421,17 +420,17 @@ export async function writeInboxSurface(shell, surfaceDir, messages, options = {
|
|
|
421
420
|
return files.length;
|
|
422
421
|
}
|
|
423
422
|
/**
|
|
424
|
-
* One mid-run inbox-surface refresh cycle:
|
|
425
|
-
* into a
|
|
423
|
+
* One mid-run inbox-surface refresh cycle: full rebuild from the append-only NDJSON (drain from cursor 0)
|
|
424
|
+
* into a fresh FakeInbox each call, provisioning the declared `recipients`, then (re)render the surface
|
|
426
425
|
* so the persona sees new mail while the session is live. Returns the total captured-send `count` + whether
|
|
427
426
|
* it rendered.
|
|
428
427
|
*
|
|
429
|
-
* The full rebuild is deliberate
|
|
430
|
-
*
|
|
428
|
+
* The full rebuild is deliberate because it is idempotent and retry-safe: a transient writeInboxSurface failure
|
|
429
|
+
* propagates (the caller retries next tick without advancing its `sinceCount`), and because each rebuild
|
|
431
430
|
* starts from a clean channel, a send is never routed twice, so the persona never sees duplicate emails.
|
|
432
|
-
* Pass `sinceCount` (the last
|
|
431
|
+
* Pass `sinceCount` (the last successfully-rendered send count) to skip the (N-file) render when nothing
|
|
433
432
|
* new has arrived. This is independent of the teardown collectCommsThread drain (its own fresh channel,
|
|
434
|
-
* also cursor 0)
|
|
433
|
+
* also cursor 0), so no evidence is lost or altered. The NDJSON is small for a run, so re-reading it is cheap.
|
|
435
434
|
*/
|
|
436
435
|
export async function refreshInboxSurface(args) {
|
|
437
436
|
const { sends } = await drainCommsCatch(args.shell, args.deployed, 0, args.requestTimeoutMs);
|
package/dist/comms/setup.d.ts
CHANGED
|
@@ -55,9 +55,9 @@ export interface CommsConfigureResult {
|
|
|
55
55
|
connection?: string;
|
|
56
56
|
}
|
|
57
57
|
/** Save a local copy, never rewrite a committed manifest or silently shadow it by handle. */
|
|
58
|
-
export declare function
|
|
58
|
+
export declare function configureCommsStudy(args: {
|
|
59
59
|
cwd: string;
|
|
60
|
-
|
|
60
|
+
study: string;
|
|
61
61
|
connection: string;
|
|
62
62
|
apply?: boolean;
|
|
63
63
|
planToken?: string;
|
package/dist/comms/setup.js
CHANGED
|
@@ -2,15 +2,17 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import { lstat, open, unlink } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { parse,
|
|
5
|
+
import { parse, parseDocument } from "yaml";
|
|
6
6
|
import { COMMS_CONFIG_PATH, readCommsConnections } from "./connections.js";
|
|
7
7
|
import { discoverProviderKeys, listUserKeys, probeKeySources, } from "../keys/key-resolution.js";
|
|
8
8
|
import { COMMS_PROVIDERS, commsProviderLabel, DEFAULT_COMMS_PROVIDER, } from "./providers.js";
|
|
9
9
|
import { createReceivingAdapter } from "./receiving-runtime.js";
|
|
10
10
|
import { RECEIVING_SCOPE_UNSUPPORTED } from "./receiving-types.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { parseStudy } from "../study/config.js";
|
|
12
|
+
import { resolveStudyManifest } from "../study/discover.js";
|
|
13
13
|
import { assertPreparedSelectedOutputDirectory, prepareManagedHumanishOutputDirectory, prepareSelectedOutputDirectory, readContainedRegularFile, writeContainedOutputFile, } from "../run/contained-output.js";
|
|
14
|
+
import { otherStudyFiles } from "../study/files.js";
|
|
15
|
+
import { isNodeError } from "../run/type-guards.js";
|
|
14
16
|
export async function checkCommsConnection(args) {
|
|
15
17
|
const base = {
|
|
16
18
|
schema: "humanish.comms-check.v1",
|
|
@@ -106,50 +108,79 @@ export async function receivingRequiredKey(cwd, connectionName) {
|
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
/** Save a local copy, never rewrite a committed manifest or silently shadow it by handle. */
|
|
109
|
-
export async function
|
|
111
|
+
export async function configureCommsStudy(args) {
|
|
110
112
|
const base = { schema: "humanish.comms-configure.v1", ok: false, applied: false };
|
|
111
113
|
try {
|
|
112
114
|
const connection = (await readCommsConnections(args.cwd)).connections[args.connection];
|
|
113
115
|
if (!connection)
|
|
114
116
|
return { ...base, message: "Save the selected connection first." };
|
|
115
|
-
const source = await
|
|
116
|
-
if (!source.ok)
|
|
117
|
-
|
|
117
|
+
const source = await resolveStudyManifest(args.cwd, args.study);
|
|
118
|
+
if (!source.ok) {
|
|
119
|
+
// A v2 source or a labs/ file gets discovery's refusal, which names the fix.
|
|
120
|
+
const named = source.error.code === "HUMANISH_STUDY_V2_UNSUPPORTED" ||
|
|
121
|
+
source.error.code === "HUMANISH_STUDY_RETIRED_DIRECTORY";
|
|
122
|
+
return {
|
|
123
|
+
...base,
|
|
124
|
+
message: named ? source.error.message : "The selected study could not be read safely.",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
118
127
|
const root = await prepareSelectedOutputDirectory(args.cwd, args.cwd);
|
|
119
128
|
const rel = path
|
|
120
129
|
.relative(root.requestedPath, path.resolve(args.cwd, source.path))
|
|
121
130
|
.replace(/\\/g, "/");
|
|
122
131
|
const bytes = await readContainedRegularFile(root, rel);
|
|
123
132
|
if (!bytes || bytes.length > 1024 * 1024)
|
|
124
|
-
return { ...base, message: "The
|
|
125
|
-
const
|
|
133
|
+
return { ...base, message: "The study file is missing or too large." };
|
|
134
|
+
const text = bytes.toString("utf8");
|
|
135
|
+
const raw = parse(text);
|
|
126
136
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
127
|
-
return { ...base, message: "The
|
|
128
|
-
const
|
|
129
|
-
const existing =
|
|
137
|
+
return { ...base, message: "The study file must be a mapping." };
|
|
138
|
+
const studyRecord = raw;
|
|
139
|
+
const existing = studyRecord.comms;
|
|
130
140
|
if (existing?.email && existing.email.connection === undefined)
|
|
131
141
|
return {
|
|
132
142
|
...base,
|
|
133
|
-
message: "This
|
|
143
|
+
message: "This study uses local capture. Preserve it or make a separate study before selecting real email.",
|
|
134
144
|
};
|
|
135
|
-
|
|
136
|
-
const
|
|
145
|
+
// Discovery resolved the source, so it is a v3 study, and the copy keeps its comments.
|
|
146
|
+
const study = parseDocument(text);
|
|
147
|
+
study.setIn(["comms", "email", "connection"], args.connection);
|
|
148
|
+
const validated = parseStudy(study.toJS());
|
|
137
149
|
if (!validated.ok)
|
|
138
150
|
return {
|
|
139
151
|
...base,
|
|
140
|
-
message: "This
|
|
152
|
+
message: "This study does not support real email receiving. Use a hosted computer-use app-url, clone or local-tree route; shared-world must be concurrent.",
|
|
141
153
|
};
|
|
142
154
|
const filename = path
|
|
143
155
|
.basename(source.path)
|
|
144
156
|
.replace(/\.ya?ml$/, "")
|
|
145
157
|
.replace(/-receiving$/, "") + "-receiving.yaml";
|
|
146
158
|
if (!/^[A-Za-z0-9_][A-Za-z0-9._-]{0,110}\.yaml$/.test(filename))
|
|
147
|
-
return { ...base, message: "Use a simple
|
|
148
|
-
const destination = `.humanish/local/
|
|
149
|
-
if (rel ===
|
|
159
|
+
return { ...base, message: "Use a simple study filename before configuring email." };
|
|
160
|
+
const destination = `.humanish/local/studies/${filename}`;
|
|
161
|
+
if (path.basename(rel) === filename)
|
|
162
|
+
return {
|
|
163
|
+
...base,
|
|
164
|
+
message: "This is already the local receiving copy. Select its original study to configure a separate copy, or edit this study file directly.",
|
|
165
|
+
};
|
|
166
|
+
// A second file with this name in another study directory would shadow it, or take the name
|
|
167
|
+
// migrate needs to move a labs/ file.
|
|
168
|
+
const stem = filename.replace(/\.yaml$/, "");
|
|
169
|
+
const others = await otherStudyFiles(stem,
|
|
170
|
+
// Any directory entry counts, a dangling symbolic link included: discovery would see it too.
|
|
171
|
+
async (candidate) => {
|
|
172
|
+
try {
|
|
173
|
+
await lstat(path.join(root.requestedPath, candidate));
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return !(isNodeError(error) && error.code === "ENOENT");
|
|
178
|
+
}
|
|
179
|
+
}, destination);
|
|
180
|
+
if (others.length > 0)
|
|
150
181
|
return {
|
|
151
182
|
...base,
|
|
152
|
-
message: "
|
|
183
|
+
message: `${others.join(", ")} already uses the name ${stem}. Rename or remove it before configuring email, so \`run ${stem}\` reads one file.`,
|
|
153
184
|
};
|
|
154
185
|
let prior;
|
|
155
186
|
try {
|
|
@@ -158,7 +189,7 @@ export async function configureCommsLab(args) {
|
|
|
158
189
|
catch {
|
|
159
190
|
return { ...base, message: "The local destination could not be read safely." };
|
|
160
191
|
}
|
|
161
|
-
const content =
|
|
192
|
+
const content = String(study);
|
|
162
193
|
const token = createHash("sha256")
|
|
163
194
|
.update(bytes)
|
|
164
195
|
.update("\0")
|
|
@@ -181,9 +212,9 @@ export async function configureCommsLab(args) {
|
|
|
181
212
|
if (args.planToken !== undefined && args.planToken !== token)
|
|
182
213
|
return {
|
|
183
214
|
...base,
|
|
184
|
-
message: "The
|
|
215
|
+
message: "The study or destination changed since preview. Preview again before saving.",
|
|
185
216
|
};
|
|
186
|
-
const directory = await prepareManagedHumanishOutputDirectory(args.cwd, "local", "
|
|
217
|
+
const directory = await prepareManagedHumanishOutputDirectory(args.cwd, "local", "studies");
|
|
187
218
|
const lockPath = path.join(directory.physicalPath, ".comms-configure.lock");
|
|
188
219
|
const lock = await open(lockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
|
|
189
220
|
const identity = await lock.stat();
|
|
@@ -205,13 +236,13 @@ export async function configureCommsLab(args) {
|
|
|
205
236
|
return {
|
|
206
237
|
...plan,
|
|
207
238
|
applied: true,
|
|
208
|
-
message: `Saved ${destination}. Start that exact
|
|
239
|
+
message: `Saved ${destination}. Start that exact study path; the original file was preserved.`,
|
|
209
240
|
};
|
|
210
241
|
}
|
|
211
242
|
catch {
|
|
212
243
|
return {
|
|
213
244
|
...base,
|
|
214
|
-
message: "Could not configure this
|
|
245
|
+
message: "Could not configure this study safely. Check configuration, paths and permissions; no provider resources were created.",
|
|
215
246
|
};
|
|
216
247
|
}
|
|
217
248
|
}
|
|
@@ -11,7 +11,7 @@ interface CommsThreadEntry {
|
|
|
11
11
|
subjectDigest?: string;
|
|
12
12
|
/** sha256-16 of each actionable link (high-entropy → non-reversible). */
|
|
13
13
|
linkDigests: string[];
|
|
14
|
-
/**
|
|
14
|
+
/** Count only: a short OTP's digest is reversible, so the code itself never lands here. */
|
|
15
15
|
codeCount: number;
|
|
16
16
|
sentAt: number;
|
|
17
17
|
deliveredAt: number;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
// Digest-only evidence for a captured comms thread
|
|
2
|
-
// the persona could act on it"
|
|
1
|
+
// Digest-only evidence for a captured comms thread. Proves "the verification mail arrived and
|
|
2
|
+
// the persona could act on it" without persisting raw PHI. Written as an adapter-artifact
|
|
3
3
|
// (humanish.comms-thread.v1), so it inherits the bundle's existence-verify + public-safety scan.
|
|
4
4
|
//
|
|
5
5
|
// Digest discipline (deliberate, see below): addresses + links are digested (high entropy → the digest
|
|
6
|
-
// is not reversible). The subject is
|
|
7
|
-
// secret-
|
|
6
|
+
// is not reversible). The subject is digested too instead of stored as text, because redactText only scrubs
|
|
7
|
+
// secret-shaped tokens/paths, not free-form PII, so a subject like "results for <name>"
|
|
8
8
|
// would pass through verbatim; a sha256-16 keeps a PII subject non-reversible while still letting you
|
|
9
|
-
// correlate identical subjects. OTP
|
|
9
|
+
// correlate identical subjects. OTP codes are a count only, never digested: a sha256 of a 6-digit code
|
|
10
10
|
// has ~10^6 preimages and is trivially brute-forced back to the code, so a "code digest" would leak it.
|
|
11
|
-
// Net:
|
|
11
|
+
// Net: no raw address/subject/link/OTP text ever lands in the artifact. Same caution as the lobby code.
|
|
12
12
|
import { digestText } from "../evidence/redaction.js";
|
|
13
13
|
const COMMS_THREAD_SCHEMA = "humanish.comms-thread.v1";
|
|
14
14
|
/** Project polled inbox messages into the digest-only thread artifact. Pure; the caller writes it into
|
package/dist/comms/types.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface CommsAddress {
|
|
|
9
9
|
participantId: string;
|
|
10
10
|
/** Runtime-only raw address, e.g. user-07@example.test | +15550137. */
|
|
11
11
|
value: string;
|
|
12
|
-
/** sha256-short(value)
|
|
12
|
+
/** sha256-short(value): the only form persisted (redaction.digestText). */
|
|
13
13
|
digest: string;
|
|
14
14
|
}
|
|
15
15
|
/** Runtime-only captured raster attachment. Bytes never enter digest-only evidence. */
|
|
@@ -22,12 +22,12 @@ export interface CommsInlineImage {
|
|
|
22
22
|
export interface CommsMessage {
|
|
23
23
|
id: string;
|
|
24
24
|
channel: CommsChannelKind;
|
|
25
|
-
/** Raw sender
|
|
25
|
+
/** Raw sender: an app-under-test address, or another actor's address. Runtime-only. */
|
|
26
26
|
from: string;
|
|
27
27
|
/** Resolved recipient inboxes this message was delivered to. */
|
|
28
28
|
to: CommsAddress[];
|
|
29
29
|
subject?: string;
|
|
30
|
-
/** Runtime-only for real; local-only for fake (never a share path without redaction
|
|
30
|
+
/** Runtime-only for real; local-only for fake (never a share path without redaction). */
|
|
31
31
|
body: string;
|
|
32
32
|
inlineImages?: CommsInlineImage[];
|
|
33
33
|
/** Actionable links extracted from the body (magic-link / invite / reset). Runtime-only. */
|
|
@@ -37,7 +37,7 @@ export interface CommsMessage {
|
|
|
37
37
|
sentAt: number;
|
|
38
38
|
deliveredAt: number;
|
|
39
39
|
}
|
|
40
|
-
/** An actor sending
|
|
40
|
+
/** An actor sending out (a reply/compose); recipients are known CommsAddresses. */
|
|
41
41
|
export interface OutboundMessage {
|
|
42
42
|
from: CommsAddress;
|
|
43
43
|
to: CommsAddress[];
|
|
@@ -45,7 +45,7 @@ export interface OutboundMessage {
|
|
|
45
45
|
body: string;
|
|
46
46
|
inlineImages?: CommsInlineImage[];
|
|
47
47
|
}
|
|
48
|
-
/** A raw inbound from an
|
|
48
|
+
/** A raw inbound from an ingress (the vendor-neutral email catch, an SMTP sink, …): recipients are
|
|
49
49
|
* raw address strings the bus resolves against its provisioned inboxes. */
|
|
50
50
|
export interface InboundRaw {
|
|
51
51
|
from: string;
|
|
@@ -67,7 +67,7 @@ export interface CommsChannel {
|
|
|
67
67
|
/** Route a composed message from one actor to addressed inboxes. Returns the delivered record. */
|
|
68
68
|
send(message: OutboundMessage): Promise<CommsMessage>;
|
|
69
69
|
/** Route a raw ingress delivery (app-under-test → recipient strings). Returns the messages that
|
|
70
|
-
* matched a provisioned inbox (unmatched recipients are dropped
|
|
70
|
+
* matched a provisioned inbox (unmatched recipients are dropped; no inbox to deliver to). */
|
|
71
71
|
deliverRaw(inbound: InboundRaw): Promise<CommsMessage[]>;
|
|
72
72
|
/** New messages delivered to `address` since `since` (exclusive), oldest-first. Drives the surface. */
|
|
73
73
|
poll(address: CommsAddress, since?: number): Promise<CommsMessage[]>;
|