humanish 0.118.0 → 0.119.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/CHANGELOG.md +21 -0
- package/dist/analysis/admission.d.ts +16 -2
- package/dist/analysis/admission.js +35 -6
- package/dist/analysis/analysis-limits.d.ts +32 -0
- package/dist/analysis/analysis-limits.js +41 -3
- package/dist/analysis/automatic-config.d.ts +5 -0
- package/dist/analysis/automatic-config.js +15 -1
- package/dist/analysis/cohorts.d.ts +103 -0
- package/dist/analysis/cohorts.js +80 -0
- package/dist/analysis/evidence.d.ts +5 -10
- package/dist/analysis/evidence.js +37 -34
- package/dist/analysis/execute.d.ts +7 -2
- package/dist/analysis/execute.js +242 -297
- package/dist/analysis/responses.d.ts +19 -0
- package/dist/analysis/responses.js +201 -0
- package/dist/analysis/store.js +2 -1
- package/dist/analysis/validation.js +13 -7
- package/dist/cli/commands/analyze.js +8 -3
- package/dist/cli/commands/study-route-computer-use.js +2 -2
- package/dist/cli/commands/study-route-scripted.js +2 -2
- package/dist/cli/commands/study-route-shared-world.js +2 -2
- package/dist/cli/commands/study-route-terminal.js +2 -2
- package/dist/cli/commands/study-run.js +3 -3
- package/dist/cli/commands/study.js +2 -2
- package/dist/cli/io.js +2 -3
- package/dist/observer-app.html +1 -1
- package/dist/routes/computer-use/fanout-bundle.js +2 -1
- package/dist/routes/computer-use/fanout-records.js +4 -0
- package/dist/routes/computer-use/participant-execution.d.ts +6 -4
- package/dist/routes/computer-use/participant-execution.js +42 -14
- package/dist/routes/computer-use/participant-model.js +18 -8
- package/dist/routes/computer-use/participant-runs.js +12 -0
- package/dist/routes/computer-use/plan.js +23 -15
- package/dist/routes/computer-use/rerun-selection.js +14 -3
- package/dist/routes/computer-use/types.d.ts +13 -1
- package/dist/routes/shared-world/bundle-records.js +14 -0
- package/dist/routes/shared-world/bundle.js +2 -1
- package/dist/routes/shared-world/external-public.js +15 -3
- package/dist/routes/shared-world/handoff.d.ts +2 -2
- package/dist/routes/shared-world/plan.d.ts +7 -0
- package/dist/routes/shared-world/plan.js +79 -34
- package/dist/routes/shared-world/provisioned.js +15 -8
- package/dist/routes/shared-world/route.js +5 -2
- package/dist/routes/shared-world/types.d.ts +2 -0
- package/dist/run/bundle-shape.js +10 -1
- package/dist/run/bundle.d.ts +15 -0
- package/dist/run/participant-records.js +1 -0
- package/dist/run/route-shell.js +5 -10
- package/dist/run/run.js +4 -2
- package/dist/study/arrivals.d.ts +114 -0
- package/dist/study/arrivals.js +204 -0
- package/dist/study/doctor.js +2 -15
- package/dist/study/keys.d.ts +1 -0
- package/dist/study/keys.js +2 -0
- package/dist/study/migrate/v2.js +3 -2
- package/dist/study/parse/actors.d.ts +1 -1
- package/dist/study/parse/actors.js +9 -3
- package/dist/study/parse/front.js +29 -1
- package/dist/study/plan-base.d.ts +1 -3
- package/dist/study/plan-base.js +0 -3
- package/dist/study/plan-participants.d.ts +2 -0
- package/dist/study/plan-participants.js +1 -0
- package/dist/study/plan-types.d.ts +8 -3
- package/dist/study/preflight-result.js +2 -2
- package/dist/study/preflight.js +12 -0
- package/dist/study/summary.d.ts +3 -3
- package/dist/study/summary.js +5 -3
- package/dist/study/types.d.ts +14 -0
- package/dist/study/validation.js +5 -0
- package/dist/study/warnings.js +11 -3
- package/docs/contracts/run-bundle.md +1 -1
- package/docs/contracts/schemas.md +19 -1
- package/docs/contracts/study-analysis.md +59 -6
- package/docs/principles/invariants-and-defaults.md +14 -14
- package/docs/product/automatic-analysis.md +7 -1
- package/package.json +1 -1
- package/dist/study/automatic-analysis-plan.d.ts +0 -28
- package/dist/study/automatic-analysis-plan.js +0 -57
package/dist/study/warnings.js
CHANGED
|
@@ -302,11 +302,19 @@ export function forwardDeclaredWarnings(config) {
|
|
|
302
302
|
];
|
|
303
303
|
// A declared cap below the participant count is legal but loud: the roster promises N live actors
|
|
304
304
|
// and the cap delivers waves of M. Say so up front (inspect + dry-run + run): a green run in
|
|
305
|
-
// waves is otherwise indistinguishable from the all-live run the author meant.
|
|
305
|
+
// waves is otherwise indistinguishable from the all-live run the author meant. A roster that
|
|
306
|
+
// declares when its participants start never promised them all at once; the plan's schedule
|
|
307
|
+
// says who waits for a slot.
|
|
306
308
|
{
|
|
307
|
-
const
|
|
309
|
+
const roster = participantList(config);
|
|
310
|
+
const participantCount = roster?.length ?? declaredParticipantCount(config) ?? 1;
|
|
308
311
|
const cap = config.execution?.concurrency;
|
|
309
|
-
|
|
312
|
+
const scheduled = roster?.some((entry) => entry.startAfterMs !== undefined) === true;
|
|
313
|
+
if (routes.cua &&
|
|
314
|
+
!scheduled &&
|
|
315
|
+
cap !== undefined &&
|
|
316
|
+
participantCount > 1 &&
|
|
317
|
+
cap < participantCount) {
|
|
310
318
|
warnings.push(`execution.concurrency ${cap} caps a ${participantCount}-participant roster: participants run in waves of ${cap}, never all live at once. Remove execution.concurrency (the default runs all ${participantCount} participants simultaneously) or set it to ${participantCount}; declare a lower cap only to bound simultaneous paid desktops.`);
|
|
311
319
|
}
|
|
312
320
|
}
|
|
@@ -65,7 +65,7 @@ scenario:
|
|
|
65
65
|
goal: "<scenario goal>"
|
|
66
66
|
source: "<scenario source>"
|
|
67
67
|
sourceDigest: "<sha256>"
|
|
68
|
-
simulations: [] # simCount entries, each paired with its streams
|
|
68
|
+
simulations: [] # simCount entries, each paired with its streams; `arrival` when the study declares starts
|
|
69
69
|
streams: []
|
|
70
70
|
events: []
|
|
71
71
|
artifacts:
|
|
@@ -215,6 +215,18 @@ The rest of this section names keys by their v3 spelling:
|
|
|
215
215
|
`<id>-01` … `<id>-NN`, even when n is 1, before the engine runs (`viewer-01`,
|
|
216
216
|
`viewer-02`, ...), so the runtime and run bundle keep one normalized participant
|
|
217
217
|
shape. Ids are checked for collisions after expansion;
|
|
218
|
+
- `participants[].startAfterMs` (computer-use and shared-world): when the participant
|
|
219
|
+
starts, in whole milliseconds after the run starts its participants, from 0 to 86,400,000
|
|
220
|
+
(24 hours). A `count` group's `startEveryMs` gives member k the start
|
|
221
|
+
`startAfterMs + (k - 1) * startEveryMs`, expanded at parse time like the ids. Refused: an
|
|
222
|
+
interval without `count`, a member past 24 hours, a start on a study of one participant, and a
|
|
223
|
+
later start for the external-public `host`. Participants start in order of their starts, each
|
|
224
|
+
at its time or when a slot frees under `execution.concurrency` and the E2B plan's limit,
|
|
225
|
+
whichever is later (`runOnSchedule` in `src/study/arrivals.ts`). Each participant's desktop is
|
|
226
|
+
created when it starts. The plan (`arrivals`, `planArrivals`) reports the first and last start
|
|
227
|
+
and the most participants at once with every session at its full budget, and the provisioned
|
|
228
|
+
shared world's subject sandbox serves until the last session can end, which the plan checks
|
|
229
|
+
against the sandbox ceiling. A rerun starts its selected participants together;
|
|
218
230
|
- `execution.concurrency` (computer-use E2B routes, including shared-world): a
|
|
219
231
|
cap on participants in flight at once. When omitted, every participant runs
|
|
220
232
|
simultaneously: both routes resolve it when they plan, from the final participant count
|
|
@@ -516,7 +528,13 @@ Core-owned fields:
|
|
|
516
528
|
- `artifactRoot`
|
|
517
529
|
- `source.git`
|
|
518
530
|
- `lifecycle`
|
|
519
|
-
- `simulations
|
|
531
|
+
- `simulations`: one record per participant. On the computer-use and shared-world routes, a
|
|
532
|
+
study that declares `participants[].startAfterMs` gives each record `arrival:
|
|
533
|
+
{ startAfterMs, scheduledAt?, startedAt? }`: the participant's offset (0 when its entry declares
|
|
534
|
+
none), the moment the run started its participants plus that offset, and when its desktop was
|
|
535
|
+
requested, which is later than `scheduledAt` when it waited for a free slot. A dry run records
|
|
536
|
+
only the offset, and a participant that never started has no `startedAt`. The record's own
|
|
537
|
+
`startedAt` is the run's creation time.
|
|
520
538
|
- `streams`
|
|
521
539
|
- `events`
|
|
522
540
|
- `redaction`
|
|
@@ -37,17 +37,27 @@ The estimate counts the instructions, evidence packet and result schema at 3
|
|
|
37
37
|
UTF-8 bytes per input token, adds 2,048 framing tokens and each capture's
|
|
38
38
|
high-detail image tokens, and prices input at the model's highest input rate.
|
|
39
39
|
The expected output is 12,000 tokens plus 1,000 per participant, at most the
|
|
40
|
-
output allowance; the worst case spends the whole allowance.
|
|
40
|
+
output allowance; the worst case spends the whole allowance. A run with more than
|
|
41
|
+
16 participants sends one request per cohort and a merge request
|
|
42
|
+
([runs with more than 16 participants](#runs-with-more-than-16-participants)),
|
|
43
|
+
and the estimate is their sum, each request priced on its own long-context tier.
|
|
44
|
+
The merge request's input is its instructions, packet and schema, counted the same
|
|
45
|
+
way, plus each cohort's report at that request's whole output allowance; it is
|
|
46
|
+
expected to write what one request covering every participant would. `admission` in
|
|
41
47
|
`--json` output has `estimatedCostUsd` (the expected cost), `worstCaseCostUsd`,
|
|
42
48
|
`admittedCostUsd` (the figure compared with the cap: the expected cost plus 10%,
|
|
43
49
|
or the worst case when that is lower), `maxCostUsd`, `inputTokenAllowance` (the
|
|
44
|
-
input tokens priced)
|
|
50
|
+
input tokens priced), `outputTokenAllowance` (each request's) and `requests` (the
|
|
51
|
+
requests the costs cover: 1, or each cohort's and the merge request; null when the
|
|
52
|
+
input or configuration was refused). The dry run's worst case for more than one
|
|
53
|
+
request says every request writes its whole allowance and names the cohort and merge
|
|
54
|
+
requests, in the words the plan's analysis line uses. On 148 billed gpt-6-astra
|
|
45
55
|
analyses the expected cost was 1.08 to 7.3 times the bill, and 1.08 to 1.32
|
|
46
56
|
times for those billed $1 or more.
|
|
47
57
|
A refusal's message gives the expected cost, the worst case, the cap, and
|
|
48
58
|
`humanish analyze --run <id> --max-cost <n>` with `n` the worst case rounded up.
|
|
49
59
|
|
|
50
|
-
The default deadline is ten minutes. With no explicit output-token limit, analysis
|
|
60
|
+
The default deadline is ten minutes for each request. With no explicit output-token limit, analysis
|
|
51
61
|
uses 32,768 tokens if admission fits the declared budget, or retains the prior
|
|
52
62
|
16,384-token allowance otherwise. Explicit limits are never adjusted. Selection
|
|
53
63
|
happens before dispatch; `admission.outputTokenAllowance` exposes it in dry-run,
|
|
@@ -64,6 +74,48 @@ success. Findings keep reported concerns and observed recovery distinct across
|
|
|
64
74
|
participants. Other supported models can be selected explicitly, but evidence
|
|
65
75
|
reference validation does not certify their interpretation of small visual details.
|
|
66
76
|
|
|
77
|
+
## Runs with more than 16 participants
|
|
78
|
+
|
|
79
|
+
One analysis covers up to 128 participants, the most streams it reads from a run
|
|
80
|
+
bundle. A request covers at most 16 (`EVIDENCE_LIMITS.cohortParticipants` in
|
|
81
|
+
`src/analysis/analysis-limits.ts`). A run with more is split into the fewest
|
|
82
|
+
cohorts of at most 16, with participants dealt in roster order: 17 participants
|
|
83
|
+
make cohorts of 9 and 8, 24 make two of 12, 40 make 14, 13 and 13, and 100 make
|
|
84
|
+
seven. Each cohort's packet is selected under the packet limits below on its own,
|
|
85
|
+
so each participant keeps at least the evidence a 16-participant run gives it.
|
|
86
|
+
Evidence IDs are numbered across the whole run, so they stay unique.
|
|
87
|
+
|
|
88
|
+
Each cohort request uses the standard instructions, schema and validation, with
|
|
89
|
+
only its own participants' evidence and captures. Up to four run at once for
|
|
90
|
+
OpenAI, which keeps four requests at the packet limits under the lowest paid
|
|
91
|
+
tier's gpt-6-astra rate limit of 1,000,000 tokens a minute; Codex runs one at a
|
|
92
|
+
time. Then one merge request reads the cohort reports, never the evidence or the
|
|
93
|
+
captures. It writes the summary, findings, design findings, concern reviews and
|
|
94
|
+
limitations for the whole run; the participant reviews are the cohorts' own.
|
|
95
|
+
Its findings, design findings and concern reviews may cite only evidence that a
|
|
96
|
+
cohort report cites in its own findings, design findings or concern reviews
|
|
97
|
+
(`analysis_validation_failed_merge_reference_invalid` otherwise), and the merged
|
|
98
|
+
report passes the same reference, membership and basis checks against the run's
|
|
99
|
+
whole packet as a single request's report does.
|
|
100
|
+
|
|
101
|
+
The attempt has one execution start, one receipt and one `analysis.json`, with the
|
|
102
|
+
run's participants, coverage and evidence and the summed usage of every request
|
|
103
|
+
sent. The artifact schema is unchanged. Its stored limits grew to hold eight
|
|
104
|
+
cohorts: 6,400 evidence items, 320 captures and 16 MiB. If a cohort request fails
|
|
105
|
+
or is rejected, no further request starts, requests already sent finish, no merge
|
|
106
|
+
request is sent and the attempt fails with that request's code, with no findings;
|
|
107
|
+
a warning names the cohort's size. A failed merge request fails the attempt the same way. The usage
|
|
108
|
+
still counts every request sent. Findings from part of the participants are not
|
|
109
|
+
kept as a report on the run.
|
|
110
|
+
|
|
111
|
+
The first live cohort analysis, on a 24-participant computer-use run, sent two
|
|
112
|
+
cohort requests of 12 participants and the merge request in 5 minutes 6 seconds.
|
|
113
|
+
Its expected cost was $6.08, 2.3 times its $2.65 bill, and each of its 447
|
|
114
|
+
citations resolved in the run's packet. A second analysis of the same run, with
|
|
115
|
+
the current merge instructions, billed $1.92, most of its input served from the
|
|
116
|
+
provider's prompt cache, and each of its 466 citations resolved. Two runs do not
|
|
117
|
+
calibrate the merge request's estimate.
|
|
118
|
+
|
|
67
119
|
## Explicit Codex account analysis
|
|
68
120
|
|
|
69
121
|
An existing completed recording can use a separate restricted Codex analyst:
|
|
@@ -85,8 +137,9 @@ converted to API authentication.
|
|
|
85
137
|
Omit `--max-cost` and `--max-output-tokens` for Codex. Numeric declarations are
|
|
86
138
|
rejected because this transport does not enforce them. `maxCostUsd` and
|
|
87
139
|
`maxOutputTokens` are stored as null. The existing evidence bounds, one analyst
|
|
88
|
-
turn
|
|
89
|
-
is not a claim of one upstream
|
|
140
|
+
turn per request (one per cohort, and the merge request), bounded response bytes
|
|
141
|
+
and whole-operation timeout still apply. One turn is not a claim of one upstream
|
|
142
|
+
billed request; account limits apply. Reported
|
|
90
143
|
tokens remain inspectable, but dollar estimates, admission dollars and rate dates
|
|
91
144
|
remain null. Interrupted token observations remain explicitly incomplete.
|
|
92
145
|
|
|
@@ -128,7 +181,7 @@ stopped, an operator can remove the empty lock directory and retry.
|
|
|
128
181
|
|
|
129
182
|
## Evidence and findings
|
|
130
183
|
|
|
131
|
-
|
|
184
|
+
Each request's packet admits up to 16 participants, 800 evidence items, 40 PNG
|
|
132
185
|
captures, 160 KiB of text and 20 MiB of images. Individual source files, image
|
|
133
186
|
dimensions and result sizes have separate limits. Count and text budgets are
|
|
134
187
|
distributed across included participants, with unused capacity from short
|
|
@@ -87,20 +87,20 @@ Right most of the time, overridable with declared friction (an explicit config f
|
|
|
87
87
|
env gate, a warning in the run output). Overriding a default is a supported use case;
|
|
88
88
|
silently drifting from one is not.
|
|
89
89
|
|
|
90
|
-
| Default | Why it is the default | Legitimate override
|
|
91
|
-
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
-
| Dry-run | Spend safety (the live-spend rule sets the floor; dry-run keeps it far away) | `mode: live`
|
|
93
|
-
| Post-run analysis on supported live studies | Findings accompany the recording; a separate $3 cap on its expected cost is visible before execution | `review.analysis: false` disables it; an explicit mapping sets another analysis budget. Dry-run and unsupported routes never dispatch; missing default credentials produce a recorded skip
|
|
94
|
-
| Separate worlds (`per-lane-worlds`) | Isolation, attribution, reproducibility | `route: shared-world`: N participants against one mutable plane for scenarios that are about interaction between participants.
|
|
95
|
-
| External key placement | Smallest blast radius: when the keyed process (e.g. a computer-use provider loop) runs outside the sandbox, its key never enters | In-sandbox placement when the keyed process runs inside (an agent harness under test); declared per actor type, with a spend budget
|
|
96
|
-
| Loopback entry URLs | Public-safety: never drive third-party sites unbidden | `policies.allowPublicTargets` for an owner-declared deployment/preview (a Vercel preview of your own app). Multi-participant public/preview fan-out needs explicit `participants[].target` for every participant, so the adapter-owned topology is declared rather than inferred. Provisioned clone subjects always serve in-sandbox on loopback
|
|
97
|
-
| Full-fidelity screenshots, local | The common case is watching a run of your own app locally; blur destroys the deliverable. Raw frames live in gitignored `.humanish/` (this repo's CI adds a binary-asset commit scan; downstream projects rely on the scaffolded `.gitignore` and their own review) | `policies.redactScreenshots: true` blurs at capture; `export --format bundle --redact-screenshots` produces a separate verified copy of a completed run
|
|
98
|
-
| Synthetic, seeded state | Pinned provenance; no real user data in evidence paths | Declared external state, recorded as unpinned in provenance
|
|
99
|
-
| Single participant | Cost + evidence simplicity | Declared fan-out where the route supports it: `participants: N` (homogeneous), an explicit `participants` list (differentiated persona/device/instruction), or list entries with a `count` that expand into one entry per participant on the computer-use E2B route (separate worlds, at most 100; `execution.concurrency` and the E2B plan bound concurrent paid participants)
|
|
100
|
-
| Stock `desktop` template | The stock E2B desktop image is right for most subjects; absent `execution.desktop.template` keeps `Sandbox.create(opts)` byte-stable | `execution.desktop.template` names a custom E2B desktop image (any name/id, no allowlist) for a subject needing baked-in runtimes the stock image lacks (e.g. node/bun/a local Postgres); it is threaded to `Sandbox.create(template, opts)` on every desktop-creating route and recorded in the bundle as `desktopTemplate` (public-safe)
|
|
101
|
-
| Desktop default URL opener | Preserve the route/image's historical browser/default opener behavior when unset | `execution.desktop.browser` set to `chrome`, `chromium` or `firefox` makes the hosted CUA/shared-world browser choice explicit, fail-closed, and recorded as `desktopBrowser` in the run bundle
|
|
102
|
-
| Loopback-only observer serving | Run bundles are local by default (public-safety plank 3); the serve/watch surfaces bind `127.0.0.1` unconditionally, and exposing them is publishing-adjacent, so exposure is fail-closed and carries declared friction | `humanish observe --all --expose` behind tunnel-edge auth (ngrok `--oauth google` with `--allow-email`/`--allow-domain` allow rules, or an operator `--public-url` you secure), or `--expose --safe` (open, gated on verify-`share_ready` runs only). `watch --expose` streams a live run behind edge auth (a live run is never `share_ready`, so `--safe` alone is refused). humanish carries no in-process auth; both print computed warnings naming exactly what became reachable
|
|
103
|
-
| Generous wall-clock safety cap | `execution.timeoutMs` bounds runaway spend, not a goal; ~30 min is reasonable for open-ended "watch it play" | A session that reaches the cap after productive activity is recorded as `budget_reached` (status `passed`, a non-failure completion, exit `0`), distinct from a zero-progress `timed_out` (a failure, exit `2`). Goal-directed studies set a tight `timeoutMs` and read `completionReason` to confirm the goal, not the cap
|
|
90
|
+
| Default | Why it is the default | Legitimate override |
|
|
91
|
+
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| Dry-run | Spend safety (the live-spend rule sets the floor; dry-run keeps it far away) | `mode: live` |
|
|
93
|
+
| Post-run analysis on supported live studies | Findings accompany the recording; a separate $3 cap on its expected cost is visible before execution | `review.analysis: false` disables it; an explicit mapping sets another analysis budget. Dry-run and unsupported routes never dispatch; missing default credentials produce a recorded skip |
|
|
94
|
+
| Separate worlds (`per-lane-worlds`) | Isolation, attribution, reproducibility | `route: shared-world`: N participants against one mutable plane for scenarios that are about interaction between participants. Participants run at once, as many as `execution.concurrency` (two or more) and the E2B plan's limit allow, unless the study declares when each one starts (`participants[].startAfterMs`). The plane is one getHost-exposed subject sandbox with synthetic seeded data, or an operator-owned public deployment (external-public), driven by N actor sandboxes. The bundle declares the weaker `attributionClass: shared-world` and a verify-enforced `attributionLimits` ceiling (`best-effort-causal-attribution` and the rest of the concurrent set). Sequential turns at `execution.concurrency: 1` were removed in 0.106.0. |
|
|
95
|
+
| External key placement | Smallest blast radius: when the keyed process (e.g. a computer-use provider loop) runs outside the sandbox, its key never enters | In-sandbox placement when the keyed process runs inside (an agent harness under test); declared per actor type, with a spend budget |
|
|
96
|
+
| Loopback entry URLs | Public-safety: never drive third-party sites unbidden | `policies.allowPublicTargets` for an owner-declared deployment/preview (a Vercel preview of your own app). Multi-participant public/preview fan-out needs explicit `participants[].target` for every participant, so the adapter-owned topology is declared rather than inferred. Provisioned clone subjects always serve in-sandbox on loopback |
|
|
97
|
+
| Full-fidelity screenshots, local | The common case is watching a run of your own app locally; blur destroys the deliverable. Raw frames live in gitignored `.humanish/` (this repo's CI adds a binary-asset commit scan; downstream projects rely on the scaffolded `.gitignore` and their own review) | `policies.redactScreenshots: true` blurs at capture; `export --format bundle --redact-screenshots` produces a separate verified copy of a completed run |
|
|
98
|
+
| Synthetic, seeded state | Pinned provenance; no real user data in evidence paths | Declared external state, recorded as unpinned in provenance |
|
|
99
|
+
| Single participant | Cost + evidence simplicity | Declared fan-out where the route supports it: `participants: N` (homogeneous), an explicit `participants` list (differentiated persona/device/instruction), or list entries with a `count` that expand into one entry per participant on the computer-use E2B route (separate worlds, at most 100; `execution.concurrency` and the E2B plan bound concurrent paid participants) |
|
|
100
|
+
| Stock `desktop` template | The stock E2B desktop image is right for most subjects; absent `execution.desktop.template` keeps `Sandbox.create(opts)` byte-stable | `execution.desktop.template` names a custom E2B desktop image (any name/id, no allowlist) for a subject needing baked-in runtimes the stock image lacks (e.g. node/bun/a local Postgres); it is threaded to `Sandbox.create(template, opts)` on every desktop-creating route and recorded in the bundle as `desktopTemplate` (public-safe) |
|
|
101
|
+
| Desktop default URL opener | Preserve the route/image's historical browser/default opener behavior when unset | `execution.desktop.browser` set to `chrome`, `chromium` or `firefox` makes the hosted CUA/shared-world browser choice explicit, fail-closed, and recorded as `desktopBrowser` in the run bundle |
|
|
102
|
+
| Loopback-only observer serving | Run bundles are local by default (public-safety plank 3); the serve/watch surfaces bind `127.0.0.1` unconditionally, and exposing them is publishing-adjacent, so exposure is fail-closed and carries declared friction | `humanish observe --all --expose` behind tunnel-edge auth (ngrok `--oauth google` with `--allow-email`/`--allow-domain` allow rules, or an operator `--public-url` you secure), or `--expose --safe` (open, gated on verify-`share_ready` runs only). `watch --expose` streams a live run behind edge auth (a live run is never `share_ready`, so `--safe` alone is refused). humanish carries no in-process auth; both print computed warnings naming exactly what became reachable |
|
|
103
|
+
| Generous wall-clock safety cap | `execution.timeoutMs` bounds runaway spend, not a goal; ~30 min is reasonable for open-ended "watch it play" | A session that reaches the cap after productive activity is recorded as `budget_reached` (status `passed`, a non-failure completion, exit `0`), distinct from a zero-progress `timed_out` (a failure, exit `2`). Goal-directed studies set a tight `timeoutMs` and read `completionReason` to confirm the goal, not the cap |
|
|
104
104
|
|
|
105
105
|
## The placement rule (worked example)
|
|
106
106
|
|
|
@@ -29,7 +29,13 @@ estimate gives two numbers: the expected cost, and the worst case if the analyst
|
|
|
29
29
|
spends its whole output allowance. `humanish study check` gives the expected cost
|
|
30
30
|
range for the study's participant count, from a run that keeps no evidence to one
|
|
31
31
|
that keeps the most evidence admission admits under the cap, at most the evidence limits. When
|
|
32
|
-
the cap admits no analysis at all, it says so instead.
|
|
32
|
+
the cap admits no analysis at all, it says so instead. A study of more than 16 participants is
|
|
33
|
+
analysed in cohorts of at most 16, one request each, and one more request merges their reports;
|
|
34
|
+
the range covers every request and the analysis line names them (see
|
|
35
|
+
[runs with more than 16 participants](../contracts/study-analysis.md#runs-with-more-than-16-participants)).
|
|
36
|
+
The default $3 cap refuses the analysis of 17 participants even for a run that keeps no evidence,
|
|
37
|
+
so a default analysis of more than 16 participants is skipped with
|
|
38
|
+
`AUTOMATIC_ANALYSIS_ADMISSION_REFUSED` unless `review.analysis.maxCostUsd` admits it. Use `analyze --dry-run --max-cost <usd>` on retained
|
|
33
39
|
evidence to inspect the expected cost, the worst case and the selected token
|
|
34
40
|
allowance before deliberately choosing a larger budget. Explicit
|
|
35
41
|
`maxOutputTokens` and `--max-output-tokens` limits are honored exactly. The output allowance
|
package/package.json
CHANGED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type AutomaticAnalysisResult } from "../analysis/automatic-completion.js";
|
|
2
|
-
import { type AutomaticAnalysisBudget } from "../analysis/automatic-config.js";
|
|
3
|
-
import type { StudyRoute } from "./routing.js";
|
|
4
|
-
/** The reason a run records for an analysis its study has too many participants for. */
|
|
5
|
-
export declare const PARTICIPANT_LIMIT_SKIP = "AUTOMATIC_ANALYSIS_PARTICIPANT_LIMIT";
|
|
6
|
-
/** The CLI's words for that reason, after "analysis: ". */
|
|
7
|
-
export declare const PARTICIPANT_LIMIT_SKIP_TEXT: string;
|
|
8
|
-
/** The skip a study of `participants` gets, or undefined when its analysis runs. */
|
|
9
|
-
export declare function analysisSkipFor(participants: number): typeof PARTICIPANT_LIMIT_SKIP | undefined;
|
|
10
|
-
/** The analysis budget the plan line shows, and the skip when the analysis will not run. */
|
|
11
|
-
export type StudyAnalysisBudget = AutomaticAnalysisBudget & {
|
|
12
|
-
readonly skip?: typeof PARTICIPANT_LIMIT_SKIP;
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* The budget of a study's automatic analysis after a live run of `participants`, or undefined
|
|
16
|
-
* when none is planned. A skipped analysis carries no expected cost, since it spends nothing.
|
|
17
|
-
*/
|
|
18
|
-
export declare function studyAnalysisBudget(raw: unknown, route: StudyRoute, participants: number): StudyAnalysisBudget | undefined;
|
|
19
|
-
/** The line `run` and `study check` print about the analysis after a live run. */
|
|
20
|
-
export declare function formatStudyAnalysisBudget(budget: StudyAnalysisBudget): string;
|
|
21
|
-
/** A run's result with its analysis recorded as skipped for its participant count. */
|
|
22
|
-
export declare function withParticipantLimitSkip<T>(result: T, trigger: "default" | "explicit"): T & AutomaticAnalysisResult;
|
|
23
|
-
/**
|
|
24
|
-
* Whether a run's automatic analysis lets the command exit 0. A participant-count skip of the
|
|
25
|
-
* analysis a study did not ask for passes, like the default analysis's missing-key skip; a skip of
|
|
26
|
-
* an explicit request fails, like that request's missing key.
|
|
27
|
-
*/
|
|
28
|
-
export declare function studyAnalysisSucceeded(result: AutomaticAnalysisResult): boolean;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// What automatic analysis does for a study of N participants. One analysis reads at most
|
|
2
|
-
// EVIDENCE_LIMITS.participants of them (the analysis module owns that number). A study with more
|
|
3
|
-
// runs every participant and records its analysis as skipped for that reason, where the analysis
|
|
4
|
-
// would otherwise cover the first participants and drop the rest. The plan line, the run's
|
|
5
|
-
// analysis record and the CLI's exit code all read the skip from here. Once the analysis covers
|
|
6
|
-
// every participant a study may have, the skip never applies and this module can go.
|
|
7
|
-
import { automaticAnalysisSucceeded, } from "../analysis/automatic-completion.js";
|
|
8
|
-
import { automaticAnalysisBudget, formatAutomaticAnalysisBudget, } from "../analysis/automatic-config.js";
|
|
9
|
-
import { EVIDENCE_LIMITS } from "../analysis/evidence.js";
|
|
10
|
-
/** The reason a run records for an analysis its study has too many participants for. */
|
|
11
|
-
export const PARTICIPANT_LIMIT_SKIP = "AUTOMATIC_ANALYSIS_PARTICIPANT_LIMIT";
|
|
12
|
-
/** The CLI's words for that reason, after "analysis: ". */
|
|
13
|
-
export const PARTICIPANT_LIMIT_SKIP_TEXT = `not run, because the study has more than ${EVIDENCE_LIMITS.participants} participants and automatic analysis reads at most ${EVIDENCE_LIMITS.participants}`;
|
|
14
|
-
/** The skip a study of `participants` gets, or undefined when its analysis runs. */
|
|
15
|
-
export function analysisSkipFor(participants) {
|
|
16
|
-
return participants > EVIDENCE_LIMITS.participants ? PARTICIPANT_LIMIT_SKIP : undefined;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* The budget of a study's automatic analysis after a live run of `participants`, or undefined
|
|
20
|
-
* when none is planned. A skipped analysis carries no expected cost, since it spends nothing.
|
|
21
|
-
*/
|
|
22
|
-
export function studyAnalysisBudget(raw, route, participants) {
|
|
23
|
-
const skip = analysisSkipFor(participants);
|
|
24
|
-
if (skip === undefined)
|
|
25
|
-
return automaticAnalysisBudget(raw, route, participants);
|
|
26
|
-
const budget = automaticAnalysisBudget(raw, route);
|
|
27
|
-
return budget && { ...budget, participants, skip };
|
|
28
|
-
}
|
|
29
|
-
/** The line `run` and `study check` print about the analysis after a live run. */
|
|
30
|
-
export function formatStudyAnalysisBudget(budget) {
|
|
31
|
-
if (budget.skip === undefined)
|
|
32
|
-
return formatAutomaticAnalysisBudget(budget);
|
|
33
|
-
const next = budget.trigger === "default"
|
|
34
|
-
? "Set review.analysis: false to leave it out of this plan."
|
|
35
|
-
: "The analysis this study asks for will be recorded as skipped and the run will exit 2; set review.analysis: false or run fewer participants.";
|
|
36
|
-
return `After live runs: automatic analysis will not run. This study has ${budget.participants ?? "more"} participants, and automatic analysis reads at most ${EVIDENCE_LIMITS.participants}. ${next}`;
|
|
37
|
-
}
|
|
38
|
-
/** A run's result with its analysis recorded as skipped for its participant count. */
|
|
39
|
-
export function withParticipantLimitSkip(result, trigger) {
|
|
40
|
-
return {
|
|
41
|
-
...result,
|
|
42
|
-
...(trigger === "default" ? { automaticAnalysisTrigger: trigger } : {}),
|
|
43
|
-
automaticAnalysis: { state: "skipped", reason: PARTICIPANT_LIMIT_SKIP },
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Whether a run's automatic analysis lets the command exit 0. A participant-count skip of the
|
|
48
|
-
* analysis a study did not ask for passes, like the default analysis's missing-key skip; a skip of
|
|
49
|
-
* an explicit request fails, like that request's missing key.
|
|
50
|
-
*/
|
|
51
|
-
export function studyAnalysisSucceeded(result) {
|
|
52
|
-
const value = result.automaticAnalysis;
|
|
53
|
-
return (automaticAnalysisSucceeded(result) ||
|
|
54
|
-
(result.automaticAnalysisTrigger === "default" &&
|
|
55
|
-
value?.state === "skipped" &&
|
|
56
|
-
value.reason === PARTICIPANT_LIMIT_SKIP));
|
|
57
|
-
}
|