humanish 0.126.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 +13 -0
- package/dist/actors/computer-use/loop/ending.d.ts +3 -1
- package/dist/actors/computer-use/loop/ending.js +5 -3
- package/dist/actors/computer-use/openai-provider.js +1 -1
- package/dist/actors/goal-source.d.ts +32 -2
- package/dist/actors/goal-source.js +41 -2
- 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/observer/data.js +8 -5
- package/dist/observer-app.html +1 -1
- package/dist/routes/computer-use/fanout-bundle.js +3 -0
- package/dist/routes/computer-use/fanout-records.js +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-runs.d.ts +9 -9
- package/dist/routes/computer-use/participant-runs.js +52 -86
- package/dist/routes/computer-use/plan.d.ts +2 -0
- package/dist/routes/computer-use/plan.js +26 -16
- package/dist/routes/computer-use/rerun-selection.js +8 -5
- package/dist/routes/computer-use/setup.js +1 -1
- package/dist/routes/computer-use/single-bundle.js +9 -1
- package/dist/routes/computer-use/types.d.ts +2 -3
- package/dist/routes/computer-use/types.js +0 -3
- package/dist/routes/shared-world/bundle-records.js +1 -1
- package/dist/routes/shared-world/external-public.js +1 -1
- package/dist/routes/shared-world/plan.d.ts +2 -0
- package/dist/routes/shared-world/plan.js +3 -3
- package/dist/routes/shared-world/provisioned.js +1 -1
- package/dist/run/bundle.d.ts +11 -1
- package/dist/run/judge.d.ts +7 -0
- package/dist/run/judge.js +4 -4
- package/dist/run/notes.js +1 -1
- package/dist/run/outcomes.d.ts +8 -2
- package/dist/run/outcomes.js +56 -14
- package/dist/study/arrivals.d.ts +84 -31
- package/dist/study/arrivals.js +165 -86
- package/dist/study/bundle-estimate.d.ts +8 -7
- package/dist/study/bundle-estimate.js +131 -70
- package/dist/study/desktop-files.js +4 -4
- package/dist/study/plan-participants.d.ts +4 -9
- package/dist/study/plan-participants.js +2 -14
- package/dist/study/plan-types.d.ts +6 -1
- package/dist/study/plan.d.ts +2 -1
- package/dist/study/plan.js +6 -4
- package/dist/study/types.d.ts +2 -1
- package/dist/substrates/e2b/lifetime.d.ts +22 -8
- package/dist/substrates/e2b/lifetime.js +54 -10
- package/docs/contracts/run-bundle.md +1 -1
- package/docs/contracts/schemas.md +24 -11
- package/package.json +1 -1
package/dist/run/judge.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ActorCompletionReason, ActorStatus, ParticipantDeclaredOutcome } from "../actors/contract.js";
|
|
2
|
+
import type { ClockEnd } from "../actors/goal-source.js";
|
|
2
3
|
import type { ReviewSummary } from "./bundle.js";
|
|
3
4
|
export type Verdict = ReviewSummary["verdict"];
|
|
4
5
|
/** How a finished session ended, as the blocker rule reads it. */
|
|
@@ -43,6 +44,12 @@ export interface ParticipantFacts {
|
|
|
43
44
|
noEngagement: boolean;
|
|
44
45
|
/** selfReportedBlocker held for its session. */
|
|
45
46
|
selfReportedBlocker: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Its session ran until its scheduled end or session cap without the participant declaring it
|
|
49
|
+
* reached the goal (ranUntilClockEnd). A pass counts under `ranUntilEnd` in place of
|
|
50
|
+
* `reachedGoal`.
|
|
51
|
+
*/
|
|
52
|
+
ranUntil?: ClockEnd;
|
|
46
53
|
}
|
|
47
54
|
/** The participant facts its bundle and result can name. */
|
|
48
55
|
export interface ParticipantRecordFacts extends ParticipantFacts {
|
package/dist/run/judge.js
CHANGED
|
@@ -68,12 +68,12 @@ export function judgeParticipantRecords(participants, wording = {}) {
|
|
|
68
68
|
: `${participant.id ?? "Participant"}: ${participant.skippedReason ?? participant.sessionError ?? participant.reason ?? "did not pass"}`,
|
|
69
69
|
};
|
|
70
70
|
});
|
|
71
|
-
const
|
|
71
|
+
const ended = (participant) => participant.status !== undefined && !participant.inProgress;
|
|
72
|
+
const terminal = records.filter((_, index) => ended(participants[index]));
|
|
73
|
+
const terminalFacts = participants.filter(ended);
|
|
72
74
|
return {
|
|
73
75
|
participants: records,
|
|
74
|
-
tally: tallyParticipantOutcomes(terminal.map((participant) => participant.judgedStatus),
|
|
75
|
-
.filter((participant) => participant.status !== undefined && !participant.inProgress)
|
|
76
|
-
.map((participant) => participant.reportedFriction === true)),
|
|
76
|
+
tally: tallyParticipantOutcomes(terminal.map((participant) => participant.judgedStatus), terminalFacts.map((participant) => participant.reportedFriction === true), terminalFacts.map((participant) => participant.ranUntil !== undefined)),
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
function participantNotPassedMessage(participant, wording) {
|
package/dist/run/notes.js
CHANGED
|
@@ -21,7 +21,7 @@ const refuse = (code, message) => ({
|
|
|
21
21
|
});
|
|
22
22
|
/**
|
|
23
23
|
* The stream id a note's participant names: a stream id, or the participant's own id from the study
|
|
24
|
-
* (`
|
|
24
|
+
* (`support-lead`). Both kinds of id are matched, so a name that is one stream's id and another
|
|
25
25
|
* stream's study id is refused, naming both, as is a study id that two streams record. Otherwise
|
|
26
26
|
* the refusal names the run's participants by caption, with the id to type for each.
|
|
27
27
|
*/
|
package/dist/run/outcomes.d.ts
CHANGED
|
@@ -6,7 +6,13 @@ import type { ParticipantOutcomes, ReviewSummary, RunTaskFunnel } from "./bundle
|
|
|
6
6
|
* `total` but nowhere else, so the parts can never exceed the whole. */
|
|
7
7
|
export declare function tallyParticipantOutcomes(statuses: readonly ActorStatus[],
|
|
8
8
|
/** Per-participant: did this one report friction or a defect? Same order as `statuses`. */
|
|
9
|
-
reportedFriction?: readonly boolean[]
|
|
9
|
+
reportedFriction?: readonly boolean[],
|
|
10
|
+
/**
|
|
11
|
+
* Per-participant: did its session run until its scheduled end or session cap without it
|
|
12
|
+
* declaring that it reached the goal (ranUntilClockEnd)? Same order as `statuses`. Such a pass
|
|
13
|
+
* counts under `ranUntilEnd` in place of `reachedGoal`.
|
|
14
|
+
*/
|
|
15
|
+
ranUntilEnd?: readonly boolean[]): ParticipantOutcomes;
|
|
10
16
|
/** Roll per-participant funnels up into the study funnel. Undefined when nothing measured one. */
|
|
11
17
|
export declare function aggregateTaskFunnels(funnels: readonly TaskFunnel[]): RunTaskFunnel | undefined;
|
|
12
18
|
/** The funnel as one line, denominator on every number: `signup 2/2 · verify-email 1/2`. */
|
|
@@ -53,7 +59,7 @@ export declare function formatParticipantOutcomes(outcomes: ParticipantOutcomes,
|
|
|
53
59
|
export declare function participantOutcomeDetails(streams: readonly {
|
|
54
60
|
actor?: unknown;
|
|
55
61
|
status?: unknown;
|
|
56
|
-
}[]): ParticipantOutcomeDetail[];
|
|
62
|
+
}[], goalSourceOf?: (actor: unknown, recordedStatus: unknown) => CuaGoalSource | undefined): ParticipantOutcomeDetail[];
|
|
57
63
|
/** Refresh a CUA completion claim from recorded traces, leaving original evidence and enums intact. */
|
|
58
64
|
export declare function withCuaReviewProvenance(review: ReviewSummary, streams: readonly {
|
|
59
65
|
actor?: unknown;
|
package/dist/run/outcomes.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { ACTOR_STATUSES } from "../actors/contract.js";
|
|
2
|
-
import { cuaGoalSource, isCuaTrace, CUA_COMPLETION_NOTE, } from "../actors/goal-source.js";
|
|
3
|
-
import { actorEnding } from "../actors/stop-cause.js";
|
|
2
|
+
import { cuaEndpointSource, cuaGoalSource, isClockEnd, isCuaTrace, CUA_COMPLETION_NOTE, } from "../actors/goal-source.js";
|
|
3
|
+
import { actorEnding, actorStopCauseLabel } from "../actors/stop-cause.js";
|
|
4
4
|
import { plural } from "./text.js";
|
|
5
5
|
import { isNonNegativeSafeInteger, isRecord } from "./type-guards.js";
|
|
6
6
|
/** Tally participant outcomes from actor statuses. Statuses this does not recognise are counted in
|
|
7
7
|
* `total` but nowhere else, so the parts can never exceed the whole. */
|
|
8
8
|
export function tallyParticipantOutcomes(statuses,
|
|
9
9
|
/** Per-participant: did this one report friction or a defect? Same order as `statuses`. */
|
|
10
|
-
reportedFriction = []
|
|
10
|
+
reportedFriction = [],
|
|
11
|
+
/**
|
|
12
|
+
* Per-participant: did its session run until its scheduled end or session cap without it
|
|
13
|
+
* declaring that it reached the goal (ranUntilClockEnd)? Same order as `statuses`. Such a pass
|
|
14
|
+
* counts under `ranUntilEnd` in place of `reachedGoal`.
|
|
15
|
+
*/
|
|
16
|
+
ranUntilEnd = []) {
|
|
11
17
|
const tally = {
|
|
12
18
|
total: statuses.length,
|
|
13
19
|
reachedGoal: 0,
|
|
@@ -17,8 +23,11 @@ reportedFriction = []) {
|
|
|
17
23
|
harnessFailed: 0,
|
|
18
24
|
reportedFriction: reportedFriction.filter(Boolean).length,
|
|
19
25
|
};
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
let clockEnds = 0;
|
|
27
|
+
statuses.forEach((status, index) => {
|
|
28
|
+
if (status === "passed" && ranUntilEnd[index] === true)
|
|
29
|
+
clockEnds += 1;
|
|
30
|
+
else if (status === "passed")
|
|
22
31
|
tally.reachedGoal += 1;
|
|
23
32
|
else if (status === "abandoned")
|
|
24
33
|
tally.abandoned += 1;
|
|
@@ -28,8 +37,8 @@ reportedFriction = []) {
|
|
|
28
37
|
tally.blocked += 1;
|
|
29
38
|
else if (status === "failed")
|
|
30
39
|
tally.harnessFailed += 1;
|
|
31
|
-
}
|
|
32
|
-
return tally;
|
|
40
|
+
});
|
|
41
|
+
return clockEnds === 0 ? tally : { ...tally, ranUntilEnd: clockEnds };
|
|
33
42
|
}
|
|
34
43
|
/** Roll per-participant funnels up into the study funnel. Undefined when nothing measured one. */
|
|
35
44
|
export function aggregateTaskFunnels(funnels) {
|
|
@@ -86,15 +95,38 @@ export function formatRunTaskFunnel(funnel) {
|
|
|
86
95
|
})
|
|
87
96
|
.join(" · ");
|
|
88
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* "1 ran until its session cap", "2 ran until their scheduled end", grouped by clock end, most
|
|
100
|
+
* first. Clock ends the details cannot account for get one line that names both.
|
|
101
|
+
*/
|
|
102
|
+
function ranUntilPhrases(count, details) {
|
|
103
|
+
const words = (n, end) => n === 1 ? `1 ran until its ${end}` : `${n} ran until their ${end}`;
|
|
104
|
+
const ends = details.flatMap((entry) => (isClockEnd(entry.goalSource) ? [entry.goalSource] : []));
|
|
105
|
+
if (ends.length !== count)
|
|
106
|
+
return [words(count, "scheduled end or session cap")];
|
|
107
|
+
return ["session_cap", "scheduled_end"]
|
|
108
|
+
.map((end) => [end, ends.filter((entry) => entry === end).length])
|
|
109
|
+
.filter(([, n]) => n > 0)
|
|
110
|
+
.sort(([, m], [, n]) => n - m)
|
|
111
|
+
.map(([end, n]) => words(n, actorStopCauseLabel(end)));
|
|
112
|
+
}
|
|
89
113
|
function participantCompletionLine(outcomes, terminalCauses) {
|
|
90
|
-
const
|
|
114
|
+
const passed = terminalCauses.filter((entry) => entry.status === "passed");
|
|
115
|
+
// A bundle without `ranUntilEnd` either has no such clock end or predates the count and tallied
|
|
116
|
+
// its clock ends under reachedGoal. Those stay among its completions, and the breakdown names
|
|
117
|
+
// them, so a re-read never changes a recorded count.
|
|
118
|
+
const completions = outcomes.ranUntilEnd === undefined
|
|
119
|
+
? passed
|
|
120
|
+
: passed.filter((entry) => !isClockEnd(entry.goalSource));
|
|
91
121
|
const reported = completions.filter((entry) => entry.goalSource === "participant_report").length;
|
|
92
122
|
const matched = completions.filter((entry) => entry.goalSource === "condition_matched").length;
|
|
123
|
+
const clockEnds = completions.filter((entry) => isClockEnd(entry.goalSource));
|
|
93
124
|
let goalLine = outcomes.reachedGoal === 0
|
|
94
125
|
? `0/${outcomes.total} recorded completions`
|
|
95
126
|
: `${outcomes.reachedGoal}/${outcomes.total} reached the goal`;
|
|
96
127
|
if (outcomes.reachedGoal > 0 && terminalCauses.some((entry) => entry.goalSource !== undefined)) {
|
|
97
128
|
const count = `${outcomes.reachedGoal}/${outcomes.total}`;
|
|
129
|
+
const other = outcomes.reachedGoal - reported - matched - clockEnds.length;
|
|
98
130
|
goalLine =
|
|
99
131
|
completions.length !== outcomes.reachedGoal
|
|
100
132
|
? `${count} recorded completions (completion source unavailable)`
|
|
@@ -105,9 +137,8 @@ function participantCompletionLine(outcomes, terminalCauses) {
|
|
|
105
137
|
: `${count} recorded completions (${[
|
|
106
138
|
reported > 0 ? `${reported} participant-reported` : undefined,
|
|
107
139
|
matched > 0 ? `${matched} condition-matched` : undefined,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
: undefined,
|
|
140
|
+
...(clockEnds.length > 0 ? ranUntilPhrases(clockEnds.length, clockEnds) : []),
|
|
141
|
+
other > 0 ? `${other} other or unavailable source` : undefined,
|
|
111
142
|
]
|
|
112
143
|
.filter(Boolean)
|
|
113
144
|
.join(", ")})`;
|
|
@@ -124,6 +155,8 @@ export function sessionEndingInWords(trace) {
|
|
|
124
155
|
const source = cuaGoalSource(trace);
|
|
125
156
|
if (source === "participant_report")
|
|
126
157
|
return "reported reaching the goal";
|
|
158
|
+
if (isClockEnd(source))
|
|
159
|
+
return `ran until its ${actorStopCauseLabel(source)}`;
|
|
127
160
|
return source === "condition_matched" ? "met its completion condition" : "reached the goal";
|
|
128
161
|
}
|
|
129
162
|
case "turn_completed":
|
|
@@ -178,6 +211,10 @@ export function formatParticipantOutcomes(outcomes, terminalCauses = []) {
|
|
|
178
211
|
if (outcomes.total === 0)
|
|
179
212
|
return "no participants reached a terminal state";
|
|
180
213
|
const parts = [participantCompletionLine(outcomes, terminalCauses)];
|
|
214
|
+
// Passed at the clock without declaring that they reached the goal: next to the completions,
|
|
215
|
+
// and never counted among them.
|
|
216
|
+
if (outcomes.ranUntilEnd !== undefined && outcomes.ranUntilEnd > 0)
|
|
217
|
+
parts.push(...ranUntilPhrases(outcomes.ranUntilEnd, terminalCauses.filter((entry) => entry.status === "passed")));
|
|
181
218
|
// Detail may explain a recorded outcome, but must never change its count or invent a match
|
|
182
219
|
// between a tally and an incomplete set of traces.
|
|
183
220
|
const append = (count, statuses, fallback) => {
|
|
@@ -212,12 +249,12 @@ export function formatParticipantOutcomes(outcomes, terminalCauses = []) {
|
|
|
212
249
|
return parts.join(", ");
|
|
213
250
|
}
|
|
214
251
|
/** Presentation details tolerate optional legacy actor payloads without changing their tallies. */
|
|
215
|
-
export function participantOutcomeDetails(streams) {
|
|
252
|
+
export function participantOutcomeDetails(streams, goalSourceOf = cuaGoalSource) {
|
|
216
253
|
return streams.flatMap((stream) => {
|
|
217
254
|
if (!isRecord(stream.actor))
|
|
218
255
|
return [];
|
|
219
256
|
const actor = stream.actor;
|
|
220
|
-
const goalSource =
|
|
257
|
+
const goalSource = goalSourceOf(actor, stream.status);
|
|
221
258
|
if (!ACTOR_STATUSES.includes(String(actor.status))) {
|
|
222
259
|
return goalSource === "unavailable" ? [{ status: "passed", goalSource }] : [];
|
|
223
260
|
}
|
|
@@ -257,6 +294,9 @@ export function withCuaReviewProvenance(review, streams) {
|
|
|
257
294
|
const { reachedGoal, total } = review.participants;
|
|
258
295
|
const bareLine = `${reachedGoal}/${total} reached the goal`;
|
|
259
296
|
const qualifiedLine = participantCompletionLine(review.participants, details);
|
|
297
|
+
// Earlier releases qualified the count with the endpoint source (cuaEndpointSource), which
|
|
298
|
+
// reads a clock end as a participant report.
|
|
299
|
+
const endpointLine = participantCompletionLine(review.participants, participantOutcomeDetails(streams, cuaEndpointSource));
|
|
260
300
|
// A summary with no count gets it in a header. Earlier releases wrote a header in other words.
|
|
261
301
|
const header = `${plural(total, "participant")} took part: ${outcomes}.${tasks} `;
|
|
262
302
|
const olderHeader = `Run gate: ${review.verdict}. Participants: ${outcomes}.${tasks} Recorded summary: `;
|
|
@@ -266,7 +306,9 @@ export function withCuaReviewProvenance(review, streams) {
|
|
|
266
306
|
? review.summary.split(bareLine).join(qualifiedLine)
|
|
267
307
|
: review.summary.includes(qualifiedLine)
|
|
268
308
|
? review.summary
|
|
269
|
-
:
|
|
309
|
+
: review.summary.includes(endpointLine)
|
|
310
|
+
? review.summary.split(endpointLine).join(qualifiedLine)
|
|
311
|
+
: `${header}${review.summary}`;
|
|
270
312
|
return {
|
|
271
313
|
...review,
|
|
272
314
|
summary,
|
package/dist/study/arrivals.d.ts
CHANGED
|
@@ -6,18 +6,41 @@ 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
|
-
/** How long before its end a participant is told the time left, unless it declares `windDownMs`. */
|
|
10
|
-
export declare const DEFAULT_WIND_DOWN_MS: number;
|
|
11
9
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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.
|
|
14
13
|
*/
|
|
15
|
-
export interface
|
|
16
|
-
|
|
14
|
+
export interface ParticipantSchedule {
|
|
15
|
+
/** Its declared start, in ms after the run starts its participants; absent, with the run. */
|
|
17
16
|
readonly startAfterMs?: number;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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;
|
|
21
44
|
}
|
|
22
45
|
/**
|
|
23
46
|
* Why a participant's declared end cannot run, or undefined: an end that is not a whole number of
|
|
@@ -33,11 +56,32 @@ export declare function windowReason(entry: {
|
|
|
33
56
|
readonly windDownMs?: unknown;
|
|
34
57
|
}, path: string): string | undefined;
|
|
35
58
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
59
|
+
* The schedule of a participants entry that windowReason accepts. A cap wins over an end, which
|
|
60
|
+
* windowReason refuses beside it anyway.
|
|
61
|
+
*/
|
|
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.
|
|
39
71
|
*/
|
|
40
|
-
export declare function
|
|
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
|
+
};
|
|
41
85
|
/**
|
|
42
86
|
* When a participant's session ends: at a time on the run's clock, or at the latest a time after
|
|
43
87
|
* its own session starts, which the route reads when the session starts.
|
|
@@ -53,25 +97,27 @@ export type ParticipantEnd = {
|
|
|
53
97
|
};
|
|
54
98
|
/**
|
|
55
99
|
* How a participant that was due at `scheduledAt` (runOnSchedule's ScheduledStart) ends, or
|
|
56
|
-
* undefined
|
|
57
|
-
*
|
|
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.
|
|
58
103
|
*/
|
|
59
|
-
export declare function participantEnd(
|
|
104
|
+
export declare function participantEnd(schedule: ParticipantSchedule, scheduledAt: number): ParticipantEnd | undefined;
|
|
60
105
|
/**
|
|
61
106
|
* Why the sandbox of the first participant whose window or cap passes `ceilingMs` cannot live that
|
|
62
|
-
* long, or undefined. Its deadline is its
|
|
63
|
-
*
|
|
107
|
+
* long, or undefined. Its deadline is its participantSession's, from the study's session budget and
|
|
108
|
+
* sandbox deadline.
|
|
64
109
|
*/
|
|
65
|
-
export declare function windowDeadlineRefusal(participants: readonly
|
|
110
|
+
export declare function windowDeadlineRefusal(participants: readonly ScheduledParticipant[], study: {
|
|
66
111
|
readonly sessionMs: number;
|
|
67
|
-
readonly
|
|
112
|
+
readonly sandboxMs: number;
|
|
68
113
|
}, ceilingMs: number): string | undefined;
|
|
69
114
|
/**
|
|
70
115
|
* Each participant's window for `study check`, when any participant declares an end or a cap: when
|
|
71
116
|
* it starts and ends and when it is first told the time left, when it starts and its cap and how
|
|
72
|
-
* far into its session it is first told the time left, or when it starts and
|
|
117
|
+
* far into its session it is first told the time left, or when it starts and the study's
|
|
118
|
+
* `sessionMs`.
|
|
73
119
|
*/
|
|
74
|
-
export declare function describeWindows(participants: readonly
|
|
120
|
+
export declare function describeWindows(participants: readonly ScheduledParticipant[], sessionMs: number): string | undefined;
|
|
75
121
|
/** The refusal of a start offset that is not one, naming its `field`. */
|
|
76
122
|
export declare function startAfterMessage(field: string): string;
|
|
77
123
|
/**
|
|
@@ -112,13 +158,13 @@ export interface StartQueue {
|
|
|
112
158
|
readonly ownSlot?: number;
|
|
113
159
|
}
|
|
114
160
|
/**
|
|
115
|
-
* The schedule a roster's
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
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).
|
|
120
166
|
*/
|
|
121
|
-
export declare function planArrivals(
|
|
167
|
+
export declare function planArrivals(schedules: readonly ParticipantSchedule[], queue: StartQueue): ArrivalPlan;
|
|
122
168
|
/**
|
|
123
169
|
* The plan's schedule, after a "schedule:" label: the first and last start, and how many run at
|
|
124
170
|
* once at the most.
|
|
@@ -128,13 +174,19 @@ export declare function describeArrivals(arrivals: ArrivalPlan): string;
|
|
|
128
174
|
* A planner's arrival plan for its participants under the start queue, and the warnings the plan
|
|
129
175
|
* records about it. A participant's desktop is created when it starts, so on computer use a late
|
|
130
176
|
* start adds nothing to any sandbox's deadline; the provisioned shared world checks its app's
|
|
131
|
-
* sandbox with servedScheduleRefusal. `lowered` is
|
|
132
|
-
* 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.
|
|
133
179
|
*/
|
|
134
|
-
export declare function planStarts(participants: readonly
|
|
180
|
+
export declare function planStarts(participants: readonly ScheduledParticipant[], queue: StartQueue, lowered: string | undefined): {
|
|
135
181
|
arrivals: ArrivalPlan;
|
|
136
182
|
warnings: string[];
|
|
137
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;
|
|
138
190
|
/**
|
|
139
191
|
* Why a sandbox that serves the app until the last participant ends, the provisioned shared
|
|
140
192
|
* world's subject sandbox, cannot live that long under `ceilingMs`; undefined when it can. Its
|
|
@@ -177,3 +229,4 @@ export declare function runOnSchedule<T, R>(items: readonly T[], options: {
|
|
|
177
229
|
readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
178
230
|
readonly signal?: AbortSignal;
|
|
179
231
|
}, start: (item: T, index: number, scheduled: ScheduledStart) => Promise<R>): Promise<R[]>;
|
|
232
|
+
export {};
|