humanish 0.133.1 → 0.134.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 +13 -0
- package/dist/actors/computer-use/loop/actions.d.ts +2 -0
- package/dist/actors/computer-use/loop/actions.js +11 -5
- package/dist/actors/computer-use/loop/backstop.d.ts +23 -2
- package/dist/actors/computer-use/loop/backstop.js +40 -7
- package/dist/actors/computer-use/loop/session.d.ts +3 -0
- package/dist/actors/computer-use/loop/session.js +3 -0
- package/dist/actors/computer-use/loop/streak.d.ts +18 -0
- package/dist/actors/computer-use/loop/streak.js +47 -0
- package/dist/actors/computer-use/loop/types.d.ts +5 -1
- package/dist/actors/computer-use/loop/watch.d.ts +22 -4
- package/dist/actors/computer-use/loop/watch.js +83 -27
- package/dist/actors/computer-use/loop.js +18 -6
- package/dist/actors/contract.d.ts +3 -1
- package/dist/analysis/service.js +3 -2
- package/dist/analysis/source.d.ts +6 -0
- package/dist/analysis/source.js +15 -13
- package/dist/cli/commands/run-signals.js +6 -2
- package/dist/observer/render.js +5 -3
- package/dist/routes/computer-use/bundle-parts.d.ts +9 -0
- package/dist/routes/computer-use/bundle-parts.js +24 -4
- package/dist/routes/computer-use/fanout-bundle.js +5 -3
- package/dist/routes/computer-use/single-bundle.js +2 -2
- package/dist/run/locate.d.ts +8 -2
- package/dist/run/locate.js +10 -4
- package/dist/run/run.js +18 -2
- package/dist/run/sandbox-ids.d.ts +4 -3
- package/dist/run/sandbox-ids.js +8 -6
- package/dist/run/stream-trace.d.ts +8 -2
- package/dist/run/stream-trace.js +13 -7
- package/dist/study/bundle-estimate.js +23 -17
- package/dist/verify/artifacts.d.ts +7 -1
- package/dist/verify/artifacts.js +15 -4
- package/dist/verify/verify.js +29 -8
- package/docs/architecture/actor-contract.md +3 -1
- package/docs/architecture/state-driven-executor.md +7 -4
- package/docs/contracts/run-bundle.md +13 -2
- package/docs/contracts/study-analysis.md +4 -2
- package/package.json +1 -1
package/dist/verify/verify.js
CHANGED
|
@@ -7,9 +7,9 @@ import { containsSensitiveJson } from "../evidence/sensitive-json.js";
|
|
|
7
7
|
import { keyedSandboxIds, readRunSandboxIds } from "../run/sandbox-ids.js";
|
|
8
8
|
import { validatePreparedRunArtifactPaths } from "../run/paths.js";
|
|
9
9
|
import { isReadRunBundleSchema, RUN_BUNDLE_FILE, RUN_BUNDLE_SCHEMA, RUN_BUNDLE_V1_SCHEMA, RUN_BUNDLE_V2_SCHEMA, RUN_BUNDLE_V3_SCHEMA, } from "../run/bundle.js";
|
|
10
|
-
import { ContainedReadRefusedError, refusalText } from "../run/contained-output.js";
|
|
10
|
+
import { ContainedReadRefusedError, readContainedRegularFile, refusalText, RUN_ARTIFACT_MAX_BYTES, } from "../run/contained-output.js";
|
|
11
11
|
import { isCleanupResult, isRunBundle } from "../run/bundle-shape.js";
|
|
12
|
-
import { loadStreamTraces } from "../run/stream-trace.js";
|
|
12
|
+
import { loadStreamTraces, StreamTraceTooLargeError } from "../run/stream-trace.js";
|
|
13
13
|
import { readRunJsonIfExists, readRunTextIfExists, resolveRunPath, runJsonValue, } from "../run/locate.js";
|
|
14
14
|
import { SANDBOX_RECEIPTS_ARTIFACT } from "../run/sandbox-receipts.js";
|
|
15
15
|
import { isRecord } from "../run/type-guards.js";
|
|
@@ -79,7 +79,8 @@ export async function verifyResolvedRun(cwd, runInput, runPaths) {
|
|
|
79
79
|
const bundlePath = path.join(runPaths.absoluteRunRoot, RUN_BUNDLE_FILE);
|
|
80
80
|
const bundleRead = await readRunJsonIfExists(runPaths, RUN_BUNDLE_FILE);
|
|
81
81
|
// Every check below reads the streams with their traces loaded; a trace that does not match its
|
|
82
|
-
// index fails the evidence-artifacts check and its stream
|
|
82
|
+
// index fails the evidence-artifacts check, and its stream, like one past the read limit, is
|
|
83
|
+
// checked without its items.
|
|
83
84
|
const traces = await loadTraces(runPaths, runJsonValue(bundleRead));
|
|
84
85
|
const { bundle, recorded, refusedTraces } = traces;
|
|
85
86
|
const cleanupRead = await readRunJsonIfExists(runPaths, "cleanup.json");
|
|
@@ -102,9 +103,7 @@ export async function verifyResolvedRun(cwd, runInput, runPaths) {
|
|
|
102
103
|
unscannedArtifacts.files.push(SANDBOX_RECEIPTS_ARTIFACT);
|
|
103
104
|
}
|
|
104
105
|
const publicSafetyFindings = await scanRunPublicSafetyArtifacts(runPaths, derivedPublicSafetyFindings, {
|
|
105
|
-
recordingPaths:
|
|
106
|
-
? bundle.streams.flatMap((stream) => (stream.recording ? [stream.recording.path] : []))
|
|
107
|
-
: []),
|
|
106
|
+
recordingPaths: recordingPaths(bundle),
|
|
108
107
|
screenshotPaths: bundle ? streamScreenshotPaths(bundle) : new Set(),
|
|
109
108
|
// A run from before 0.110 with no receipts still names its ids in run.json.
|
|
110
109
|
sandboxIds: [...new Set([...journaledSandboxIds, ...keyedSandboxIds(recorded)])],
|
|
@@ -127,7 +126,7 @@ export async function verifyResolvedRun(cwd, runInput, runPaths) {
|
|
|
127
126
|
: `public-safety findings: ${publicSafetyFindings.slice(0, 5).join(", ")}`,
|
|
128
127
|
});
|
|
129
128
|
const missingEvidenceArtifacts = bundle
|
|
130
|
-
? await missingLocalEvidenceArtifacts(runPaths, bundle)
|
|
129
|
+
? await missingLocalEvidenceArtifacts(runPaths, bundle, traces.pastReadLimit)
|
|
131
130
|
: [];
|
|
132
131
|
const redactedShapeFrames = bundle
|
|
133
132
|
? await redactedShapeFramePaths(runPaths, bundle)
|
|
@@ -155,6 +154,7 @@ export async function verifyResolvedRun(cwd, runInput, runPaths) {
|
|
|
155
154
|
: { warnings: [], unfinished: undefined };
|
|
156
155
|
const warnings = bundle
|
|
157
156
|
? [
|
|
157
|
+
...traces.unreadTraces,
|
|
158
158
|
...rawScreenshotPostureWarnings(bundle, redactedShapeFrames),
|
|
159
159
|
...undeclaredSubjectStateWarnings(bundle),
|
|
160
160
|
...desktopGeometryWarnings(bundle),
|
|
@@ -238,6 +238,8 @@ async function loadTraces(runPaths, stored) {
|
|
|
238
238
|
recorded: stored,
|
|
239
239
|
refusedTraces: [],
|
|
240
240
|
refusedStreams: new Set(),
|
|
241
|
+
unreadTraces: [],
|
|
242
|
+
pastReadLimit: new Set(),
|
|
241
243
|
...scanned,
|
|
242
244
|
pages: [],
|
|
243
245
|
};
|
|
@@ -250,15 +252,34 @@ async function loadTraces(runPaths, stored) {
|
|
|
250
252
|
scanned[scan === "sensitive" ? "sensitive" : "opaque"].push(file);
|
|
251
253
|
},
|
|
252
254
|
});
|
|
255
|
+
// A file whose index records more than the read limit is not read. One past the limit on disk
|
|
256
|
+
// too is listed as unscanned, so its run grades local_only; one under it does not match its index.
|
|
257
|
+
const unread = [];
|
|
258
|
+
for (const error of refused)
|
|
259
|
+
if (error instanceof StreamTraceTooLargeError && (await pastReadLimit(runPaths, error.file))) {
|
|
260
|
+
unread.push(error);
|
|
261
|
+
scanned.opaque.push(error.file);
|
|
262
|
+
}
|
|
253
263
|
return {
|
|
254
264
|
bundle,
|
|
255
265
|
recorded: bundle,
|
|
256
|
-
refusedTraces: refused
|
|
266
|
+
refusedTraces: refused
|
|
267
|
+
.filter((error) => !unread.some((past) => past === error))
|
|
268
|
+
.map((error) => error.message),
|
|
257
269
|
refusedStreams: new Set(refused.map((error) => error.streamId)),
|
|
270
|
+
unreadTraces: unread.map((error) => error.message),
|
|
271
|
+
pastReadLimit: new Set(unread.map((error) => error.file)),
|
|
258
272
|
...scanned,
|
|
259
273
|
pages: pageFindings(bundle),
|
|
260
274
|
};
|
|
261
275
|
}
|
|
276
|
+
/** Whether the run file at `file` holds more than RUN_ARTIFACT_MAX_BYTES, read only when it does not. */
|
|
277
|
+
async function pastReadLimit(runPaths, file) {
|
|
278
|
+
const read = await readContainedRegularFile(runPaths, file, RUN_ARTIFACT_MAX_BYTES);
|
|
279
|
+
return read.status === "refused" && read.reason === "too-large";
|
|
280
|
+
}
|
|
281
|
+
/** The continuous recordings the bundle registers, which CONTINUOUS_MEDIA grades. */
|
|
282
|
+
const recordingPaths = (bundle) => new Set(bundle?.streams.flatMap((stream) => (stream.recording ? [stream.recording.path] : [])) ?? []);
|
|
262
283
|
/** The trace scan's results, beside the walk's: each file named once, then each page finding. */
|
|
263
284
|
function addTraceScan(traces, findings, unscanned) {
|
|
264
285
|
for (const file of traces.sensitive)
|
|
@@ -168,7 +168,9 @@ export interface ActorTraceItem {
|
|
|
168
168
|
screenshotRef?: { path: string; redaction: "blurred" | "ocr_scrubbed" | "none" };
|
|
169
169
|
text?: string; // redacted
|
|
170
170
|
page?: { origin: string; path: string }; // computer-use screenshot items; redacted, no query
|
|
171
|
-
// computer-use: the wait ui_action during which a watch of a turn that only waited ended
|
|
171
|
+
// computer-use: the wait ui_action during which a watch of a turn that only waited ended, or a
|
|
172
|
+
// "watched the screen" notice for a watch no wait carries (a held participant's turn of only
|
|
173
|
+
// screenshots, or the watch after its repeated action past the no-progress limit)
|
|
172
174
|
watch?: {
|
|
173
175
|
limitMs: number;
|
|
174
176
|
askedMs: number; // what the turn's waits asked for; above limitMs past the watch's growth cap
|
|
@@ -100,10 +100,13 @@ action**, not a fatal error:
|
|
|
100
100
|
`budget_reached` after material progress), a `CuaAbortError` as `harness_error`,
|
|
101
101
|
and any genuine non-`CommandExitError` adapter fault as `actor_error`.
|
|
102
102
|
- **No infinite loop.** A skipped action changes nothing on screen, so it is not
|
|
103
|
-
progress. A run that keeps failing
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
progress. A run that keeps repeating a failing action makes no progress and
|
|
104
|
+
ends through the no-progress backstop (`gave_up`) at 20 such turns in a row.
|
|
105
|
+
A participant held to its scheduled end keeps its session through repeats on
|
|
106
|
+
an unchanged screen, which is how it waits on a page that changes only on
|
|
107
|
+
reload, but 20 repeats in a row that never reached the desktop end it the
|
|
108
|
+
same way. The resilience only converts a _single flaky command_ from fatal
|
|
109
|
+
to survivable.
|
|
107
110
|
|
|
108
111
|
Public-safety: the notice text carries only the substrate's own stderr (tailed +
|
|
109
112
|
whitespace-collapsed) and a numeric exit code, and is still run through the loop's
|
|
@@ -20,7 +20,7 @@ writer (`src/run/run.ts`, through `splitStreamTraces` in `src/run/stream-trace.t
|
|
|
20
20
|
each stream with an `actor`:
|
|
21
21
|
|
|
22
22
|
- the trace file: the actor trace (`humanish.actor-trace.v1`) with every item, plus `artifacts`,
|
|
23
|
-
the stream's screenshot entries. Its path is the stream's actor trace artifact (`actor.json` for
|
|
23
|
+
the stream's screenshot entries, as one line of compact JSON. Its path is the stream's actor trace artifact (`actor.json` for
|
|
24
24
|
one participant, `actors/<stream>.json` for more, `actor-<surface>.json` on the scripted route),
|
|
25
25
|
or `actors/<n>.json`, `n` its position in `streams[]` (from 1), for a stream with none or whose
|
|
26
26
|
trace artifact an earlier stream claimed. Names of that form are never reused as a stream's own
|
|
@@ -65,7 +65,12 @@ names, is refused before any file is read. The writer claims names the same way.
|
|
|
65
65
|
must have an index: an actor with no index, with
|
|
66
66
|
or without inline `items`, is refused, and so is one with both an index and inline `items`.
|
|
67
67
|
verify reports a refused trace in the `local evidence artifacts exist` check, so the run is
|
|
68
|
-
`blocked`.
|
|
68
|
+
`blocked`. A trace or live file whose index records more than the limit is reported that way too,
|
|
69
|
+
unless the file on disk is a single-link regular file verify can open that holds more than 32 MiB.
|
|
70
|
+
verify lists such a file under `UNSCANNED_ARTIFACT`, so the run is `local_only`. It adds the
|
|
71
|
+
refusal to its warnings and checks that participant without its items. Where the run cites the
|
|
72
|
+
file as evidence, verify counts it as present if it is still past 32 MiB when that check reads it;
|
|
73
|
+
a citation as a screenshot or recording is still checked as one, so the run is `blocked`. verify also scans the bytes of each trace it loaded with the public-safety text scan,
|
|
69
74
|
apart from its walk of the run folder, so no path exemption the walk makes applies to a trace.
|
|
70
75
|
run.json's digest, which an analysis record binds, covers each index and so each trace.
|
|
71
76
|
|
|
@@ -371,6 +376,12 @@ An Observer that does not render is an `evidence` failure known only after
|
|
|
371
376
|
show the run without it. Messages pass the same redaction and sandbox id scrub
|
|
372
377
|
as the rest of the bundle.
|
|
373
378
|
|
|
379
|
+
After that write, `Run.finish` replaces each raw sandbox id `sandbox-receipts.ndjson` names in the
|
|
380
|
+
run's other text files with its label (`scrubRunSandboxIds` in `src/run/sandbox-ids.ts`). A file
|
|
381
|
+
the sweep cannot read, such as a trace past the 32 MiB read limit, may still name a sandbox. The run
|
|
382
|
+
still finishes: `Run.finish` writes `run.json` again with a `sandbox-ids.unresolved` warn event that
|
|
383
|
+
names each such file and why.
|
|
384
|
+
|
|
374
385
|
When a signal stops a live run, the handler writes `interrupted` into the last
|
|
375
386
|
flushed `run.json`, and every later write of that run keeps it. A run stopped
|
|
376
387
|
before its first flush has no `run.json`; its `status.json` is the record.
|
|
@@ -194,8 +194,10 @@ item's text. A run.json past 16 MiB is refused as `ANALYSIS_SOURCE_TOO_LARGE`, a
|
|
|
194
194
|
a run.json or trace without the fields analysis reads as `ANALYSIS_SOURCE_INVALID`;
|
|
195
195
|
each message names the file, says why, and says no request was sent. Analysis
|
|
196
196
|
verifies the run before it reads a trace, so a trace that does not match its
|
|
197
|
-
index in run.json
|
|
198
|
-
`
|
|
197
|
+
index in run.json fails verify and `humanish analyze` reports
|
|
198
|
+
`ANALYSIS_VERIFY_FAILED`. A trace past 32 MiB leaves the run `local_only`, and
|
|
199
|
+
analysis refuses it as `ANALYSIS_SOURCE_TOO_LARGE`, naming the participant and
|
|
200
|
+
the file. Count and text budgets are
|
|
199
201
|
distributed across included participants, with unused capacity from short
|
|
200
202
|
sessions available to longer ones. Reclaimed capture slots go first to eligible
|
|
201
203
|
participants with fewer admitted images, including those deferred by their
|