humanish 0.125.0 → 0.127.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 +29 -0
- package/dist/actors/computer-use/actor.d.ts +2 -0
- package/dist/actors/computer-use/actor.js +1 -0
- package/dist/actors/computer-use/loop/clock.d.ts +9 -8
- package/dist/actors/computer-use/loop/clock.js +27 -17
- package/dist/actors/computer-use/loop/debrief.d.ts +1 -1
- package/dist/actors/computer-use/loop/debrief.js +15 -5
- package/dist/actors/computer-use/loop/ending.d.ts +10 -5
- package/dist/actors/computer-use/loop/ending.js +12 -7
- package/dist/actors/computer-use/loop/observation.d.ts +6 -0
- package/dist/actors/computer-use/loop/observation.js +19 -1
- package/dist/actors/computer-use/loop/session.d.ts +4 -2
- package/dist/actors/computer-use/loop/session.js +4 -1
- package/dist/actors/computer-use/loop/trace.js +4 -2
- package/dist/actors/computer-use/loop/types.d.ts +22 -8
- package/dist/actors/computer-use/loop.js +5 -5
- package/dist/actors/computer-use/openai-provider.js +1 -1
- package/dist/actors/contract.d.ts +31 -8
- package/dist/actors/contract.js +3 -0
- package/dist/actors/goal-source.d.ts +32 -2
- package/dist/actors/goal-source.js +41 -2
- package/dist/actors/stop-cause.js +1 -0
- package/dist/analysis/evidence-sources.js +2 -2
- package/dist/analysis/types.d.ts +2 -2
- package/dist/analysis/validation.js +2 -1
- package/dist/cli/commands/study.js +8 -0
- package/dist/evidence/redaction.d.ts +1 -0
- package/dist/evidence/redaction.js +1 -0
- package/dist/evidence/trace-page.d.ts +6 -0
- package/dist/evidence/trace-page.js +90 -0
- package/dist/observer/data.js +8 -5
- package/dist/observer/feed-cursors.d.ts +14 -0
- package/dist/observer/feed-cursors.js +61 -0
- package/dist/observer/participant-items.d.ts +7 -0
- package/dist/observer/pinned-files.d.ts +2 -1
- package/dist/observer/pinned-files.js +2 -2
- package/dist/observer/run-routes.d.ts +1 -1
- package/dist/observer/run-routes.js +38 -17
- package/dist/observer-app.html +7 -7
- package/dist/routes/computer-use/bundle.js +4 -0
- package/dist/routes/computer-use/diagnostics.d.ts +1 -1
- package/dist/routes/computer-use/diagnostics.js +3 -3
- package/dist/routes/computer-use/e2b-desktop/desktop.js +2 -1
- package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +6 -0
- package/dist/routes/computer-use/e2b-desktop/prepare.js +47 -1
- package/dist/routes/computer-use/e2b-desktop/start.d.ts +2 -1
- package/dist/routes/computer-use/e2b-desktop/start.js +15 -3
- package/dist/routes/computer-use/e2b-desktop/state.d.ts +3 -1
- package/dist/routes/computer-use/e2b-desktop/state.js +2 -0
- package/dist/routes/computer-use/fanout-bundle.js +3 -0
- package/dist/routes/computer-use/fanout-records.js +5 -1
- package/dist/routes/computer-use/participant-deps.d.ts +1 -1
- package/dist/routes/computer-use/participant-desktop.d.ts +1 -1
- package/dist/routes/computer-use/participant-execution.js +6 -2
- package/dist/routes/computer-use/participant-facts.js +4 -1
- package/dist/routes/computer-use/participant-model.js +16 -6
- package/dist/routes/computer-use/participant-runs.d.ts +10 -10
- package/dist/routes/computer-use/participant-runs.js +54 -84
- package/dist/routes/computer-use/plan.d.ts +2 -0
- package/dist/routes/computer-use/plan.js +65 -17
- package/dist/routes/computer-use/rerun-selection.js +8 -5
- package/dist/routes/computer-use/setup.d.ts +1 -0
- package/dist/routes/computer-use/setup.js +29 -1
- package/dist/routes/computer-use/single-bundle.d.ts +5 -1
- package/dist/routes/computer-use/single-bundle.js +11 -1
- package/dist/routes/computer-use/types.d.ts +18 -13
- package/dist/routes/computer-use/types.js +0 -3
- package/dist/routes/shared-world/bundle-records.js +4 -1
- package/dist/routes/shared-world/external-public.js +1 -1
- package/dist/routes/shared-world/participant-specs.js +3 -2
- package/dist/routes/shared-world/plan.d.ts +2 -0
- package/dist/routes/shared-world/plan.js +8 -3
- package/dist/routes/shared-world/provisioned.js +1 -1
- package/dist/run/bundle.d.ts +11 -1
- package/dist/run/contained-output.d.ts +7 -3
- package/dist/run/contained-output.js +11 -7
- package/dist/run/judge.d.ts +14 -4
- package/dist/run/judge.js +17 -12
- package/dist/run/live-items.d.ts +19 -0
- package/dist/run/live-items.js +28 -8
- package/dist/run/notes.js +1 -1
- package/dist/run/outcomes.d.ts +8 -2
- package/dist/run/outcomes.js +56 -14
- package/dist/run/stream-trace.d.ts +25 -4
- package/dist/run/stream-trace.js +55 -17
- package/dist/run/streams.d.ts +16 -0
- package/dist/study/arrivals.d.ts +105 -34
- package/dist/study/arrivals.js +179 -74
- package/dist/study/bundle-estimate.d.ts +12 -10
- package/dist/study/bundle-estimate.js +138 -72
- package/dist/study/desktop-files.d.ts +37 -0
- package/dist/study/desktop-files.js +174 -0
- package/dist/study/keys.d.ts +8 -0
- package/dist/study/keys.js +3 -0
- package/dist/study/migrate/v2.js +12 -3
- package/dist/study/parse/actors.d.ts +1 -1
- package/dist/study/parse/actors.js +28 -20
- package/dist/study/parse/clock.d.ts +18 -0
- package/dist/study/parse/clock.js +36 -0
- package/dist/study/plan-participants.d.ts +19 -10
- package/dist/study/plan-participants.js +13 -9
- package/dist/study/plan-types.d.ts +6 -1
- package/dist/study/plan.d.ts +2 -1
- package/dist/study/plan.js +28 -15
- package/dist/study/preflight.js +4 -0
- package/dist/study/types.d.ts +35 -5
- package/dist/study/validation.js +3 -0
- package/dist/study/warnings.d.ts +2 -2
- package/dist/study/warnings.js +7 -0
- package/dist/subject/local-tree-archive.d.ts +7 -0
- package/dist/subject/local-tree-archive.js +12 -0
- package/dist/substrates/e2b/desktop-browser.d.ts +4 -1
- package/dist/substrates/e2b/desktop-browser.js +6 -2
- package/dist/substrates/e2b/desktop-clock.d.ts +10 -0
- package/dist/substrates/e2b/desktop-clock.js +29 -0
- package/dist/substrates/e2b/lifetime.d.ts +22 -8
- package/dist/substrates/e2b/lifetime.js +54 -10
- package/dist/substrates/e2b/sdk.d.ts +6 -0
- package/dist/verify/pages.d.ts +7 -0
- package/dist/verify/pages.js +50 -0
- package/dist/verify/verify.js +10 -3
- package/docs/architecture/actor-contract.md +2 -0
- package/docs/architecture/external-public-shared-world.md +4 -1
- package/docs/architecture/observer.md +10 -1
- package/docs/contracts/policy.md +7 -0
- package/docs/contracts/run-bundle.md +31 -1
- package/docs/contracts/schemas.md +93 -11
- package/package.json +2 -2
package/dist/run/stream-trace.js
CHANGED
|
@@ -8,7 +8,7 @@ import { createHash } from "node:crypto";
|
|
|
8
8
|
import { RUN_BUNDLE_FILE, RUN_BUNDLE_SCHEMA, RUN_BUNDLE_V2_SCHEMA, } from "./bundle.js";
|
|
9
9
|
import { isRunBundle } from "./bundle-shape.js";
|
|
10
10
|
import { readContainedPrefix, readContainedRegularFile, refusalText, RUN_ARTIFACT_MAX_BYTES, } from "./contained-output.js";
|
|
11
|
-
import { isLiveItemsIndex, LIVE_ACTOR_SCHEMA, LIVE_ACTOR_V1_SCHEMA, liveItemsFromBytes, } from "./live-items.js";
|
|
11
|
+
import { isLiveItemsIndex, LIVE_ACTOR_SCHEMA, LIVE_ACTOR_V1_SCHEMA, liveItemsAfter, liveItemsFromBytes, } from "./live-items.js";
|
|
12
12
|
import { isRunStreamArtifact, isStreamTraceIndex, isStreamTracePath } from "./stream-shape.js";
|
|
13
13
|
import { isRecord } from "./type-guards.js";
|
|
14
14
|
/** A trace that does not match its index, or cannot be read. The message names the stream. */
|
|
@@ -87,10 +87,10 @@ export function splitStreamTraces(bundle) {
|
|
|
87
87
|
return { bundle: { ...bundle, schema: RUN_BUNDLE_SCHEMA, streams }, files };
|
|
88
88
|
}
|
|
89
89
|
function containedReader(runPaths) {
|
|
90
|
-
return async (relativePath, maxBytes, length) => {
|
|
90
|
+
return async (relativePath, maxBytes, length, start) => {
|
|
91
91
|
const read = length === undefined
|
|
92
92
|
? await readContainedRegularFile(runPaths, relativePath, maxBytes)
|
|
93
|
-
: await readContainedPrefix(runPaths, relativePath, length);
|
|
93
|
+
: await readContainedPrefix(runPaths, relativePath, length, start);
|
|
94
94
|
if (read.status === "missing")
|
|
95
95
|
return { refused: `${relativePath} is missing` };
|
|
96
96
|
if (read.status === "refused")
|
|
@@ -152,7 +152,7 @@ class LiveFileUnreadableError extends StreamTraceRefusedError {
|
|
|
152
152
|
* file's path and those bytes. A live actor with inline items (humanish.live-actor.v1) is returned
|
|
153
153
|
* as it is; bundleRefusal refuses one in a v3 run.json.
|
|
154
154
|
*/
|
|
155
|
-
async function readLiveItems(read, stream, maxBytes) {
|
|
155
|
+
async function readLiveItems(read, stream, maxBytes, liveAfter) {
|
|
156
156
|
const live = stream.liveActor;
|
|
157
157
|
if (live === undefined || isInlineLiveActor(live))
|
|
158
158
|
return { stream };
|
|
@@ -168,6 +168,15 @@ async function readLiveItems(read, stream, maxBytes) {
|
|
|
168
168
|
refuse("run.json holds in-progress items beside their live file");
|
|
169
169
|
if (index.bytes > maxBytes)
|
|
170
170
|
refuse(`${index.path} is indexed at ${index.bytes} bytes, more than the ${maxBytes} read`);
|
|
171
|
+
const { itemsFile: _index, ...summary } = live;
|
|
172
|
+
const after = liveAfter?.(index);
|
|
173
|
+
const tail = after === undefined ? undefined : await readLiveTail(read, stream, index, after);
|
|
174
|
+
if (tail !== undefined)
|
|
175
|
+
return {
|
|
176
|
+
stream: { ...stream, liveActor: { ...summary, items: tail.items } },
|
|
177
|
+
file: { path: index.path, bytes: tail.bytes },
|
|
178
|
+
read: { streamId: stream.id, index, after: after },
|
|
179
|
+
};
|
|
171
180
|
const file = await read(index.path, maxBytes, index.bytes);
|
|
172
181
|
if ("refused" in file)
|
|
173
182
|
throw new LiveFileUnreadableError(stream.id, file.refused);
|
|
@@ -175,17 +184,38 @@ async function readLiveItems(read, stream, maxBytes) {
|
|
|
175
184
|
const joined = liveItemsFromBytes(file.bytes.subarray(0, index.bytes), index);
|
|
176
185
|
if ("refused" in joined)
|
|
177
186
|
return refuse(joined.refused);
|
|
178
|
-
const { itemsFile: _index, ...summary } = live;
|
|
179
187
|
return {
|
|
180
188
|
stream: { ...stream, liveActor: { ...summary, items: joined.items } },
|
|
181
|
-
|
|
189
|
+
file: { path: index.path, bytes: file.bytes.subarray(0, index.bytes) },
|
|
190
|
+
read: { streamId: stream.id, index },
|
|
182
191
|
};
|
|
183
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* The items of the live file `index` describes past `after`, and the bytes they were read from;
|
|
195
|
+
* undefined when the cursor does not fit the index or the file's lines, so the caller reads the
|
|
196
|
+
* whole prefix. A cursor at the indexed end reads nothing. A file the reader cannot read throws
|
|
197
|
+
* LiveFileUnreadableError, as a whole read does.
|
|
198
|
+
*/
|
|
199
|
+
async function readLiveTail(read, stream, index, after) {
|
|
200
|
+
if (after.bytes === 0 ||
|
|
201
|
+
after.items > index.items ||
|
|
202
|
+
after.bytes > index.bytes ||
|
|
203
|
+
(after.items === index.items) !== (after.bytes === index.bytes))
|
|
204
|
+
return undefined;
|
|
205
|
+
if (after.bytes === index.bytes)
|
|
206
|
+
return { items: [], bytes: Buffer.alloc(0) };
|
|
207
|
+
const file = await read(index.path, index.bytes - after.bytes + 1, index.bytes, after.bytes - 1);
|
|
208
|
+
if ("refused" in file)
|
|
209
|
+
throw new LiveFileUnreadableError(stream.id, file.refused);
|
|
210
|
+
const tail = file.bytes.subarray(0, index.bytes - after.bytes + 1);
|
|
211
|
+
const joined = liveItemsAfter(tail, index, after);
|
|
212
|
+
return "refused" in joined ? undefined : { items: joined.items, bytes: tail.subarray(1) };
|
|
213
|
+
}
|
|
184
214
|
/**
|
|
185
215
|
* The stream with its trace and live items joined, and each file's path and the bytes it was
|
|
186
216
|
* joined from.
|
|
187
217
|
*/
|
|
188
|
-
async function readStreamTrace(read, stream, maxBytes) {
|
|
218
|
+
async function readStreamTrace(read, stream, maxBytes, liveAfter) {
|
|
189
219
|
const files = [];
|
|
190
220
|
let joined = stream;
|
|
191
221
|
if (stream.trace !== undefined) {
|
|
@@ -196,10 +226,10 @@ async function readStreamTrace(read, stream, maxBytes) {
|
|
|
196
226
|
joined = joinTrace(stream, file.bytes);
|
|
197
227
|
files.push({ path: stream.trace.path, bytes: file.bytes });
|
|
198
228
|
}
|
|
199
|
-
const live = await readLiveItems(read, joined, maxBytes);
|
|
200
|
-
if (live.
|
|
201
|
-
files.push(live.
|
|
202
|
-
return { stream: live.stream, files };
|
|
229
|
+
const live = await readLiveItems(read, joined, maxBytes, liveAfter);
|
|
230
|
+
if (live.file !== undefined)
|
|
231
|
+
files.push(live.file);
|
|
232
|
+
return { stream: live.stream, files, ...(live.read === undefined ? {} : { live: live.read }) };
|
|
203
233
|
}
|
|
204
234
|
/**
|
|
205
235
|
* The stream as a v1 run.json held it: its actor with every item and every artifact entry. A
|
|
@@ -249,6 +279,11 @@ function bundleRefusal(bundle, stream, owners) {
|
|
|
249
279
|
* receives each trace and live file's path and the bytes the stream was joined from, after they
|
|
250
280
|
* matched the index, so a caller scans exactly what it reads.
|
|
251
281
|
*
|
|
282
|
+
* `liveAfter` names, for a live file's index, the prefix the caller holds already (the Observer
|
|
283
|
+
* feed's cursor). The loader then reads and joins only the items past it, checked by liveItemsAfter
|
|
284
|
+
* without the indexed sha256, and `live` records the cursor; a cursor past the index or off the
|
|
285
|
+
* file's lines gets the whole prefix, checked as always.
|
|
286
|
+
*
|
|
252
287
|
* A live file that cannot be read may have been removed by the writer after `bundle` was read: it
|
|
253
288
|
* removes a superseded generation once run.json names the next one, and every live file once the
|
|
254
289
|
* run finished. The loader then reads run.json again, once, through the same reader, and loads
|
|
@@ -257,15 +292,15 @@ function bundleRefusal(bundle, stream, owners) {
|
|
|
257
292
|
export async function loadStreamTraces(source, bundle, options = {}) {
|
|
258
293
|
const read = typeof source === "function" ? source : containedReader(source);
|
|
259
294
|
const maxBytes = options.maxBytes ?? RUN_ARTIFACT_MAX_BYTES;
|
|
260
|
-
let loaded = await loadEveryStream(read, bundle, maxBytes);
|
|
295
|
+
let loaded = await loadEveryStream(read, bundle, maxBytes, options.liveAfter);
|
|
261
296
|
if (loaded.refused.some((error) => error instanceof LiveFileUnreadableError)) {
|
|
262
297
|
const current = await rereadBundle(read, bundle.runId);
|
|
263
298
|
if (current !== undefined)
|
|
264
|
-
loaded = await loadEveryStream(read, current, maxBytes);
|
|
299
|
+
loaded = await loadEveryStream(read, current, maxBytes, options.liveAfter);
|
|
265
300
|
}
|
|
266
301
|
for (const file of loaded.files)
|
|
267
302
|
options.onTrace?.(file.path, file.bytes);
|
|
268
|
-
return { bundle: loaded.bundle, refused: loaded.refused };
|
|
303
|
+
return { bundle: loaded.bundle, refused: loaded.refused, live: loaded.live };
|
|
269
304
|
}
|
|
270
305
|
/** run.json as it is now, when it is a run bundle of run `runId`. */
|
|
271
306
|
async function rereadBundle(read, runId) {
|
|
@@ -280,7 +315,7 @@ async function rereadBundle(read, runId) {
|
|
|
280
315
|
return undefined;
|
|
281
316
|
}
|
|
282
317
|
}
|
|
283
|
-
async function loadEveryStream(read, bundle, maxBytes) {
|
|
318
|
+
async function loadEveryStream(read, bundle, maxBytes, liveAfter) {
|
|
284
319
|
const owners = new Map();
|
|
285
320
|
for (const stream of bundle.streams)
|
|
286
321
|
if (isRecord(stream.trace)) {
|
|
@@ -290,13 +325,16 @@ async function loadEveryStream(read, bundle, maxBytes) {
|
|
|
290
325
|
const streams = [];
|
|
291
326
|
const refused = [];
|
|
292
327
|
const files = [];
|
|
328
|
+
const live = [];
|
|
293
329
|
for (const stream of bundle.streams) {
|
|
294
330
|
try {
|
|
295
331
|
const reason = bundleRefusal(bundle, stream, owners);
|
|
296
332
|
if (reason !== undefined)
|
|
297
333
|
throw new StreamTraceRefusedError(stream.id, reason);
|
|
298
|
-
const loaded = await readStreamTrace(read, stream, maxBytes);
|
|
334
|
+
const loaded = await readStreamTrace(read, stream, maxBytes, liveAfter);
|
|
299
335
|
files.push(...loaded.files);
|
|
336
|
+
if (loaded.live !== undefined)
|
|
337
|
+
live.push(loaded.live);
|
|
300
338
|
streams.push(loaded.stream);
|
|
301
339
|
}
|
|
302
340
|
catch (error) {
|
|
@@ -306,7 +344,7 @@ async function loadEveryStream(read, bundle, maxBytes) {
|
|
|
306
344
|
streams.push(stream);
|
|
307
345
|
}
|
|
308
346
|
}
|
|
309
|
-
return { bundle: { ...bundle, streams }, refused, files };
|
|
347
|
+
return { bundle: { ...bundle, streams }, refused, files, live };
|
|
310
348
|
}
|
|
311
349
|
/**
|
|
312
350
|
* A loaded bundle as a reader that shows a run presents it. A refused stream keeps run.json's
|
package/dist/run/streams.d.ts
CHANGED
|
@@ -175,6 +175,15 @@ export interface RunParticipantAssignment {
|
|
|
175
175
|
goal: string;
|
|
176
176
|
}>;
|
|
177
177
|
}
|
|
178
|
+
/** One file humanish copied onto a participant's desktop before it started (participants[].files). */
|
|
179
|
+
export interface RunDesktopFile {
|
|
180
|
+
/** Where it landed, relative to the desktop user's home folder. */
|
|
181
|
+
as: string;
|
|
182
|
+
/** Its size in bytes, confirmed on the desktop. */
|
|
183
|
+
size: number;
|
|
184
|
+
/** The sha256 of the bytes humanish copied, 64 hex characters. */
|
|
185
|
+
sha256: string;
|
|
186
|
+
}
|
|
178
187
|
export interface RunStream {
|
|
179
188
|
id: string;
|
|
180
189
|
simId: string;
|
|
@@ -232,6 +241,13 @@ export interface RunStream {
|
|
|
232
241
|
};
|
|
233
242
|
/** Truthful screen/window/viewport evidence for hosted desktop browser participants. */
|
|
234
243
|
desktopGeometry?: RunDesktopGeometry;
|
|
244
|
+
/** The files copied onto this participant's desktop before it started. Never their contents. */
|
|
245
|
+
desktopFiles?: RunDesktopFile[];
|
|
246
|
+
/**
|
|
247
|
+
* The participant's time zone (participants[].clock, else actor.clock): its turns gave the time
|
|
248
|
+
* in it, and its hosted browser ran in it. Absent when it had none.
|
|
249
|
+
*/
|
|
250
|
+
timeZone?: string;
|
|
235
251
|
terminal?: {
|
|
236
252
|
title: string;
|
|
237
253
|
format: "ansi" | "plain";
|
package/dist/study/arrivals.d.ts
CHANGED
|
@@ -6,54 +6,118 @@ import type { RunParticipantArrival } from "../run/bundle.js";
|
|
|
6
6
|
export declare const LATEST_START_AFTER_MS: number;
|
|
7
7
|
/** Whether `value` is a start offset humanish runs: whole milliseconds from 0 to 24 hours. */
|
|
8
8
|
export declare function isStartAfterMs(value: unknown): value is number;
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
/**
|
|
10
|
+
* When a participant starts and how its session ends, resolved once from its participants entry
|
|
11
|
+
* when the plan builds the participant (participantSchedule). The plan, `study check`, the bundle
|
|
12
|
+
* estimate and the route read a participant's time from here.
|
|
13
|
+
*/
|
|
14
|
+
export interface ParticipantSchedule {
|
|
15
|
+
/** Its declared start, in ms after the run starts its participants; absent, with the run. */
|
|
14
16
|
readonly startAfterMs?: number;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* How its session ends, when it declares `endAfterMs` or `maxSessionMs`. Without either it works
|
|
19
|
+
* up to the study's session budget.
|
|
20
|
+
*/
|
|
21
|
+
readonly end?: ScheduleEnd;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A declared end. A `scheduled_end` is `endAfterMs` on the run's clock and holds the participant
|
|
25
|
+
* until then; a `session_cap` is `maxSessionMs` from the start of the participant's own session,
|
|
26
|
+
* which it may end sooner. `windowMs` is how long it may work: from its start to its end, or its
|
|
27
|
+
* cap. `windDownMs` is how long before the end each turn tells it the time left. `sessionMs` is its
|
|
28
|
+
* session budget and how long it holds a slot: the window plus SCHEDULED_END_ROOM_MS.
|
|
29
|
+
*/
|
|
30
|
+
type ScheduleEnd = {
|
|
31
|
+
readonly windowMs: number;
|
|
32
|
+
readonly windDownMs: number;
|
|
33
|
+
readonly sessionMs: number;
|
|
34
|
+
} & ({
|
|
35
|
+
readonly kind: "scheduled_end";
|
|
36
|
+
readonly endAfterMs: number;
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: "session_cap";
|
|
39
|
+
});
|
|
40
|
+
/** A participant as the schedule's readers name it. */
|
|
41
|
+
export interface ScheduledParticipant {
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly schedule: ParticipantSchedule;
|
|
17
44
|
}
|
|
18
45
|
/**
|
|
19
46
|
* Why a participant's declared end cannot run, or undefined: an end that is not a whole number of
|
|
20
|
-
* milliseconds up to 24 hours, one at or before its start,
|
|
21
|
-
*
|
|
47
|
+
* milliseconds up to 24 hours, one at or before its start, a cap that is not a whole number of
|
|
48
|
+
* milliseconds from 1 to 24 hours, a cap beside an end, or a wind-down that is not a whole number
|
|
49
|
+
* of milliseconds or has neither. `path` names the entry, as in "participants[1]".
|
|
22
50
|
*/
|
|
23
51
|
export declare function windowReason(entry: {
|
|
24
52
|
readonly id?: string;
|
|
25
53
|
readonly startAfterMs?: number;
|
|
26
54
|
readonly endAfterMs?: unknown;
|
|
55
|
+
readonly maxSessionMs?: unknown;
|
|
27
56
|
readonly windDownMs?: unknown;
|
|
28
57
|
}, path: string): string | undefined;
|
|
29
58
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
59
|
+
* The schedule of a participants entry that windowReason accepts. A cap wins over an end, which
|
|
60
|
+
* windowReason refuses beside it anyway.
|
|
32
61
|
*/
|
|
33
|
-
export declare function
|
|
62
|
+
export declare function participantSchedule(entry: {
|
|
63
|
+
readonly startAfterMs?: number;
|
|
64
|
+
readonly endAfterMs?: number;
|
|
65
|
+
readonly maxSessionMs?: number;
|
|
66
|
+
readonly windDownMs?: number;
|
|
67
|
+
}): ParticipantSchedule;
|
|
68
|
+
/**
|
|
69
|
+
* The schedule a rerun gives a participant: it starts with the rerun, and a scheduled end keeps its
|
|
70
|
+
* time on the rerun's clock and its wind-down, so its window runs from the rerun's start.
|
|
71
|
+
*/
|
|
72
|
+
export declare function startingWithTheRun(schedule: ParticipantSchedule): ParticipantSchedule;
|
|
73
|
+
/**
|
|
74
|
+
* A participant's own session budget and sandbox deadline. With an end or a cap it is the end's
|
|
75
|
+
* `sessionMs`, and the sandbox keeps the study sandbox's headroom over the study session on top;
|
|
76
|
+
* without one, the study's own.
|
|
77
|
+
*/
|
|
78
|
+
export declare function participantSession(schedule: ParticipantSchedule, study: {
|
|
79
|
+
readonly sessionMs: number;
|
|
80
|
+
readonly sandboxMs: number;
|
|
81
|
+
}): {
|
|
82
|
+
sessionMs: number;
|
|
83
|
+
sandboxMs: number;
|
|
84
|
+
};
|
|
34
85
|
/**
|
|
35
|
-
* When a participant
|
|
36
|
-
*
|
|
37
|
-
* at its declared time.
|
|
86
|
+
* When a participant's session ends: at a time on the run's clock, or at the latest a time after
|
|
87
|
+
* its own session starts, which the route reads when the session starts.
|
|
38
88
|
*/
|
|
39
|
-
export
|
|
89
|
+
export type ParticipantEnd = {
|
|
90
|
+
readonly kind: "scheduled_end";
|
|
40
91
|
readonly atMs: number;
|
|
41
92
|
readonly windDownMs: number;
|
|
42
|
-
} |
|
|
93
|
+
} | {
|
|
94
|
+
readonly kind: "session_cap";
|
|
95
|
+
readonly afterMs: number;
|
|
96
|
+
readonly windDownMs: number;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* How a participant that was due at `scheduledAt` (runOnSchedule's ScheduledStart) ends, or
|
|
100
|
+
* undefined without an end or a cap. A scheduled end comes its window after the participant's
|
|
101
|
+
* time, which is its `endAfterMs` on the run's clock, even when it waited for a slot; a cap is
|
|
102
|
+
* whole from wherever its session starts.
|
|
103
|
+
*/
|
|
104
|
+
export declare function participantEnd(schedule: ParticipantSchedule, scheduledAt: number): ParticipantEnd | undefined;
|
|
43
105
|
/**
|
|
44
|
-
* Why the sandbox of the first participant whose window passes `ceilingMs` cannot live that
|
|
45
|
-
* or undefined. Its deadline is its
|
|
46
|
-
*
|
|
106
|
+
* Why the sandbox of the first participant whose window or cap passes `ceilingMs` cannot live that
|
|
107
|
+
* long, or undefined. Its deadline is its participantSession's, from the study's session budget and
|
|
108
|
+
* sandbox deadline.
|
|
47
109
|
*/
|
|
48
|
-
export declare function windowDeadlineRefusal(participants: readonly
|
|
110
|
+
export declare function windowDeadlineRefusal(participants: readonly ScheduledParticipant[], study: {
|
|
49
111
|
readonly sessionMs: number;
|
|
50
|
-
readonly
|
|
112
|
+
readonly sandboxMs: number;
|
|
51
113
|
}, ceilingMs: number): string | undefined;
|
|
52
114
|
/**
|
|
53
|
-
* Each participant's window for `study check`, when any participant declares an end: when
|
|
54
|
-
* and ends and when it is first told the time left,
|
|
115
|
+
* Each participant's window for `study check`, when any participant declares an end or a cap: when
|
|
116
|
+
* it starts and ends and when it is first told the time left, when it starts and its cap and how
|
|
117
|
+
* far into its session it is first told the time left, or when it starts and the study's
|
|
118
|
+
* `sessionMs`.
|
|
55
119
|
*/
|
|
56
|
-
export declare function describeWindows(participants: readonly
|
|
120
|
+
export declare function describeWindows(participants: readonly ScheduledParticipant[], sessionMs: number): string | undefined;
|
|
57
121
|
/** The refusal of a start offset that is not one, naming its `field`. */
|
|
58
122
|
export declare function startAfterMessage(field: string): string;
|
|
59
123
|
/**
|
|
@@ -94,13 +158,13 @@ export interface StartQueue {
|
|
|
94
158
|
readonly ownSlot?: number;
|
|
95
159
|
}
|
|
96
160
|
/**
|
|
97
|
-
* The schedule a roster's
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
161
|
+
* The schedule a roster's participants give under the queue. Participants start in schedule order,
|
|
162
|
+
* by offset and then roster position, each at its time or when a slot frees, whichever is later;
|
|
163
|
+
* this is the rule runOnSchedule follows. A participant with an end or a cap holds its slot for its
|
|
164
|
+
* own session budget, and the others for `queue.sessionMs`. A participant whose slot would free
|
|
165
|
+
* only at or after its scheduled end takes none: the run skips it (pastEndReason).
|
|
102
166
|
*/
|
|
103
|
-
export declare function planArrivals(
|
|
167
|
+
export declare function planArrivals(schedules: readonly ParticipantSchedule[], queue: StartQueue): ArrivalPlan;
|
|
104
168
|
/**
|
|
105
169
|
* The plan's schedule, after a "schedule:" label: the first and last start, and how many run at
|
|
106
170
|
* once at the most.
|
|
@@ -110,13 +174,19 @@ export declare function describeArrivals(arrivals: ArrivalPlan): string;
|
|
|
110
174
|
* A planner's arrival plan for its participants under the start queue, and the warnings the plan
|
|
111
175
|
* records about it. A participant's desktop is created when it starts, so on computer use a late
|
|
112
176
|
* start adds nothing to any sandbox's deadline; the provisioned shared world checks its app's
|
|
113
|
-
* sandbox with servedScheduleRefusal. `lowered` is
|
|
114
|
-
* roster in waves.
|
|
177
|
+
* sandbox with servedScheduleRefusal. `lowered` is participantsAtOnce's warning that the E2B plan's
|
|
178
|
+
* limit or HUMANISH_CUA_MAX_CONCURRENCY runs the roster in waves.
|
|
115
179
|
*/
|
|
116
|
-
export declare function planStarts(participants: readonly
|
|
180
|
+
export declare function planStarts(participants: readonly ScheduledParticipant[], queue: StartQueue, lowered: string | undefined): {
|
|
117
181
|
arrivals: ArrivalPlan;
|
|
118
182
|
warnings: string[];
|
|
119
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* Why a participant whose start comes at `nowMs` is skipped, or undefined: its scheduled end came
|
|
186
|
+
* while it waited, so its session would end before it began. The plan's simulation applies the
|
|
187
|
+
* same rule (planArrivals).
|
|
188
|
+
*/
|
|
189
|
+
export declare function pastEndReason(end: ParticipantEnd | undefined, nowMs: number): string | undefined;
|
|
120
190
|
/**
|
|
121
191
|
* Why a sandbox that serves the app until the last participant ends, the provisioned shared
|
|
122
192
|
* world's subject sandbox, cannot live that long under `ceilingMs`; undefined when it can. Its
|
|
@@ -159,3 +229,4 @@ export declare function runOnSchedule<T, R>(items: readonly T[], options: {
|
|
|
159
229
|
readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
160
230
|
readonly signal?: AbortSignal;
|
|
161
231
|
}, start: (item: T, index: number, scheduled: ScheduledStart) => Promise<R>): Promise<R[]>;
|
|
232
|
+
export {};
|