humanish 0.112.0 → 0.114.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/CONTEXT.md +1 -0
- package/README.md +12 -1
- package/TELEMETRY.md +21 -0
- package/dist/actors/codex/restricted-participant-policy.d.ts +21 -1
- package/dist/actors/codex/restricted-participant-policy.js +20 -2
- package/dist/actors/codex/restricted-participant.d.ts +1 -1
- package/dist/actors/codex/restricted-participant.js +2 -1
- package/dist/actors/computer-use/loop/debrief.d.ts +12 -4
- package/dist/actors/computer-use/loop/debrief.js +57 -20
- package/dist/actors/computer-use/loop/ending.d.ts +5 -2
- package/dist/actors/computer-use/loop/impressions.d.ts +14 -0
- package/dist/actors/computer-use/loop/impressions.js +40 -0
- package/dist/actors/computer-use/loop/session.d.ts +3 -1
- package/dist/actors/computer-use/loop/session.js +2 -0
- package/dist/actors/computer-use/loop/trace.js +2 -0
- package/dist/actors/computer-use/loop/types.d.ts +8 -1
- package/dist/actors/computer-use/loop.d.ts +2 -1
- package/dist/actors/computer-use/loop.js +15 -3
- package/dist/actors/computer-use/openai-conversation.d.ts +9 -4
- package/dist/actors/computer-use/openai-conversation.js +60 -29
- package/dist/actors/computer-use/openai-provider.js +18 -12
- package/dist/actors/contract.d.ts +47 -5
- package/dist/actors/contract.js +13 -0
- package/dist/analysis/admission.d.ts +37 -0
- package/dist/analysis/admission.js +60 -0
- package/dist/analysis/automatic-completion.d.ts +0 -6
- package/dist/analysis/automatic-completion.js +3 -3
- package/dist/analysis/automatic-config.d.ts +10 -2
- package/dist/analysis/automatic-config.js +29 -14
- package/dist/analysis/automatic.js +7 -0
- package/dist/analysis/evidence-sources.d.ts +3 -1
- package/dist/analysis/evidence-sources.js +10 -1
- package/dist/analysis/evidence.js +2 -2
- package/dist/analysis/execute.d.ts +20 -6
- package/dist/analysis/execute.js +61 -28
- package/dist/analysis/image-tokens.d.ts +2 -0
- package/dist/analysis/image-tokens.js +1 -1
- package/dist/analysis/job.d.ts +9 -1
- package/dist/analysis/job.js +11 -2
- package/dist/analysis/service.js +6 -3
- package/dist/cli/commands/analyze.js +7 -2
- package/dist/cli/commands/notes.d.ts +3 -0
- package/dist/cli/commands/notes.js +144 -0
- package/dist/cli/commands/observe.js +1 -1
- package/dist/cli/commands/runs.js +33 -10
- package/dist/cli/commands/setup.js +95 -6
- package/dist/cli/commands/study-format.js +7 -4
- package/dist/cli/commands/study-run.js +2 -1
- package/dist/cli/commands/study.js +1 -1
- package/dist/cli/commands/tui.js +1 -1
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.js +18 -0
- package/dist/cli/findings.js +4 -1
- package/dist/cli/io.d.ts +3 -0
- package/dist/cli/io.js +11 -12
- package/dist/cli/program.js +37 -1
- package/dist/cli/secret-prompt.d.ts +5 -1
- package/dist/cli/secret-prompt.js +6 -2
- package/dist/cli/update-check-worker.d.ts +1 -0
- package/dist/cli/update-check-worker.js +5 -0
- package/dist/cli/update-check.d.ts +46 -0
- package/dist/cli/update-check.js +203 -0
- package/dist/evidence/encoded-text.d.ts +15 -2
- package/dist/evidence/encoded-text.js +68 -4
- package/dist/evidence/secret-scrub.d.ts +1 -1
- package/dist/evidence/secret-scrub.js +23 -2
- package/dist/feedback/draft.d.ts +19 -0
- package/dist/feedback/draft.js +69 -1
- package/dist/feedback/export-bundle.js +2 -2
- package/dist/feedback/export.js +47 -3
- package/dist/feedback/feedback.js +42 -2
- package/dist/keys/key-resolution.js +2 -0
- package/dist/keys/key-status.d.ts +37 -0
- package/dist/keys/key-status.js +68 -0
- package/dist/observer/artifact.d.ts +5 -0
- package/dist/observer/artifact.js +18 -2
- package/dist/observer/notes-route.d.ts +21 -0
- package/dist/observer/notes-route.js +140 -0
- package/dist/observer/pinned-files.d.ts +8 -1
- package/dist/observer/pinned-files.js +32 -3
- package/dist/observer/render.js +38 -14
- package/dist/observer/run-routes.d.ts +8 -2
- package/dist/observer/run-routes.js +47 -6
- package/dist/observer/serve.d.ts +3 -0
- package/dist/observer/serve.js +28 -3
- package/dist/observer-app.html +7 -6
- package/dist/routes/computer-use/local-vm.js +9 -2
- package/dist/routes/computer-use/participant-desktop.d.ts +6 -0
- package/dist/routes/computer-use/participant-execution.js +9 -1
- package/dist/routes/computer-use/rerun-selection.js +23 -21
- package/dist/routes/computer-use/result.js +3 -2
- package/dist/routes/computer-use/self-report.js +7 -1
- package/dist/routes/computer-use/setup.js +14 -0
- package/dist/routes/computer-use/types.d.ts +4 -1
- package/dist/routes/shared-world/bundle-records.js +8 -7
- package/dist/routes/shared-world/bundle.js +1 -1
- package/dist/run/contained-output.d.ts +7 -0
- package/dist/run/contained-output.js +35 -1
- package/dist/run/detail.js +4 -4
- package/dist/run/note-files.d.ts +73 -0
- package/dist/run/note-files.js +184 -0
- package/dist/run/notes.d.ts +37 -0
- package/dist/run/notes.js +198 -0
- package/dist/run/participant-caption.d.ts +11 -1
- package/dist/run/participant-caption.js +30 -1
- package/dist/run/participant-records.d.ts +3 -1
- package/dist/run/participant-records.js +6 -0
- package/dist/run-study.js +17 -1
- package/dist/study/discover.js +6 -1
- package/dist/study/doctor.d.ts +2 -0
- package/dist/study/doctor.js +46 -8
- package/dist/study/init-templates.js +10 -5
- package/dist/study/plan.js +3 -2
- package/dist/study/preflight-result.d.ts +1 -0
- package/dist/study/preflight-result.js +3 -2
- package/dist/study/preflight.d.ts +2 -0
- package/dist/study/preflight.js +54 -14
- package/dist/study/summary.js +2 -2
- package/dist/study/warnings.d.ts +5 -0
- package/dist/study/warnings.js +79 -2
- package/dist/substrates/local/capacity.d.ts +75 -0
- package/dist/substrates/local/capacity.js +143 -0
- package/dist/substrates/local/firecracker-desktop.d.ts +8 -0
- package/dist/substrates/local/firecracker-desktop.js +34 -3
- package/dist/substrates/local/runtime-host.d.ts +16 -3
- package/dist/substrates/local/runtime-host.js +49 -4
- package/dist/substrates/local/runtime.d.ts +10 -0
- package/dist/substrates/local/runtime.js +44 -5
- package/dist/tui-app.js +58 -58
- package/docs/architecture/actor-contract.md +20 -6
- package/docs/contracts/errors.md +2 -2
- package/docs/contracts/feedback.md +8 -1
- package/docs/contracts/run-bundle.md +57 -0
- package/docs/contracts/schemas.md +62 -36
- package/docs/contracts/study-analysis.md +41 -8
- package/docs/principles/invariants-and-defaults.md +2 -2
- package/docs/product/automatic-analysis.md +22 -16
- package/package.json +2 -2
- package/skills/humanish/SKILL.md +26 -10
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ComputerUseProviderError } from "./provider-error.js";
|
|
2
2
|
import { runActionBatch } from "./loop/actions.js";
|
|
3
3
|
import { advanceBackstop, startBackstop } from "./loop/backstop.js";
|
|
4
|
-
import { requestDebrief } from "./loop/debrief.js";
|
|
4
|
+
import { requestDebrief, validClosingReport } from "./loop/debrief.js";
|
|
5
5
|
import * as stops from "./loop/ending.js";
|
|
6
6
|
import { declaredOutcomeOf } from "./loop/ending.js";
|
|
7
|
+
import { notCollected, recordImpressions } from "./loop/impressions.js";
|
|
7
8
|
import { DesktopObserver } from "./loop/observation.js";
|
|
8
9
|
import { retryAfterOutputLimit } from "./loop/output-limit.js";
|
|
9
10
|
import { requestTurn } from "./loop/provider-call.js";
|
|
@@ -13,7 +14,8 @@ import { accountBillingConflicts } from "./loop/usage.js";
|
|
|
13
14
|
import { loopResult, notice } from "./loop/trace.js";
|
|
14
15
|
export { describeCuaAction } from "./loop/actions.js";
|
|
15
16
|
export { stableProgressKey } from "./loop/backstop.js";
|
|
16
|
-
export { validClosingReport } from "./loop/debrief.js";
|
|
17
|
+
export { validClosingReport, validImpressionsReply } from "./loop/debrief.js";
|
|
18
|
+
export { IMPRESSIONS_ASK } from "./loop/impressions.js";
|
|
17
19
|
/**
|
|
18
20
|
* Drive the loop to one explicit completion and return its CuaLoopResult: status, completion
|
|
19
21
|
* reason, public reason and ActorTrace. Model-authored text passes through redactNarration before
|
|
@@ -118,8 +120,18 @@ async function runTurns(session, conversation) {
|
|
|
118
120
|
// The declared outcome is kept even if redacting the participant's summary fails.
|
|
119
121
|
session.declaredOutcome = declaredOutcomeOf(turn);
|
|
120
122
|
const ended = stops.participantEnded(turn, session.declaredOutcome, (text) => session.redactNarration(text));
|
|
123
|
+
const report = validClosingReport(turn.closingReport) ? turn.closingReport : undefined;
|
|
124
|
+
// A provider that can ask for impressions alone does so after the session, like a debrief.
|
|
125
|
+
if (report?.impressions !== undefined || session.provider.requestImpressions === undefined) {
|
|
126
|
+
session.impressions =
|
|
127
|
+
report === undefined
|
|
128
|
+
? notCollected("the participant ended the session without a structured closing account")
|
|
129
|
+
: recordImpressions(session, report.impressions);
|
|
130
|
+
}
|
|
121
131
|
await observer.observeFinalTasks(turnNumber);
|
|
122
|
-
return
|
|
132
|
+
return session.impressions === undefined
|
|
133
|
+
? { ...ended, debriefTrigger: { kind: "participant_end", observation } }
|
|
134
|
+
: ended;
|
|
123
135
|
}
|
|
124
136
|
const batch = await runActionBatch(session, turn.actions, turn.shortenedWaits);
|
|
125
137
|
conversation.previousExecution = batch.execution;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CuaTurn, CuaTurnRequest } from "./loop.js";
|
|
2
2
|
import type { ReasoningEffort } from "../reasoning-effort.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type ActorConversation } from "../contract.js";
|
|
4
4
|
import { type OpenAiReasoningSummary } from "./openai-wire.js";
|
|
5
5
|
/** How a conversation starts. The model settings stay fixed for the whole session. */
|
|
6
6
|
export interface OpenAiConversationSettings {
|
|
@@ -30,6 +30,11 @@ export interface ConversationRequest {
|
|
|
30
30
|
}
|
|
31
31
|
/** Which answer told the provider that the organization keeps no server-side conversation. */
|
|
32
32
|
export type ZdrRejection = NonNullable<ActorConversation["rejection"]>;
|
|
33
|
+
/**
|
|
34
|
+
* What a closing request asks for: the closing report after a harness-owned stop, or only
|
|
35
|
+
* impressions after the participant ended the session itself.
|
|
36
|
+
*/
|
|
37
|
+
export type ClosingRequest = "report" | "impressions";
|
|
33
38
|
export declare class OpenAiConversation {
|
|
34
39
|
private readonly settings;
|
|
35
40
|
private lastResponseId;
|
|
@@ -46,9 +51,9 @@ export declare class OpenAiConversation {
|
|
|
46
51
|
constructor(settings: OpenAiConversationSettings);
|
|
47
52
|
/**
|
|
48
53
|
* The next request, carrying `req`'s screen: the opening request, or the outputs the latest
|
|
49
|
-
* reply's computer calls are owed. A closing
|
|
54
|
+
* reply's computer calls are owed. A closing request asks for strict JSON and no tools.
|
|
50
55
|
*/
|
|
51
|
-
request(req: CuaTurnRequest, closing?:
|
|
56
|
+
request(req: CuaTurnRequest, closing?: ClosingRequest): ConversationRequest;
|
|
52
57
|
/**
|
|
53
58
|
* The organization rejected server-side state: carry the conversation on the client from the
|
|
54
59
|
* request being sent, and record which answer made the switch. False when the conversation is
|
|
@@ -80,7 +85,7 @@ export declare class OpenAiConversation {
|
|
|
80
85
|
* ahead of `sent`.
|
|
81
86
|
*/
|
|
82
87
|
private turnBody;
|
|
83
|
-
/**
|
|
88
|
+
/** A read-only closing request: no tools, its own output limit, a strict schema. */
|
|
84
89
|
private closingBody;
|
|
85
90
|
/**
|
|
86
91
|
* Move the conversation on to a reply to a request that added `sent`. A participant reply cut
|
|
@@ -1,7 +1,51 @@
|
|
|
1
|
+
import { PARTICIPANT_IMPRESSION_KINDS } from "../contract.js";
|
|
1
2
|
import { CarriedConversation, estimateTokens } from "./openai-context.js";
|
|
2
3
|
import { buildCallOutput, buildContinuationRequest, buildInitialRequest, hintItems, openingMessage, parseOpenAiResponse, } from "./openai-wire.js";
|
|
3
|
-
/**
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The closing request's output limit: its own cap, or the declared limit when that is lower. It
|
|
6
|
+
* counts reasoning too, and leaves room for six impressions after the summary and friction reports.
|
|
7
|
+
*/
|
|
8
|
+
const CLOSING_OUTPUT_LIMIT = 3072;
|
|
9
|
+
const IMPRESSIONS_SCHEMA = {
|
|
10
|
+
type: "array",
|
|
11
|
+
items: {
|
|
12
|
+
type: "object",
|
|
13
|
+
additionalProperties: false,
|
|
14
|
+
required: ["kind", "text"],
|
|
15
|
+
properties: {
|
|
16
|
+
kind: { type: "string", enum: [...PARTICIPANT_IMPRESSION_KINDS] },
|
|
17
|
+
text: { type: "string" },
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const CLOSING_FORMATS = {
|
|
22
|
+
report: {
|
|
23
|
+
type: "json_schema",
|
|
24
|
+
name: "participant_closing_report",
|
|
25
|
+
strict: true,
|
|
26
|
+
schema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
required: ["summary", "frictionReports", "impressions"],
|
|
30
|
+
properties: {
|
|
31
|
+
summary: { type: "string" },
|
|
32
|
+
frictionReports: { type: "array", items: { type: "string" } },
|
|
33
|
+
impressions: IMPRESSIONS_SCHEMA,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
impressions: {
|
|
38
|
+
type: "json_schema",
|
|
39
|
+
name: "participant_impressions",
|
|
40
|
+
strict: true,
|
|
41
|
+
schema: {
|
|
42
|
+
type: "object",
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
required: ["impressions"],
|
|
45
|
+
properties: { impressions: IMPRESSIONS_SCHEMA },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
5
49
|
export class OpenAiConversation {
|
|
6
50
|
settings;
|
|
7
51
|
lastResponseId;
|
|
@@ -23,12 +67,12 @@ export class OpenAiConversation {
|
|
|
23
67
|
}
|
|
24
68
|
/**
|
|
25
69
|
* The next request, carrying `req`'s screen: the opening request, or the outputs the latest
|
|
26
|
-
* reply's computer calls are owed. A closing
|
|
70
|
+
* reply's computer calls are owed. A closing request asks for strict JSON and no tools.
|
|
27
71
|
*/
|
|
28
|
-
request(req, closing
|
|
72
|
+
request(req, closing) {
|
|
29
73
|
const sent = this.newItems(req);
|
|
30
74
|
return {
|
|
31
|
-
body: () =>
|
|
75
|
+
body: () => closing === undefined ? this.turnBody(req, sent) : this.closingBody(req, sent, closing),
|
|
32
76
|
accept: (reply) => this.accept(reply, closing, sent),
|
|
33
77
|
};
|
|
34
78
|
}
|
|
@@ -110,28 +154,13 @@ export class OpenAiConversation {
|
|
|
110
154
|
...(this.mode === "explicit_context" ? { explicitContextItems: this.carried.carried() } : {}),
|
|
111
155
|
});
|
|
112
156
|
}
|
|
113
|
-
/**
|
|
114
|
-
closingBody(req, sent) {
|
|
157
|
+
/** A read-only closing request: no tools, its own output limit, a strict schema. */
|
|
158
|
+
closingBody(req, sent, closing) {
|
|
115
159
|
return {
|
|
116
160
|
...this.turnBody(req, sent),
|
|
117
161
|
tool_choice: "none",
|
|
118
162
|
max_output_tokens: Math.min(this.settings.maxOutputTokens ?? CLOSING_OUTPUT_LIMIT, CLOSING_OUTPUT_LIMIT),
|
|
119
|
-
text: {
|
|
120
|
-
format: {
|
|
121
|
-
type: "json_schema",
|
|
122
|
-
name: "participant_closing_report",
|
|
123
|
-
strict: true,
|
|
124
|
-
schema: {
|
|
125
|
-
type: "object",
|
|
126
|
-
additionalProperties: false,
|
|
127
|
-
required: ["summary", "frictionReports"],
|
|
128
|
-
properties: {
|
|
129
|
-
summary: { type: "string" },
|
|
130
|
-
frictionReports: { type: "array", items: { type: "string" } },
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
},
|
|
163
|
+
text: { format: CLOSING_FORMATS[closing] },
|
|
135
164
|
};
|
|
136
165
|
}
|
|
137
166
|
/**
|
|
@@ -142,10 +171,10 @@ export class OpenAiConversation {
|
|
|
142
171
|
accept(reply, closing, sent) {
|
|
143
172
|
// Recorded before the reply is accepted: accepting can cut the carried history, and the record
|
|
144
173
|
// counts what this request carried.
|
|
145
|
-
if (
|
|
146
|
-
this.evidence.requested(this.mode, this.carried, sent);
|
|
174
|
+
if (closing !== "report")
|
|
175
|
+
this.evidence.requested(this.mode, this.carried, sent, closing);
|
|
147
176
|
const parsed = parseOpenAiResponse(reply);
|
|
148
|
-
if (
|
|
177
|
+
if (closing === undefined && parsed.turn.interruption === "output_limit")
|
|
149
178
|
return parsed.turn;
|
|
150
179
|
if (parsed.turn.responseId !== undefined)
|
|
151
180
|
this.lastResponseId = parsed.turn.responseId;
|
|
@@ -177,10 +206,11 @@ class ConversationRecord {
|
|
|
177
206
|
// Counted like requests[], which includes replies set aside at their output limit.
|
|
178
207
|
this.switchedAtRequest = this.requests.length + 1;
|
|
179
208
|
}
|
|
180
|
-
/** A participant request went out with `sent` after `conversation`. */
|
|
181
|
-
requested(mode, conversation, sent) {
|
|
209
|
+
/** A participant turn or impressions request went out with `sent` after `conversation`. */
|
|
210
|
+
requested(mode, conversation, sent, kind) {
|
|
211
|
+
const marker = kind === undefined ? {} : { kind };
|
|
182
212
|
if (mode !== "explicit_context") {
|
|
183
|
-
this.requests.push({ mode: "threaded" });
|
|
213
|
+
this.requests.push({ mode: "threaded", ...marker });
|
|
184
214
|
return;
|
|
185
215
|
}
|
|
186
216
|
const carried = conversation.size();
|
|
@@ -188,6 +218,7 @@ class ConversationRecord {
|
|
|
188
218
|
this.summarizedTurns = carried.notedTurns;
|
|
189
219
|
this.requests.push({
|
|
190
220
|
mode: "explicit_context",
|
|
221
|
+
...marker,
|
|
191
222
|
carriedExchanges: carried.exchanges,
|
|
192
223
|
carriedScreenshots: carried.screenshots,
|
|
193
224
|
estimatedInputTokens: carried.estimatedTokens + estimateTokens(sent),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { validClosingReport } from "./loop.js";
|
|
1
|
+
import { validClosingReport, validImpressionsReply } from "./loop.js";
|
|
2
2
|
import { ComputerUseAdmissionLimitError, isComputerUseAdmissionLimitError, } from "./admission-limit.js";
|
|
3
3
|
import { ComputerUsePromptRefusedError } from "./provider-error.js";
|
|
4
4
|
import { OpenAiConversation, } from "./openai-conversation.js";
|
|
@@ -455,21 +455,24 @@ export function createOpenAiResponsesProvider(options) {
|
|
|
455
455
|
}
|
|
456
456
|
}
|
|
457
457
|
};
|
|
458
|
-
const requestTurn = async (req, signal, closing
|
|
458
|
+
const requestTurn = async (req, signal, closing, spend) => {
|
|
459
459
|
await capture.prepareNext();
|
|
460
460
|
const request = conversation.request(req, closing);
|
|
461
|
-
// A closing
|
|
462
|
-
const raw = closing
|
|
463
|
-
? await
|
|
464
|
-
: await
|
|
461
|
+
// A closing request makes exactly one request: no HTTP or policy-latch retries.
|
|
462
|
+
const raw = closing === undefined
|
|
463
|
+
? await postTurn(request, signal, spend)
|
|
464
|
+
: await post(request.body(), signal, 0, false);
|
|
465
465
|
const turn = request.accept(raw);
|
|
466
|
-
if (closing) {
|
|
466
|
+
if (closing !== undefined) {
|
|
467
467
|
// Refusals, incomplete output, malformed JSON, and invalid shapes remain no-report results.
|
|
468
468
|
// Never promote raw JSON or a fallback paragraph into a structured finding.
|
|
469
469
|
try {
|
|
470
|
-
const
|
|
471
|
-
if (asRecord(raw).status === "completed"
|
|
472
|
-
|
|
470
|
+
const reply = JSON.parse(turn.message ?? "");
|
|
471
|
+
if (asRecord(raw).status === "completed") {
|
|
472
|
+
if (closing === "report" && validClosingReport(reply))
|
|
473
|
+
return { ...turn, closingReport: reply };
|
|
474
|
+
if (closing === "impressions" && validImpressionsReply(reply))
|
|
475
|
+
return { ...turn, impressions: reply.impressions };
|
|
473
476
|
}
|
|
474
477
|
}
|
|
475
478
|
catch {
|
|
@@ -496,15 +499,18 @@ export function createOpenAiResponsesProvider(options) {
|
|
|
496
499
|
get interactionUsageIncomplete() {
|
|
497
500
|
return interactionUsageIncomplete;
|
|
498
501
|
},
|
|
499
|
-
nextTurn: (req, signal, spend) => requestTurn(req, signal,
|
|
502
|
+
nextTurn: (req, signal, spend) => requestTurn(req, signal, undefined, spend),
|
|
500
503
|
get conversation() {
|
|
501
504
|
return conversation.record();
|
|
502
505
|
},
|
|
503
506
|
// A retrospective report reads the session the server holds; see serverHoldsSession.
|
|
504
507
|
get debrief() {
|
|
505
508
|
return conversation.serverHoldsSession
|
|
506
|
-
? (req, signal) => requestTurn(req, signal,
|
|
509
|
+
? (req, signal) => requestTurn(req, signal, "report")
|
|
507
510
|
: undefined;
|
|
508
511
|
},
|
|
512
|
+
// Offered in both modes. An explicit_context conversation carries its newest screens whole and
|
|
513
|
+
// earlier turns as text, which serves impressions; a closing report needs the whole session.
|
|
514
|
+
requestImpressions: (req, signal) => requestTurn(req, signal, "impressions"),
|
|
509
515
|
};
|
|
510
516
|
}
|
|
@@ -186,7 +186,37 @@ export declare function validActorProviderRequests(value: unknown): value is Act
|
|
|
186
186
|
export interface ParticipantClosingReport {
|
|
187
187
|
summary: string;
|
|
188
188
|
frictionReports: string[];
|
|
189
|
+
/** Absent when the provider did not ask for impressions; empty when the participant had none. */
|
|
190
|
+
impressions?: ParticipantImpression[];
|
|
189
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* What a participant can say about the product at the end of a session, beyond friction.
|
|
194
|
+
* `unlike_my_work` compares the screen with how the persona does the same task in its own work or
|
|
195
|
+
* life.
|
|
196
|
+
*/
|
|
197
|
+
export declare const PARTICIPANT_IMPRESSION_KINDS: readonly ["unclear", "unfinished", "untrustworthy", "liked", "missing", "unlike_my_work"];
|
|
198
|
+
type ParticipantImpressionKind = (typeof PARTICIPANT_IMPRESSION_KINDS)[number];
|
|
199
|
+
/** One first-person impression about something the participant saw in the session. */
|
|
200
|
+
export interface ParticipantImpression {
|
|
201
|
+
kind: ParticipantImpressionKind;
|
|
202
|
+
text: string;
|
|
203
|
+
}
|
|
204
|
+
/** An impression as the trace keeps it, with the message that quotes it. */
|
|
205
|
+
interface RecordedImpression extends ParticipantImpression {
|
|
206
|
+
/** The trace message carrying the impression, which the analysis quotes and cites. */
|
|
207
|
+
messageId: string;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The impressions a session collected, or why it collected none. An empty `items` list means the
|
|
211
|
+
* participant had none to give.
|
|
212
|
+
*/
|
|
213
|
+
export type ParticipantImpressions = {
|
|
214
|
+
status: "collected";
|
|
215
|
+
items: RecordedImpression[];
|
|
216
|
+
} | {
|
|
217
|
+
status: "not_collected";
|
|
218
|
+
reason: string;
|
|
219
|
+
};
|
|
190
220
|
/** Runtime declarations and executable-version observations; not provider request attestation. */
|
|
191
221
|
export interface ActorRuntimeProvenance {
|
|
192
222
|
schema: "humanish.actor-runtime.v1";
|
|
@@ -231,11 +261,13 @@ export interface ActorConversation {
|
|
|
231
261
|
*/
|
|
232
262
|
summarizedTurns: number;
|
|
233
263
|
/**
|
|
234
|
-
* Per participant request, in order: its mode and, in explicit_context,
|
|
235
|
-
* estimated input. `tokenUsage.turns` has each request's billed input.
|
|
264
|
+
* Per participant turn or impressions request, in order: its mode and, in explicit_context,
|
|
265
|
+
* what it carried and its estimated input. `tokenUsage.turns` has each request's billed input.
|
|
236
266
|
*/
|
|
237
267
|
requests: Array<{
|
|
238
268
|
mode: "threaded" | "explicit_context";
|
|
269
|
+
/** Present for the impressions-only request; absent for participant turns and older bundles. */
|
|
270
|
+
kind?: "impressions";
|
|
239
271
|
carriedExchanges?: number;
|
|
240
272
|
carriedScreenshots?: number;
|
|
241
273
|
estimatedInputTokens?: number;
|
|
@@ -315,17 +347,27 @@ export interface ActorTrace {
|
|
|
315
347
|
* each fixed a false refusal and each left the next shape unhandled.
|
|
316
348
|
*/
|
|
317
349
|
declaredOutcome?: ParticipantDeclaredOutcome;
|
|
318
|
-
/**
|
|
350
|
+
/**
|
|
351
|
+
* The read-only closing request: a closing report after a harness-owned stop (`stop_when`,
|
|
352
|
+
* `dwell`), or impressions only after the participant ended the session itself
|
|
353
|
+
* (`participant_end`, never with `report`). Does not change task outcomes or permit actions.
|
|
354
|
+
*/
|
|
319
355
|
debrief?: {
|
|
320
|
-
trigger: "stop_when" | "dwell";
|
|
356
|
+
trigger: "stop_when" | "dwell" | "participant_end";
|
|
321
357
|
status: "completed" | "skipped" | "failed";
|
|
322
358
|
reason: string;
|
|
323
359
|
/** Absent if no request was made; false means token accounting is incomplete. */
|
|
324
360
|
usageReported?: boolean;
|
|
325
|
-
report
|
|
361
|
+
/** The report's impressions are kept in `impressions`, with the messages that quote them. */
|
|
362
|
+
report?: Omit<ParticipantClosingReport, "impressions">;
|
|
326
363
|
/** Links the readable projection so it is not heuristically classified a second time. */
|
|
327
364
|
messageId?: string;
|
|
328
365
|
};
|
|
366
|
+
/**
|
|
367
|
+
* What the participant said about the product at the end of a computer-use session, or why
|
|
368
|
+
* nothing was collected. Absent on other routes and on older traces.
|
|
369
|
+
*/
|
|
370
|
+
impressions?: ParticipantImpressions;
|
|
329
371
|
items: ActorTraceItem[];
|
|
330
372
|
tokenUsage?: ActorTokenUsage;
|
|
331
373
|
/** A stalled or adapter-reported ambiguous interaction may have additional unreported usage.
|
package/dist/actors/contract.js
CHANGED
|
@@ -168,6 +168,19 @@ export function validActorProviderRequests(value) {
|
|
|
168
168
|
usage.input));
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* What a participant can say about the product at the end of a session, beyond friction.
|
|
173
|
+
* `unlike_my_work` compares the screen with how the persona does the same task in its own work or
|
|
174
|
+
* life.
|
|
175
|
+
*/
|
|
176
|
+
export const PARTICIPANT_IMPRESSION_KINDS = [
|
|
177
|
+
"unclear",
|
|
178
|
+
"unfinished",
|
|
179
|
+
"untrustworthy",
|
|
180
|
+
"liked",
|
|
181
|
+
"missing",
|
|
182
|
+
"unlike_my_work",
|
|
183
|
+
];
|
|
171
184
|
export const CODEX_APP_SERVER_CAPABILITIES = {
|
|
172
185
|
headless: true,
|
|
173
186
|
structuredTrace: true,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ModelRate } from "../run/pricing.js";
|
|
2
|
+
/** Admission compares the expected cost times this margin with the cap. */
|
|
3
|
+
export declare const ADMISSION_MARGIN = 1.1;
|
|
4
|
+
export interface AnalysisRequestSize {
|
|
5
|
+
/** UTF-8 bytes of the instructions, the evidence packet and the result schema. */
|
|
6
|
+
textBytes: number;
|
|
7
|
+
imageTokens: number;
|
|
8
|
+
participants: number;
|
|
9
|
+
/** The request's output limit. The worst case spends all of it. */
|
|
10
|
+
outputAllowance: number;
|
|
11
|
+
}
|
|
12
|
+
export interface AnalysisCostEstimate {
|
|
13
|
+
inputTokens: number;
|
|
14
|
+
expectedOutputTokens: number;
|
|
15
|
+
expectedCostUsd: number;
|
|
16
|
+
worstCaseCostUsd: number;
|
|
17
|
+
/** The expected cost with the margin, never above the worst case: what admission compares. */
|
|
18
|
+
admittedCostUsd: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function estimateAnalysisCost(rate: ModelRate, size: AnalysisRequestSize): AnalysisCostEstimate;
|
|
21
|
+
/** The numbers a cost refusal reports. */
|
|
22
|
+
export interface RefusedAnalysisCost {
|
|
23
|
+
expectedCostUsd: number;
|
|
24
|
+
worstCaseCostUsd: number;
|
|
25
|
+
maxCostUsd: number;
|
|
26
|
+
}
|
|
27
|
+
/** The --max-cost that admits a refused analysis: its worst case, rounded up to a whole dollar. */
|
|
28
|
+
export declare function admittingMaxCost(cost: RefusedAnalysisCost): number;
|
|
29
|
+
/** Why admission refused an analysis, in plain words. */
|
|
30
|
+
export declare function costRefusalText(cost: RefusedAnalysisCost): string;
|
|
31
|
+
/** The admission's refused cost, when it refused the analysis for its cost. */
|
|
32
|
+
export declare function refusedCost(admission: {
|
|
33
|
+
error: string | null;
|
|
34
|
+
estimatedCostUsd: number | null;
|
|
35
|
+
worstCaseCostUsd: number | null;
|
|
36
|
+
maxCostUsd: number | null;
|
|
37
|
+
}): RefusedAnalysisCost | undefined;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// The admission cost model for one study analysis request: the expected cost, the worst case, and
|
|
2
|
+
// the figure admission compares with the cap.
|
|
3
|
+
/**
|
|
4
|
+
* UTF-8 bytes per input token for the instructions, evidence packet and schema. On 148 billed
|
|
5
|
+
* gpt-6-astra analyses the evidence packet ran 3.1 to 3.3 bytes per token and the instructions
|
|
6
|
+
* more, so 3 stays above every billed input. Chinese, Japanese and Korean text is 3 bytes per
|
|
7
|
+
* character in UTF-8, so it also counts at least a token per character.
|
|
8
|
+
*/
|
|
9
|
+
const BYTES_PER_INPUT_TOKEN = 3;
|
|
10
|
+
/** Request framing on top of the text and the images. */
|
|
11
|
+
const FRAMING_TOKENS = 2048;
|
|
12
|
+
/**
|
|
13
|
+
* The output an analysis is expected to write, reasoning included. The most any billed analysis
|
|
14
|
+
* used was 12,511 tokens for one participant and 16,722 for eight.
|
|
15
|
+
*/
|
|
16
|
+
const EXPECTED_OUTPUT_TOKENS = 12_000;
|
|
17
|
+
const EXPECTED_OUTPUT_TOKENS_PER_PARTICIPANT = 1_000;
|
|
18
|
+
/** Admission compares the expected cost times this margin with the cap. */
|
|
19
|
+
export const ADMISSION_MARGIN = 1.1;
|
|
20
|
+
/** Rounded up to the micro-dollar, so a small positive boundary never rounds into admission. */
|
|
21
|
+
const roundUp = (usd) => Math.ceil(usd * 1e6) / 1e6;
|
|
22
|
+
/** One request's cost. Input is priced at the highest input rate the model bills, cache writes
|
|
23
|
+
* included, and the long-context tier re-prices the whole request past its threshold. */
|
|
24
|
+
function requestCost(rate, inputTokens, outputTokens) {
|
|
25
|
+
const long = rate.longContext !== undefined && inputTokens > rate.longContext.thresholdInputTokens;
|
|
26
|
+
const inputRate = Math.max(rate.inputUsdPerToken, rate.cacheWriteUsdPerToken ?? 0, rate.cachedInputUsdPerToken ?? 0);
|
|
27
|
+
return roundUp(inputTokens * inputRate * (long ? rate.longContext.inputMultiplier : 1) +
|
|
28
|
+
outputTokens * rate.outputUsdPerToken * (long ? rate.longContext.outputMultiplier : 1));
|
|
29
|
+
}
|
|
30
|
+
export function estimateAnalysisCost(rate, size) {
|
|
31
|
+
const inputTokens = Math.ceil(size.textBytes / BYTES_PER_INPUT_TOKEN) + FRAMING_TOKENS + size.imageTokens;
|
|
32
|
+
const expectedOutputTokens = Math.min(size.outputAllowance, EXPECTED_OUTPUT_TOKENS + EXPECTED_OUTPUT_TOKENS_PER_PARTICIPANT * size.participants);
|
|
33
|
+
const expectedCostUsd = requestCost(rate, inputTokens, expectedOutputTokens);
|
|
34
|
+
const worstCaseCostUsd = requestCost(rate, inputTokens, size.outputAllowance);
|
|
35
|
+
return {
|
|
36
|
+
inputTokens,
|
|
37
|
+
expectedOutputTokens,
|
|
38
|
+
expectedCostUsd,
|
|
39
|
+
worstCaseCostUsd,
|
|
40
|
+
admittedCostUsd: Math.min(worstCaseCostUsd, roundUp(expectedCostUsd * ADMISSION_MARGIN)),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** The --max-cost that admits a refused analysis: its worst case, rounded up to a whole dollar. */
|
|
44
|
+
export function admittingMaxCost(cost) {
|
|
45
|
+
return Math.max(1, Math.ceil(cost.worstCaseCostUsd));
|
|
46
|
+
}
|
|
47
|
+
/** Why admission refused an analysis, in plain words. */
|
|
48
|
+
export function costRefusalText(cost) {
|
|
49
|
+
return `The expected cost is $${cost.expectedCostUsd.toFixed(2)} and the worst case is $${cost.worstCaseCostUsd.toFixed(2)}. With a ${Math.round((ADMISSION_MARGIN - 1) * 100)}% margin the expected cost is over the $${cost.maxCostUsd} cap, so no request was sent.`;
|
|
50
|
+
}
|
|
51
|
+
/** The admission's refused cost, when it refused the analysis for its cost. */
|
|
52
|
+
export function refusedCost(admission) {
|
|
53
|
+
const { error, estimatedCostUsd, worstCaseCostUsd, maxCostUsd } = admission;
|
|
54
|
+
return error === "analysis_budget_exceeded" &&
|
|
55
|
+
estimatedCostUsd !== null &&
|
|
56
|
+
worstCaseCostUsd !== null &&
|
|
57
|
+
maxCostUsd !== null
|
|
58
|
+
? { expectedCostUsd: estimatedCostUsd, worstCaseCostUsd, maxCostUsd }
|
|
59
|
+
: undefined;
|
|
60
|
+
}
|
|
@@ -35,10 +35,4 @@ export declare function completeAutomaticAnalysis<T extends {
|
|
|
35
35
|
/** The route's reason not to analyze this run; see AutomaticAnalysisDeps.refusal. */
|
|
36
36
|
refusal?: AutomaticAnalysisDeps["refusal"];
|
|
37
37
|
}): Promise<T & AutomaticAnalysisResult>;
|
|
38
|
-
/**
|
|
39
|
-
* True when an analysis the study never declared was refused because its conservative estimate is
|
|
40
|
-
* over the default cap. The author did not ask for it, so the refusal does not fail the run;
|
|
41
|
-
* `humanish analyze --max-cost` can still run it.
|
|
42
|
-
*/
|
|
43
|
-
export declare function defaultAnalysisOverBudget(result: AutomaticAnalysisResult): boolean;
|
|
44
38
|
export declare function automaticAnalysisSucceeded(result: AutomaticAnalysisResult): boolean;
|
|
@@ -85,11 +85,11 @@ export async function completeAutomaticAnalysis(result, finished, config, input,
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
|
-
* True when an analysis the study never declared was refused because its
|
|
89
|
-
* over the default cap. The author did not ask for it, so the refusal does not fail the run;
|
|
88
|
+
* True when an analysis the study never declared was refused because its expected cost, with the
|
|
89
|
+
* admission margin, is over the default cap. The author did not ask for it, so the refusal does not fail the run;
|
|
90
90
|
* `humanish analyze --max-cost` can still run it.
|
|
91
91
|
*/
|
|
92
|
-
|
|
92
|
+
function defaultAnalysisOverBudget(result) {
|
|
93
93
|
const value = result.automaticAnalysis;
|
|
94
94
|
return (result.automaticAnalysisTrigger === "default" &&
|
|
95
95
|
value?.state === "skipped" &&
|
|
@@ -34,8 +34,16 @@ export interface AutomaticAnalysisBudget {
|
|
|
34
34
|
model: string;
|
|
35
35
|
maxCostUsd: number | null;
|
|
36
36
|
trigger: "default" | "explicit";
|
|
37
|
+
/** The participants the expected cost range is for. */
|
|
38
|
+
participants?: number;
|
|
39
|
+
/** The expected cost, from a run that keeps no evidence to one at the evidence limits. */
|
|
40
|
+
expectedCostUsd?: {
|
|
41
|
+
low: number;
|
|
42
|
+
high: number;
|
|
43
|
+
};
|
|
37
44
|
}
|
|
38
|
-
/** Metadata only: resolving the future live-run budget never reads keys or dispatches.
|
|
39
|
-
|
|
45
|
+
/** Metadata only: resolving the future live-run budget never reads keys or dispatches. With a
|
|
46
|
+
* participant count it also gives the expected cost range for that many participants. */
|
|
47
|
+
export declare function automaticAnalysisBudget(raw: unknown, route: StudyRoute, participants?: number): AutomaticAnalysisBudget | undefined;
|
|
40
48
|
export declare function formatAutomaticAnalysisBudget(budget: AutomaticAnalysisBudget): string;
|
|
41
49
|
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CODEX_ANALYSIS_MODEL, codexAnalysisIdentity } from "./codex-config.js";
|
|
2
|
-
import { DEFAULT_ANALYSIS_MAX_OUTPUT_TOKENS, MAX_ANALYSIS_OUTPUT_TOKENS, isSupportedAnalysisModel, } from "./execute.js";
|
|
2
|
+
import { DEFAULT_ANALYSIS_MAX_OUTPUT_TOKENS, MAX_ANALYSIS_OUTPUT_TOKENS, analysisCostRange, isSupportedAnalysisModel, } from "./execute.js";
|
|
3
|
+
import { ADMISSION_MARGIN } from "./admission.js";
|
|
4
|
+
import { plural } from "../run/text.js";
|
|
3
5
|
import { containsSensitive } from "../evidence/redaction.js";
|
|
4
6
|
export const DEFAULT_ANALYSIS_TIMEOUT_MS = 600_000;
|
|
5
7
|
const MAX_ANALYSIS_TIMEOUT_MS = 600_000;
|
|
@@ -111,25 +113,38 @@ export function resolveAutomaticAnalysis(raw) {
|
|
|
111
113
|
...(value.maxOutputTokens === undefined ? { preferLargerOutput: true } : {}),
|
|
112
114
|
};
|
|
113
115
|
}
|
|
114
|
-
/** Metadata only: resolving the future live-run budget never reads keys or dispatches.
|
|
115
|
-
|
|
116
|
+
/** Metadata only: resolving the future live-run budget never reads keys or dispatches. With a
|
|
117
|
+
* participant count it also gives the expected cost range for that many participants. */
|
|
118
|
+
export function automaticAnalysisBudget(raw, route, participants) {
|
|
116
119
|
// The synthetic preview has no participants, so nothing is analyzed after it.
|
|
117
120
|
if (route === "preview")
|
|
118
121
|
return undefined;
|
|
119
122
|
const resolved = resolveAutomaticAnalysis(raw);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
if (!resolved.ok || !resolved.config)
|
|
124
|
+
return undefined;
|
|
125
|
+
const { config } = resolved;
|
|
126
|
+
const range = participants === undefined || config.provider === "codex"
|
|
127
|
+
? undefined
|
|
128
|
+
: analysisCostRange(resolved.preferLargerOutput
|
|
129
|
+
? { ...config, maxOutputTokens: MAX_ANALYSIS_OUTPUT_TOKENS }
|
|
130
|
+
: config, participants);
|
|
131
|
+
return {
|
|
132
|
+
...(config.provider === "codex"
|
|
133
|
+
? { provider: "codex", billing: "account-unknown" }
|
|
134
|
+
: {}),
|
|
135
|
+
model: config.model,
|
|
136
|
+
maxCostUsd: config.maxCostUsd,
|
|
137
|
+
trigger: raw === undefined ? "default" : "explicit",
|
|
138
|
+
...(range === undefined || participants === undefined
|
|
139
|
+
? {}
|
|
140
|
+
: { participants, expectedCostUsd: range }),
|
|
141
|
+
};
|
|
130
142
|
}
|
|
131
143
|
export function formatAutomaticAnalysisBudget(budget) {
|
|
132
144
|
if (budget.provider === "codex")
|
|
133
145
|
return `After live runs: Codex account analysis · ${budget.model} · separate restricted analyst with remote inference. Account limits apply; dollar cost and output-token ceiling are unknown. Set review.analysis: false to disable.`;
|
|
134
|
-
|
|
146
|
+
const range = budget.expectedCostUsd === undefined || budget.participants === undefined
|
|
147
|
+
? ""
|
|
148
|
+
: ` · expected $${budget.expectedCostUsd.low.toFixed(2)} to $${budget.expectedCostUsd.high.toFixed(2)} for ${plural(budget.participants, "participant")}, depending on how much evidence the run keeps`;
|
|
149
|
+
return `After live runs: ${budget.trigger} analysis · ${budget.model}${range} · refused before it starts if the expected cost plus a ${Math.round((ADMISSION_MARGIN - 1) * 100)}% margin is over $${budget.maxCostUsd}; this is not a billing cap. Set review.analysis: false to disable.`;
|
|
135
150
|
}
|
|
@@ -5,6 +5,7 @@ import { refreshObserver, analyzeRun, readCompletedAnalysisSource, resolveAnalys
|
|
|
5
5
|
import { preferLargerAnalysisOutput, ANALYSIS_PROMPT_VERSION } from "./execute.js";
|
|
6
6
|
import { captureEvidence } from "./evidence.js";
|
|
7
7
|
import { hashAnalysisValue } from "./validation.js";
|
|
8
|
+
import { refusedCost } from "./admission.js";
|
|
8
9
|
import { claimAutomaticAnalysis, readAutomaticAnalysisPrepared, requestAutomaticAnalysisCancellationPrepared, } from "./job.js";
|
|
9
10
|
import { ANALYSIS_ID_PATTERN } from "./types.js";
|
|
10
11
|
import { readAnalysisVersion } from "./store.js";
|
|
@@ -105,6 +106,11 @@ async function readAutomaticSource(cwd, prepared, configInput, deps, hasKey) {
|
|
|
105
106
|
return skipped("AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE");
|
|
106
107
|
}
|
|
107
108
|
}
|
|
109
|
+
/** The cost of an analysis admission refused for its cost, kept so review and Observer can say it. */
|
|
110
|
+
function withRefusedCost(result) {
|
|
111
|
+
const cost = result?.admission && refusedCost(result.admission);
|
|
112
|
+
return cost ? { admission: cost } : {};
|
|
113
|
+
}
|
|
108
114
|
/**
|
|
109
115
|
* Bind the job's terminal metadata to the exact safely published execution. Reads never promote a
|
|
110
116
|
* completed-looking sidecar without rechecking these bindings. Null when storage failed.
|
|
@@ -130,6 +136,7 @@ async function persistOutcome(job, prepared, outcome) {
|
|
|
130
136
|
receiptSha256: hashAnalysisValue(receipt),
|
|
131
137
|
}),
|
|
132
138
|
...(entry?.analysis ? { analysisSha256: hashAnalysisValue(entry.analysis) } : {}),
|
|
139
|
+
...withRefusedCost(outcome.result),
|
|
133
140
|
});
|
|
134
141
|
const persisted = await readAutomaticAnalysisPrepared(prepared);
|
|
135
142
|
return persisted?.state === "unknown"
|
|
@@ -20,4 +20,6 @@ export interface SourceEntry {
|
|
|
20
20
|
export declare function sourceEntries(bundle: RunBundle, stream: RunStream, captureVersion?: CaptureVersion): SourceEntry[];
|
|
21
21
|
/** Max-min allocation; stable ID order breaks a remainder tie by at most one slot. */
|
|
22
22
|
export declare function fairShares(budget: number, capacities: number[]): number[];
|
|
23
|
-
|
|
23
|
+
/** The trace messages that carry a participant's closing impressions. */
|
|
24
|
+
export declare function impressionEventIds(stream: RunStream): ReadonlySet<string>;
|
|
25
|
+
export declare function sourceOrder(entries: SourceEntry[], capturesOnly: boolean, impressions?: ReadonlySet<string>): SourceEntry[];
|