humanish 0.110.4 → 0.111.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 +15 -0
- package/CONTEXT.md +2 -2
- package/dist/actors/local-agent/claude-session.js +1 -1
- package/dist/actors/local-agent/cli.js +1 -1
- package/dist/actors/scripted-browser/actor.d.ts +2 -2
- package/dist/cli/commands/study.js +2 -4
- package/dist/routes/computer-use/participant-model.js +1 -1
- package/dist/routes/computer-use/plan.js +4 -6
- package/dist/routes/computer-use/preflight.js +1 -1
- package/dist/routes/computer-use/result.js +1 -1
- package/dist/routes/computer-use/route.js +1 -1
- package/dist/routes/computer-use/types.d.ts +4 -4
- package/dist/routes/scripted/route.js +1 -1
- package/dist/routes/scripted/scenario.d.ts +1 -1
- package/dist/routes/scripted/scenario.js +3 -3
- package/dist/routes/scripted/types.d.ts +2 -2
- package/dist/routes/shared-world/handoff.js +1 -1
- package/dist/routes/shared-world/participant-specs.js +1 -1
- package/dist/routes/shared-world/plan.js +3 -4
- package/dist/routes/shared-world/types.d.ts +1 -1
- package/dist/routes/terminal/bundle.d.ts +3 -3
- package/dist/routes/terminal/ledger.d.ts +2 -2
- package/dist/routes/terminal/lifetime.js +1 -1
- package/dist/routes/terminal/live-finish.js +1 -1
- package/dist/routes/terminal/plan.js +1 -1
- package/dist/routes/terminal/route.js +1 -1
- package/dist/routes/terminal/types.d.ts +1 -1
- package/dist/run/results.d.ts +1 -1
- package/dist/run-study.d.ts +5 -3
- package/dist/run-study.js +7 -11
- package/dist/study/adapter-scorer-loader.d.ts +1 -1
- package/dist/study/adapter-scorer-loader.js +3 -3
- package/dist/study/composition-rules.js +21 -11
- package/dist/study/config.d.ts +29 -13
- package/dist/study/config.js +174 -157
- package/dist/study/discover.d.ts +1 -1
- package/dist/study/discover.js +1 -1
- package/dist/study/keys.d.ts +250 -2
- package/dist/study/keys.js +32 -62
- package/dist/study/migrate/convert.js +3 -4
- package/dist/study/migrate/v2.d.ts +17 -7
- package/dist/study/migrate/v2.js +150 -23
- package/dist/study/parse/actors.d.ts +11 -32
- package/dist/study/parse/actors.js +11 -156
- package/dist/study/parse/comms.js +6 -2
- package/dist/study/parse/execution.d.ts +9 -4
- package/dist/study/parse/execution.js +4 -39
- package/dist/study/parse/{study-v3.d.ts → front.d.ts} +6 -17
- package/dist/study/parse/{study-v3.js → front.js} +22 -91
- package/dist/study/parse/subject.d.ts +2 -0
- package/dist/study/parse/subject.js +15 -18
- package/dist/study/persona-resolve.js +7 -9
- package/dist/study/plan-base.d.ts +1 -1
- package/dist/study/plan-base.js +3 -4
- package/dist/study/plan-participants.d.ts +2 -2
- package/dist/study/plan-types.d.ts +5 -5
- package/dist/study/plan.js +1 -1
- package/dist/study/preflight-probes.js +2 -2
- package/dist/study/preflight.d.ts +1 -1
- package/dist/study/preflight.js +1 -1
- package/dist/study/routing.d.ts +5 -5
- package/dist/study/routing.js +5 -5
- package/dist/study/run-study-options.d.ts +1 -1
- package/dist/study/study-deps.d.ts +1 -1
- package/dist/study/study-fields.d.ts +12 -21
- package/dist/study/study-fields.js +29 -34
- package/dist/study/summary.js +9 -4
- package/dist/study/types.d.ts +50 -128
- package/dist/study/types.js +0 -1
- package/dist/study/url-credentials.js +3 -4
- package/dist/study/validation.d.ts +2 -10
- package/dist/study/validation.js +16 -46
- package/dist/study/warnings.d.ts +3 -3
- package/dist/study/warnings.js +40 -109
- package/dist/substrates/local/runtime-config.js +9 -11
- package/docs/contracts/schemas.md +23 -4
- package/package.json +1 -1
- package/dist/study/parse/study.d.ts +0 -38
- package/dist/study/parse/study.js +0 -225
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,21 @@ The Unreleased section holds the full notes for the next version until it is tag
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 0.111.0: StudyConfig has the keys of a v3 study file (2026-10-05)
|
|
12
|
+
|
|
13
|
+
humanish 0.111.0 is the first breaking release under the compatibility policy. It breaks library
|
|
14
|
+
callers that read or build a `StudyConfig`, the type `parseStudy` returns and `runStudy` takes:
|
|
15
|
+
the type now has the keys of a `humanish.study.v3` file, such as `actor`, `route`,
|
|
16
|
+
`participants`, `caps` and `mode`, where it had `actors[0]`, `execution.caps` and
|
|
17
|
+
`scenario.mode`. The release notes carry the migration table. `runStudy` refuses a config that
|
|
18
|
+
still sets a 0.110 field with `HUMANISH_STUDY_V2_UNSUPPORTED`, so a budget left in
|
|
19
|
+
`execution.caps` cannot run uncapped. Study files and CLI commands are unchanged, except two JSON
|
|
20
|
+
outputs: `humanish study show --json` prints the v3 config, and `humanish study check --json`
|
|
21
|
+
names a participant's target `participants[<i>].target`. The `--env-file` and `cleanup` aliases
|
|
22
|
+
still work and are removed in 0.112.0, on or after 2026-11-03.
|
|
23
|
+
|
|
24
|
+
[Release notes](https://github.com/danielgwilson/humanish/releases/tag/v0.111.0)
|
|
25
|
+
|
|
11
26
|
## 0.110.4: Scripted runs reclaim clean, review says interrupted, Chrome under a long TMPDIR (2026-10-05)
|
|
12
27
|
|
|
13
28
|
humanish 0.110.4 fixes four findings of the 0.110.3 live smoke. `humanish reclaim --check` reports
|
package/CONTEXT.md
CHANGED
|
@@ -18,7 +18,7 @@ bundle and output fields keep older spellings; the second table maps them.
|
|
|
18
18
|
| persona | Who a participant is: traits, background, device. `humanish/personas/<id>.yaml`. | `src/study/persona.ts`, `src/study/persona-resolve.ts` |
|
|
19
19
|
| mission | The free-form brief a model-driven participant receives: `actor.mission`. `actor.tasks[]` adds discrete tasks: each has a `goal` the participant sees and an optional `success` check it does not. | `StudyActor` in `src/study/types.ts` |
|
|
20
20
|
| assignment | What one participant was asked to do: the mission, a `participants` entry's `instruction` or the homogeneous `participants.instruction` (recorded as `focus`), and the tasks. Each stream records it as `streams[].assignment`. | `participantAssignment` in `src/study/participant-assignment.ts`; `RunParticipantAssignment` in `src/run/streams.ts` |
|
|
21
|
-
| scenario | A scenario file, `humanish/scenarios/<id>.yaml` (`humanish.scenario.v1`): a goal and ordered steps with expectations. A scripted study names one in `scenario:`, and only the scripted route executes those steps. A v2 study file's `scenario` block held run settings (`mode`, `caps`, `ref`), which v3 moves to the top level. A model-driven participant follows its mission. | `
|
|
21
|
+
| scenario | A scenario file, `humanish/scenarios/<id>.yaml` (`humanish.scenario.v1`): a goal and ordered steps with expectations. A scripted study names one in `scenario:`, and only the scripted route executes those steps. A v2 study file's `scenario` block held run settings (`mode`, `caps`, `ref`), which v3 moves to the top level. A model-driven participant follows its mission. | `scenario` in `StudyConfig` (`src/study/types.ts`); `src/routes/scripted/route.ts` resolves the scenario, and `executeBrowserPersonaStep` (`src/actors/scripted-browser/steps.ts`) runs each step |
|
|
22
22
|
| surface | One browser viewport that a scripted run replays its journey on: `desktop` or `mobile`. A scripted study runs the desktop surface by default, and `surfaces: [desktop, mobile]` adds the mobile one. Each surface writes `actor-<surface>.json` and `traces/<surface>.json`. | `BrowserSurface` and `browserSurfaces` in `src/actors/scripted-browser/types.ts`; the plan's `surfaces` in `src/routes/scripted/plan.ts` |
|
|
23
23
|
| journey | A scenario's `browser.steps` parsed into ordered steps with a goal and a start path. The scripted route replays it once on each surface, and each surface's session reports the first step that did not pass. | `BrowserPersonaJourney` in `src/actors/scripted-browser/types.ts`; `parseBrowserPersonaJourneyFromScenario` in `src/actors/scripted-browser/journey.ts` |
|
|
24
24
|
| subject | The product under study, declared by `subject.source`: `clone`, `app-url`, `local-tree`, `terminal-product`, `desktop-cli`, `local-app` or `this-repo`. `run.json` records its provenance under `subject`. | `StudySubject` in `src/study/types.ts`, parsed in `src/study/parse/subject.ts` |
|
|
@@ -33,7 +33,7 @@ bundle and output fields keep older spellings; the second table maps them.
|
|
|
33
33
|
| participant desktop | The desktop one computer-use or shared-world participant drives: a substrate plus the route's concerns, such as subject provisioning, comms and the participant's plan. It lives in the route, so `src/substrates/` holds only provider primitives ([ADR 0004](docs/decisions/0004-participant-desktops-stay-in-the-route.md)). | `ParticipantDesktop` in `src/routes/computer-use/participant-desktop.ts`; `createE2BParticipantDesktop` in `src/routes/computer-use/e2b-desktop/desktop.ts`; the local VM's in `src/routes/computer-use/local-vm.ts` |
|
|
34
34
|
| receipt | A line in a run's `sandbox-receipts.ndjson` that records a sandbox's id, provider, the participant or subject it belongs to (`laneId`), and its time limit as soon as the sandbox is created, before any work runs in it. `humanish reclaim` kills sandboxes by these ids after an interrupted run. | `SandboxReceipt` in `src/run/sandbox-receipts.ts`; `reclaimRunSandboxes` in `src/run/reclaim.ts` |
|
|
35
35
|
| owner tags | The E2B metadata every sandbox humanish creates carries: `tool: humanish`, the run's `runId`, and `runKey`, a digest of the run directory's inode and creation time. The run journals them in an owner line before its first create. `humanish reclaim` lists sandboxes that match all three, which finds one whose id never reached a receipt. | `sandboxOwnerTags` in `src/run/sandbox-creates.ts`; `findE2BSandboxesByTags` in `src/substrates/e2b/sandbox.ts` |
|
|
36
|
-
| topology | Whether participants share one instance of the subject. `route: shared-world` gives one for all; every other route gives each participant its own (`per-lane-worlds`). `run.json` records it as `sharedWorld.topology`. | `
|
|
36
|
+
| topology | Whether participants share one instance of the subject. `route: shared-world` gives one for all; every other route gives each participant its own (`per-lane-worlds`). `run.json` records it as `sharedWorld.topology`. | `declaresSharedWorld` in `src/study/study-fields.ts` |
|
|
37
37
|
| plane | The one instance of the subject that a shared-world study's participants use together. A `provisioned` plane is a `clone` or `local-tree` subject that humanish serves in a sandbox, with a declared subject state. An `external-public` plane is a deployment already running at `subject.appUrl`, whose owner the study names in `subject.publicTarget.owner`. Each plane has at least two participants. | `SharedWorldPlane` in `src/study/plan-types.ts`; `src/routes/shared-world/` |
|
|
38
38
|
| stream | One live or recorded view of a participant, such as a browser or a terminal, in `run.json` and the Observer. | `RunStream` in `src/run/streams.ts` |
|
|
39
39
|
| Observer | The single-file review page built from `observer/observer-data.json` (`humanish.observer-data.v1`). | `observer/` (the page), `src/observer/` (data, render, serve) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// One Claude Code process as the computer-use brain, instead of a fresh `claude -p` per turn.
|
|
2
2
|
//
|
|
3
|
-
// Why: `
|
|
3
|
+
// Why: `actor.localAgent: codex` already runs through a persistent app-server thread,
|
|
4
4
|
// so the participant remembers what it tried. `claude` spawned `claude -p` per turn, so every
|
|
5
5
|
// turn started cold. Measured on the same study with the same credentials (n=1 each): one-shot,
|
|
6
6
|
// 188 actions over 90 turns and never finished; a thread that remembers, 21 actions over 8 turns
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// mapping and JSON reading the two Claude Code paths share, and the one-shot Claude Code provider.
|
|
4
4
|
//
|
|
5
5
|
// A developer trying humanish often already has a coding agent signed in (Codex on a ChatGPT plan,
|
|
6
|
-
// Claude Code on a Max plan), so `
|
|
6
|
+
// Claude Code on a Max plan), so `actor.type: local-agent` lets that agent decide the next
|
|
7
7
|
// action instead of a provider API key. Codex runs as a restricted app-server participant
|
|
8
8
|
// (actors/codex/restricted-participant.ts) and Claude Code as one session for the whole run
|
|
9
9
|
// (claude-session.ts). The one-shot provider below spawns `claude -p` per turn, with no memory of
|
|
@@ -8,11 +8,11 @@ export interface ScriptedBrowserSessionOptions {
|
|
|
8
8
|
evidenceAppUrl?: string;
|
|
9
9
|
/** Defaults to loopback. Provisioned subjects drive a private URL but persist redacted labels. */
|
|
10
10
|
urlPolicy?: ScriptedBrowserEvidenceUrlPolicy;
|
|
11
|
-
/** Parsed + validated by the backend (scenario
|
|
11
|
+
/** Parsed + validated by the backend (`scenario` is consumed there, fail-closed). */
|
|
12
12
|
journey: BrowserPersonaJourney;
|
|
13
13
|
/** One session per surface. */
|
|
14
14
|
surface: BrowserSurface;
|
|
15
|
-
/** id =
|
|
15
|
+
/** id = actor.persona ?? "scripted-journey"; promptDigest = journey.sourceDigest prefix
|
|
16
16
|
* (the step manifest is the "prompt"; no model prompt exists on this route). */
|
|
17
17
|
persona: ActorPersonaRef;
|
|
18
18
|
/** Journey wall-clock budget in ms; the route passes execution.timeoutMs or 300_000. */
|
|
@@ -4,7 +4,7 @@ import { Command, Option } from "commander";
|
|
|
4
4
|
import { inspectStudyManifest, listStudyManifests } from "../../study/discover.js";
|
|
5
5
|
import { runStudyPreflight, } from "../../study/preflight.js";
|
|
6
6
|
import { applyEnvFileOption, CWD_OPTION_DESCRIPTION, DOTENV_OPTION_DESCRIPTION, dotenvPathOf, envFileAliasOption, JSON_OPTION_DESCRIPTION, parsePositiveInteger, writeResult, humanError, } from "../io.js";
|
|
7
|
-
import {
|
|
7
|
+
import { actorOf } from "../../study/study-fields.js";
|
|
8
8
|
export function registerStudyCommands(parent, io) {
|
|
9
9
|
const study = parent
|
|
10
10
|
.command("study")
|
|
@@ -115,9 +115,7 @@ function formatStudyShowHuman(result) {
|
|
|
115
115
|
`id: ${config.id}`,
|
|
116
116
|
`subject: ${config.subject.source}`,
|
|
117
117
|
...(config.execution?.target ? [`execution: ${config.execution.target}`] : []),
|
|
118
|
-
`
|
|
119
|
-
.map((actor) => actor.type)
|
|
120
|
-
.join(", ")}`,
|
|
118
|
+
`actor: ${actorOf(config)?.type ?? ""}`,
|
|
121
119
|
...(config.title ? [`title: ${config.title}`] : []),
|
|
122
120
|
...(config.description ? [`description: ${config.description}`] : []),
|
|
123
121
|
...(result.path ? [`path: ${result.path}`] : []),
|
|
@@ -84,7 +84,7 @@ export async function startParticipantModel(spec, deps, executor) {
|
|
|
84
84
|
/** The options the participant's session runs with: prompt, model settings, spend caps and callbacks. */
|
|
85
85
|
export function participantSessionOptions(spec, deps, ready, provider, writeScreenshot) {
|
|
86
86
|
const { executor, inbox } = ready;
|
|
87
|
-
// The fail-closed spend cap (
|
|
87
|
+
// The fail-closed spend cap (caps.maxUsd) is wired into the loop as maxUsd + an
|
|
88
88
|
// injected pure per-turn estimator keyed on the resolved model. Preflight already refused a
|
|
89
89
|
// cap on an unpriced model, so the estimate is measurable whenever a cap is in force. The
|
|
90
90
|
// model id here matches provider.version (openai-responses-cu resolves the default when unset).
|
|
@@ -10,7 +10,7 @@ import { subjectStateInvalidReason } from "../../study/parse/subject-state.js";
|
|
|
10
10
|
import { brainOf, callerBrainOf, planCaps, desktopRequirements, planBase, provisionedSubject, } from "../../study/plan-base.js";
|
|
11
11
|
import { computerUseParticipants, declaredTargets } from "../../study/plan-participants.js";
|
|
12
12
|
import { MAX_COMPUTER_USE_PARTICIPANTS } from "../../study/routing.js";
|
|
13
|
-
import { cloneTargetValidationReason, computerUseValidationReason, desktopMediaValidationReason, outputTokenLimitValidationReason, receivingEmailValidationReason,
|
|
13
|
+
import { cloneTargetValidationReason, computerUseValidationReason, desktopMediaValidationReason, outputTokenLimitValidationReason, receivingEmailValidationReason, taskProtocolValidationReason, } from "../../study/validation.js";
|
|
14
14
|
import { desktopCliProductReason } from "../../study/composition-rules.js";
|
|
15
15
|
import { isLocalBrowserStudy } from "../../substrates/local/runtime-config.js";
|
|
16
16
|
import { boundedConcurrency, defaultSessionTimeoutMs, resolveParticipantSandboxMs, } from "./participant-runs.js";
|
|
@@ -84,9 +84,7 @@ function cuaStudyRejection(config, hasRunSession, driving, subjectRoute) {
|
|
|
84
84
|
}
|
|
85
85
|
/** A declaration this route cannot honor, for any subject or with the caller's own driver. */
|
|
86
86
|
function unsupportedDeclarationReason(config, hasRunSession, driving, { inProcessRoute }) {
|
|
87
|
-
const reason = desktopMediaValidationReason(config) ||
|
|
88
|
-
outputTokenLimitValidationReason(config) ||
|
|
89
|
-
scenarioCapsValidationReason(config);
|
|
87
|
+
const reason = desktopMediaValidationReason(config) || outputTokenLimitValidationReason(config);
|
|
90
88
|
if (reason)
|
|
91
89
|
return invalid(reason);
|
|
92
90
|
if (actorOf(config)?.maxOutputTokens !== undefined &&
|
|
@@ -177,7 +175,7 @@ function driverReason(driving, { localAppSubject, inProcessRoute }) {
|
|
|
177
175
|
}
|
|
178
176
|
/** The participant roster, then the sandbox deadline its session budget derives. */
|
|
179
177
|
function rosterShapeReason(config) {
|
|
180
|
-
//
|
|
178
|
+
// Device XOR raw resolution, cap, unique ids,
|
|
181
179
|
// allowPublicTargets with more than one participant, clone.fanout.
|
|
182
180
|
const fanoutReason = computerUseValidationReason(config);
|
|
183
181
|
if (fanoutReason)
|
|
@@ -293,7 +291,7 @@ export function planComputerUseStudy(config, input) {
|
|
|
293
291
|
concurrency: boundedConcurrency(declared, n),
|
|
294
292
|
sessionBudgetMs: config.execution?.timeoutMs ?? defaultSessionTimeoutMs(config),
|
|
295
293
|
sandboxMs: resolveParticipantSandboxMs(config),
|
|
296
|
-
caps: planCaps(config
|
|
294
|
+
caps: planCaps(config),
|
|
297
295
|
...(input.rerun === undefined ? {} : { rerun: rerunPlan(input.rerun) }),
|
|
298
296
|
requirements: base.dryRun || runner.desktop === "in-process"
|
|
299
297
|
? []
|
|
@@ -59,7 +59,7 @@ export async function liveCuaRejection(args) {
|
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
// Fail-closed cap: a maxUsd cap needs a measurable per-turn estimate.
|
|
62
|
-
// If the operator set
|
|
62
|
+
// If the operator set caps.maxUsd but src/run/pricing.ts has no rate for the resolved
|
|
63
63
|
// model, the loop could not enforce the cap, and silently running uncapped would break the
|
|
64
64
|
// runaway-retry protection. Refuse at preflight (before any sandbox/spend) rather than run
|
|
65
65
|
// uncapped: an unenforceable cap is more dangerous than none. The operator picks a priced model
|
|
@@ -193,7 +193,7 @@ function computerUseExecutionFailures(outcomes, runId) {
|
|
|
193
193
|
];
|
|
194
194
|
}
|
|
195
195
|
/**
|
|
196
|
-
*
|
|
196
|
+
* caps.maxUsd is enforced inside each participant's loop independently, so an
|
|
197
197
|
* N-participant fan-out can spend up to N × maxUsd before any participant aborts, while the run cost summary reports the larger
|
|
198
198
|
* aggregate. The warning names that ceiling, unless the study declared a shared maxTotalUsd budget.
|
|
199
199
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The computer-use route: a subject (an app-url the caller provisioned, or a repo the
|
|
2
2
|
// humanish clones and serves in the sandbox) driven by a registry-resolved computer-use actor inside a
|
|
3
|
-
// hosted E2B desktop. On this path `
|
|
3
|
+
// hosted E2B desktop. On this path `actor.type` selects the actor: the
|
|
4
4
|
// descriptor returned by the registry runs the session; the route provisions the desktop and
|
|
5
5
|
// subject, composes the prompt from config, persists the evidence bundle, and tears down.
|
|
6
6
|
//
|
|
@@ -52,8 +52,8 @@ export interface ComputerUseRunInput {
|
|
|
52
52
|
cwd: string;
|
|
53
53
|
open?: boolean;
|
|
54
54
|
runId?: string;
|
|
55
|
-
/** CLI `--count` override for the homogeneous fan-out participant count (ignored when a `
|
|
56
|
-
*
|
|
55
|
+
/** CLI `--count` override for the homogeneous fan-out participant count (ignored when a `participants`
|
|
56
|
+
* list is declared; a roster's length is authoritative). */
|
|
57
57
|
countOverride?: number;
|
|
58
58
|
/** Explicitly create a new run containing failed or selected participants from a prior fan-out run. */
|
|
59
59
|
rerun?: {
|
|
@@ -182,7 +182,7 @@ export interface CuaParticipantSummary {
|
|
|
182
182
|
concurrency: number;
|
|
183
183
|
waves: number;
|
|
184
184
|
}
|
|
185
|
-
export type CuaActorStudyErrorCode = "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_COMPUTER_USE_FAILED" | "HUMANISH_COMPUTER_USE_KEYS_MISSING" | "HUMANISH_COMPUTER_USE_AGENT_MISSING" | "HUMANISH_COMPUTER_USE_AGENT_SIGNIN_REQUIRED" | "HUMANISH_COMPUTER_USE_SUBJECT_ENV_MISSING" | "HUMANISH_COMPUTER_USE_ACTOR_UNSUPPORTED" | "HUMANISH_COMPUTER_USE_SUBJECT_INVALID" | "HUMANISH_COMPUTER_USE_SUBJECT_UNSAFE" | "HUMANISH_COMPUTER_USE_EXECUTOR_NO_PROVIDER" | "HUMANISH_COMPUTER_USE_LOCAL_APP_NO_EXECUTOR" | "HUMANISH_COMPUTER_USE_LOCAL_DESKTOP_MISSING" | "HUMANISH_COMPUTER_USE_FANOUT_INVALID" | "HUMANISH_COMPUTER_USE_RERUN_INVALID" | "HUMANISH_COMPUTER_USE_DEVICE_GEOMETRY" | "HUMANISH_RUN_ID_IN_USE" | "HUMANISH_COMPUTER_USE_UNPRICED_CAP" | "HUMANISH_COMPUTER_USE_COMMS_CATCH_UNREACHABLE" | "HUMANISH_COMPUTER_USE_COMMS_TOKEN_INVALID" | "HUMANISH_WATCH_ALLOW_REQUIRES_OAUTH" | "HUMANISH_WATCH_OAUTH_REQUIRES_TUNNEL" | "HUMANISH_WATCH_OPTION_CONFLICT" | "HUMANISH_WATCH_TUNNEL_REQUIRES_EXPOSE" | "HUMANISH_WATCH_EXPOSE_REQUIRES_EDGE_AUTH" | "HUMANISH_WATCH_EXPOSE_REQUIRES_LIVE_FOLLOW" | "HUMANISH_WATCH_SAFE_NOT_APPLICABLE" | "HUMANISH_SERVE_TUNNEL_NOT_FOUND" | "HUMANISH_SERVE_TUNNEL_START_FAILED";
|
|
185
|
+
export type CuaActorStudyErrorCode = "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_STUDY_INVALID" | "HUMANISH_COMPUTER_USE_FAILED" | "HUMANISH_COMPUTER_USE_KEYS_MISSING" | "HUMANISH_COMPUTER_USE_AGENT_MISSING" | "HUMANISH_COMPUTER_USE_AGENT_SIGNIN_REQUIRED" | "HUMANISH_COMPUTER_USE_SUBJECT_ENV_MISSING" | "HUMANISH_COMPUTER_USE_ACTOR_UNSUPPORTED" | "HUMANISH_COMPUTER_USE_SUBJECT_INVALID" | "HUMANISH_COMPUTER_USE_SUBJECT_UNSAFE" | "HUMANISH_COMPUTER_USE_EXECUTOR_NO_PROVIDER" | "HUMANISH_COMPUTER_USE_LOCAL_APP_NO_EXECUTOR" | "HUMANISH_COMPUTER_USE_LOCAL_DESKTOP_MISSING" | "HUMANISH_COMPUTER_USE_FANOUT_INVALID" | "HUMANISH_COMPUTER_USE_RERUN_INVALID" | "HUMANISH_COMPUTER_USE_DEVICE_GEOMETRY" | "HUMANISH_RUN_ID_IN_USE" | "HUMANISH_COMPUTER_USE_UNPRICED_CAP" | "HUMANISH_COMPUTER_USE_COMMS_CATCH_UNREACHABLE" | "HUMANISH_COMPUTER_USE_COMMS_TOKEN_INVALID" | "HUMANISH_WATCH_ALLOW_REQUIRES_OAUTH" | "HUMANISH_WATCH_OAUTH_REQUIRES_TUNNEL" | "HUMANISH_WATCH_OPTION_CONFLICT" | "HUMANISH_WATCH_TUNNEL_REQUIRES_EXPOSE" | "HUMANISH_WATCH_EXPOSE_REQUIRES_EDGE_AUTH" | "HUMANISH_WATCH_EXPOSE_REQUIRES_LIVE_FOLLOW" | "HUMANISH_WATCH_SAFE_NOT_APPLICABLE" | "HUMANISH_SERVE_TUNNEL_NOT_FOUND" | "HUMANISH_SERVE_TUNNEL_START_FAILED";
|
|
186
186
|
/** Subject provenance projection: what the actor actually drove. */
|
|
187
187
|
export interface CuaSubjectProjection {
|
|
188
188
|
source: "app-url" | "clone" | "local-tree";
|
|
@@ -385,7 +385,7 @@ export interface ParticipantModelDeps {
|
|
|
385
385
|
artifactRoot: PreparedOutputRoot;
|
|
386
386
|
redactScreenshots: boolean;
|
|
387
387
|
scrubKnownValues: (text: string) => string;
|
|
388
|
-
/** The study's shared spend ledger, present exactly when
|
|
388
|
+
/** The study's shared spend ledger, present exactly when caps.maxTotalUsd is set on a
|
|
389
389
|
* live run. Preflight already refused the cap on an unpriced model. */
|
|
390
390
|
runBudget?: CuaRunBudget;
|
|
391
391
|
/**
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Spend: scripted steps make no model requests, and their traces record zero token usage. A local
|
|
8
8
|
// app-url run spends no sandbox minutes; a live clone run spends E2B minutes to clone and serve the
|
|
9
|
-
// subject. `
|
|
9
|
+
// subject. `mode: live` is required anyway, because a live run actuates a real browser
|
|
10
10
|
// against a real app (it fills forms and clicks buttons). A dry run parses and digest-pins the
|
|
11
11
|
// scenario and writes the contract bundle without touching anything.
|
|
12
12
|
//
|
|
@@ -8,7 +8,7 @@ interface ResolvedScriptedScenario {
|
|
|
8
8
|
sourceDigest: string;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Resolve and consume `scenario
|
|
11
|
+
* Resolve and consume `scenario`. Path-style refs (contain a separator or end .yaml/.yml)
|
|
12
12
|
* resolve against cwd and are clamped inside it: a ../../ escape is rejected, never recorded
|
|
13
13
|
* as repo-relative provenance. Id-style refs must be public-safe tokens and resolve to
|
|
14
14
|
* humanish/scenarios/<ref>.yaml (then .yml). Every failure mode is fail-closed. planScriptedStudy
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Resolve a scripted-browser study's `scenario
|
|
1
|
+
// Resolve a scripted-browser study's `scenario` to a committed scenario file and parse its
|
|
2
2
|
// `browser.steps` into the journey the actor runs. The ref is an id or a repo-relative path that must
|
|
3
3
|
// stay inside the project; provenance records it repo-relative with the scenario's digest.
|
|
4
4
|
import { parse as parseYaml } from "yaml";
|
|
@@ -6,11 +6,11 @@ import path from "node:path";
|
|
|
6
6
|
import { parseBrowserPersonaJourneyFromScenario } from "../../actors/scripted-browser/journey.js";
|
|
7
7
|
import { digestText } from "../../evidence/redaction.js";
|
|
8
8
|
import { readContainedRegularFile, } from "../../run/contained-output.js";
|
|
9
|
-
// Same public-safe token shape the study id uses; an id-style scenario
|
|
9
|
+
// Same public-safe token shape the study id uses; an id-style `scenario` must match it before
|
|
10
10
|
// it is interpolated into a repo path.
|
|
11
11
|
const SCENARIO_REF_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
|
|
12
12
|
/**
|
|
13
|
-
* Resolve and consume `scenario
|
|
13
|
+
* Resolve and consume `scenario`. Path-style refs (contain a separator or end .yaml/.yml)
|
|
14
14
|
* resolve against cwd and are clamped inside it: a ../../ escape is rejected, never recorded
|
|
15
15
|
* as repo-relative provenance. Id-style refs must be public-safe tokens and resolve to
|
|
16
16
|
* humanish/scenarios/<ref>.yaml (then .yml). Every failure mode is fail-closed. planScriptedStudy
|
|
@@ -47,7 +47,7 @@ export interface ScriptedBrowserStudyResult extends AutomaticAnalysisResult, Stu
|
|
|
47
47
|
killed: boolean;
|
|
48
48
|
};
|
|
49
49
|
hostDigest?: string;
|
|
50
|
-
/** The consumed scenario
|
|
50
|
+
/** The consumed `scenario`: digest-pinned provenance of the executable steps. */
|
|
51
51
|
scenario?: {
|
|
52
52
|
id: string;
|
|
53
53
|
source: string;
|
|
@@ -58,7 +58,7 @@ export interface ScriptedBrowserStudyResult extends AutomaticAnalysisResult, Stu
|
|
|
58
58
|
observer?: ObserverResult;
|
|
59
59
|
warnings: string[];
|
|
60
60
|
error?: {
|
|
61
|
-
code: "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_SCRIPTED_FAILED" | "HUMANISH_SCRIPTED_ACTOR_UNSUPPORTED" | "HUMANISH_SCRIPTED_SCENARIO_INVALID" | "HUMANISH_SCRIPTED_SUBJECT_UNSAFE" | "HUMANISH_SCRIPTED_BROWSER_MISSING" | "HUMANISH_SCRIPTED_KEYS_MISSING" | "HUMANISH_SCRIPTED_SUBJECT_ENV_MISSING" | "HUMANISH_SCRIPTED_GETHOST_UNAVAILABLE" | "HUMANISH_RUN_ID_IN_USE";
|
|
61
|
+
code: "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_STUDY_INVALID" | "HUMANISH_SCRIPTED_FAILED" | "HUMANISH_SCRIPTED_ACTOR_UNSUPPORTED" | "HUMANISH_SCRIPTED_SCENARIO_INVALID" | "HUMANISH_SCRIPTED_SUBJECT_UNSAFE" | "HUMANISH_SCRIPTED_BROWSER_MISSING" | "HUMANISH_SCRIPTED_KEYS_MISSING" | "HUMANISH_SCRIPTED_SUBJECT_ENV_MISSING" | "HUMANISH_SCRIPTED_GETHOST_UNAVAILABLE" | "HUMANISH_RUN_ID_IN_USE";
|
|
62
62
|
message: string;
|
|
63
63
|
};
|
|
64
64
|
}
|
|
@@ -30,7 +30,7 @@ const HANDOFF_DEADLINE_BUDGET_FRACTION = 0.4;
|
|
|
30
30
|
// convergence proof): at most this many single-frame reads before the participant is assumed to be
|
|
31
31
|
// somewhere without a code. Each reader stops the instant it has what it needs, so in practice only
|
|
32
32
|
// a handful fire (a participant reaches its /lobby within a few turns). NOTE: these reads are
|
|
33
|
-
// out-of-band OpenAI calls (external-public route only) and are left out of
|
|
33
|
+
// out-of-band OpenAI calls (external-public route only) and are left out of caps.maxUsd,
|
|
34
34
|
// so this hard cap is what bounds their spend (each read is one cheap single-frame OCR call). If
|
|
35
35
|
// this route ever runs under a strict budget, fold the estimate in.
|
|
36
36
|
const MAX_LOBBY_CODE_VISION_READS = 30;
|
|
@@ -200,7 +200,7 @@ export function participantRunDeps(ctx, live, scrubKnownValues) {
|
|
|
200
200
|
* scrubbed of the run's known secret values.
|
|
201
201
|
*/
|
|
202
202
|
export async function buildParticipantSpecs(participants, cwd, scrubKnownValues) {
|
|
203
|
-
// Only the personas the participants use: an
|
|
203
|
+
// Only the personas the participants use: an actor.persona that every participant overrides
|
|
204
204
|
// is never applied, so it is not read.
|
|
205
205
|
const personaResolution = await resolveCommittedPersonasForCwd(cwd, participants.map((participant) => participant.personaId));
|
|
206
206
|
const actorSpecs = participants.map((participant) => buildActorSpec(participant, personaResolution.personas));
|
|
@@ -10,7 +10,7 @@ import { brainOf, planCaps, desktopRequirements, isNonEmpty, planBase, } from ".
|
|
|
10
10
|
import { sharedWorldParticipants } from "../../study/plan-participants.js";
|
|
11
11
|
import { PUBLIC_TARGET_OWNER_PATTERN } from "../../study/parse/subject.js";
|
|
12
12
|
import { REPO_SLUG_PATTERN } from "../../study/parse/values.js";
|
|
13
|
-
import { concurrentSharedWorldValidationReason, desktopMediaValidationReason, externalPublicSharedWorldValidationReason, outputTokenLimitValidationReason, receivingEmailValidationReason,
|
|
13
|
+
import { concurrentSharedWorldValidationReason, desktopMediaValidationReason, externalPublicSharedWorldValidationReason, outputTokenLimitValidationReason, receivingEmailValidationReason, taskProtocolValidationReason, } from "../../study/validation.js";
|
|
14
14
|
import { MODEL_RATES, unpricedCapMessage } from "../../run/pricing.js";
|
|
15
15
|
import { actorOf, capsOf } from "../../study/study-fields.js";
|
|
16
16
|
/** The registered descriptor for a planned actor; planSharedWorldStudy checked the registry. */
|
|
@@ -54,7 +54,6 @@ export function planSharedWorldStudy(config, input) {
|
|
|
54
54
|
// A library caller's config skips the parser, which refuses these first.
|
|
55
55
|
studyUrlCredentialReason(config) ??
|
|
56
56
|
outputTokenLimitValidationReason(config) ??
|
|
57
|
-
scenarioCapsValidationReason(config) ??
|
|
58
57
|
(externalPublic
|
|
59
58
|
? externalPublicSharedWorldValidationReason(config)
|
|
60
59
|
: concurrentSharedWorldValidationReason(config));
|
|
@@ -62,7 +61,7 @@ export function planSharedWorldStudy(config, input) {
|
|
|
62
61
|
return refuse(invalid, invalidReason, actor);
|
|
63
62
|
if (actorOf(config)?.maxOutputTokens !== undefined && input.hasRunSession === true)
|
|
64
63
|
return refuse(invalid, "maxOutputTokens cannot be enforced by a custom runSession.", actor);
|
|
65
|
-
const caps = capsOf(config
|
|
64
|
+
const caps = capsOf(config);
|
|
66
65
|
if (!input.dryRun && (caps?.maxUsd !== undefined || caps?.maxTotalUsd !== undefined)) {
|
|
67
66
|
const model = (actorOf(config)?.model ?? DEFAULT_OPENAI_CU_MODEL).trim().toLowerCase();
|
|
68
67
|
if (!MODEL_RATES[model])
|
|
@@ -107,7 +106,7 @@ export function planSharedWorldStudy(config, input) {
|
|
|
107
106
|
? {}
|
|
108
107
|
: { sessionTimeoutMs: config.execution.timeoutMs }),
|
|
109
108
|
brain,
|
|
110
|
-
caps: planCaps(config
|
|
109
|
+
caps: planCaps(config),
|
|
111
110
|
requirements: base.dryRun
|
|
112
111
|
? []
|
|
113
112
|
: [
|
|
@@ -54,7 +54,7 @@ export interface SharedWorldRunInput {
|
|
|
54
54
|
* core-stamped onto the bundle as evidence. Absent for library callers. */
|
|
55
55
|
scorerProvenance?: RunScorerProvenance;
|
|
56
56
|
}
|
|
57
|
-
export type ConcurrentSharedWorldStudyErrorCode = "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_SHARED_WORLD_FAILED" | "HUMANISH_SHARED_WORLD_ACTOR_UNSUPPORTED" | "HUMANISH_SHARED_WORLD_INVALID" | "HUMANISH_SHARED_WORLD_KEYS_MISSING"
|
|
57
|
+
export type ConcurrentSharedWorldStudyErrorCode = "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_STUDY_INVALID" | "HUMANISH_SHARED_WORLD_FAILED" | "HUMANISH_SHARED_WORLD_ACTOR_UNSUPPORTED" | "HUMANISH_SHARED_WORLD_INVALID" | "HUMANISH_SHARED_WORLD_KEYS_MISSING"
|
|
58
58
|
/** A local-agent brain's CLI is not on `PATH`. */
|
|
59
59
|
| "HUMANISH_SHARED_WORLD_AGENT_MISSING"
|
|
60
60
|
/** A local-agent brain's CLI is signed out, or could not report its sign-in status. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ActorPersonaRef, ActorTrace } from "../../actors/contract.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { StudyCaps, StudyRuntimeAuth } from "../../study/types.js";
|
|
3
3
|
import { type RunBundle, type RunCostSummary, type BundleRun } from "../../run/bundle.js";
|
|
4
4
|
import { type Verdict } from "../../run/judge.js";
|
|
5
5
|
import type { TerminalLedgers } from "./types.js";
|
|
@@ -21,7 +21,7 @@ export declare function buildTerminalProductBundle(args: {
|
|
|
21
21
|
persona: ActorPersonaRef;
|
|
22
22
|
productName: string;
|
|
23
23
|
publicSurfaces: string[];
|
|
24
|
-
caps?:
|
|
24
|
+
caps?: StudyCaps;
|
|
25
25
|
runtimeAuth?: string;
|
|
26
26
|
stdin: "disabled" | "planned" | "sent";
|
|
27
27
|
policies: {
|
|
@@ -51,7 +51,7 @@ export declare function buildLiveTerminalProductBundle(args: {
|
|
|
51
51
|
persona: ActorPersonaRef;
|
|
52
52
|
productName: string;
|
|
53
53
|
publicSurfaces: string[];
|
|
54
|
-
caps?:
|
|
54
|
+
caps?: StudyCaps;
|
|
55
55
|
runtimeAuthKeyName: string;
|
|
56
56
|
runtimeAuth?: StudyRuntimeAuth;
|
|
57
57
|
policies: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ActorTokenUsage } from "../../actors/contract.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { StudyCaps } from "../../study/types.js";
|
|
3
3
|
import { type CostCategory } from "../../run/terminal-contract.js";
|
|
4
4
|
import type { CostLine, NoSpendProof, TerminalCostLedger } from "./types.js";
|
|
5
5
|
/**
|
|
@@ -41,7 +41,7 @@ export declare function buildNoSpendProof(ledger: TerminalCostLedger, maxUsd: nu
|
|
|
41
41
|
* grant a green pass: the no-spend proof reports them as unmeasured. maxMinutes is wall-clock and is
|
|
42
42
|
* enforced separately (runWithWallClock); it is not a ledger-derived cap.
|
|
43
43
|
*/
|
|
44
|
-
export declare function evaluateCapsAgainstLedger(ledger: TerminalCostLedger, caps:
|
|
44
|
+
export declare function evaluateCapsAgainstLedger(ledger: TerminalCostLedger, caps: StudyCaps): {
|
|
45
45
|
ok: true;
|
|
46
46
|
} | {
|
|
47
47
|
ok: false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// How long a live terminal sandbox may live. Its server-side timeout has to cover everything that
|
|
2
|
-
// runs in it before the codex command, the command's own wall clock (
|
|
2
|
+
// runs in it before the codex command, the command's own wall clock (caps.maxMinutes) and
|
|
3
3
|
// the reclamation buffer after that clock's kill. A timeout shorter than that lets E2B kill the
|
|
4
4
|
// sandbox before the command's deadline and before the evidence is read.
|
|
5
5
|
import { NODE_BOOTSTRAP_TIMEOUT_MS } from "../../subject/node-bootstrap.js";
|
|
@@ -117,7 +117,7 @@ export async function finishLiveTerminalSession(inputs) {
|
|
|
117
117
|
const judgment = judgeTerminal({ dryRun: false, participant });
|
|
118
118
|
// The run cost summary, as the computer-use route records it: the sandbox's compute time from
|
|
119
119
|
// its span and observed size, and the participant's tokens (unpriced for Codex). It is not part
|
|
120
|
-
// of the cap ledger above, whose lines sum against
|
|
120
|
+
// of the cap ledger above, whose lines sum against caps.maxUsd.
|
|
121
121
|
const desktops = session.runCostDesktops();
|
|
122
122
|
const runCost = buildRunCostSummary({
|
|
123
123
|
participants: [{ trace }],
|
|
@@ -84,7 +84,7 @@ export function planTerminalStudy(config, input) {
|
|
|
84
84
|
...(egressAllow === undefined ? {} : { egressAllow }),
|
|
85
85
|
...(stdin === undefined ? {} : { stdin }),
|
|
86
86
|
};
|
|
87
|
-
const caps = capsOf(config
|
|
87
|
+
const caps = capsOf(config);
|
|
88
88
|
if (input.dryRun)
|
|
89
89
|
return { ok: true, plan: { ...shared, dryRun: true, ...(caps ? { caps } : {}) } };
|
|
90
90
|
// The live route injects the runtime key into one command only, and only a registered actor
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// spending. Opt-in openai-env injects the raw key command-scoped, never through
|
|
18
18
|
// Sandbox.create({envs}).
|
|
19
19
|
// Enforced in credentials.ts and runtime-auth.ts.
|
|
20
|
-
// 2. Fail-closed cap. The live key is never exercised without
|
|
20
|
+
// 2. Fail-closed cap. The live key is never exercised without `caps` in force: maxUsd
|
|
21
21
|
// (default/require 0 = no-spend) + maxMinutes (wall-clock kill of the codex command).
|
|
22
22
|
// Enforced in plan.ts (caps required), live-sandbox.ts and lifetime.ts (the wall clock).
|
|
23
23
|
// 3. Public surfaces only. The mission references only subject.product.publicSurfaces + the
|
|
@@ -171,7 +171,7 @@ export interface TerminalProductStudyResult extends AutomaticAnalysisResult, Stu
|
|
|
171
171
|
observer?: ObserverResult;
|
|
172
172
|
warnings: string[];
|
|
173
173
|
error?: {
|
|
174
|
-
code: "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_TERMINAL_FAILED" | "HUMANISH_TERMINAL_ACTOR_UNSUPPORTED" | "HUMANISH_TERMINAL_SUBJECT_INVALID" | "HUMANISH_TERMINAL_KEYPLACEMENT_INVALID" | "HUMANISH_TERMINAL_RUNTIME_AUTH_MISSING" | "HUMANISH_TERMINAL_KEYS_MISSING" | "HUMANISH_TERMINAL_CAPS_MISSING" | "HUMANISH_TERMINAL_CAPS_INVALID" | "HUMANISH_TERMINAL_UNPRICED_CAP" | "HUMANISH_TERMINAL_CAPS_EXCEEDED" | "HUMANISH_TERMINAL_CREDENTIAL_DENIED" | "HUMANISH_TERMINAL_CLEANUP_UNPROVEN" | "HUMANISH_RUN_ID_IN_USE";
|
|
174
|
+
code: "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_STUDY_INVALID" | "HUMANISH_TERMINAL_FAILED" | "HUMANISH_TERMINAL_ACTOR_UNSUPPORTED" | "HUMANISH_TERMINAL_SUBJECT_INVALID" | "HUMANISH_TERMINAL_KEYPLACEMENT_INVALID" | "HUMANISH_TERMINAL_RUNTIME_AUTH_MISSING" | "HUMANISH_TERMINAL_KEYS_MISSING" | "HUMANISH_TERMINAL_CAPS_MISSING" | "HUMANISH_TERMINAL_CAPS_INVALID" | "HUMANISH_TERMINAL_UNPRICED_CAP" | "HUMANISH_TERMINAL_CAPS_EXCEEDED" | "HUMANISH_TERMINAL_CREDENTIAL_DENIED" | "HUMANISH_TERMINAL_CLEANUP_UNPROVEN" | "HUMANISH_RUN_ID_IN_USE";
|
|
175
175
|
message: string;
|
|
176
176
|
};
|
|
177
177
|
}
|
package/dist/run/results.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface RunResult {
|
|
|
30
30
|
observer?: ObserverResult;
|
|
31
31
|
warnings: string[];
|
|
32
32
|
error?: {
|
|
33
|
-
code: "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_ANALYSIS_UNSUPPORTED" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_STUDY_COMMS_UNSUPPORTED" | "HUMANISH_LIVE_RUN_UNIMPLEMENTED" | "HUMANISH_INVALID_CWD" | "HUMANISH_RUN_ID_IN_USE" | "HUMANISH_INVALID_PARTICIPANT_COUNT" | "HUMANISH_INVALID_PORT" | "HUMANISH_UNSUPPORTED_RERUN_FLAGS" | "HUMANISH_WATCH_OPTION_CONFLICT" | "HUMANISH_WATCH_SAFE_NOT_APPLICABLE" | "HUMANISH_RUN_OPTION_CONFLICT" | "HUMANISH_OBSERVE_OPTION_CONFLICT" | "HUMANISH_STUDY_SCORER_BAD_REF" | "HUMANISH_STUDY_SCORER_NOT_FOUND" | "HUMANISH_STUDY_SCORER_LOAD_FAILED" | "HUMANISH_STUDY_SCORER_NO_HOOKS" | "HUMANISH_STUDY_SCORER_UNSUPPORTED_BACKEND";
|
|
33
|
+
code: "HUMANISH_STUDY_ANALYSIS_INVALID" | "HUMANISH_STUDY_ANALYSIS_UNSUPPORTED" | "HUMANISH_STUDY_TASKS_UNSUPPORTED" | "HUMANISH_STUDY_OPTION_UNSUPPORTED" | "HUMANISH_STUDY_V2_UNSUPPORTED" | "HUMANISH_STUDY_INVALID" | "HUMANISH_STUDY_COMMS_UNSUPPORTED" | "HUMANISH_LIVE_RUN_UNIMPLEMENTED" | "HUMANISH_INVALID_CWD" | "HUMANISH_RUN_ID_IN_USE" | "HUMANISH_INVALID_PARTICIPANT_COUNT" | "HUMANISH_INVALID_PORT" | "HUMANISH_UNSUPPORTED_RERUN_FLAGS" | "HUMANISH_WATCH_OPTION_CONFLICT" | "HUMANISH_WATCH_SAFE_NOT_APPLICABLE" | "HUMANISH_RUN_OPTION_CONFLICT" | "HUMANISH_OBSERVE_OPTION_CONFLICT" | "HUMANISH_STUDY_SCORER_BAD_REF" | "HUMANISH_STUDY_SCORER_NOT_FOUND" | "HUMANISH_STUDY_SCORER_LOAD_FAILED" | "HUMANISH_STUDY_SCORER_NO_HOOKS" | "HUMANISH_STUDY_SCORER_UNSUPPORTED_BACKEND";
|
|
34
34
|
message: string;
|
|
35
35
|
};
|
|
36
36
|
}
|
package/dist/run-study.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type LateScorer } from "./study/route-inputs.js";
|
|
|
3
3
|
import { type StudyRoute } from "./study/plan.js";
|
|
4
4
|
import type { StudyDeps } from "./study/study-deps.js";
|
|
5
5
|
import type { RunStudyDriving, RunStudyHomes } from "./study/run-study-homes.js";
|
|
6
|
-
import {
|
|
6
|
+
import type { StudyConfig } from "./study/types.js";
|
|
7
7
|
import type { ObserverResult } from "./observer/render.js";
|
|
8
8
|
import { type CuaActorStudyResult } from "./routes/computer-use/types.js";
|
|
9
9
|
import { type PreviewStudyResult } from "./routes/preview.js";
|
|
@@ -20,8 +20,10 @@ import type { RunStudyProvenance } from "./run/study-provenance.js";
|
|
|
20
20
|
export declare function runStudyWith(config: StudyConfig, options: InternalRunStudyOptions, deps?: StudyDeps): Promise<StudyOutcome>;
|
|
21
21
|
/**
|
|
22
22
|
* The package's study runner (`src/index.ts`): runStudyWith with the public options only. A
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* config that skipped parseStudy is checked first (libraryConfigRefusal): a humanish.lab.v2 config
|
|
24
|
+
* or one with a humanish 0.110 field, no `actor`, or a `route` the config does not take. That, or a
|
|
25
|
+
* field RunStudyOptions no longer has, is refused in the route's own result envelope before
|
|
26
|
+
* anything runs.
|
|
25
27
|
*/
|
|
26
28
|
export declare function runStudy(config: StudyConfig, options: RunStudyOptions): Promise<StudyOutcome>;
|
|
27
29
|
/** A study planned once: the route's refusal, or the run of its plan. */
|
package/dist/run-study.js
CHANGED
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
import { computerUseInput, scriptedInput, sharedWorldInput, terminalInput, } from "./study/route-inputs.js";
|
|
8
8
|
import { planStudy, resolveStudyDryRun, routeOf } from "./study/plan.js";
|
|
9
9
|
import { normalizeRunStudyOptions, optionRefusalOutcome, removedOptionRefusal, } from "./study/run-study-options.js";
|
|
10
|
-
import {
|
|
11
|
-
import { V2_UNSUPPORTED_MESSAGE } from "./study/config.js";
|
|
10
|
+
import { libraryConfigRefusal } from "./study/config.js";
|
|
12
11
|
import { admitComputerUsePlan, computerUseStudyRefusal } from "./routes/computer-use/route.js";
|
|
13
12
|
import {} from "./routes/computer-use/types.js";
|
|
14
13
|
import { admitPreviewPlan, previewStudyRefusal, } from "./routes/preview.js";
|
|
@@ -32,17 +31,14 @@ export async function runStudyWith(config, options, deps = {}) {
|
|
|
32
31
|
}
|
|
33
32
|
/**
|
|
34
33
|
* The package's study runner (`src/index.ts`): runStudyWith with the public options only. A
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* config that skipped parseStudy is checked first (libraryConfigRefusal): a humanish.lab.v2 config
|
|
35
|
+
* or one with a humanish 0.110 field, no `actor`, or a `route` the config does not take. That, or a
|
|
36
|
+
* field RunStudyOptions no longer has, is refused in the route's own result envelope before
|
|
37
|
+
* anything runs.
|
|
37
38
|
*/
|
|
38
39
|
export async function runStudy(config, options) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
ok: false,
|
|
42
|
-
code: "HUMANISH_STUDY_V2_UNSUPPORTED",
|
|
43
|
-
message: V2_UNSUPPORTED_MESSAGE,
|
|
44
|
-
}
|
|
45
|
-
: removedOptionRefusal(options);
|
|
40
|
+
const rejected = libraryConfigRefusal(config);
|
|
41
|
+
const refusal = rejected === undefined ? removedOptionRefusal(options) : { ok: false, ...rejected };
|
|
46
42
|
if (refusal === undefined)
|
|
47
43
|
return runStudyWith(config, options);
|
|
48
44
|
const study = localBrowserDefaults(config);
|
|
@@ -50,7 +50,7 @@ export type AdapterScorerLoadResult = {
|
|
|
50
50
|
};
|
|
51
51
|
};
|
|
52
52
|
/**
|
|
53
|
-
* Resolve + load a config-declared scorer module. Resolution clones scenario
|
|
53
|
+
* Resolve + load a config-declared scorer module. Resolution clones the scripted `scenario` resolution exactly
|
|
54
54
|
* (prepareSelectedOutputDirectory → cwd-clamp → readContainedRegularFile), then `import()`s the
|
|
55
55
|
* entry file in a broad try/catch. The digest is over the readContainedRegularFile entry bytes.
|
|
56
56
|
*/
|
|
@@ -54,7 +54,7 @@ const SCORER_CAPABLE_ROUTES = new Set([
|
|
|
54
54
|
* package.json `type`. `.ts` is rejected: the compiled `dist/` CLI ships no TypeScript loader. */
|
|
55
55
|
const SCORER_EXTENSIONS = new Set([".mjs", ".js", ".cjs"]);
|
|
56
56
|
/**
|
|
57
|
-
* Resolve + load a config-declared scorer module. Resolution clones scenario
|
|
57
|
+
* Resolve + load a config-declared scorer module. Resolution clones the scripted `scenario` resolution exactly
|
|
58
58
|
* (prepareSelectedOutputDirectory → cwd-clamp → readContainedRegularFile), then `import()`s the
|
|
59
59
|
* entry file in a broad try/catch. The digest is over the readContainedRegularFile entry bytes.
|
|
60
60
|
*/
|
|
@@ -84,10 +84,10 @@ export async function loadAdapterScorer(args) {
|
|
|
84
84
|
if (!SCORER_EXTENSIONS.has(ext)) {
|
|
85
85
|
return fail("HUMANISH_STUDY_SCORER_BAD_REF", `review.scorer.ref "${trimmed}" must be a path relative to the project ending in .mjs (recommended), .js or .cjs.`);
|
|
86
86
|
}
|
|
87
|
-
// Root token: realpath(cwd) → prepareSelectedOutputDirectory(dirname, cwd). Mirrors scenario
|
|
87
|
+
// Root token: realpath(cwd) → prepareSelectedOutputDirectory(dirname, cwd). Mirrors the scripted `scenario`.
|
|
88
88
|
const physicalCwd = await realpath(path.resolve(args.cwd));
|
|
89
89
|
const root = await prepareSelectedOutputDirectory(path.dirname(physicalCwd), physicalCwd);
|
|
90
|
-
// Clamp verbatim from the scripted scenario
|
|
90
|
+
// Clamp verbatim from the scripted `scenario` branch: resolve → relative → reject escape/absolute.
|
|
91
91
|
const absolutePath = path.resolve(root.physicalPath, trimmed);
|
|
92
92
|
const relative = path.relative(root.physicalPath, absolutePath);
|
|
93
93
|
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|