humanish 0.111.2 → 0.111.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +1 -1
- package/CHANGELOG.md +17 -0
- package/dist/cli/commands/study-route-computer-use.js +3 -8
- package/dist/cli/commands/study-route-preview.js +4 -6
- package/dist/cli/commands/study-route-shared-world.js +6 -7
- package/dist/evidence/redaction.d.ts +2 -2
- package/dist/evidence/redaction.js +3 -3
- package/dist/routes/computer-use/bundle-parts.d.ts +1 -32
- package/dist/routes/computer-use/bundle-parts.js +5 -92
- package/dist/routes/computer-use/e2b-desktop/prepare.js +11 -4
- package/dist/routes/computer-use/fanout-bundle.js +3 -2
- package/dist/routes/computer-use/live-phase.js +3 -7
- package/dist/routes/computer-use/participant-deps.d.ts +46 -0
- package/dist/routes/computer-use/participant-deps.js +48 -0
- package/dist/routes/computer-use/participant-facts.d.ts +5 -4
- package/dist/routes/computer-use/participant-facts.js +37 -2
- package/dist/routes/computer-use/participant-feedback.d.ts +33 -0
- package/dist/routes/computer-use/participant-feedback.js +92 -0
- package/dist/routes/computer-use/participant-model.d.ts +11 -0
- package/dist/routes/computer-use/participant-model.js +15 -0
- package/dist/routes/computer-use/preflight.js +33 -65
- package/dist/routes/computer-use/result.js +13 -52
- package/dist/routes/computer-use/route.js +6 -7
- package/dist/routes/computer-use/setup.d.ts +2 -6
- package/dist/routes/computer-use/setup.js +47 -93
- package/dist/routes/computer-use/single-bundle.js +3 -2
- package/dist/routes/computer-use/types.d.ts +2 -2
- package/dist/routes/preview.js +2 -8
- package/dist/routes/scripted/bundle.d.ts +2 -6
- package/dist/routes/scripted/result.d.ts +2 -1
- package/dist/routes/scripted/result.js +4 -4
- package/dist/routes/scripted/route.js +13 -38
- package/dist/routes/scripted/setup.d.ts +2 -1
- package/dist/routes/scripted/setup.js +21 -28
- package/dist/routes/scripted/subject.d.ts +3 -18
- package/dist/routes/scripted/subject.js +22 -78
- package/dist/routes/scripted/types.d.ts +2 -2
- package/dist/routes/shared-world/bundle.js +36 -1
- package/dist/routes/shared-world/comms.d.ts +2 -1
- package/dist/routes/shared-world/comms.js +3 -7
- package/dist/routes/shared-world/handoff.d.ts +2 -2
- package/dist/routes/shared-world/handoff.js +1 -1
- package/dist/routes/shared-world/participant-specs.d.ts +4 -5
- package/dist/routes/shared-world/participant-specs.js +19 -25
- package/dist/routes/shared-world/plan.js +11 -7
- package/dist/routes/shared-world/provisioned.d.ts +2 -1
- package/dist/routes/shared-world/provisioned.js +37 -80
- package/dist/routes/shared-world/result.js +17 -35
- package/dist/routes/shared-world/route.js +33 -36
- package/dist/routes/shared-world/setup.d.ts +2 -3
- package/dist/routes/shared-world/setup.js +11 -29
- package/dist/routes/shared-world/types.d.ts +6 -15
- package/dist/routes/terminal/dry-run.js +14 -24
- package/dist/routes/terminal/live-finish.d.ts +0 -1
- package/dist/routes/terminal/live-finish.js +2 -2
- package/dist/routes/terminal/route.js +9 -15
- package/dist/routes/terminal/session.d.ts +2 -3
- package/dist/routes/terminal/session.js +28 -40
- package/dist/routes/terminal/trace.d.ts +1 -1
- package/dist/routes/terminal/types.d.ts +2 -2
- package/dist/run/run.d.ts +55 -1
- package/dist/run/run.js +38 -2
- package/dist/run/secrets.d.ts +22 -0
- package/dist/run/secrets.js +33 -0
- package/dist/run/study-result.d.ts +34 -0
- package/dist/run/study-result.js +16 -0
- package/dist/study/requirements.d.ts +60 -5
- package/dist/study/requirements.js +88 -2
- package/dist/study/run-study-events.d.ts +4 -3
- package/dist/study/run-study-events.js +9 -14
- package/dist/study/run-study-options.d.ts +4 -1
- package/dist/study/run-study-options.js +22 -32
- package/dist/substrates/e2b/subject-sandbox.d.ts +58 -0
- package/dist/substrates/e2b/subject-sandbox.js +94 -0
- package/docs/contracts/core.md +5 -4
- package/docs/contracts/feedback.md +12 -5
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ScriptedPlan } from "../../study/plan-types.js";
|
|
2
2
|
import { type PreparedRunArtifactPaths } from "../../run/paths.js";
|
|
3
3
|
import type { RunSubjectStateStepRecord } from "../../run/bundle.js";
|
|
4
|
-
import {
|
|
4
|
+
import { E2BSubjectSandbox } from "../../substrates/e2b/subject-sandbox.js";
|
|
5
5
|
import type { StudyDeps } from "../../study/study-deps.js";
|
|
6
6
|
import type { RunStudyHomes } from "../../study/run-study-homes.js";
|
|
7
7
|
type ScriptedCloneSubject = Extract<ScriptedPlan["subject"], {
|
|
@@ -22,33 +22,18 @@ export interface ScriptedSubjectInputs {
|
|
|
22
22
|
hasGithubToken: boolean;
|
|
23
23
|
scrubKnownValues: (text: string) => string;
|
|
24
24
|
now: () => number;
|
|
25
|
-
/** The run's warnings.
|
|
25
|
+
/** The run's warnings. The subject sandbox's release appends to it. */
|
|
26
26
|
warnings: string[];
|
|
27
27
|
}
|
|
28
28
|
/** The subject sandbox and what provisioning it recorded. */
|
|
29
29
|
export declare class ScriptedSubject {
|
|
30
|
-
|
|
31
|
-
killed: boolean;
|
|
32
|
-
/** The scrubbed release warning when the subject's release is unconfirmed. */
|
|
33
|
-
releaseWarning: string | undefined;
|
|
30
|
+
readonly sandbox: E2BSubjectSandbox;
|
|
34
31
|
commit: string | undefined;
|
|
35
32
|
hostDigest: string | undefined;
|
|
36
33
|
readonly stateStepRecords: RunSubjectStateStepRecord[];
|
|
37
34
|
private readonly inputs;
|
|
38
|
-
private allocation;
|
|
39
|
-
private createdAtMs;
|
|
40
|
-
private tornDownAtMs;
|
|
41
|
-
private resources;
|
|
42
35
|
constructor(inputs: ScriptedSubjectInputs);
|
|
43
36
|
/** Acquires, provisions and serves the subject. Returns the tokenless getHost URL to drive. */
|
|
44
37
|
provision(): Promise<string>;
|
|
45
|
-
/** Releases the subject by exact id, when one was acquired. An unconfirmed release is reported. */
|
|
46
|
-
teardown(): Promise<void>;
|
|
47
|
-
/** The subject desktop's billed span and size, when one was acquired. */
|
|
48
|
-
desktopUsage(): {
|
|
49
|
-
durationMs: number | undefined;
|
|
50
|
-
observation: DesktopResourceObservation | undefined;
|
|
51
|
-
killed: boolean;
|
|
52
|
-
} | undefined;
|
|
53
38
|
}
|
|
54
39
|
export {};
|
|
@@ -5,11 +5,9 @@ import { commandDigestOf } from "../../subject/state.js";
|
|
|
5
5
|
import { validatePreparedRunArtifactPaths, } from "../../run/paths.js";
|
|
6
6
|
import { provisionCloneSubject } from "../../subject/clone.js";
|
|
7
7
|
import { loadE2BDesktopModule } from "../../substrates/e2b/sdk.js";
|
|
8
|
-
import {
|
|
9
|
-
import { acquireE2BDesktopSandbox, readE2BRelease } from "../../substrates/e2b/sandbox.js";
|
|
8
|
+
import { E2BSubjectSandbox } from "../../substrates/e2b/subject-sandbox.js";
|
|
10
9
|
import { e2bShell } from "../../substrates/e2b/shell.js";
|
|
11
|
-
import { e2bRequestTimeoutMs
|
|
12
|
-
import { DEFAULT_STATE_STEP_TIMEOUT_MS } from "../../subject/state.js";
|
|
10
|
+
import { e2bRequestTimeoutMs } from "../../substrates/e2b/lifetime.js";
|
|
13
11
|
function servePort(serveUrl) {
|
|
14
12
|
const url = new URL(serveUrl);
|
|
15
13
|
if (url.port)
|
|
@@ -35,65 +33,44 @@ function hostOriginDigest(url) {
|
|
|
35
33
|
}
|
|
36
34
|
/** The subject sandbox and what provisioning it recorded. */
|
|
37
35
|
export class ScriptedSubject {
|
|
38
|
-
|
|
39
|
-
killed = false;
|
|
40
|
-
/** The scrubbed release warning when the subject's release is unconfirmed. */
|
|
41
|
-
releaseWarning;
|
|
36
|
+
sandbox;
|
|
42
37
|
commit;
|
|
43
38
|
hostDigest;
|
|
44
39
|
stateStepRecords = [];
|
|
45
40
|
inputs;
|
|
46
|
-
allocation;
|
|
47
|
-
createdAtMs;
|
|
48
|
-
tornDownAtMs;
|
|
49
|
-
resources;
|
|
50
41
|
constructor(inputs) {
|
|
51
42
|
this.inputs = inputs;
|
|
43
|
+
this.sandbox = new E2BSubjectSandbox({
|
|
44
|
+
warnings: inputs.warnings,
|
|
45
|
+
scrub: inputs.scrubKnownValues,
|
|
46
|
+
now: inputs.now,
|
|
47
|
+
});
|
|
52
48
|
}
|
|
53
49
|
/** Acquires, provisions and serves the subject. Returns the tokenless getHost URL to drive. */
|
|
54
50
|
async provision() {
|
|
55
51
|
const { plan, clone, deps, prepareDesktop, env, e2bApiKey, runPaths, timeoutMs } = this.inputs;
|
|
56
|
-
const { subjectEnvNames, hasGithubToken, scrubKnownValues
|
|
52
|
+
const { subjectEnvNames, hasGithubToken, scrubKnownValues } = this.inputs;
|
|
57
53
|
const requestTimeoutMs = e2bRequestTimeoutMs(env);
|
|
58
54
|
const timers = deps.detachedTimers ?? {};
|
|
59
|
-
const subjectSandboxTimeoutMs = timeoutMs +
|
|
60
|
-
SUBJECT_PROVISION_BUDGET_MS +
|
|
61
|
-
(clone.state?.seed ?? []).reduce((sum, step) => sum + (step.timeoutMs ?? DEFAULT_STATE_STEP_TIMEOUT_MS), 0) +
|
|
62
|
-
SANDBOX_TIMEOUT_BUFFER_MS;
|
|
63
55
|
const subjectModule = await (deps.desktopModule ?? loadE2BDesktopModule)();
|
|
64
56
|
await validatePreparedRunArtifactPaths(runPaths);
|
|
65
|
-
|
|
66
|
-
// it by exact id when this process dies mid-run; the finally block below only runs while
|
|
67
|
-
// the process is alive.
|
|
68
|
-
const subject = await acquireE2BDesktopSandbox({
|
|
57
|
+
const subjectDesktop = await this.sandbox.acquire({
|
|
69
58
|
module: subjectModule,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
...(subjectEnvNames.length > 0
|
|
82
|
-
? {
|
|
83
|
-
envs: Object.fromEntries(subjectEnvNames.map((name) => [name, env[name]])),
|
|
84
|
-
}
|
|
85
|
-
: {}),
|
|
86
|
-
dpi: 96,
|
|
87
|
-
lifecycle: { onTimeout: "kill" },
|
|
59
|
+
apiKey: e2bApiKey,
|
|
60
|
+
requestTimeoutMs,
|
|
61
|
+
sessionTimeoutMs: timeoutMs,
|
|
62
|
+
seed: clone.state?.seed ?? [],
|
|
63
|
+
metadata: {
|
|
64
|
+
mode: "scripted-browser-lab",
|
|
65
|
+
tool: "humanish",
|
|
66
|
+
labId: plan.studyId,
|
|
67
|
+
kind: "subject",
|
|
68
|
+
actor: plan.actor,
|
|
88
69
|
},
|
|
70
|
+
envs: Object.fromEntries(subjectEnvNames.map((name) => [name, env[name]])),
|
|
89
71
|
template: plan.residual.execution?.desktop?.template,
|
|
90
|
-
|
|
72
|
+
root: runPaths,
|
|
91
73
|
});
|
|
92
|
-
const subjectDesktop = subject.sandbox;
|
|
93
|
-
this.allocation = subject.allocation;
|
|
94
|
-
this.sandboxId = subject.allocation.resourceId;
|
|
95
|
-
this.createdAtMs = now();
|
|
96
|
-
this.resources = await observeDesktopResources(subjectDesktop);
|
|
97
74
|
if (prepareDesktop) {
|
|
98
75
|
await prepareDesktop(subjectDesktop, { kind: "subject" });
|
|
99
76
|
await validatePreparedRunArtifactPaths(runPaths);
|
|
@@ -125,37 +102,4 @@ export class ScriptedSubject {
|
|
|
125
102
|
this.hostDigest = hostOriginDigest(hostUrl);
|
|
126
103
|
return hostUrl;
|
|
127
104
|
}
|
|
128
|
-
/** Releases the subject by exact id, when one was acquired. An unconfirmed release is reported. */
|
|
129
|
-
async teardown() {
|
|
130
|
-
const { warnings, scrubKnownValues, now } = this.inputs;
|
|
131
|
-
if (this.allocation === undefined)
|
|
132
|
-
return;
|
|
133
|
-
const released = await this.allocation.close();
|
|
134
|
-
const reading = readE2BRelease(released, {
|
|
135
|
-
label: "Subject sandbox",
|
|
136
|
-
scrub: scrubKnownValues,
|
|
137
|
-
costSpan: true,
|
|
138
|
-
});
|
|
139
|
-
this.killed = reading.released;
|
|
140
|
-
if (reading.warning)
|
|
141
|
-
warnings.push(reading.warning);
|
|
142
|
-
if (!reading.released)
|
|
143
|
-
this.releaseWarning = reading.warning ?? "Subject sandbox release is unconfirmed.";
|
|
144
|
-
// Without a kill method, or in E2B debug mode, no kill reached E2B, so there is no teardown
|
|
145
|
-
// time to record.
|
|
146
|
-
if (released.status !== "unconfirmed" || released.reason !== "release_unavailable")
|
|
147
|
-
this.tornDownAtMs = now();
|
|
148
|
-
}
|
|
149
|
-
/** The subject desktop's billed span and size, when one was acquired. */
|
|
150
|
-
desktopUsage() {
|
|
151
|
-
if (this.sandboxId === undefined)
|
|
152
|
-
return undefined;
|
|
153
|
-
return {
|
|
154
|
-
durationMs: this.createdAtMs === undefined || this.tornDownAtMs === undefined
|
|
155
|
-
? undefined
|
|
156
|
-
: Math.max(0, this.tornDownAtMs - this.createdAtMs),
|
|
157
|
-
observation: this.resources,
|
|
158
|
-
killed: this.killed,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
105
|
}
|
|
@@ -5,7 +5,7 @@ import type { ObserverResult } from "../../observer/render.js";
|
|
|
5
5
|
import type { RunSubjectProvenance } from "../../run/bundle.js";
|
|
6
6
|
import type { StudyDeps } from "../../study/study-deps.js";
|
|
7
7
|
import type { RunStudyHomes } from "../../study/run-study-homes.js";
|
|
8
|
-
import { type StudyResultIdentity } from "../../run/study-result.js";
|
|
8
|
+
import { type StudyResultErrorCode, type StudyResultIdentity } from "../../run/study-result.js";
|
|
9
9
|
/** What a scripted run takes besides its plan. The plan carries the config, dry run and study. */
|
|
10
10
|
export interface ScriptedRunInput {
|
|
11
11
|
/** Cancels post-run analysis only. */
|
|
@@ -58,7 +58,7 @@ export interface ScriptedBrowserStudyResult extends AutomaticAnalysisResult, Stu
|
|
|
58
58
|
observer?: ObserverResult;
|
|
59
59
|
warnings: string[];
|
|
60
60
|
error?: {
|
|
61
|
-
code:
|
|
61
|
+
code: StudyResultErrorCode | "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";
|
|
62
62
|
message: string;
|
|
63
63
|
};
|
|
64
64
|
}
|
|
@@ -8,6 +8,7 @@ import { SHARED_WORLD_SCHEMA, concurrencyFacts, sharedWorldParticipantKeys, } fr
|
|
|
8
8
|
import { commandDigestOf } from "../../subject/state.js";
|
|
9
9
|
import { buildRunCostSummary, desktopSpanToMinutes } from "../../run/cost-summary.js";
|
|
10
10
|
import { participantFactsOf } from "../computer-use/participant-facts.js";
|
|
11
|
+
import { participantFeedbackCandidates } from "../computer-use/participant-feedback.js";
|
|
11
12
|
import { judgeSharedWorld, participantPassed, sharedWorldShortfall, verdictText, } from "../../run/judge.js";
|
|
12
13
|
import { reviewOutcome } from "../../run/display.js";
|
|
13
14
|
import { combineCheckpointDigest } from "./checkpoints.js";
|
|
@@ -369,7 +370,7 @@ export function buildConcurrentSharedWorldBundle(args) {
|
|
|
369
370
|
},
|
|
370
371
|
artifacts: bundleArtifacts(),
|
|
371
372
|
review,
|
|
372
|
-
feedbackCandidates:
|
|
373
|
+
feedbackCandidates: sharedWorldFeedbackCandidates(args),
|
|
373
374
|
// Custom desktop image provenance (subject + every actor sandbox launched on it); omitted on the default.
|
|
374
375
|
...(plan.residual.execution?.desktop?.template === undefined
|
|
375
376
|
? {}
|
|
@@ -380,6 +381,40 @@ export function buildConcurrentSharedWorldBundle(args) {
|
|
|
380
381
|
...(cost === undefined ? {} : { cost }),
|
|
381
382
|
};
|
|
382
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* What the participants reported, built by the same builder as a computer-use fan-out: one
|
|
386
|
+
* candidate per participant who reported friction or abandoned the goal, named by its participant
|
|
387
|
+
* id, with that participant's own instructions as `expected`. Dry-run and in-progress bundles
|
|
388
|
+
* carry none: there is no participant yet to quote. The comms thread belongs to the one shared
|
|
389
|
+
* app, so each candidate cites it.
|
|
390
|
+
*/
|
|
391
|
+
function sharedWorldFeedbackCandidates(args) {
|
|
392
|
+
if (args.dryRun || args.inProgress === true)
|
|
393
|
+
return [];
|
|
394
|
+
return participantFeedbackCandidates({
|
|
395
|
+
runId: args.run.runId,
|
|
396
|
+
scenarioId: `concurrent-shared-world-${args.plan.studyId}`,
|
|
397
|
+
adapterId: args.plan.studyId,
|
|
398
|
+
// planSharedWorldStudy refuses any target other than e2b-desktop on both planes.
|
|
399
|
+
substrate: "e2b-desktop",
|
|
400
|
+
participants: args.actorSpecs.map((spec, index) => {
|
|
401
|
+
const outcome = args.actorResults[index]?.outcome;
|
|
402
|
+
return {
|
|
403
|
+
participantId: spec.planned.id,
|
|
404
|
+
streamId: spec.streamId,
|
|
405
|
+
personaId: spec.persona.id,
|
|
406
|
+
goal: redactText(spec.evidenceInstructions ?? spec.instructions),
|
|
407
|
+
...(outcome?.session === undefined
|
|
408
|
+
? {}
|
|
409
|
+
: { session: outcome.session, traceArtifactPath: spec.traceArtifactPath }),
|
|
410
|
+
screenshots: outcome?.screenshots ?? [],
|
|
411
|
+
...(args.commsArtifactPath === undefined
|
|
412
|
+
? {}
|
|
413
|
+
: { commsArtifactPath: args.commsArtifactPath }),
|
|
414
|
+
};
|
|
415
|
+
}),
|
|
416
|
+
});
|
|
417
|
+
}
|
|
383
418
|
/**
|
|
384
419
|
* Priced like `per-lane-worlds`: each participant's model tokens and desktop, plus the provisioned plane's
|
|
385
420
|
* desktop. Absent when nothing ran, so dry runs and in-progress snapshots stay byte-stable.
|
|
@@ -3,6 +3,7 @@ import type { SharedWorldPlan } from "../../study/plan-types.js";
|
|
|
3
3
|
import type { CommsReceivingRun } from "../../comms/receiving.js";
|
|
4
4
|
import { type DeployedCommsCatch } from "../../comms/sandbox-catch.js";
|
|
5
5
|
import type { StudyCommsEmail, StudyConfig } from "../../study/types.js";
|
|
6
|
+
import type { RunSecrets } from "../../run/secrets.js";
|
|
6
7
|
import type { Shell } from "../../substrates/shell.js";
|
|
7
8
|
import type { ConcurrentSharedWorldPlaneClass, ExternalCommsWiring, PlaneContext } from "./types.js";
|
|
8
9
|
/** The in-sandbox email catch a provisioned plane deploys, when the study declares one. */
|
|
@@ -58,7 +59,7 @@ export declare function prepareEmailReceiving(args: {
|
|
|
58
59
|
env: Record<string, string | undefined>;
|
|
59
60
|
participants: string[];
|
|
60
61
|
runPaths: PlaneContext["runPaths"];
|
|
61
|
-
|
|
62
|
+
secrets: RunSecrets;
|
|
62
63
|
dryRun: boolean;
|
|
63
64
|
}): Promise<{
|
|
64
65
|
ok: true;
|
|
@@ -116,7 +116,7 @@ export async function drainExternalComms(ctx, comms) {
|
|
|
116
116
|
email: comms.email,
|
|
117
117
|
env: ctx.env,
|
|
118
118
|
runPaths: ctx.runPaths,
|
|
119
|
-
knownSecretValues: ctx.
|
|
119
|
+
knownSecretValues: ctx.run.secrets.values(),
|
|
120
120
|
});
|
|
121
121
|
ctx.warnings.push(...warnings);
|
|
122
122
|
return path;
|
|
@@ -136,7 +136,7 @@ export function receivingSourceOf(residual, env) {
|
|
|
136
136
|
* when setup fails.
|
|
137
137
|
*/
|
|
138
138
|
export async function prepareEmailReceiving(args) {
|
|
139
|
-
const { source,
|
|
139
|
+
const { source, secrets } = args;
|
|
140
140
|
if (args.dryRun || source.comms?.email?.kind !== "real")
|
|
141
141
|
return { ok: true, receiving: undefined };
|
|
142
142
|
try {
|
|
@@ -147,11 +147,7 @@ export async function prepareEmailReceiving(args) {
|
|
|
147
147
|
env: args.env,
|
|
148
148
|
participants: args.participants,
|
|
149
149
|
runPaths: args.runPaths,
|
|
150
|
-
registerSecrets: (values) =>
|
|
151
|
-
for (const value of values)
|
|
152
|
-
if (value.length >= 4 && !knownSecretValues.includes(value))
|
|
153
|
-
knownSecretValues.push(value);
|
|
154
|
-
},
|
|
150
|
+
registerSecrets: (values) => secrets.add(values),
|
|
155
151
|
});
|
|
156
152
|
return { ok: true, receiving };
|
|
157
153
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { DesktopParticipantRun, ParticipantRunOutcome } from "../computer-use/types.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { DesktopParticipantDeps } from "../computer-use/participant-deps.js";
|
|
3
3
|
import type { ActorRunResult, ExternalCommsWiring } from "./types.js";
|
|
4
4
|
/** What the host and follower participants need besides the handoff. */
|
|
5
5
|
export interface HandoffParticipantDeps {
|
|
6
|
-
runDeps:
|
|
6
|
+
runDeps: DesktopParticipantDeps;
|
|
7
7
|
publicAppUrl: string;
|
|
8
8
|
inbox: ExternalCommsWiring | undefined;
|
|
9
9
|
now: () => number;
|
|
@@ -14,7 +14,7 @@ import { withInboxMission } from "../computer-use/participant-prompt.js";
|
|
|
14
14
|
import { runCuaParticipant } from "../computer-use/participant-execution.js";
|
|
15
15
|
import { extractLobbyCode, extractLobbyCodeFromNarration } from "./lobby-code.js";
|
|
16
16
|
import { hostOriginDigest } from "./provenance.js";
|
|
17
|
-
import { makeBlockedFollowerOutcome, withLobbyCodeMission
|
|
17
|
+
import { makeBlockedFollowerOutcome, withLobbyCodeMission } from "./participant-specs.js";
|
|
18
18
|
// The floor for the host-first handoff barrier deadline (ms). The host must surface a
|
|
19
19
|
// shared-session (/lobby/CODE) URL within the deadline or the run fails closed and no follower
|
|
20
20
|
// opens. The effective deadline scales with the per-participant run budget (execution.timeoutMs): a
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { SharedWorldPlan } from "../../study/plan-types.js";
|
|
2
2
|
import type { Participant, SharedWorldParticipant } from "../../study/plan-participants.js";
|
|
3
3
|
import type { RunBundle } from "../../run/bundle.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { DesktopParticipantRun, ParticipantRunOutcome } from "../computer-use/types.js";
|
|
5
|
+
import { type DesktopParticipantDeps } from "../computer-use/participant-deps.js";
|
|
5
6
|
import type { LiveParticipants, PlaneContext } from "./types.js";
|
|
6
7
|
export declare function defaultSessionTimeoutMs(plan: SharedWorldPlan): number;
|
|
7
8
|
/** Resolve an actor's participant URL against the harness-minted getHost base (entry is a
|
|
@@ -16,8 +17,6 @@ export declare function withLobbyCodeMission(spec: DesktopParticipantRun, code:
|
|
|
16
17
|
/** A follower blocked by an expired deadline or an ended host. It never opened a browser;
|
|
17
18
|
* keep the actual reason rather than turning every upstream failure into a timeout. */
|
|
18
19
|
export declare function makeBlockedFollowerOutcome(spec: DesktopParticipantRun, reason: string, timedOut: boolean): ParticipantRunOutcome;
|
|
19
|
-
/** The computer-use runner deps every participant shares, on either plane. */
|
|
20
|
-
export type ParticipantRunDeps = Omit<CuaParticipantDeps, "signalProvisioned" | "appUrl" | "onObservedUrl">;
|
|
21
20
|
/**
|
|
22
21
|
* A live run publishes an in-progress bundle before its participants start, whether or not an
|
|
23
22
|
* Observer is attached, and the participants' live traces rewrite it as they go, as on the
|
|
@@ -30,9 +29,9 @@ export declare function startParticipantFlush(ctx: PlaneContext, live: LiveParti
|
|
|
30
29
|
* out of every actor sandbox: the subject env names' values, the GitHub token, and clone
|
|
31
30
|
* or local-tree provisioning. The committed subject.envValues still reach it, non-secret by parse.
|
|
32
31
|
* `scrubKnownValues` is the plane's scrub: the external-public plane also scrubs the latched lobby
|
|
33
|
-
* code.
|
|
32
|
+
* code. A plane calls this once per run, since the deps carry the run's one budget.
|
|
34
33
|
*/
|
|
35
|
-
export declare function participantRunDeps(ctx: PlaneContext, live: LiveParticipants, scrubKnownValues: (text: string) => string):
|
|
34
|
+
export declare function participantRunDeps(ctx: PlaneContext, live: LiveParticipants, scrubKnownValues: (text: string) => string): DesktopParticipantDeps;
|
|
36
35
|
/**
|
|
37
36
|
* Each participant's actor spec, with committed personas compiled in so each prompt carries real
|
|
38
37
|
* behavioral directives. Evidence copies of the assignment, instructions and persona are
|
|
@@ -7,6 +7,7 @@ import { resolveParticipant } from "../../run/participant.js";
|
|
|
7
7
|
import { attachObserverRuntimeStreamUrls } from "../../observer/render.js";
|
|
8
8
|
import { composeParticipantInstructions } from "../computer-use/participant-prompt.js";
|
|
9
9
|
import { startLiveTraceFlush } from "../computer-use/live-flush.js";
|
|
10
|
+
import { desktopParticipantDeps, } from "../computer-use/participant-deps.js";
|
|
10
11
|
import { resolveCommittedPersonasForCwd } from "../../study/persona-resolve.js";
|
|
11
12
|
import { participantAssignment } from "../../study/participant-assignment.js";
|
|
12
13
|
import { redactText } from "../../evidence/redaction.js";
|
|
@@ -143,26 +144,25 @@ function participantStreams(onStream, live) {
|
|
|
143
144
|
* out of every actor sandbox: the subject env names' values, the GitHub token, and clone
|
|
144
145
|
* or local-tree provisioning. The committed subject.envValues still reach it, non-secret by parse.
|
|
145
146
|
* `scrubKnownValues` is the plane's scrub: the external-public plane also scrubs the latched lobby
|
|
146
|
-
* code.
|
|
147
|
+
* code. A plane calls this once per run, since the deps carry the run's one budget.
|
|
147
148
|
*/
|
|
148
149
|
export function participantRunDeps(ctx, live, scrubKnownValues) {
|
|
149
|
-
const { config,
|
|
150
|
-
return {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
const { config, plan } = ctx;
|
|
151
|
+
return desktopParticipantDeps({
|
|
152
|
+
plan,
|
|
153
|
+
// A local-agent brain runs each participant on the operator's signed-in agent, as on
|
|
154
|
+
// computer use.
|
|
155
|
+
brain: plan.brain,
|
|
155
156
|
subject: {
|
|
156
157
|
kind: "shared-app",
|
|
157
158
|
...(config.subject.serve?.url === undefined ? {} : { serveUrl: config.subject.serve.url }),
|
|
158
159
|
},
|
|
159
|
-
env,
|
|
160
|
+
env: ctx.env,
|
|
160
161
|
openaiApiKey: ctx.openaiApiKey,
|
|
161
162
|
e2bApiKey: ctx.e2bApiKey,
|
|
162
|
-
requestTimeoutMs: ctx.requestTimeoutMs,
|
|
163
|
-
sandboxMs: ctx.timeoutMs + SANDBOX_TIMEOUT_BUFFER_MS,
|
|
164
163
|
timeoutMs: ctx.timeoutMs,
|
|
165
|
-
|
|
164
|
+
sandboxMs: ctx.timeoutMs + SANDBOX_TIMEOUT_BUFFER_MS,
|
|
165
|
+
participantCount: plan.plane.participants.length,
|
|
166
166
|
artifactRoot: ctx.runPaths,
|
|
167
167
|
studyCwd: ctx.cwd,
|
|
168
168
|
redactScreenshots: ctx.redactScreenshots,
|
|
@@ -172,27 +172,21 @@ export function participantRunDeps(ctx, live, scrubKnownValues) {
|
|
|
172
172
|
ctx.run.participantStarted();
|
|
173
173
|
return ctx.runSession(options);
|
|
174
174
|
},
|
|
175
|
-
|
|
176
|
-
// use.
|
|
177
|
-
brain: ctx.plan.brain,
|
|
178
|
-
...(receiving ? { receiving } : {}),
|
|
179
|
-
now: ctx.now,
|
|
180
|
-
// The participant's desktop seams, and the caller's prepareDesktop with the participant as its
|
|
181
|
-
// target.
|
|
182
|
-
...(ctx.deps.desktopModule === undefined ? {} : { desktopModule: ctx.deps.desktopModule }),
|
|
183
|
-
...(ctx.deps.detachedTimers === undefined ? {} : { detachedTimers: ctx.deps.detachedTimers }),
|
|
184
|
-
...(ctx.input.prepareDesktop === undefined ? {} : { prepareDesktop: ctx.input.prepareDesktop }),
|
|
175
|
+
liveFlush: () => live.flush?.flush,
|
|
185
176
|
onStream: participantStreams(ctx.input.onStream, live),
|
|
186
|
-
// A participant visits the shared app and provisions no subject, so it reports no phase of
|
|
187
|
-
// own.
|
|
177
|
+
// A participant visits the shared app and provisions no subject, so it reports no phase of
|
|
178
|
+
// its own.
|
|
188
179
|
reportSubjectPhase: defaultSubjectPhaseSink,
|
|
189
|
-
|
|
180
|
+
seams: ctx.deps,
|
|
181
|
+
prepareDesktop: ctx.input.prepareDesktop,
|
|
182
|
+
}, {
|
|
183
|
+
receiving: ctx.receiving,
|
|
190
184
|
// Concurrent participants are independent evidence: a requested-vs-verified screen
|
|
191
185
|
// mismatch is recorded as separate facts + a warning instead of failing the participant's
|
|
192
186
|
// device claim closed, so one participant's window-manager drift cannot abort the whole
|
|
193
187
|
// live multi-actor world (the single-participant/fan-out routes keep fail-closed).
|
|
194
188
|
screenMismatchPolicy: "record-evidence",
|
|
195
|
-
};
|
|
189
|
+
});
|
|
196
190
|
}
|
|
197
191
|
/**
|
|
198
192
|
* Each participant's actor spec, with committed personas compiled in so each prompt carries real
|
|
@@ -11,7 +11,7 @@ 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
13
|
import { concurrentSharedWorldValidationReason, desktopMediaValidationReason, externalPublicSharedWorldValidationReason, outputTokenLimitValidationReason, receivingEmailValidationReason, taskProtocolValidationReason, } from "../../study/validation.js";
|
|
14
|
-
import {
|
|
14
|
+
import { unpricedCapCheck } from "../../study/requirements.js";
|
|
15
15
|
/** The registered descriptor for a planned actor; planSharedWorldStudy checked the registry. */
|
|
16
16
|
export function sharedWorldDescriptorOf(actorType) {
|
|
17
17
|
const descriptor = actorRegistry[actorType];
|
|
@@ -60,12 +60,16 @@ export function planSharedWorldStudy(config, input) {
|
|
|
60
60
|
return refuse(invalid, invalidReason, actor);
|
|
61
61
|
if (config.actor?.maxOutputTokens !== undefined && input.hasRunSession === true)
|
|
62
62
|
return refuse(invalid, "maxOutputTokens cannot be enforced by a custom runSession.", actor);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
// A live cap is priced here, at plan time, so its refusal wins over the route's key checks.
|
|
64
|
+
const unpriced = input.dryRun
|
|
65
|
+
? undefined
|
|
66
|
+
: unpricedCapCheck({
|
|
67
|
+
caps: config.caps ?? {},
|
|
68
|
+
model: (config.actor?.model ?? DEFAULT_OPENAI_CU_MODEL).trim().toLowerCase(),
|
|
69
|
+
code: "HUMANISH_SHARED_WORLD_UNPRICED_CAP",
|
|
70
|
+
});
|
|
71
|
+
if (unpriced)
|
|
72
|
+
return refuse(unpriced.code, unpriced.message, actor);
|
|
69
73
|
const receivingReason = receivingEmailValidationReason(config);
|
|
70
74
|
if (receivingReason)
|
|
71
75
|
return refuse(invalid, receivingReason, actor);
|
|
@@ -3,8 +3,9 @@ import type { RunSubjectStateStepRecord } from "../../run/bundle.js";
|
|
|
3
3
|
import type { SharedWorldStateSnapshot } from "../../run/shared-world-evidence.js";
|
|
4
4
|
import type { LocalTreeArchive } from "../../subject/local-tree-archive.js";
|
|
5
5
|
import type { DetachedTimers } from "../../substrates/detached.js";
|
|
6
|
+
import { type SubjectDesktopUsage } from "../../substrates/e2b/subject-sandbox.js";
|
|
6
7
|
import type { StudyDeps } from "../../study/study-deps.js";
|
|
7
|
-
import { type ActorRunResult, type
|
|
8
|
+
import { type ActorRunResult, type LiveParticipants, type PlaneContext } from "./types.js";
|
|
8
9
|
/** What the provisioned plane needs besides the shared plane context. */
|
|
9
10
|
export interface ProvisionedPlaneSetup {
|
|
10
11
|
serve: StudySubjectServe;
|