humanish 0.115.0 → 0.117.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 +41 -0
- package/README.md +7 -6
- package/TELEMETRY.md +6 -0
- package/dist/actors/codex/app-server-ui.js +11 -4
- package/dist/actors/codex/restricted-participant-policy.d.ts +0 -2
- package/dist/actors/codex/restricted-participant-policy.js +4 -32
- package/dist/actors/codex/restricted-participant.d.ts +5 -0
- package/dist/actors/codex/restricted-participant.js +3 -2
- package/dist/actors/computer-use/actor.d.ts +4 -0
- package/dist/actors/computer-use/actor.js +2 -0
- package/dist/actors/computer-use/loop/actions.d.ts +4 -4
- package/dist/actors/computer-use/loop/actions.js +25 -18
- package/dist/actors/computer-use/loop/debrief.js +61 -27
- package/dist/actors/computer-use/loop/session.d.ts +3 -0
- package/dist/actors/computer-use/loop/session.js +9 -1
- package/dist/actors/computer-use/loop/trace.js +1 -0
- package/dist/actors/computer-use/loop/types.d.ts +16 -14
- package/dist/actors/computer-use/loop.d.ts +1 -1
- package/dist/actors/computer-use/loop.js +1 -1
- package/dist/actors/computer-use/wait.d.ts +74 -0
- package/dist/actors/computer-use/wait.js +100 -0
- package/dist/actors/contract.d.ts +14 -0
- package/dist/actors/scripted-browser/steps.js +6 -5
- package/dist/analysis/analysis-limits.d.ts +35 -0
- package/dist/analysis/analysis-limits.js +41 -0
- package/dist/analysis/validation.js +14 -21
- package/dist/browser-control/protocol.d.ts +86 -6
- package/dist/browser-control/protocol.js +13 -6
- package/dist/cli/commands/analyze.js +2 -1
- package/dist/cli/commands/notes.js +2 -1
- package/dist/cli/commands/run-signals.js +5 -1
- package/dist/cli/commands/runs.js +2 -1
- package/dist/cli/commands/setup.js +3 -3
- package/dist/cli/commands/study-format.d.ts +5 -2
- package/dist/cli/commands/study-format.js +29 -9
- package/dist/cli/commands/study-route-computer-use.js +2 -2
- package/dist/cli/commands/study-route-scripted.js +1 -1
- package/dist/cli/commands/tui.js +59 -17
- package/dist/cli/doctor.js +9 -2
- package/dist/cli/findings-text.d.ts +5 -0
- package/dist/cli/findings-text.js +169 -0
- package/dist/cli/findings.d.ts +0 -4
- package/dist/cli/findings.js +5 -167
- package/dist/cli/io.js +6 -1
- package/dist/cli/update-check.js +4 -1
- package/dist/comms/connections.js +4 -3
- package/dist/comms/setup.js +18 -12
- package/dist/evidence/redaction.js +9 -1
- package/dist/evidence/secret-scrub.d.ts +15 -5
- package/dist/evidence/secret-scrub.js +67 -9
- package/dist/feedback/draft.js +1 -1
- package/dist/feedback/export-bundle.js +18 -14
- package/dist/feedback/feedback.js +25 -7
- package/dist/guest/desktop-executor.js +3 -1
- package/dist/guest/desktop-keys.js +59 -5
- package/dist/keys/key-resolution.d.ts +9 -0
- package/dist/keys/key-resolution.js +60 -48
- package/dist/keys/key-status.d.ts +2 -0
- package/dist/keys/key-status.js +9 -16
- package/dist/observer/pinned-files.js +1 -1
- package/dist/observer/render.js +3 -2
- package/dist/observer-app.html +5 -5
- package/dist/routes/computer-use/bundle-parts.d.ts +4 -4
- package/dist/routes/computer-use/bundle-parts.js +2 -2
- package/dist/routes/computer-use/bundle.d.ts +4 -5
- package/dist/routes/computer-use/bundle.js +1 -3
- package/dist/routes/computer-use/e2b-desktop/prepare.js +40 -31
- package/dist/routes/computer-use/fanout-records.js +10 -11
- package/dist/routes/computer-use/live-phase.d.ts +1 -1
- package/dist/routes/computer-use/live-phase.js +0 -1
- package/dist/routes/computer-use/local-vm.js +5 -2
- package/dist/routes/computer-use/participant-execution.d.ts +3 -2
- package/dist/routes/computer-use/participant-model.js +9 -0
- package/dist/routes/computer-use/participant-runs.d.ts +6 -2
- package/dist/routes/computer-use/participant-runs.js +9 -5
- package/dist/routes/computer-use/plan.d.ts +3 -0
- package/dist/routes/computer-use/plan.js +18 -13
- package/dist/routes/computer-use/rerun-selection.js +2 -2
- package/dist/routes/computer-use/result.js +3 -6
- package/dist/routes/computer-use/setup.d.ts +4 -6
- package/dist/routes/computer-use/setup.js +2 -6
- package/dist/routes/computer-use/single-bundle.d.ts +4 -5
- package/dist/routes/computer-use/single-bundle.js +22 -25
- package/dist/routes/computer-use/subject-projection.d.ts +4 -8
- package/dist/routes/computer-use/subject-projection.js +9 -31
- package/dist/routes/computer-use/types.d.ts +5 -42
- package/dist/routes/scripted/scenario.js +28 -12
- package/dist/routes/scripted/session-result.js +6 -3
- package/dist/routes/shared-world/bundle-records.js +2 -8
- package/dist/routes/shared-world/participant-specs.d.ts +0 -2
- package/dist/routes/shared-world/participant-specs.js +1 -20
- package/dist/routes/shared-world/plan.d.ts +3 -0
- package/dist/routes/shared-world/plan.js +25 -4
- package/dist/routes/shared-world/setup.js +2 -2
- package/dist/routes/terminal/ledger.d.ts +9 -9
- package/dist/routes/terminal/ledger.js +63 -40
- package/dist/routes/terminal/live-finish.js +7 -10
- package/dist/routes/terminal/live-sandbox.d.ts +1 -1
- package/dist/routes/terminal/live-sandbox.js +14 -11
- package/dist/routes/terminal/plan.d.ts +3 -0
- package/dist/routes/terminal/plan.js +8 -5
- package/dist/routes/terminal/sandbox.d.ts +4 -3
- package/dist/routes/terminal/sandbox.js +5 -5
- package/dist/routes/terminal/token-usage.d.ts +1 -2
- package/dist/routes/terminal/token-usage.js +4 -6
- package/dist/routes/terminal/types.d.ts +9 -1
- package/dist/run/bundle-shape.js +6 -1
- package/dist/run/bundle.d.ts +5 -3
- package/dist/run/contained-output.d.ts +53 -5
- package/dist/run/contained-output.js +141 -42
- package/dist/run/detail.d.ts +2 -1
- package/dist/run/detail.js +12 -7
- package/dist/run/escape-sequences.d.ts +11 -0
- package/dist/run/escape-sequences.js +93 -0
- package/dist/run/evidence-files.d.ts +8 -8
- package/dist/run/evidence-files.js +19 -23
- package/dist/run/locate.d.ts +32 -3
- package/dist/run/locate.js +45 -40
- package/dist/run/notes.d.ts +0 -8
- package/dist/run/notes.js +5 -51
- package/dist/run/outcomes.d.ts +24 -0
- package/dist/run/outcomes.js +19 -0
- package/dist/run/paths.d.ts +2 -0
- package/dist/run/paths.js +5 -1
- package/dist/run/preflight-receipts.js +13 -8
- package/dist/run/project.js +7 -4
- package/dist/run/reclaim-outcomes.d.ts +8 -6
- package/dist/run/reclaim-outcomes.js +10 -6
- package/dist/run/reclaim.d.ts +5 -0
- package/dist/run/reclaim.js +43 -16
- package/dist/run/run-clock.d.ts +50 -0
- package/dist/run/run-clock.js +66 -0
- package/dist/run/run.js +5 -2
- package/dist/run/sandbox-ids.d.ts +7 -2
- package/dist/run/sandbox-ids.js +27 -11
- package/dist/run/secrets.d.ts +2 -1
- package/dist/run/secrets.js +18 -22
- package/dist/run/stored-runs.js +14 -7
- package/dist/run/transient-comms-secrets.js +6 -17
- package/dist/run-study.js +22 -13
- package/dist/study/adapter-scorer-loader.js +6 -4
- package/dist/study/config.js +4 -1
- package/dist/study/discover.js +9 -6
- package/dist/study/init-paths.js +7 -5
- package/dist/study/keys.d.ts +2 -0
- package/dist/study/keys.js +2 -0
- package/dist/study/migrate/v2.js +2 -1
- package/dist/study/parse/actors.js +8 -0
- package/dist/study/persona-resolve.d.ts +2 -2
- package/dist/study/persona-resolve.js +9 -5
- package/dist/study/plan-participants.d.ts +4 -0
- package/dist/study/plan-participants.js +4 -0
- package/dist/study/plan-types.d.ts +2 -2
- package/dist/study/plan.d.ts +5 -3
- package/dist/study/plan.js +18 -9
- package/dist/study/preflight-probes.js +11 -3
- package/dist/study/preflight.js +9 -5
- package/dist/study/summary.d.ts +8 -2
- package/dist/study/summary.js +20 -22
- package/dist/study/types.d.ts +14 -0
- package/dist/study/validation.d.ts +5 -0
- package/dist/study/validation.js +8 -0
- package/dist/study/warnings.d.ts +19 -12
- package/dist/study/warnings.js +50 -17
- package/dist/subject/serve.d.ts +3 -0
- package/dist/subject/serve.js +4 -1
- package/dist/substrates/detached.js +14 -7
- package/dist/substrates/e2b/connection.d.ts +12 -0
- package/dist/substrates/e2b/connection.js +35 -0
- package/dist/substrates/e2b/desktop-executor.d.ts +1 -5
- package/dist/substrates/e2b/desktop-executor.js +45 -12
- package/dist/substrates/e2b/lifetime.d.ts +24 -4
- package/dist/substrates/e2b/lifetime.js +41 -4
- package/dist/substrates/e2b/sandbox.d.ts +4 -3
- package/dist/substrates/e2b/sandbox.js +13 -18
- package/dist/substrates/e2b/sdk.d.ts +20 -9
- package/dist/substrates/local/capacity.d.ts +18 -2
- package/dist/substrates/local/capacity.js +21 -2
- package/dist/substrates/local/runtime-host.js +21 -14
- package/dist/substrates/local/runtime.js +3 -11
- package/dist/tui/contract.d.ts +30 -5
- package/dist/tui/contract.js +4 -1
- package/dist/tui-app.js +150 -147
- package/dist/verify/artifacts.d.ts +9 -2
- package/dist/verify/artifacts.js +37 -13
- package/dist/verify/liveness.js +19 -5
- package/dist/verify/verify.js +54 -27
- package/docs/architecture/browser-control.md +10 -4
- package/docs/architecture/guest-desktop.md +7 -2
- package/docs/architecture/state-driven-executor.md +4 -2
- package/docs/contracts/run-bundle.md +18 -10
- package/docs/contracts/schemas.md +46 -29
- package/docs/contracts/study-analysis.md +10 -1
- package/docs/product/automatic-analysis.md +7 -1
- package/package.json +2 -2
|
@@ -12,6 +12,8 @@ function desktopParticipant(config, entry, index, kind, focus) {
|
|
|
12
12
|
const dwell = entry?.dwell ?? actor?.dwell;
|
|
13
13
|
const reasoningEffort = entry?.reasoningEffort ?? actor?.reasoningEffort;
|
|
14
14
|
const maxOutputTokens = actor?.maxOutputTokens;
|
|
15
|
+
const maxWaitMs = actor?.maxWaitMs;
|
|
16
|
+
const idleWaitMs = actor?.idleWaitMs;
|
|
15
17
|
return {
|
|
16
18
|
id: participantIdAt(index, entry?.id, kind),
|
|
17
19
|
index,
|
|
@@ -31,6 +33,8 @@ function desktopParticipant(config, entry, index, kind, focus) {
|
|
|
31
33
|
...(dwell === undefined ? {} : { dwell }),
|
|
32
34
|
...(reasoningEffort === undefined ? {} : { reasoningEffort }),
|
|
33
35
|
...(maxOutputTokens === undefined ? {} : { maxOutputTokens }),
|
|
36
|
+
...(maxWaitMs === undefined ? {} : { maxWaitMs }),
|
|
37
|
+
...(idleWaitMs === undefined ? {} : { idleWaitMs }),
|
|
34
38
|
},
|
|
35
39
|
};
|
|
36
40
|
}
|
|
@@ -196,8 +196,8 @@ export interface SharedWorldPlan extends PlanBase {
|
|
|
196
196
|
readonly plane: SharedWorldPlane;
|
|
197
197
|
/** At least 2. */
|
|
198
198
|
readonly concurrency: number;
|
|
199
|
-
/**
|
|
200
|
-
readonly sessionTimeoutMs
|
|
199
|
+
/** Each participant's session budget: execution.timeoutMs, else the plane's derived default. */
|
|
200
|
+
readonly sessionTimeoutMs: number;
|
|
201
201
|
readonly brain: Extract<Brain, {
|
|
202
202
|
kind: "openai" | "local-agent";
|
|
203
203
|
}>;
|
package/dist/study/plan.d.ts
CHANGED
|
@@ -6,8 +6,10 @@ export { routeOf, type StudyRoute } from "./routing.js";
|
|
|
6
6
|
/** Resolve dry-run: explicit override wins, else the scenario mode, else the given fallback. */
|
|
7
7
|
export declare function resolveStudyDryRun(config: StudyConfig, override: boolean | undefined, fallback: boolean | undefined): boolean | undefined;
|
|
8
8
|
/**
|
|
9
|
-
* The plan a study runs under, built without reading files
|
|
10
|
-
*
|
|
11
|
-
* route's
|
|
9
|
+
* The plan a study runs under, built without reading files or the network. From the environment
|
|
10
|
+
* (options.env, else process.env, as the routes read it) it reads one value, the E2B sandbox
|
|
11
|
+
* ceiling, which no study file can know. Each route's planner makes every refusal that route
|
|
12
|
+
* makes, in the route's order and with its codes and messages; the route's exported runner calls
|
|
13
|
+
* the same planner.
|
|
12
14
|
*/
|
|
13
15
|
export declare function planStudy(config: StudyConfig, options: InternalRunStudyOptions, deps?: StudyDeps): PlanResult;
|
package/dist/study/plan.js
CHANGED
|
@@ -6,13 +6,14 @@ import { callerDrivingOf, planComputerUseStudy } from "../routes/computer-use/pl
|
|
|
6
6
|
import { injectedBrowser, planScriptedStudy } from "../routes/scripted/plan.js";
|
|
7
7
|
import { planSharedWorldStudy } from "../routes/shared-world/plan.js";
|
|
8
8
|
import { planTerminalStudy } from "../routes/terminal/plan.js";
|
|
9
|
+
import { sandboxCeiling } from "../substrates/e2b/lifetime.js";
|
|
9
10
|
import { localBrowserDefaults } from "../substrates/local/runtime-config.js";
|
|
10
11
|
import { THIS_REPO_DRY_RUN_ONLY } from "./composition-rules.js";
|
|
11
12
|
import { planBase } from "./plan-base.js";
|
|
12
13
|
import { routeOf } from "./routing.js";
|
|
13
14
|
import { automaticAnalysisRouteReason, taskProtocolValidationReason } from "./validation.js";
|
|
14
15
|
import { declaredParticipantCount } from "./study-fields.js";
|
|
15
|
-
import {
|
|
16
|
+
import { recordedStudyWarnings } from "./warnings.js";
|
|
16
17
|
export { routeOf } from "./routing.js";
|
|
17
18
|
/** Resolve dry-run: explicit override wins, else the scenario mode, else the given fallback. */
|
|
18
19
|
export function resolveStudyDryRun(config, override, fallback) {
|
|
@@ -63,20 +64,22 @@ function planPreview(config, options, input) {
|
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
/**
|
|
66
|
-
* The plan a study runs under, built without reading files
|
|
67
|
-
*
|
|
68
|
-
* route's
|
|
67
|
+
* The plan a study runs under, built without reading files or the network. From the environment
|
|
68
|
+
* (options.env, else process.env, as the routes read it) it reads one value, the E2B sandbox
|
|
69
|
+
* ceiling, which no study file can know. Each route's planner makes every refusal that route
|
|
70
|
+
* makes, in the route's order and with its codes and messages; the route's exported runner calls
|
|
71
|
+
* the same planner.
|
|
69
72
|
*/
|
|
70
73
|
export function planStudy(config, options, deps = {}) {
|
|
71
74
|
const study = localBrowserDefaults(config);
|
|
72
75
|
const input = { dryRun: resolveStudyDryRun(study, options.dryRun, true) ?? true };
|
|
73
|
-
const
|
|
76
|
+
const ceiling = sandboxCeiling(options.env ?? process.env);
|
|
77
|
+
const result = planRoute(routeOf(config), study, options, input, deps, ceiling);
|
|
74
78
|
if (!result.ok)
|
|
75
79
|
return result;
|
|
76
80
|
// The manifest the CLI resolved enters the plan here and nowhere else; the routes read
|
|
77
81
|
// plan.study for the run's status record and bundle, and plan.warnings for its bundle events.
|
|
78
|
-
const
|
|
79
|
-
const warnings = [...(warning === undefined ? [] : [warning]), ...scriptedMissionWarnings(study)];
|
|
82
|
+
const warnings = recordedStudyWarnings(study);
|
|
80
83
|
const plan = {
|
|
81
84
|
...result.plan,
|
|
82
85
|
...(options.study === undefined ? {} : { study: options.study }),
|
|
@@ -84,7 +87,7 @@ export function planStudy(config, options, deps = {}) {
|
|
|
84
87
|
};
|
|
85
88
|
return { ok: true, planned: { plan } };
|
|
86
89
|
}
|
|
87
|
-
function planRoute(route, study, options, input, deps) {
|
|
90
|
+
function planRoute(route, study, options, input, deps, sandboxCeiling) {
|
|
88
91
|
switch (route) {
|
|
89
92
|
case "preview":
|
|
90
93
|
return planPreview(study, options, input);
|
|
@@ -95,14 +98,20 @@ function planRoute(route, study, options, input, deps) {
|
|
|
95
98
|
driving: callerDrivingOf(options),
|
|
96
99
|
...(options.count === undefined ? {} : { countOverride: options.count }),
|
|
97
100
|
...(options.rerun === undefined ? {} : { rerun: options.rerun }),
|
|
101
|
+
sandboxCeiling,
|
|
98
102
|
});
|
|
99
103
|
case "shared-world":
|
|
100
104
|
return planSharedWorldStudy(study, {
|
|
101
105
|
...input,
|
|
102
106
|
hasRunSession: deps.runSession !== undefined,
|
|
107
|
+
sandboxCeiling,
|
|
103
108
|
});
|
|
104
109
|
case "terminal":
|
|
105
|
-
return planTerminalStudy(study, {
|
|
110
|
+
return planTerminalStudy(study, {
|
|
111
|
+
...input,
|
|
112
|
+
hasCostProbe: deps.costProbe !== undefined,
|
|
113
|
+
sandboxCeiling,
|
|
114
|
+
});
|
|
106
115
|
case "scripted":
|
|
107
116
|
return planScriptedStudy(study, { ...input, injectedBrowser: injectedBrowser(deps) });
|
|
108
117
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// the study and dispatches here.
|
|
5
5
|
import { cloneProvisioningBudgetMs, provisionCloneSubject } from "../subject/clone.js";
|
|
6
6
|
import { CUA_ACTOR_STUDY_PROVIDER_METADATA } from "../routes/computer-use/e2b-desktop/prepare.js";
|
|
7
|
-
import {
|
|
7
|
+
import { sandboxCeiling } from "../substrates/e2b/lifetime.js";
|
|
8
8
|
import { abandonPreflightJournal, discardPreflightJournal, openPreflightJournal, } from "../run/preflight-receipts.js";
|
|
9
9
|
import { probeUrl } from "../substrates/detached.js";
|
|
10
10
|
import { loadE2BDesktopModule } from "../substrates/e2b/sdk.js";
|
|
@@ -147,8 +147,16 @@ async function withPreflightSandbox(ctx, args, callback) {
|
|
|
147
147
|
let allocation;
|
|
148
148
|
let failureMessage;
|
|
149
149
|
// The lease is sized to the probe's work, never longer than a declared sandbox timeout (the run
|
|
150
|
-
// gets no more than that either) or
|
|
151
|
-
const
|
|
150
|
+
// gets no more than that either) or the sandbox ceiling.
|
|
151
|
+
const ceiling = sandboxCeiling(ctx.env);
|
|
152
|
+
if (!ceiling.ok)
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
result: fail(ctx, "HUMANISH_STUDY_PREFLIGHT_PROVISION_FAILED", ceiling.message, [
|
|
156
|
+
{ name: "sandbox preflight", ok: false, message: ceiling.message },
|
|
157
|
+
]),
|
|
158
|
+
};
|
|
159
|
+
const timeoutMs = Math.min(args.leaseMs, ctx.config.execution?.desktop?.sandboxTimeoutMs ?? ceiling.ms);
|
|
152
160
|
// The receipt goes to a journal under .humanish/preflight, so `humanish reclaim --preflight`
|
|
153
161
|
// can kill the probe if this process dies before the finally block does.
|
|
154
162
|
let journal;
|
package/dist/study/preflight.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { personaBackgroundWarnings } from "./warnings.js";
|
|
1
|
+
import { personaBackgroundWarnings, plannedParticipants } from "./warnings.js";
|
|
2
2
|
import { resolveCommittedPersonasForCwd, studyPersonaIds } from "./persona-resolve.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { isLoopbackUrl } from "./parse/subject.js";
|
|
@@ -37,9 +37,10 @@ export async function runStudyPreflight(options) {
|
|
|
37
37
|
error: resolved.error,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
const participants = plannedParticipants(resolved.config);
|
|
41
|
+
const personas = participants.length === 0
|
|
42
|
+
? new Map()
|
|
43
|
+
: (await resolveCommittedPersonasForCwd(cwd, studyPersonaIds(resolved.config))).personas;
|
|
43
44
|
const route = routeOf(resolved.config);
|
|
44
45
|
const ctx = {
|
|
45
46
|
cwd,
|
|
@@ -59,7 +60,10 @@ export async function runStudyPreflight(options) {
|
|
|
59
60
|
],
|
|
60
61
|
targets: collectTargets(resolved.config),
|
|
61
62
|
sandbox: { created: false },
|
|
62
|
-
warnings: [
|
|
63
|
+
warnings: [
|
|
64
|
+
...resolved.warnings,
|
|
65
|
+
...personaBackgroundWarnings(resolved.config.id, participants, personas),
|
|
66
|
+
],
|
|
63
67
|
};
|
|
64
68
|
const machine = machineCheck(ctx.config);
|
|
65
69
|
// A local study's app and desktops run on this machine, so the hosted probes do not apply.
|
package/dist/study/summary.d.ts
CHANGED
|
@@ -38,9 +38,15 @@ export interface StudySummary {
|
|
|
38
38
|
* default. A study variable you cannot see is one nobody chose.
|
|
39
39
|
*/
|
|
40
40
|
reasoningEffort?: string;
|
|
41
|
-
caps
|
|
41
|
+
/** Absent on routes whose dollar caps the summary does not show; `{}` when none is declared. */
|
|
42
|
+
caps?: StudyCaps;
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
+
* What `humanish run <study>` runs this file as: its `mode`, a dry run when it sets none. The TUI
|
|
45
|
+
* cannot start a live run of a dry-run file, because no `run` flag overrides the file to live.
|
|
46
|
+
*/
|
|
47
|
+
mode: "live" | "dry-run";
|
|
48
|
+
/**
|
|
49
|
+
* Whether the keys a live run of this study needs resolve, whatever its mode. This checks key
|
|
44
50
|
* presence, not local CLI authentication or provider validity. Undefined when not checked.
|
|
45
51
|
*/
|
|
46
52
|
keysReady?: boolean;
|
package/dist/study/summary.js
CHANGED
|
@@ -7,10 +7,9 @@ import { localRuntimeStatus } from "../substrates/local/runtime.js";
|
|
|
7
7
|
// what a stakeholder reads on the screen where they decide whether to press Start.
|
|
8
8
|
//
|
|
9
9
|
// Resolved analysis defaults are shown independently of declared participant caps. A cap that is not
|
|
10
|
-
// declared is not "unlimited" and not "$0"
|
|
11
|
-
import { resolveStudyDryRun } from "./plan.js";
|
|
12
|
-
import {
|
|
13
|
-
import { localCodexParticipantCheck, planCliRun } from "./doctor.js";
|
|
10
|
+
// declared is not "unlimited" and not "$0": the screen says that no cap is set.
|
|
11
|
+
import { planStudy, resolveStudyDryRun, routeOf } from "./plan.js";
|
|
12
|
+
import { localCodexParticipantCheck } from "./doctor.js";
|
|
14
13
|
import { requiredKeys, requiredSubjectEnv } from "./requirements.js";
|
|
15
14
|
import { automaticAnalysisBudget, formatAutomaticAnalysisBudget, } from "../analysis/automatic-config.js";
|
|
16
15
|
import { DEFAULT_OPENAI_CU_MODEL, DEFAULT_OPENAI_CU_REASONING_EFFORT, } from "../actors/computer-use/openai-provider.js";
|
|
@@ -70,7 +69,7 @@ function participantsOf(config) {
|
|
|
70
69
|
/** The computer-use caps. Other routes draw none. */
|
|
71
70
|
function summaryCapsOf(config) {
|
|
72
71
|
if (!isComputerUseComposition(config))
|
|
73
|
-
return
|
|
72
|
+
return undefined;
|
|
74
73
|
const caps = config.caps;
|
|
75
74
|
return {
|
|
76
75
|
...(typeof caps?.maxUsd === "number" ? { laneUsd: caps.maxUsd } : {}),
|
|
@@ -89,18 +88,17 @@ export async function readStudySummary(cwd, study, options = {}) {
|
|
|
89
88
|
const route = routeOf(inspected.config);
|
|
90
89
|
let keysReady;
|
|
91
90
|
let missingKeys;
|
|
92
|
-
const
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
91
|
+
const mode = resolveStudyDryRun(inspected.config, undefined, true) === true ? "dry-run" : "live";
|
|
92
|
+
// The keys are the live plan's, whatever the file's mode: the screen offers a live run of every
|
|
93
|
+
// study, and a dry run needs no keys to report. A study the planner refuses live has none to
|
|
94
|
+
// check, so the summary reports the refusal in place of its keys.
|
|
95
|
+
const planned = options.checkKeys === true ? planStudy(inspected.config, { cwd, dryRun: false }) : undefined;
|
|
96
96
|
const planRefusal = planned?.ok === false ? planned.refusal.message : undefined;
|
|
97
|
-
if (
|
|
98
|
-
const requirements = planned
|
|
97
|
+
if (planned?.ok === true) {
|
|
98
|
+
const requirements = planned.planned.plan.requirements;
|
|
99
99
|
const subjectKeys = requiredSubjectEnv(requirements);
|
|
100
100
|
const email = inspected.config.comms?.email;
|
|
101
|
-
const receivingKey =
|
|
102
|
-
? await receivingRequiredKey(cwd, email.connection)
|
|
103
|
-
: undefined;
|
|
101
|
+
const receivingKey = email?.kind === "real" ? await receivingRequiredKey(cwd, email.connection) : undefined;
|
|
104
102
|
const candidates = [
|
|
105
103
|
"OPENAI_API_KEY",
|
|
106
104
|
"CODEX_API_KEY",
|
|
@@ -108,13 +106,11 @@ export async function readStudySummary(cwd, study, options = {}) {
|
|
|
108
106
|
...subjectKeys,
|
|
109
107
|
...(receivingKey ? [receivingKey] : []),
|
|
110
108
|
];
|
|
111
|
-
const probes =
|
|
112
|
-
|
|
113
|
-
:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
...(options.keyDeps === undefined ? {} : { deps: options.keyDeps }),
|
|
117
|
-
}).catch(() => []);
|
|
109
|
+
const probes = await probeKeySources(candidates, {
|
|
110
|
+
cwd,
|
|
111
|
+
env: options.env ?? process.env,
|
|
112
|
+
...(options.keyDeps === undefined ? {} : { deps: options.keyDeps }),
|
|
113
|
+
}).catch(() => []);
|
|
118
114
|
const present = new Set(probes.filter((probe) => probe.source !== null).map((probe) => probe.name));
|
|
119
115
|
const required = requiredKeys(requirements, (name) => present.has(name));
|
|
120
116
|
const missing = [
|
|
@@ -130,6 +126,7 @@ export async function readStudySummary(cwd, study, options = {}) {
|
|
|
130
126
|
const analysis = automaticAnalysisBudget(inspected.config.review?.analysis, route, computerUseParticipantCount(inspected.config));
|
|
131
127
|
const subject = subjectOf(config);
|
|
132
128
|
const participants = participantsOf(inspected.config);
|
|
129
|
+
const caps = summaryCapsOf(inspected.config);
|
|
133
130
|
const runtime = options.checkKeys === true && isLocalBrowserStudy(inspected.config)
|
|
134
131
|
? await localRuntimeStatus({
|
|
135
132
|
...(options.env ? { env: options.env } : {}),
|
|
@@ -170,7 +167,8 @@ export async function readStudySummary(cwd, study, options = {}) {
|
|
|
170
167
|
...(participants === undefined ? {} : { participants }),
|
|
171
168
|
model: inspected.config.actor?.model ?? DEFAULT_OPENAI_CU_MODEL,
|
|
172
169
|
reasoningEffort: reasoningEffortOf(inspected.config),
|
|
173
|
-
caps:
|
|
170
|
+
...(caps === undefined ? {} : { caps }),
|
|
171
|
+
mode,
|
|
174
172
|
...(keysReady === undefined ? {} : { keysReady }),
|
|
175
173
|
...(missingKeys === undefined ? {} : { missingKeys }),
|
|
176
174
|
...(planRefusal === undefined ? {} : { planRefusal }),
|
package/dist/study/types.d.ts
CHANGED
|
@@ -349,6 +349,20 @@ export interface StudyActor {
|
|
|
349
349
|
model?: string;
|
|
350
350
|
/** First-party OpenAI CUA only: per-response output limit including reasoning, not a dollar cap. */
|
|
351
351
|
maxOutputTokens?: number;
|
|
352
|
+
/**
|
|
353
|
+
* Computer-use and shared-world: the longest one `wait` action lasts, in whole milliseconds from
|
|
354
|
+
* 1000 to 600000. Default 120000, or 30000 on a desktop with speech. A longer wait is shortened
|
|
355
|
+
* to it, the trace records a `wait shortened` notice, and the participant is told on its next
|
|
356
|
+
* turn.
|
|
357
|
+
*/
|
|
358
|
+
maxWaitMs?: number;
|
|
359
|
+
/**
|
|
360
|
+
* Computer-use and shared-world: how long a `wait` that names no duration lasts when the
|
|
361
|
+
* participant's turn only waits or takes screenshots, in whole milliseconds from 1000 to 600000
|
|
362
|
+
* and no longer than maxWaitMs. Default 5000. After an action in the same turn such a wait lasts
|
|
363
|
+
* 500 ms. OpenAI's computer-use `wait` never names a duration.
|
|
364
|
+
*/
|
|
365
|
+
idleWaitMs?: number;
|
|
352
366
|
/**
|
|
353
367
|
* `local-agent` only: which locally signed-in coding agent is the brain. Absent = codex.
|
|
354
368
|
*
|
|
@@ -36,6 +36,11 @@ export declare function taskProtocolValidationReason(config: StudyConfig, suppor
|
|
|
36
36
|
export declare function cloneTargetValidationReason(config: StudyConfig): string | null;
|
|
37
37
|
/** Refuse a claimed output bound when the route cannot pass it to the first-party provider. */
|
|
38
38
|
export declare function outputTokenLimitValidationReason(config: StudyConfig): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Refuse a wait field out of range, or an idle wait longer than the longest wait. On a route whose
|
|
41
|
+
* participants take no wait actions the fields are inert (warnings.ts).
|
|
42
|
+
*/
|
|
43
|
+
export declare function waitLimitValidationReason(config: StudyConfig): string | null;
|
|
39
44
|
/**
|
|
40
45
|
* Cross-validate a concurrent shared-world declaration. Returns the failure message,
|
|
41
46
|
* or null when valid. Includes the base shared-world checks plus the concurrent extras: a synthetic
|
package/dist/study/validation.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isMaxOutputTokens } from "../actors/output-token-limit.js";
|
|
2
|
+
import { waitFieldsReason } from "../actors/computer-use/wait.js";
|
|
2
3
|
import { PARTICIPANT_ID_MAX_CHARS, PARTICIPANT_ID_PATTERN } from "./parse/actors.js";
|
|
3
4
|
import { isHttpUrl, isLoopbackUrl } from "./parse/subject.js";
|
|
4
5
|
import { declaredTargets } from "./plan-participants.js";
|
|
@@ -235,6 +236,13 @@ export function outputTokenLimitValidationReason(config) {
|
|
|
235
236
|
}
|
|
236
237
|
return null;
|
|
237
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Refuse a wait field out of range, or an idle wait longer than the longest wait. On a route whose
|
|
241
|
+
* participants take no wait actions the fields are inert (warnings.ts).
|
|
242
|
+
*/
|
|
243
|
+
export function waitLimitValidationReason(config) {
|
|
244
|
+
return config.actor === undefined ? null : waitFieldsReason(config.actor, "actor.");
|
|
245
|
+
}
|
|
238
246
|
/**
|
|
239
247
|
* Shared-world participants share one live app, so at least two must be live at once. The
|
|
240
248
|
* sequential shared-world route (`execution.concurrency: 1`) was removed in 0.106.0; the parser
|
package/dist/study/warnings.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ResolvedPersona } from "./persona.js";
|
|
2
2
|
import type { StudyConfig } from "./types.js";
|
|
3
|
+
import { type Participant } from "./plan-participants.js";
|
|
3
4
|
/**
|
|
4
5
|
* The fields a config sets that its route does not read, each as `field` or `field (reason)`.
|
|
5
6
|
* parseStudy refuses a study for any of them.
|
|
@@ -10,17 +11,23 @@ export declare function inertFieldLabels(config: StudyConfig): string[];
|
|
|
10
11
|
* `participants[].entry` or `execution.timeoutMs`. `humanish migrate` drops each one.
|
|
11
12
|
*/
|
|
12
13
|
export declare function inertFieldPaths(config: StudyConfig): string[];
|
|
14
|
+
export declare function forwardDeclaredWarnings(config: StudyConfig): string[];
|
|
13
15
|
/**
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* Removed in the first minor release on or after 2026-11-04: deprecated on 2026-10-05. That release refuses the field off the terminal
|
|
19
|
-
* route.
|
|
16
|
+
* The warnings about a study's own fields that a run records in its bundle as `study.warning`
|
|
17
|
+
* events: `execution.egressAllow` off the terminal route, and participant text that reads like a
|
|
18
|
+
* script. parseStudy reports them too, after its other warnings, so `study check` and every run
|
|
19
|
+
* print them.
|
|
20
20
|
*/
|
|
21
|
-
export declare function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
export declare function recordedStudyWarnings(config: StudyConfig): string[];
|
|
22
|
+
/**
|
|
23
|
+
* A warning for each participant whose persona has no background, which gives a participant a
|
|
24
|
+
* reason to act beyond the assigned task. `participants` are the records the planner builds: a
|
|
25
|
+
* computer-use or shared-world plan's participants, or `plannedParticipants` before a plan exists.
|
|
26
|
+
*/
|
|
27
|
+
export declare function personaBackgroundWarnings(studyId: string, participants: readonly Pick<Participant, "id" | "personaId">[], personas: ReadonlyMap<string, ResolvedPersona>): string[];
|
|
28
|
+
/**
|
|
29
|
+
* The participant records planStudy builds for a study's declared participants, for `study check`
|
|
30
|
+
* and `study show`, which run no plan. Only computer-use and shared-world studies have them; a run's
|
|
31
|
+
* `--count` is not applied.
|
|
32
|
+
*/
|
|
33
|
+
export declare function plannedParticipants(config: StudyConfig): readonly Participant[];
|
package/dist/study/warnings.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cli } from "../cli/invocation.js";
|
|
2
2
|
import { isComputerUseComposition, isScriptedBrowserComposition, isSharedWorldComposition, isTerminalProductComposition, } from "./routing.js";
|
|
3
|
-
import { declaredParticipantIds } from "./plan-participants.js";
|
|
3
|
+
import { computerUseParticipants, declaredParticipantIds, sharedWorldParticipants, } from "./plan-participants.js";
|
|
4
4
|
import { addressedRecipients } from "./parse/comms.js";
|
|
5
5
|
import { participantList, participantInstruction, declaredParticipantCount, } from "./study-fields.js";
|
|
6
6
|
// The computer-use and terminal routes consume mission/persona/model (they compose the agent prompt
|
|
@@ -50,6 +50,18 @@ const ACTOR_ROWS = [
|
|
|
50
50
|
field: "actor.model",
|
|
51
51
|
applies: (config, routes) => otherRoute(routes) && Boolean(config.actor?.model),
|
|
52
52
|
},
|
|
53
|
+
// Only a computer-use loop dispatches wait actions; terminal, scripted and preview participants
|
|
54
|
+
// take none.
|
|
55
|
+
{
|
|
56
|
+
field: "actor.maxWaitMs",
|
|
57
|
+
reason: "the longest wait action of a computer-use participant; needs route: computer-use or shared-world",
|
|
58
|
+
applies: (config, routes) => config.actor?.maxWaitMs !== undefined && !routes.cua && !routes.shared,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
field: "actor.idleWaitMs",
|
|
62
|
+
reason: "how long a computer-use participant's wait with no duration lasts; needs route: computer-use or shared-world",
|
|
63
|
+
applies: (config, routes) => config.actor?.idleWaitMs !== undefined && !routes.cua && !routes.shared,
|
|
64
|
+
},
|
|
53
65
|
];
|
|
54
66
|
const TERMINAL_ONLY = "needs subject.source: terminal-product + a registered terminal actor";
|
|
55
67
|
const RESERVED = "reserved; no route reads it yet";
|
|
@@ -269,12 +281,11 @@ export function inertFieldPaths(config) {
|
|
|
269
281
|
/**
|
|
270
282
|
* The warning for `execution.egressAllow` on a route that ignores it, or undefined. Only the
|
|
271
283
|
* terminal route creates its sandbox with the list as an outbound allowlist; on any other route the
|
|
272
|
-
* study author would trust a list that blocks nothing.
|
|
273
|
-
* it, and the run records it in its bundle.
|
|
284
|
+
* study author would trust a list that blocks nothing.
|
|
274
285
|
* Removed in the first minor release on or after 2026-11-04: deprecated on 2026-10-05. That release refuses the field off the terminal
|
|
275
286
|
* route.
|
|
276
287
|
*/
|
|
277
|
-
|
|
288
|
+
function egressAllowIgnoredWarning(config) {
|
|
278
289
|
if (config.execution?.egressAllow === undefined || config.route === "terminal")
|
|
279
290
|
return undefined;
|
|
280
291
|
return `\`execution.egressAllow\` is ignored on route: ${config.route}. Only route: terminal creates its sandbox with that outbound allowlist, and this route's browser desktops can reach any site. Remove the field: the first minor release on or after 2026-11-04 refuses it on every route except terminal.`;
|
|
@@ -289,9 +300,6 @@ export function forwardDeclaredWarnings(config) {
|
|
|
289
300
|
: [
|
|
290
301
|
`These fields are set, but no route reads them yet, so they have no effect: ${inert.join(", ")}.`,
|
|
291
302
|
];
|
|
292
|
-
const egressIgnored = egressAllowIgnoredWarning(config);
|
|
293
|
-
if (egressIgnored !== undefined)
|
|
294
|
-
warnings.push(egressIgnored);
|
|
295
303
|
// A declared cap below the participant count is legal but loud: the roster promises N live actors
|
|
296
304
|
// and the cap delivers waves of M. Say so up front (inspect + dry-run + run): a green run in
|
|
297
305
|
// waves is otherwise indistinguishable from the all-live run the author meant.
|
|
@@ -312,7 +320,20 @@ export function forwardDeclaredWarnings(config) {
|
|
|
312
320
|
warnings.push(`comms.email covers ${covered.size} of ${participantIds.length} participants. These get no inbox and are never told one exists: ${uncovered.join(", ")}. Add a recipient with an address for each one whose flow needs email.`);
|
|
313
321
|
}
|
|
314
322
|
}
|
|
315
|
-
return [...warnings, ...
|
|
323
|
+
return [...warnings, ...recordedStudyWarnings(config)];
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* The warnings about a study's own fields that a run records in its bundle as `study.warning`
|
|
327
|
+
* events: `execution.egressAllow` off the terminal route, and participant text that reads like a
|
|
328
|
+
* script. parseStudy reports them too, after its other warnings, so `study check` and every run
|
|
329
|
+
* print them.
|
|
330
|
+
*/
|
|
331
|
+
export function recordedStudyWarnings(config) {
|
|
332
|
+
const egressIgnored = egressAllowIgnoredWarning(config);
|
|
333
|
+
return [
|
|
334
|
+
...(egressIgnored === undefined ? [] : [egressIgnored]),
|
|
335
|
+
...scriptedMissionWarnings(config),
|
|
336
|
+
];
|
|
316
337
|
}
|
|
317
338
|
const UI_ACTION = /\b(?:click|tap|press|type\b[^.!?\n]*?\binto|select\b[^.!?\n]*?\bfrom)\b/gi;
|
|
318
339
|
function normalizedUrl(value) {
|
|
@@ -343,7 +364,7 @@ function actionChain(text) {
|
|
|
343
364
|
/\bthen\b/i.test(text.slice((actions[0]?.index ?? 0) + (actions[0]?.[0].length ?? 0)))));
|
|
344
365
|
}
|
|
345
366
|
/** Authored participant text is checked before runtime instructions or researcher criteria join it. */
|
|
346
|
-
|
|
367
|
+
function scriptedMissionWarnings(config) {
|
|
347
368
|
if (config.route !== "computer-use" && config.route !== "shared-world")
|
|
348
369
|
return [];
|
|
349
370
|
const fields = [
|
|
@@ -374,20 +395,32 @@ export function scriptedMissionWarnings(config) {
|
|
|
374
395
|
];
|
|
375
396
|
});
|
|
376
397
|
}
|
|
377
|
-
/**
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
398
|
+
/**
|
|
399
|
+
* A warning for each participant whose persona has no background, which gives a participant a
|
|
400
|
+
* reason to act beyond the assigned task. `participants` are the records the planner builds: a
|
|
401
|
+
* computer-use or shared-world plan's participants, or `plannedParticipants` before a plan exists.
|
|
402
|
+
*/
|
|
403
|
+
export function personaBackgroundWarnings(studyId, participants, personas) {
|
|
404
|
+
return participants.flatMap(({ id, personaId }) => {
|
|
384
405
|
if (personaId && personas.get(personaId)?.background)
|
|
385
406
|
return [];
|
|
386
407
|
const reason = personaId
|
|
387
408
|
? `persona ${personaId} has no readable background`
|
|
388
409
|
: "no persona is assigned";
|
|
389
410
|
return [
|
|
390
|
-
`Participant ${id} has no persona background because ${reason}. Add a short, fictional background describing their experience and situation. Run ${cli(`study show ${
|
|
411
|
+
`Participant ${id} has no persona background because ${reason}. Add a short, fictional background describing their experience and situation. Run ${cli(`study show ${studyId} --json`)} to see what the participant receives.`,
|
|
391
412
|
];
|
|
392
413
|
});
|
|
393
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* The participant records planStudy builds for a study's declared participants, for `study check`
|
|
417
|
+
* and `study show`, which run no plan. Only computer-use and shared-world studies have them; a run's
|
|
418
|
+
* `--count` is not applied.
|
|
419
|
+
*/
|
|
420
|
+
export function plannedParticipants(config) {
|
|
421
|
+
if (config.route === "computer-use")
|
|
422
|
+
return computerUseParticipants(config);
|
|
423
|
+
if (config.route === "shared-world")
|
|
424
|
+
return sharedWorldParticipants(config).participants;
|
|
425
|
+
return [];
|
|
426
|
+
}
|
package/dist/subject/serve.d.ts
CHANGED
|
@@ -37,3 +37,6 @@ export declare function runSubjectServePipeline(shell: Shell, args: {
|
|
|
37
37
|
* an in-sandbox git refresh, because the archive excludes .git). */
|
|
38
38
|
onPhaseComplete?: () => Promise<void>;
|
|
39
39
|
} & DetachedTimers): Promise<void>;
|
|
40
|
+
/** A serve.build that exited non-zero or ran out of time; the message carries its redacted log tail. */
|
|
41
|
+
export declare class SubjectBuildError extends Error {
|
|
42
|
+
}
|
package/dist/subject/serve.js
CHANGED
|
@@ -196,6 +196,9 @@ async function installSubject(step, command) {
|
|
|
196
196
|
throw new Error(`${headline}: ${failureTail(step.scrub(install.logTail))}`);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
+
/** A serve.build that exited non-zero or ran out of time; the message carries its redacted log tail. */
|
|
200
|
+
export class SubjectBuildError extends Error {
|
|
201
|
+
}
|
|
199
202
|
/** serve.build, once. */
|
|
200
203
|
async function buildSubject(step, command) {
|
|
201
204
|
const { now } = step;
|
|
@@ -211,7 +214,7 @@ async function buildSubject(step, command) {
|
|
|
211
214
|
});
|
|
212
215
|
emitPhaseCompleted(step.onPhase, now, buildStartedAt, "build", build.ok, build.ok ? "subject build complete" : "subject build failed");
|
|
213
216
|
if (!build.ok) {
|
|
214
|
-
throw new
|
|
217
|
+
throw new SubjectBuildError(`subject build ${build.timedOut ? "timed out" : `failed (exit ${build.exitCode})`}: ${failureTail(step.scrub(build.logTail))}`);
|
|
215
218
|
}
|
|
216
219
|
}
|
|
217
220
|
/** serve.start as a detached process that keeps running. */
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
// - Scripts are written via `writeFile`, never heredocs, which eliminates the
|
|
5
5
|
// sentinel-collision bug class (a command line that equals the heredoc terminator) by
|
|
6
|
-
// construction.
|
|
6
|
+
// construction. The author's command is a script file of its own, so bash parses it apart
|
|
7
|
+
// from the wrapper: a syntax error in it is that script's exit 2, logged, and the wrapper
|
|
8
|
+
// still writes the status.
|
|
7
9
|
// - Bounded steps (install/build) run detached with an atomically-written status file
|
|
8
10
|
// (write tmp + mv), polled by short foreground commands; a timeout kills the process
|
|
9
11
|
// group and surfaces a capped log tail for the caller to redact and persist.
|
|
@@ -34,10 +36,11 @@ function assertName(name) {
|
|
|
34
36
|
function stepDir(name) {
|
|
35
37
|
return `${WORK_ROOT}/${name}`;
|
|
36
38
|
}
|
|
37
|
-
// The wrapper script: runs the command from its own session (Shell.start makes the
|
|
38
|
-
// process-group leader, so `kill -- -PID` reclaims the whole tree), logs everything,
|
|
39
|
-
// writes the exit code atomically so a poller can never read a half-written status.
|
|
40
|
-
|
|
39
|
+
// The wrapper script: runs the command script from its own session (Shell.start makes the
|
|
40
|
+
// wrapper the process-group leader, so `kill -- -PID` reclaims the whole tree), logs everything,
|
|
41
|
+
// and writes the exit code atomically so a poller can never read a half-written status. It holds
|
|
42
|
+
// no author text, so it always parses and always reaches the status write.
|
|
43
|
+
function wrapperScript(name, cwd) {
|
|
41
44
|
const dir = stepDir(name);
|
|
42
45
|
return [
|
|
43
46
|
"#!/bin/bash",
|
|
@@ -46,7 +49,7 @@ function wrapperScript(name, command, cwd) {
|
|
|
46
49
|
cwd === undefined
|
|
47
50
|
? ": # no cwd override"
|
|
48
51
|
: `cd ${shellQuote(cwd)} || { echo 127 > ${shellQuote(`${dir}/status.tmp`)}; mv ${shellQuote(`${dir}/status.tmp`)} ${shellQuote(`${dir}/status`)}; exit 127; }`,
|
|
49
|
-
`
|
|
52
|
+
`bash ${shellQuote(`${dir}/command.sh`)} > ${shellQuote(`${dir}/log.txt`)} 2>&1`,
|
|
50
53
|
"code=$?",
|
|
51
54
|
`echo $code > ${shellQuote(`${dir}/status.tmp`)}`,
|
|
52
55
|
`mv ${shellQuote(`${dir}/status.tmp`)} ${shellQuote(`${dir}/status`)}`,
|
|
@@ -59,7 +62,11 @@ async function writeAndLaunch(shell, name, command, cwd, requestTimeoutMs) {
|
|
|
59
62
|
const dir = stepDir(name);
|
|
60
63
|
const scriptPath = `${dir}/run.sh`;
|
|
61
64
|
await runOrThrow(shell, `mkdir -p ${shellQuote(dir)}`, { requestTimeoutMs });
|
|
62
|
-
|
|
65
|
+
// The study parser trims each command, so a YAML `|` block arrives without its final newline.
|
|
66
|
+
// bash closes a heredoc whose delimiter is a script's last line either way; the newline makes
|
|
67
|
+
// the file plain text.
|
|
68
|
+
await shell.writeFile(`${dir}/command.sh`, `${command}\n`);
|
|
69
|
+
await shell.writeFile(scriptPath, wrapperScript(name, cwd));
|
|
63
70
|
throwOnExit(await shell.start(`bash ${shellQuote(scriptPath)}`, { requestTimeoutMs }));
|
|
64
71
|
}
|
|
65
72
|
/** Read the capped log tail for a step (raw; caller redacts). */
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { E2BConnection, E2BDesktopModule } from "./sdk.js";
|
|
2
|
+
/** The connection an env names: E2B_API_KEY and E2B_DOMAIN, trimmed, each only when set. */
|
|
3
|
+
export declare function e2bConnection(env: Readonly<Record<string, string | undefined>>): E2BConnection;
|
|
4
|
+
/** The calls humanish makes on sandboxes that already exist. */
|
|
5
|
+
export type E2BAccount = Pick<E2BDesktopModule["Sandbox"], "kill" | "getInfo" | "list">;
|
|
6
|
+
/**
|
|
7
|
+
* The kill, getInfo and list of `module`, each carrying `connection`'s key and domain over the
|
|
8
|
+
* call's own options. `connection` may be a create's options, so the release of a sandbox reaches
|
|
9
|
+
* the account that created it. Each method is bound now: a hook that later replaces one on the
|
|
10
|
+
* shared module does not change this account. A method the installed SDK lacks stays absent.
|
|
11
|
+
*/
|
|
12
|
+
export declare function e2bAccount(module: E2BDesktopModule, connection: E2BConnection): E2BAccount;
|