@vostride/agent-qa-runner 0.0.1 → 0.2.1
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/LICENSE.md +105 -0
- package/NOTICE.md +11 -0
- package/dist/index.cjs +12125 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3903 -0
- package/dist/index.d.ts +3903 -0
- package/dist/index.js +11980 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -3
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,3903 @@
|
|
|
1
|
+
import { StepEvidence, RecordingEntry, Action as Action$1, RecordingAnnotationWitness, Platform, ActionsFor as ActionsFor$1, LiveStreamKind, LiveFrame, ScreenState, RecordingContentType, ScopeKey, BriefBlock, ServedRetrievalTraceV1, SearchOptions, IndexEntry, RecordId, ReadOptions, RecordBody, Anchor, CautionBlock, ProcedureBindingAvailability, ProcedureMatch, CanaryOutcome, ElementState, ElementLabels, Ref, ElementInfo, ScreenStateMetadata, CacheKey, ClipboardEvidence, SubmitOutcome, SubmitReason, FillVerdict, InputPath, DownloadEvidence, IdempotentNoopMeta, AssertValueOp, ConditionFileCheck, OperationEvidence, GroundingAttempt, WorkingMemoryEntry, ConditionVerdict, ConditionNeed, CitationSource, InterpolatedVariable, ToolExchangeEvidence, ModelCallRecord, RunEventEnvelope, CompletedRunHandoff, HookStartedPayload, HookFinishedPayload, TestRetryScheduledPayload, DeclaredModelProfile, ModelIdentity, RunId, PostProcessingDisposition, RunStatus, RunArtifactStageInput, RunArtifactFinalizeInput, ExecutionContext, QuarantineAppliedEntry, PlannerLimitsRecord, ServingRecordMetadata, ServingAdmissionDecision, ServedAdmissionSurface, ServedAdmissionEvent, ArtifactRef, EvidenceDocument, RecordingRef, StepFinalizedPayload, ModelUsageObservations, SubActionEvidence, SubActionRecordedPayload, RecordedRetrievalRung, ProcedureBodyPayload, ReadTrace, Surface, SamplingParams, BriefLine, ServedBrief, ServedPromptTurn, ActionResult as ActionResult$1, CautionLine, PlanningFailureEvidence, ReportedValueEvidence, TerminationEvidence, LimitStopEvidence, ConditionVerdictsEvidence, OperationCaptureAvailability, StepLimitUsage, AiJudgmentEvidence, AssertValueEvidence, VerdictSource, StepPlanSource, CacheMissReason, Decision, VerifierVerdict, Viewport, ActionAnnotation, MaskFn, RedactedEnvVar, CacheableStepInput, NonCacheableStepInput, RunEventType, RunEventPayload, LiveFrameEndReason, LiveFrameSubscription, LiveStreamDeclaration, RunSession, PerTestOutcome, MemberTestOutcome, Verdict, RiskActionDescriptor } from '@vostride/agent-qa-schema';
|
|
2
|
+
export { IdempotentNoopMeta, Ref, asRef, readIdempotentNoopMeta } from '@vostride/agent-qa-schema';
|
|
3
|
+
import { LanguageModel, ModelMessage, LanguageModelUsage, ToolSet, SystemModelMessage, Tool } from 'ai';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
type StepStatus = 'passed' | 'failed' | 'healed' | 'skipped' | 'cancelled';
|
|
7
|
+
type TestStatus = 'passed' | 'failed' | 'skipped' | 'cancelled';
|
|
8
|
+
interface StepAnnotation {
|
|
9
|
+
clickPoint?: {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
};
|
|
13
|
+
boundingBox?: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
failureHighlight?: {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
};
|
|
25
|
+
type: 'click' | 'fill' | 'tap' | 'hover' | 'scroll' | 'swipe' | 'pinch' | 'navigate' | 'other';
|
|
26
|
+
viewport?: {
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
};
|
|
30
|
+
startPoint?: {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
};
|
|
34
|
+
endPoint?: {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
};
|
|
38
|
+
direction?: 'up' | 'down' | 'left' | 'right';
|
|
39
|
+
pinchScale?: 'in' | 'out';
|
|
40
|
+
}
|
|
41
|
+
interface TokenUsage {
|
|
42
|
+
promptTokens: number;
|
|
43
|
+
completionTokens: number;
|
|
44
|
+
totalTokens: number;
|
|
45
|
+
}
|
|
46
|
+
interface SubActionTrace {
|
|
47
|
+
index: number;
|
|
48
|
+
observation: string;
|
|
49
|
+
reasoning: string;
|
|
50
|
+
plannedAction: Action;
|
|
51
|
+
result: 'success' | 'failure' | 'no-input';
|
|
52
|
+
error?: string;
|
|
53
|
+
noInputSent?: {
|
|
54
|
+
currentState: string;
|
|
55
|
+
};
|
|
56
|
+
screenStateBefore: string;
|
|
57
|
+
screenStateAfter?: string;
|
|
58
|
+
screenContextBefore?: string;
|
|
59
|
+
screenContextAfter?: string;
|
|
60
|
+
confidence?: number;
|
|
61
|
+
verifierReasoning?: string;
|
|
62
|
+
cached: boolean;
|
|
63
|
+
tokenUsage?: TokenUsage;
|
|
64
|
+
phaseDurations?: {
|
|
65
|
+
observe?: number;
|
|
66
|
+
plan?: number;
|
|
67
|
+
execute?: number;
|
|
68
|
+
verify?: number;
|
|
69
|
+
};
|
|
70
|
+
screenshotBefore?: Buffer;
|
|
71
|
+
screenshotAfter?: Buffer;
|
|
72
|
+
annotation?: StepAnnotation;
|
|
73
|
+
data?: unknown;
|
|
74
|
+
}
|
|
75
|
+
interface StepTrace {
|
|
76
|
+
observation: string;
|
|
77
|
+
reasoning: string;
|
|
78
|
+
plannedAction: Action;
|
|
79
|
+
result: 'success' | 'failure';
|
|
80
|
+
error?: string;
|
|
81
|
+
screenStateBefore: string;
|
|
82
|
+
screenStateAfter?: string;
|
|
83
|
+
screenContextBefore?: string;
|
|
84
|
+
screenContextAfter?: string;
|
|
85
|
+
confidence?: number;
|
|
86
|
+
verifierReasoning?: string;
|
|
87
|
+
tokenUsage?: TokenUsage;
|
|
88
|
+
phaseDurations?: {
|
|
89
|
+
observe?: number;
|
|
90
|
+
plan?: number;
|
|
91
|
+
execute?: number;
|
|
92
|
+
verify?: number;
|
|
93
|
+
};
|
|
94
|
+
subActions?: SubActionTrace[];
|
|
95
|
+
}
|
|
96
|
+
interface StepPhaseEvent {
|
|
97
|
+
phase: 'observe' | 'plan' | 'execute' | 'verify';
|
|
98
|
+
subActionIndex?: number;
|
|
99
|
+
phaseOrdinal?: number;
|
|
100
|
+
text?: string;
|
|
101
|
+
confidence?: number;
|
|
102
|
+
action?: unknown;
|
|
103
|
+
success?: boolean;
|
|
104
|
+
duration?: number;
|
|
105
|
+
}
|
|
106
|
+
interface AccessibilityViolation {
|
|
107
|
+
ruleId: string;
|
|
108
|
+
impact: 'minor' | 'moderate' | 'serious' | 'critical';
|
|
109
|
+
description: string;
|
|
110
|
+
help: string;
|
|
111
|
+
helpUrl: string;
|
|
112
|
+
nodes: Array<{
|
|
113
|
+
html: string;
|
|
114
|
+
target: string[];
|
|
115
|
+
}>;
|
|
116
|
+
}
|
|
117
|
+
interface ConsoleLogEntry {
|
|
118
|
+
level: string;
|
|
119
|
+
text: string;
|
|
120
|
+
location?: {
|
|
121
|
+
url: string;
|
|
122
|
+
lineNumber: number;
|
|
123
|
+
columnNumber: number;
|
|
124
|
+
};
|
|
125
|
+
timestamp: number;
|
|
126
|
+
}
|
|
127
|
+
interface NetworkLogEntry {
|
|
128
|
+
url: string;
|
|
129
|
+
method: string;
|
|
130
|
+
status: number;
|
|
131
|
+
requestHeaders: Record<string, string>;
|
|
132
|
+
responseHeaders: Record<string, string>;
|
|
133
|
+
body?: string;
|
|
134
|
+
requestBody?: string;
|
|
135
|
+
startTime: number;
|
|
136
|
+
endTime: number;
|
|
137
|
+
timing?: Record<string, number>;
|
|
138
|
+
}
|
|
139
|
+
interface StepResult {
|
|
140
|
+
id?: string;
|
|
141
|
+
name: string;
|
|
142
|
+
status: StepStatus;
|
|
143
|
+
duration: number;
|
|
144
|
+
action?: Action;
|
|
145
|
+
observation?: string;
|
|
146
|
+
error?: string;
|
|
147
|
+
screenshot?: Buffer;
|
|
148
|
+
screenshotBefore?: Buffer;
|
|
149
|
+
trace?: StepTrace;
|
|
150
|
+
/**
|
|
151
|
+
* The §A8 evidence accumulated by the runner loop, produced by construction (D-14).
|
|
152
|
+
* ADDITIVE (Phase 353): the legacy `trace` is projected FROM this, never the reverse
|
|
153
|
+
* (Open Q5). Core's transitional orchestration persists it via the run-artifact path
|
|
154
|
+
* (D-16) until Phase 361 lifts the accumulator into RunSession/CompletedRunHandoff.
|
|
155
|
+
*/
|
|
156
|
+
evidence?: StepEvidence;
|
|
157
|
+
retryCount?: number;
|
|
158
|
+
capturedVariables?: Record<string, string>;
|
|
159
|
+
annotation?: StepAnnotation;
|
|
160
|
+
accessibilityViolations?: AccessibilityViolation[];
|
|
161
|
+
consoleLogs?: ConsoleLogEntry[];
|
|
162
|
+
networkLogs?: NetworkLogEntry[];
|
|
163
|
+
variableSnapshot?: Record<string, {
|
|
164
|
+
value: string;
|
|
165
|
+
source: string;
|
|
166
|
+
}>;
|
|
167
|
+
originalStepName?: string;
|
|
168
|
+
}
|
|
169
|
+
interface TestResult {
|
|
170
|
+
runId?: string;
|
|
171
|
+
name: string;
|
|
172
|
+
filePath: string;
|
|
173
|
+
status: TestStatus;
|
|
174
|
+
steps: StepResult[];
|
|
175
|
+
duration: number;
|
|
176
|
+
metadata?: Record<string, unknown>;
|
|
177
|
+
retryCount?: number;
|
|
178
|
+
recording?: RecordingEntry;
|
|
179
|
+
failureSummary?: string;
|
|
180
|
+
/** One detached actual failure, retained only when a subsequent attempt runs. */
|
|
181
|
+
firstFailedAttempt?: {
|
|
182
|
+
/** Zero-based member attempt index, independently of the final retryCount. */
|
|
183
|
+
attempt: number;
|
|
184
|
+
result: Omit<TestResult, 'firstFailedAttempt'>;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
interface ObserveOptions {
|
|
189
|
+
extractDom?: boolean;
|
|
190
|
+
}
|
|
191
|
+
type Action = Action$1;
|
|
192
|
+
type ActionsFor<P extends Platform = Platform> = ActionsFor$1<P>;
|
|
193
|
+
/**
|
|
194
|
+
* An adapter's LIVE STREAM capability (403 D-05). The adapter owns the producer (a CDP screencast,
|
|
195
|
+
* an MJPEG broadcaster, a WebRTC track, or a snapshot producer over a capture that is safe to run
|
|
196
|
+
* beside a step); the runner owns nothing but this object, so no `Page` and no session handle ever
|
|
197
|
+
* leaves the adapter. Lifecycle is lazy and refcounted by the run's {@link LiveStreamHub}: `start`
|
|
198
|
+
* on the first viewer, `ack` per consumed frame (the producer's flow control), `stop` on the
|
|
199
|
+
* last — idempotent, so a double stop is a no-op rather than a throw.
|
|
200
|
+
*
|
|
201
|
+
* The labelled 1s snapshot producer (`createSnapshotLiveStream`) sits behind this same contract
|
|
202
|
+
* (428 D-11): the composition registers it as the whole preview when an adapter has no continuous
|
|
203
|
+
* stream, and as the fallback the hub starts when a continuous stream fails to start (D-12).
|
|
204
|
+
*/
|
|
205
|
+
interface LiveStreamCapability {
|
|
206
|
+
readonly kind: LiveStreamKind;
|
|
207
|
+
start(onFrame: (frame: LiveFrame) => void): Promise<void>;
|
|
208
|
+
ack(frameId: number): Promise<void>;
|
|
209
|
+
stop(): Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
interface RecordingStart {
|
|
212
|
+
zeroMs?: number;
|
|
213
|
+
}
|
|
214
|
+
interface RecordingStop {
|
|
215
|
+
stagingPath: string;
|
|
216
|
+
contentType: RecordingContentType;
|
|
217
|
+
}
|
|
218
|
+
interface RecordingCapability {
|
|
219
|
+
start(): Promise<RecordingStart>;
|
|
220
|
+
stop(): Promise<RecordingStop | undefined>;
|
|
221
|
+
/** Bracket one actual execution; completion withholds unsupported or changed scenes. */
|
|
222
|
+
beginAnnotation?(): (() => RecordingAnnotationWitness | undefined) | undefined;
|
|
223
|
+
}
|
|
224
|
+
interface OperationCaptureSupport {
|
|
225
|
+
download: boolean;
|
|
226
|
+
clipboard: boolean;
|
|
227
|
+
/** Why a capture is unsupported, e.g. the browser name. */
|
|
228
|
+
reason?: string;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 2026-10-08 (436-15, D-19): what the loop hands one dispatch. `remainingStepMs` is set only when
|
|
232
|
+
* a step deadline is armed; adapters that run no post-action wait ignore both.
|
|
233
|
+
*/
|
|
234
|
+
interface ExecuteOptions {
|
|
235
|
+
remainingStepMs?: number;
|
|
236
|
+
abortSignal?: AbortSignal;
|
|
237
|
+
}
|
|
238
|
+
interface PlatformAdapter<P extends Platform = Platform> {
|
|
239
|
+
readonly platform: P;
|
|
240
|
+
setup(config: PlatformConfig): Promise<void>;
|
|
241
|
+
cleanup(): Promise<void>;
|
|
242
|
+
observe(options?: ObserveOptions): Promise<ScreenState>;
|
|
243
|
+
execute(action: ActionsFor<P>, options?: ExecuteOptions): Promise<ActionResult>;
|
|
244
|
+
screenshot?(): Promise<Buffer | undefined>;
|
|
245
|
+
/**
|
|
246
|
+
* 403 D-05: the optional live-stream producer for this adapter's current target. ABSENT means
|
|
247
|
+
* "no continuous stream" (non-Chromium, remote farm), not "no preview": the composition's
|
|
248
|
+
* `liveStreamFor` wraps `screenshot()` in the labelled 1s snapshot producer instead, and uses
|
|
249
|
+
* that producer as the fallback when a continuous stream fails to start (428 D-11/D-12). Only an
|
|
250
|
+
* adapter with neither reads `{ kind: 'none' }`. An adapter whose `screenshot()` mutates shared
|
|
251
|
+
* state (the mobile adapters switch the Appium context) must expose its own snapshot
|
|
252
|
+
* `liveStream` over a side-effect-free capture instead, so `screenshot()` is never wrapped.
|
|
253
|
+
*/
|
|
254
|
+
readonly liveStream?: LiveStreamCapability;
|
|
255
|
+
readonly recording?: RecordingCapability;
|
|
256
|
+
/**
|
|
257
|
+
* 436 D-23: which operation-specific captures this adapter runs. ABSENT means none; the loop
|
|
258
|
+
* then records both as unavailable, with `reason` or else 'platform'.
|
|
259
|
+
*/
|
|
260
|
+
readonly operationCaptureSupport?: OperationCaptureSupport;
|
|
261
|
+
drainConsoleLogs?(level?: string): ConsoleLogEntry[];
|
|
262
|
+
drainNetworkLogs?(urlPattern?: string): NetworkLogEntry[];
|
|
263
|
+
/** 439 D-14: the undrained network log, read without consuming it; the step-end drain still sees it. */
|
|
264
|
+
peekNetworkLogs?(urlPattern?: string): NetworkLogEntry[];
|
|
265
|
+
/**
|
|
266
|
+
* 439 D-04: the bytes of a completed download with this sha256, held in memory and bounded by
|
|
267
|
+
* the adapter; undefined when the adapter holds none (over its cap, evicted, or disposed).
|
|
268
|
+
*/
|
|
269
|
+
readDownloadBytes?(sha256: string): Uint8Array | undefined;
|
|
270
|
+
}
|
|
271
|
+
declare function assertNever(value: never): never;
|
|
272
|
+
interface ActionResult {
|
|
273
|
+
recordingAnnotation?: RecordingAnnotationWitness;
|
|
274
|
+
success: boolean;
|
|
275
|
+
error?: string;
|
|
276
|
+
screenshot?: Buffer;
|
|
277
|
+
metadata?: Record<string, unknown>;
|
|
278
|
+
coordinates?: {
|
|
279
|
+
x: number;
|
|
280
|
+
y: number;
|
|
281
|
+
};
|
|
282
|
+
boundingBox?: {
|
|
283
|
+
x: number;
|
|
284
|
+
y: number;
|
|
285
|
+
width: number;
|
|
286
|
+
height: number;
|
|
287
|
+
};
|
|
288
|
+
startCoordinates?: {
|
|
289
|
+
x: number;
|
|
290
|
+
y: number;
|
|
291
|
+
};
|
|
292
|
+
endCoordinates?: {
|
|
293
|
+
x: number;
|
|
294
|
+
y: number;
|
|
295
|
+
};
|
|
296
|
+
data?: unknown;
|
|
297
|
+
}
|
|
298
|
+
interface RuntimeAuthStateConfig {
|
|
299
|
+
version: number;
|
|
300
|
+
kind: 'web';
|
|
301
|
+
targetName: string;
|
|
302
|
+
stateName: string;
|
|
303
|
+
capturedAt: string;
|
|
304
|
+
storageStatePath: string;
|
|
305
|
+
}
|
|
306
|
+
interface PlatformConfig {
|
|
307
|
+
platform: 'web' | 'android' | 'ios';
|
|
308
|
+
browser?: BrowserConfig;
|
|
309
|
+
device?: DeviceConfig;
|
|
310
|
+
bundleId?: string;
|
|
311
|
+
appPackage?: string;
|
|
312
|
+
appActivity?: string;
|
|
313
|
+
deepLinkAppId?: string;
|
|
314
|
+
appState?: 'preserve' | 'reset';
|
|
315
|
+
appPath?: string;
|
|
316
|
+
browserstackApp?: string;
|
|
317
|
+
appiumUrl?: string;
|
|
318
|
+
timeouts?: TimeoutConfig;
|
|
319
|
+
recording?: {
|
|
320
|
+
enabled: boolean;
|
|
321
|
+
videoDir: string;
|
|
322
|
+
videoSize?: {
|
|
323
|
+
width: number;
|
|
324
|
+
height: number;
|
|
325
|
+
};
|
|
326
|
+
};
|
|
327
|
+
verbose?: boolean;
|
|
328
|
+
farmSession?: {
|
|
329
|
+
hostname: string;
|
|
330
|
+
port: number;
|
|
331
|
+
path: string;
|
|
332
|
+
capabilities: Record<string, unknown>;
|
|
333
|
+
};
|
|
334
|
+
logCapture?: {
|
|
335
|
+
console?: boolean;
|
|
336
|
+
network?: boolean;
|
|
337
|
+
};
|
|
338
|
+
authState?: RuntimeAuthStateConfig;
|
|
339
|
+
}
|
|
340
|
+
interface BrowserConfig {
|
|
341
|
+
name: 'chromium' | 'firefox' | 'webkit';
|
|
342
|
+
headless?: boolean;
|
|
343
|
+
viewport?: {
|
|
344
|
+
width: number;
|
|
345
|
+
height: number;
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
interface DeviceConfig {
|
|
349
|
+
name: string;
|
|
350
|
+
platform: 'android' | 'ios';
|
|
351
|
+
transport: 'local' | 'browserstack';
|
|
352
|
+
match: Record<string, unknown>;
|
|
353
|
+
}
|
|
354
|
+
interface TimeoutConfig {
|
|
355
|
+
step?: number;
|
|
356
|
+
test?: number;
|
|
357
|
+
navigation?: number;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* The Channel-1 brief and the serve-time trace that explains it, as ONE value (KREV-02 · D-16).
|
|
362
|
+
*
|
|
363
|
+
* The pairing is the guarantee, not a convenience. A reader implementation cannot satisfy this
|
|
364
|
+
* port while producing a brief and no trace, and a consumer cannot receive a brief without also
|
|
365
|
+
* being handed how it was chosen — which is what makes 387-06's why-verb able to answer about the
|
|
366
|
+
* moment of serving rather than about a re-query of today's index.
|
|
367
|
+
*/
|
|
368
|
+
interface ServedRunBrief {
|
|
369
|
+
brief: BriefBlock;
|
|
370
|
+
trace: ServedRetrievalTraceV1;
|
|
371
|
+
}
|
|
372
|
+
/** The Channel-3 caution block with its own serve-time trace, on the same one-value contract. */
|
|
373
|
+
interface ServedCautions {
|
|
374
|
+
block: CautionBlock;
|
|
375
|
+
trace: ServedRetrievalTraceV1;
|
|
376
|
+
}
|
|
377
|
+
interface KnowledgeHintReader {
|
|
378
|
+
getRunBrief(scope: ScopeKey, budget: number): ServedRunBrief;
|
|
379
|
+
search(query: string, options: SearchOptions): IndexEntry[];
|
|
380
|
+
read(id: RecordId, options?: ReadOptions): RecordBody | undefined;
|
|
381
|
+
getCautions(anchors: Anchor[], budget: number): ServedCautions;
|
|
382
|
+
/**
|
|
383
|
+
* D-08 port evolution: source-tagged binding NAMES cross here (variables, memory, secrets) so
|
|
384
|
+
* matcher fillability mirrors loop binding; no value ever crosses the reader port in any form.
|
|
385
|
+
* Value binding stays loop-side at replay time. Absent availability is treated as empty, so a
|
|
386
|
+
* referenced-parameter procedure downranks to hint (fail-conservative).
|
|
387
|
+
*/
|
|
388
|
+
getProcedure(intent: string, screenState: ScreenState, availability?: ProcedureBindingAvailability): ProcedureMatch | undefined;
|
|
389
|
+
emitCanaryOutcome(payload: CanaryOutcome): void;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Adapter -> builder input for a single observed node.
|
|
394
|
+
*
|
|
395
|
+
* Structural links are carried as INDICES because refs do not exist yet — the
|
|
396
|
+
* builder mints them in document order (RESEARCH Pitfall #7).
|
|
397
|
+
*/
|
|
398
|
+
interface InterimNode {
|
|
399
|
+
/** Schema role vocabulary — adapters map native roles BEFORE the builder. */
|
|
400
|
+
role: string;
|
|
401
|
+
/** Raw accessible name; the builder normalizes it (ElementInfo.name = name ?? ''). */
|
|
402
|
+
name?: string;
|
|
403
|
+
value?: string;
|
|
404
|
+
/** Index of the nearest INCLUDED ancestor in the same nodes array. */
|
|
405
|
+
parentIndex?: number;
|
|
406
|
+
bounds?: {
|
|
407
|
+
x: number;
|
|
408
|
+
y: number;
|
|
409
|
+
width: number;
|
|
410
|
+
height: number;
|
|
411
|
+
};
|
|
412
|
+
/** Optional; the builder derives the geometric center from bounds when absent. */
|
|
413
|
+
center?: {
|
|
414
|
+
x: number;
|
|
415
|
+
y: number;
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* ONLY real platform signals (D-08 — never fabricated). The builder derives the
|
|
419
|
+
* required booleans from documented defaults when a signal is absent:
|
|
420
|
+
* enabled = state.enabled ?? true (absence of a disabled signal means enabled)
|
|
421
|
+
* focused = state.focused ?? false
|
|
422
|
+
* visible = state.visible ?? true (present in the a11y tree means exposed;
|
|
423
|
+
* adapters set false explicitly for offscreen)
|
|
424
|
+
* Optional bits (selected/checked/editable/clickable) pass through only when the
|
|
425
|
+
* platform supplied them — typed-absent otherwise.
|
|
426
|
+
*/
|
|
427
|
+
state?: Partial<ElementState>;
|
|
428
|
+
labels?: ElementLabels;
|
|
429
|
+
/** Raw platform bag, passed through to ElementInfo.attributes (default {}). */
|
|
430
|
+
attributes?: Record<string, string>;
|
|
431
|
+
/** Static text folded in from children that are not nodes; copied to ElementInfo.text. */
|
|
432
|
+
text?: string;
|
|
433
|
+
/** Adapter-private hint (e.g. android class / XCUI type); the builder ignores it. */
|
|
434
|
+
nativeType?: string;
|
|
435
|
+
/** D-10; the builder maps containerIndex -> the minted container Ref. */
|
|
436
|
+
scrollContext?: {
|
|
437
|
+
containerIndex?: number;
|
|
438
|
+
offsetX: number;
|
|
439
|
+
offsetY: number;
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
interface BuildScreenStateInput {
|
|
443
|
+
platform: Platform;
|
|
444
|
+
/** Document order — this is the ref-minting order. */
|
|
445
|
+
nodes: InterimNode[];
|
|
446
|
+
viewport: {
|
|
447
|
+
width: number;
|
|
448
|
+
height: number;
|
|
449
|
+
};
|
|
450
|
+
/**
|
|
451
|
+
* EXACT on web; BEST-EFFORT on mobile (D-10) — mobile callers pass a best-effort
|
|
452
|
+
* scroll derived from scrollable-node bounds. The field is precise only on web.
|
|
453
|
+
*/
|
|
454
|
+
scroll: {
|
|
455
|
+
offsetX: number;
|
|
456
|
+
offsetY: number;
|
|
457
|
+
contentWidth: number;
|
|
458
|
+
contentHeight: number;
|
|
459
|
+
};
|
|
460
|
+
coordSpace: 'viewport' | 'content' | 'screen';
|
|
461
|
+
/** Caller-supplied — keeps the builder pure and deterministic. */
|
|
462
|
+
timestamp: number;
|
|
463
|
+
url?: string;
|
|
464
|
+
/** Web-only (D-04); lands in metadata.domContext. */
|
|
465
|
+
domContext?: string;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Execute-time resolution stays adapter-private: `index` is the live node's place in the
|
|
469
|
+
* input nodes array. No same-screen position is minted for anything to pick by (438 D-13).
|
|
470
|
+
*/
|
|
471
|
+
interface RefBinding {
|
|
472
|
+
index: number;
|
|
473
|
+
}
|
|
474
|
+
interface BuildScreenStateResult {
|
|
475
|
+
/** Schema type — never redefined. */
|
|
476
|
+
state: ScreenState;
|
|
477
|
+
/** Execute-time ref -> live-node resolution; stays adapter-private. */
|
|
478
|
+
binding: Record<Ref, RefBinding>;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* The capture stamp the loop applies after compression (D-11). `full` carries the
|
|
482
|
+
* real image dimensions; `textOnly` attaches no image.
|
|
483
|
+
*/
|
|
484
|
+
type CaptureStamp = {
|
|
485
|
+
mode: 'full';
|
|
486
|
+
imageWidth: number;
|
|
487
|
+
imageHeight: number;
|
|
488
|
+
} | {
|
|
489
|
+
mode: 'textOnly';
|
|
490
|
+
};
|
|
491
|
+
/**
|
|
492
|
+
* Assemble a schema v2 ScreenState plus an adapter-private ref->node binding from a
|
|
493
|
+
* platform-neutral interim node list. Pure and deterministic: identical input always
|
|
494
|
+
* yields byte-identical output.
|
|
495
|
+
*/
|
|
496
|
+
declare function buildScreenState(input: BuildScreenStateInput): BuildScreenStateResult;
|
|
497
|
+
/**
|
|
498
|
+
* Deterministic LLM-facing tree text, derived from the structured elements (D-02) —
|
|
499
|
+
* one grammar for all platforms. Exported so Phase 357's slim/rich dual-projection is
|
|
500
|
+
* a renderer-only change with zero adapter edits.
|
|
501
|
+
*
|
|
502
|
+
* Grammar (pinned; RESEARCH Pitfall #10):
|
|
503
|
+
* [Viewport: {viewportWidth}x{viewportHeight}] [{elements.length} elements]
|
|
504
|
+
* {2 spaces × depth}- {role}{ "name"?} [ref={ref}]{ @(x,y wxh)?}{ [disabled]?}{ [offscreen]?}{ [checked]?}{ [selected]?}{ [editable]?}{ text: <row text as a JSON string>?}
|
|
505
|
+
* depth = length of the parentRef chain (root elements depth 0).
|
|
506
|
+
* A rendered name collapses each line break, with the whitespace around it, to one space, so
|
|
507
|
+
* every element stays on one line; element.name and value keep the raw text.
|
|
508
|
+
* ` text: "…"` appears only on a row container (ROW_CONTAINER_ROLES) whose element.text is
|
|
509
|
+
* non-empty (438 D-02: the planner must see a row to state it); line breaks collapse the same
|
|
510
|
+
* way and the rendered text is capped at 120 characters plus `…`. The row text is a JSON string
|
|
511
|
+
* (JSON.stringify, as quoted() renders names in the refusal line), so a quote or backslash in app
|
|
512
|
+
* text cannot end the string or forge a ref, flag or line (438 gap round, WR-02, T-438-04).
|
|
513
|
+
* element.text keeps the full text the judge and replay compare against.
|
|
514
|
+
*/
|
|
515
|
+
declare function renderScreenTree(elements: ElementInfo[], metadata: ScreenStateMetadata): string;
|
|
516
|
+
/**
|
|
517
|
+
* Pure re-stamp of the discriminated captureMode metadata member (D-11). Constructs a
|
|
518
|
+
* fresh metadata object from the base fields and returns a NEW ScreenState — zero `as`
|
|
519
|
+
* casts, zero mutation of the input.
|
|
520
|
+
*/
|
|
521
|
+
declare function stampCaptureMode(state: ScreenState, capture: CaptureStamp): ScreenState;
|
|
522
|
+
|
|
523
|
+
/** The element the planner meant beside its ref (D-01); `row` names a repeated label's row. */
|
|
524
|
+
interface StatedTarget {
|
|
525
|
+
role?: string;
|
|
526
|
+
name: string;
|
|
527
|
+
row?: string;
|
|
528
|
+
}
|
|
529
|
+
/** The element lanes an action can carry a stated target on. */
|
|
530
|
+
type StatedTargetLane = 'ref' | 'fromRef' | 'toRef';
|
|
531
|
+
/** The stated target per element lane, kept by the gate for the pre-dispatch check (D-01, D-04). */
|
|
532
|
+
type StatedTargets = Partial<Record<StatedTargetLane, StatedTarget>>;
|
|
533
|
+
type TargetMismatchReason = 'role-kind' | 'name-no-shared-word' | 'name-number' | 'row';
|
|
534
|
+
type StatedTargetJudgement = {
|
|
535
|
+
verdict: 'agrees';
|
|
536
|
+
rowStated: boolean;
|
|
537
|
+
rowKnown: boolean;
|
|
538
|
+
observedRow?: string;
|
|
539
|
+
} | {
|
|
540
|
+
verdict: 'contradicts';
|
|
541
|
+
reasons: TargetMismatchReason[];
|
|
542
|
+
rowStated: boolean;
|
|
543
|
+
observedRow?: string;
|
|
544
|
+
};
|
|
545
|
+
/**
|
|
546
|
+
* Judge a stated target against the element its ref names (D-03). `elements` is the observation
|
|
547
|
+
* the element belongs to; the element's row is read from it (D-02).
|
|
548
|
+
*/
|
|
549
|
+
declare function judgeStatedTarget(stated: StatedTarget, element: ElementInfo, elements: readonly ElementInfo[]): StatedTargetJudgement;
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* The subset of the action union that may be persisted in a cached plan (D-08). The
|
|
553
|
+
* non-deterministic AI-assertion verbs — `assertAI` (judge lane) and `extractAI`
|
|
554
|
+
* (extraction lane) — are `Exclude`d from the composed `Action` union so a plan carrying
|
|
555
|
+
* one CANNOT typecheck into a cache write. Plan 10's runtime guard at the write site is the
|
|
556
|
+
* narrowing proof; this static boundary is the belt-and-suspenders (the 356
|
|
557
|
+
* `NonCacheableStepInput` type-level idiom, mirrored at the value shape).
|
|
558
|
+
*/
|
|
559
|
+
type CacheablePlannedAction = Exclude<Action, {
|
|
560
|
+
type: 'assertAI';
|
|
561
|
+
} | {
|
|
562
|
+
type: 'extractAI';
|
|
563
|
+
}>;
|
|
564
|
+
/**
|
|
565
|
+
* Descriptor-only locator (D-12, 438 D-10/D-11). Identity is the observed role, the exact
|
|
566
|
+
* observed name with spacing tidied (numbers and badges kept) and, when the planner stated a
|
|
567
|
+
* row the element's row holds, those row words. Never a position, a ref, a selector or
|
|
568
|
+
* coordinates: replay re-finds the one element with this identity in a fresh observation.
|
|
569
|
+
*/
|
|
570
|
+
interface GroundedLocator {
|
|
571
|
+
role: string;
|
|
572
|
+
name: string;
|
|
573
|
+
row?: string;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* The cached plan value. `plannedActions` are stored in sanitized (locator-free,
|
|
577
|
+
* data-free) form AND statically exclude the AI-assertion verbs (D-08); grounding is carried
|
|
578
|
+
* only by `groundedLocator` descriptors so no run-specific ref/selector/coordinate or step
|
|
579
|
+
* `data` value ever persists (D-12).
|
|
580
|
+
*/
|
|
581
|
+
interface CachedPlanValue {
|
|
582
|
+
plannedActions: CacheablePlannedAction[];
|
|
583
|
+
groundedLocator: GroundedLocator[];
|
|
584
|
+
verificationOutcome: 'passed' | 'failed';
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Redesigned per-key cache port (D-11). Get/set/invalidate over a branded CacheKey
|
|
588
|
+
* — the v1 prefix/index sub-action model is dropped. A storage implementation (Plan
|
|
589
|
+
* 05's FsActionCache) implements this interface; runner stays storage-neutral.
|
|
590
|
+
*/
|
|
591
|
+
interface ActionPlanCache {
|
|
592
|
+
get(key: CacheKey): Promise<CachedPlanValue | null>;
|
|
593
|
+
set(key: CacheKey, value: CachedPlanValue): Promise<void>;
|
|
594
|
+
invalidate(key: CacheKey): Promise<void>;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Project a planned action's target element to a descriptor-only locator: its role, its exact
|
|
599
|
+
* name, and the stated row when the element's known row holds it. Returns null when the action
|
|
600
|
+
* targets no element (e.g. navigate) or the ref no longer resolves.
|
|
601
|
+
*/
|
|
602
|
+
declare function toGroundedLocator(action: Action, elements: readonly ElementInfo[], stated?: StatedTarget): GroundedLocator | null;
|
|
603
|
+
interface BuildCachedPlanValueInput {
|
|
604
|
+
plannedActions: readonly Action[];
|
|
605
|
+
elements: readonly ElementInfo[];
|
|
606
|
+
verificationOutcome: 'passed' | 'failed';
|
|
607
|
+
/** The completing decision's stated target per lane; supplies a locator's row words. */
|
|
608
|
+
statedTargets?: StatedTargets;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Assemble a descriptor-only CachedPlanValue. Planned actions are sanitized of every
|
|
612
|
+
* locator binding and step `data` value; grounding is carried solely by the
|
|
613
|
+
* descriptor locators so nothing run-specific is ever persisted (D-12 overfit guard).
|
|
614
|
+
*/
|
|
615
|
+
declare function buildCachedPlanValue(input: BuildCachedPlanValueInput): CachedPlanValue;
|
|
616
|
+
|
|
617
|
+
declare const ROW_CONTAINER_ROLES: readonly string[];
|
|
618
|
+
/**
|
|
619
|
+
* Word tokens of a text: digit groups joined, NFKC, lowercased, split on every run that is not a
|
|
620
|
+
* letter or digit.
|
|
621
|
+
*/
|
|
622
|
+
declare function wordTokens(text: string | undefined): string[];
|
|
623
|
+
/**
|
|
624
|
+
* The row text of an element in its observation, or undefined when the element has no row
|
|
625
|
+
* container or the container's text is empty (D-02: the row is unknown).
|
|
626
|
+
*/
|
|
627
|
+
declare function rowTextOf(element: ElementInfo, elements: readonly ElementInfo[]): string | undefined;
|
|
628
|
+
/** Whether every stated row word is in the row text (equal, or a prefix of a non-number word). */
|
|
629
|
+
declare function rowContains(rowText: string, statedRow: string): boolean;
|
|
630
|
+
|
|
631
|
+
type StrictMatchResult = {
|
|
632
|
+
kind: 'resolved';
|
|
633
|
+
ref: Ref;
|
|
634
|
+
} | {
|
|
635
|
+
kind: 'absent';
|
|
636
|
+
} | {
|
|
637
|
+
kind: 'not-unique';
|
|
638
|
+
count: number;
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
/** The values a readable fill compared: requested and read back (sentinels for a secure field). */
|
|
642
|
+
interface FillRereadMeta {
|
|
643
|
+
expected: string;
|
|
644
|
+
actual: string;
|
|
645
|
+
}
|
|
646
|
+
/** What ended a scroll: the target appeared, the swipe budget ran out, or a boundary signal. */
|
|
647
|
+
type ScrollEndedBy = 'target-visible' | 'max-swipes' | 'boundary';
|
|
648
|
+
/** Outcomes new runs write (436-02, D-24). */
|
|
649
|
+
declare const SCROLL_OUTCOMES_WRITTEN: readonly ["found", "not-found", "unresolved"];
|
|
650
|
+
/** Outcomes only pre-436 runs wrote; still read so persisted metadata stays parseable (D-17). */
|
|
651
|
+
declare const SCROLL_OUTCOMES_READ_ONLY: readonly ["no-change", "cap"];
|
|
652
|
+
/** EXEC-02 scroll outcome block. `endedBy` is absent on pre-436 metadata. */
|
|
653
|
+
interface ScrollUntilVisibleMeta {
|
|
654
|
+
swipes: number;
|
|
655
|
+
reversedOnce: boolean;
|
|
656
|
+
outcome: (typeof SCROLL_OUTCOMES_WRITTEN)[number] | (typeof SCROLL_OUTCOMES_READ_ONLY)[number];
|
|
657
|
+
endedBy?: ScrollEndedBy;
|
|
658
|
+
}
|
|
659
|
+
/** EXEC-03 openApp resolution block. */
|
|
660
|
+
interface OpenAppMeta {
|
|
661
|
+
resolvedId: string;
|
|
662
|
+
matchKind: 'exact' | 'fuzzy' | 'explicit';
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
declare function readFillRereadMeta(actionResult?: ActionResult): FillRereadMeta | undefined;
|
|
666
|
+
declare function readScrollUntilVisibleMeta(actionResult?: MetadataCarrier): ScrollUntilVisibleMeta | undefined;
|
|
667
|
+
declare function readOpenAppMeta(actionResult?: ActionResult): OpenAppMeta | undefined;
|
|
668
|
+
/** EXEC-05 reportValue answer block (D-01/D-03). Keyed `reportedValue`. */
|
|
669
|
+
interface ReportedValueMeta {
|
|
670
|
+
value: string;
|
|
671
|
+
}
|
|
672
|
+
/** EXEC-06 uniqueValue mint block (D-16). Keyed `uniqueValue`. */
|
|
673
|
+
interface UniqueValueMeta {
|
|
674
|
+
value: string;
|
|
675
|
+
prefix: string;
|
|
676
|
+
}
|
|
677
|
+
/** EXEC-07 AI-judgment block (D-05/D-06/D-07/D-09). Keyed `aiJudgment`. */
|
|
678
|
+
interface AiJudgmentMeta {
|
|
679
|
+
verb: 'assertAI' | 'extractAI';
|
|
680
|
+
verdict?: boolean;
|
|
681
|
+
}
|
|
682
|
+
/** EXEC-07 assertValue outcome block (D-10/D-11). Keyed `assertValueOutcome`. */
|
|
683
|
+
interface AssertValueOutcomeMeta {
|
|
684
|
+
source: string;
|
|
685
|
+
op: string;
|
|
686
|
+
passed: boolean;
|
|
687
|
+
expected?: string;
|
|
688
|
+
actual?: string | null;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* The minimal structural input the 360 metadata readers narrow: any ActionResult-like carrier of a
|
|
692
|
+
* metadata bag. Broader than `ActionResult` so the seam's loosely-typed sub-action result surfaces
|
|
693
|
+
* (which only guarantee an optional `metadata`) narrow cleanly — the defensive idiom returns
|
|
694
|
+
* undefined on any shape miss regardless.
|
|
695
|
+
*/
|
|
696
|
+
type MetadataCarrier = {
|
|
697
|
+
metadata?: Record<string, unknown>;
|
|
698
|
+
};
|
|
699
|
+
declare function readReportedValueMeta(actionResult?: MetadataCarrier): ReportedValueMeta | undefined;
|
|
700
|
+
declare function readUniqueValueMeta(actionResult?: MetadataCarrier): UniqueValueMeta | undefined;
|
|
701
|
+
declare function readAiJudgmentMeta(actionResult?: MetadataCarrier): AiJudgmentMeta | undefined;
|
|
702
|
+
declare function readAssertValueOutcomeMeta(actionResult?: MetadataCarrier): AssertValueOutcomeMeta | undefined;
|
|
703
|
+
/** Typed refusal of a fill/clearText before any text was typed. Keyed `fillRefusal`. */
|
|
704
|
+
interface FillRefusalMeta {
|
|
705
|
+
verb: 'fill' | 'clearText';
|
|
706
|
+
cause: 'not-editable' | 'target-not-found' | 'single-line-field';
|
|
707
|
+
evidence: 'no-editable-signal' | 'no-keyboard-focus' | 'focus-elsewhere' | 'stale-target' | 'text-has-line-breaks';
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* What happened to a requested submit after a fill (437 D-17). Keyed `fillSubmit`. Pre-437 blocks
|
|
711
|
+
* carry `sent` alone or `{ sent: false, reason: 'multi-line-field' }` and still read.
|
|
712
|
+
*/
|
|
713
|
+
interface FillSubmitMeta {
|
|
714
|
+
sent: boolean;
|
|
715
|
+
outcome?: SubmitOutcome;
|
|
716
|
+
reason?: SubmitReason;
|
|
717
|
+
detail?: string;
|
|
718
|
+
}
|
|
719
|
+
/** A shell-routed action refused or failed by the driver (437 D-18). Keyed `shellAction`. */
|
|
720
|
+
interface ShellActionMeta {
|
|
721
|
+
verb: 'keyevent' | 'openQuickSettings';
|
|
722
|
+
outcome: 'unavailable' | 'failed';
|
|
723
|
+
reason?: 'adb-shell-disabled';
|
|
724
|
+
detail?: string;
|
|
725
|
+
}
|
|
726
|
+
declare function readFillRefusalMeta(actionResult?: MetadataCarrier): FillRefusalMeta | undefined;
|
|
727
|
+
/** How a fill or clear's read-back compared with the requested text (437 D-06). Keyed `fillVerdict`. */
|
|
728
|
+
interface FillVerdictMeta {
|
|
729
|
+
verb: 'fill' | 'clearText';
|
|
730
|
+
verdict: FillVerdict;
|
|
731
|
+
path?: InputPath;
|
|
732
|
+
secure?: true;
|
|
733
|
+
}
|
|
734
|
+
declare function readFillVerdictMeta(actionResult?: MetadataCarrier): FillVerdictMeta | undefined;
|
|
735
|
+
declare function readFillSubmitMeta(actionResult?: MetadataCarrier): FillSubmitMeta | undefined;
|
|
736
|
+
declare function readShellActionMeta(actionResult?: MetadataCarrier): ShellActionMeta | undefined;
|
|
737
|
+
declare function readDownloadMeta(actionResult?: MetadataCarrier): DownloadEvidence[] | undefined;
|
|
738
|
+
/** The most masked clipboard text one write keeps; longer text is cut and marked. */
|
|
739
|
+
declare const CLIPBOARD_TEXT_LIMIT = 2000;
|
|
740
|
+
declare const CLIPBOARD_TRUNCATION_MARKER = "\u2026[truncated]";
|
|
741
|
+
type ClipboardWriteMeta = Pick<ClipboardEvidence, 'source' | 'text' | 'readBack'>;
|
|
742
|
+
declare function readClipboardMeta(actionResult?: MetadataCarrier): ClipboardWriteMeta[] | undefined;
|
|
743
|
+
|
|
744
|
+
interface FillVerifyInput {
|
|
745
|
+
expected: string;
|
|
746
|
+
actual: string | undefined;
|
|
747
|
+
secure: boolean;
|
|
748
|
+
verb?: 'fill' | 'clearText';
|
|
749
|
+
}
|
|
750
|
+
interface FillVerifyResult {
|
|
751
|
+
verdict: FillVerdict;
|
|
752
|
+
ok: boolean;
|
|
753
|
+
secure?: true;
|
|
754
|
+
meta?: FillRereadMeta;
|
|
755
|
+
selfCorrectingError?: string;
|
|
756
|
+
}
|
|
757
|
+
declare const FILL_SEPARATORS: ReadonlySet<string>;
|
|
758
|
+
declare function compareFillReread(input: FillVerifyInput): FillVerifyResult;
|
|
759
|
+
|
|
760
|
+
type DesiredState = 'checked' | 'unchecked' | 'selected' | 'unselected';
|
|
761
|
+
interface DesiredStateAction {
|
|
762
|
+
type: string;
|
|
763
|
+
ref?: string;
|
|
764
|
+
desiredState?: DesiredState;
|
|
765
|
+
value?: string;
|
|
766
|
+
}
|
|
767
|
+
declare function desiredStateSatisfied(action: DesiredStateAction, screenState: ScreenState): IdempotentNoopMeta | undefined;
|
|
768
|
+
|
|
769
|
+
type Direction = 'up' | 'down' | 'left' | 'right';
|
|
770
|
+
/** What one swipe proved: the container moved, it is at its extent, or nothing is known. */
|
|
771
|
+
type SwipeSignal = 'moved' | 'at-boundary' | 'unknown';
|
|
772
|
+
/** The target is on screen, definitively not on screen, or cannot be judged. */
|
|
773
|
+
type TargetVisibility = 'visible' | 'hidden' | 'unknown';
|
|
774
|
+
interface ScrollPrimitives {
|
|
775
|
+
/** One platform swipe in `direction`, optionally inside container `ref`. */
|
|
776
|
+
swipeOnce(direction: Direction, ref?: Ref): Promise<SwipeSignal>;
|
|
777
|
+
/** Native search-scroll toward `target`; its `true` is confirmed by the visibility probe. */
|
|
778
|
+
nativeSearchScroll?(target: string, ref?: Ref, direction?: Direction): Promise<boolean>;
|
|
779
|
+
/** Light probe: interim nodes only, NO refMap mint, NO screenshot. Read for visibility only. */
|
|
780
|
+
readInterimTree(): Promise<InterimNode[]>;
|
|
781
|
+
/**
|
|
782
|
+
* Is `target` visible, definitively not on screen, or impossible to judge? When supplied it
|
|
783
|
+
* replaces the interim-tree name match; any other answer counts as unknown.
|
|
784
|
+
*/
|
|
785
|
+
probeTargetVisible?(target: string, ref?: Ref): Promise<TargetVisibility>;
|
|
786
|
+
}
|
|
787
|
+
interface UntilVisibleParams {
|
|
788
|
+
target: string;
|
|
789
|
+
direction: Direction;
|
|
790
|
+
maxSwipes: number;
|
|
791
|
+
retryOpposite: boolean;
|
|
792
|
+
ref?: Ref;
|
|
793
|
+
}
|
|
794
|
+
interface ScrollLoopResult {
|
|
795
|
+
meta: ScrollUntilVisibleMeta;
|
|
796
|
+
found: boolean;
|
|
797
|
+
}
|
|
798
|
+
interface ScrollOffsets {
|
|
799
|
+
before: number;
|
|
800
|
+
after: number;
|
|
801
|
+
/** Largest offset the container can reach on the swiped axis. */
|
|
802
|
+
max: number;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* The offset rule shared by every surface that can read real scroll offsets (web containers,
|
|
806
|
+
* Android WebView): a changed offset is movement; an unchanged offset at the container's extent
|
|
807
|
+
* in the swiped direction is the boundary; anything else is unknown.
|
|
808
|
+
*/
|
|
809
|
+
declare function swipeSignalFromOffsets(direction: Direction, offsets: ScrollOffsets | undefined): SwipeSignal;
|
|
810
|
+
/** A page's scrolling element, read before and after a gesture that is not bound to a container. */
|
|
811
|
+
interface DocumentScrollOffsets {
|
|
812
|
+
x: number;
|
|
813
|
+
y: number;
|
|
814
|
+
maxX: number;
|
|
815
|
+
maxY: number;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* The offset rule for a gesture that lands on whatever is under the pointer (web wheel, WebView
|
|
819
|
+
* touch swipe), over every scroller the gesture may have acted on, as aligned before/after arrays
|
|
820
|
+
* (436-18 · D-25). Any member that moved is movement. A boundary needs at least one member with
|
|
821
|
+
* range on the swiped axis and every member with range at its extent; a member short of its
|
|
822
|
+
* extent, no member with range, or missing, empty or misaligned arrays prove nothing.
|
|
823
|
+
*/
|
|
824
|
+
declare function scrollerSetSwipeSignal(direction: Direction, before: readonly DocumentScrollOffsets[] | undefined, after: readonly DocumentScrollOffsets[] | undefined): SwipeSignal;
|
|
825
|
+
/**
|
|
826
|
+
* The scroller-set rule over the document scroller alone: a document with no scroll range on the
|
|
827
|
+
* swiped axis proves nothing, because the gesture may have moved a nested scroller.
|
|
828
|
+
*/
|
|
829
|
+
declare function documentSwipeSignal(direction: Direction, before: DocumentScrollOffsets | undefined, after: DocumentScrollOffsets | undefined): SwipeSignal;
|
|
830
|
+
declare function runUntilVisibleLoop(p: ScrollPrimitives, params: UntilVisibleParams): Promise<ScrollLoopResult>;
|
|
831
|
+
interface DirectionScrollParams {
|
|
832
|
+
direction: Direction;
|
|
833
|
+
ref?: Ref;
|
|
834
|
+
}
|
|
835
|
+
/** Direction mode is one gesture: no opposite retry, whatever the signal says. */
|
|
836
|
+
declare function runDirectionScroll(p: ScrollPrimitives, params: DirectionScrollParams): Promise<ScrollLoopResult>;
|
|
837
|
+
declare const MAX_TO_END_SWIPES = 20;
|
|
838
|
+
/**
|
|
839
|
+
* toEnd: swipe until a boundary signal or the cap. Reaching the boundary is the goal, so it
|
|
840
|
+
* reports 'found'; the cap without a boundary signal reports 'unresolved'.
|
|
841
|
+
*/
|
|
842
|
+
declare function runToEndScroll(p: ScrollPrimitives, direction: Direction, ref?: Ref, cap?: number): Promise<ScrollLoopResult>;
|
|
843
|
+
|
|
844
|
+
interface AppRegistryEntry {
|
|
845
|
+
id: string;
|
|
846
|
+
label?: string;
|
|
847
|
+
}
|
|
848
|
+
interface ScorerOptions {
|
|
849
|
+
fuzzyThreshold?: number;
|
|
850
|
+
fuzzyMargin?: number;
|
|
851
|
+
editDistanceBound?: number;
|
|
852
|
+
candidateCount?: number;
|
|
853
|
+
}
|
|
854
|
+
type AppResolution = {
|
|
855
|
+
kind: 'exact' | 'fuzzy';
|
|
856
|
+
id: string;
|
|
857
|
+
} | {
|
|
858
|
+
kind: 'ambiguous' | 'unknown';
|
|
859
|
+
candidates: {
|
|
860
|
+
id: string;
|
|
861
|
+
label?: string;
|
|
862
|
+
}[];
|
|
863
|
+
};
|
|
864
|
+
/**
|
|
865
|
+
* Minimum score a fuzzy top candidate must clear to be launchable. Internal runner constant;
|
|
866
|
+
* injectable via ScorerOptions for tests only. Do NOT promote to user-facing config or thread
|
|
867
|
+
* through LoopPorts / any config schema (353 K=2 precedent).
|
|
868
|
+
*/
|
|
869
|
+
declare const DEFAULT_FUZZY_THRESHOLD = 0.5;
|
|
870
|
+
/**
|
|
871
|
+
* Minimum lead the top fuzzy candidate must hold over the runner-up to launch (else ambiguous).
|
|
872
|
+
* Internal runner constant; injectable for tests only. Do NOT promote to user-facing config.
|
|
873
|
+
*/
|
|
874
|
+
declare const DEFAULT_FUZZY_MARGIN = 0.15;
|
|
875
|
+
/**
|
|
876
|
+
* Levenshtein cap: matches beyond this edit distance do not count as edit-rung candidates.
|
|
877
|
+
* Internal runner constant; injectable for tests only. Do NOT promote to user-facing config.
|
|
878
|
+
*/
|
|
879
|
+
declare const DEFAULT_EDIT_DISTANCE_BOUND = 3;
|
|
880
|
+
/**
|
|
881
|
+
* How many candidates an ambiguous/unknown resolution surfaces. Internal runner constant;
|
|
882
|
+
* injectable for tests only. Do NOT promote to user-facing config.
|
|
883
|
+
*/
|
|
884
|
+
declare const DEFAULT_CANDIDATE_COUNT = 3;
|
|
885
|
+
declare function resolveAppName(name: string, registry: AppRegistryEntry[], opts?: ScorerOptions): AppResolution;
|
|
886
|
+
|
|
887
|
+
/** The closed set of runner-minted (platform-less) verb names dispatched without adapter.execute. */
|
|
888
|
+
declare const MINT_VERBS: readonly ["reportValue", "uniqueValue"];
|
|
889
|
+
type MintVerb = (typeof MINT_VERBS)[number];
|
|
890
|
+
/** Emission params for a `reportValue` mint (mirrors reportValueSchema). */
|
|
891
|
+
interface ReportValueInput {
|
|
892
|
+
value: string;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Mint the D-01/D-03 `reportValue` success result. The answer rides `metadata.reportedValue`
|
|
896
|
+
* (readReportedValueMeta narrows it). NOTE: `value` here is the emission value — the Plan 10 seam
|
|
897
|
+
* masks before recording into StepEvidence.reportedValue (masked-before-record, T-360-01).
|
|
898
|
+
*/
|
|
899
|
+
declare function mintReportedValue(input: ReportValueInput): ActionResult;
|
|
900
|
+
/** Emission params for a `uniqueValue` mint (mirrors uniqueValueSchema). */
|
|
901
|
+
interface UniqueValueInput {
|
|
902
|
+
prefix: string;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* The injectable suffix generator (353 K=2 determinism, D-16): a pure `() => string` overridden in
|
|
906
|
+
* tests for deterministic output, defaulting to {@link defaultUniqueValueGenerator}.
|
|
907
|
+
*/
|
|
908
|
+
type UniqueValueGenerator = () => string;
|
|
909
|
+
/**
|
|
910
|
+
* Mint the D-16 `uniqueValue` success result: `prefix` + a generated suffix, carried on
|
|
911
|
+
* `metadata.uniqueValue` ({ value, prefix }). The generator is injectable for deterministic tests;
|
|
912
|
+
* uniqueness is the semantic, so cache replay re-mints fresh. The Plan 10 seam auto-writes the
|
|
913
|
+
* minted value into run-scoped working memory keyed by the prefix.
|
|
914
|
+
*/
|
|
915
|
+
declare function mintUniqueValue(input: UniqueValueInput, generator?: UniqueValueGenerator): ActionResult;
|
|
916
|
+
/**
|
|
917
|
+
* Default suffix source (D-16): timestamp + entropy, base36-encoded, non-empty and distinct across
|
|
918
|
+
* calls. Injectable so deterministic tests pass a fixed generator instead. Randomness/time IS the
|
|
919
|
+
* semantic here (uniqueness), unlike the deterministic app-resolution ladder.
|
|
920
|
+
*/
|
|
921
|
+
declare function defaultUniqueValueGenerator(): string;
|
|
922
|
+
|
|
923
|
+
/** Comparator input: the source label, the closed op, the (optional) expected, and the raw actual. */
|
|
924
|
+
interface AssertValueInput {
|
|
925
|
+
source: string;
|
|
926
|
+
op: AssertValueOp;
|
|
927
|
+
/** Absent for the no-expected ops (isNull/isDefined). */
|
|
928
|
+
expected?: string;
|
|
929
|
+
/** The raw value the adapter read. `null` is a read; `undefined` is unread and fails every op. */
|
|
930
|
+
actual?: string | null;
|
|
931
|
+
/** Each network log entry's JSON, for `source: 'network'`. Absent or empty is unread. */
|
|
932
|
+
entries?: string[];
|
|
933
|
+
}
|
|
934
|
+
/** Comparator outcome — projects onto the schema AssertValueEvidence block. */
|
|
935
|
+
interface AssertValueResult {
|
|
936
|
+
source: string;
|
|
937
|
+
op: AssertValueOp;
|
|
938
|
+
expected?: string;
|
|
939
|
+
actual?: string | null;
|
|
940
|
+
passed: boolean;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Apply the closed assertValue op deterministically. Type-aware per the Block A pins: gt/lt coerce
|
|
944
|
+
* numeric; equals/notEquals/contains compare as strings; isNull passes only on a null read. An
|
|
945
|
+
* unread value fails every op (439 D-14). A network read passes only on a matching entry.
|
|
946
|
+
*/
|
|
947
|
+
declare function compareAssertValue(input: AssertValueInput): AssertValueResult;
|
|
948
|
+
|
|
949
|
+
/** The most characters of each side `firstDifference` shows. */
|
|
950
|
+
declare const FILE_DIFFERENCE_WINDOW = 80;
|
|
951
|
+
interface SavedFileExpectation {
|
|
952
|
+
name: string;
|
|
953
|
+
/** 'none': the name only; 'exact': the whole text; 'lines': each line whole, in order. */
|
|
954
|
+
match: 'none' | 'exact' | 'lines';
|
|
955
|
+
text: string;
|
|
956
|
+
}
|
|
957
|
+
interface SavedFileComparison extends ConditionFileCheck {
|
|
958
|
+
/** The 1-based file line of the first difference, when the content differs. */
|
|
959
|
+
line?: number;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Compare a held download with the verifier's expectation. The name is exact (case and extension);
|
|
963
|
+
* 'exact' needs the whole text, 'lines' each expected line as a whole file line in order, and an
|
|
964
|
+
* empty expected text matches only a zero-byte file. `bytes` undefined, or bytes that are not valid
|
|
965
|
+
* UTF-8, leave the content 'unverified'.
|
|
966
|
+
*/
|
|
967
|
+
declare function compareSavedFile(input: {
|
|
968
|
+
expected: SavedFileExpectation;
|
|
969
|
+
name: string;
|
|
970
|
+
bytes: Uint8Array | undefined;
|
|
971
|
+
mask: (value: string) => string;
|
|
972
|
+
}): SavedFileComparison;
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* A complete, valid v2 `ScreenState` with `captureMode: 'textOnly'`. Callers override
|
|
976
|
+
* individual fields; the base is deterministic (fixed refs/bounds/timestamp/scroll).
|
|
977
|
+
*/
|
|
978
|
+
declare function makeScreenState(overrides?: Partial<ScreenState>): ScreenState;
|
|
979
|
+
/** The platform-neutral node list + the builder input (minus nodes/platform). */
|
|
980
|
+
interface InterimFixture {
|
|
981
|
+
nodes: InterimNode[];
|
|
982
|
+
buildInput: Omit<BuildScreenStateInput, 'nodes' | 'platform'>;
|
|
983
|
+
}
|
|
984
|
+
type InterimFixtureKind = 'login-screen' | 'mixed-geometry' | 'nested';
|
|
985
|
+
/**
|
|
986
|
+
* Platform-neutral interim nodes plus the matching builder input for a scenario. The
|
|
987
|
+
* conformance cases feed these through `buildScreenState` (per platform) to assert the
|
|
988
|
+
* shared contract holds identically across all three adapters.
|
|
989
|
+
*/
|
|
990
|
+
declare function makeInterimNodes(kind: InterimFixtureKind): InterimFixture;
|
|
991
|
+
|
|
992
|
+
type RecordedActionOutcome = 'success' | 'failure' | 'no-input' | 'refused';
|
|
993
|
+
interface RecordedAction {
|
|
994
|
+
/** 1-based position of the action in the step. */
|
|
995
|
+
index: number;
|
|
996
|
+
action: Action;
|
|
997
|
+
/** The element the action's ref named on the screen it acted on. */
|
|
998
|
+
element?: {
|
|
999
|
+
role: string;
|
|
1000
|
+
name: string;
|
|
1001
|
+
};
|
|
1002
|
+
outcome: RecordedActionOutcome;
|
|
1003
|
+
error?: string;
|
|
1004
|
+
operationEvidence?: OperationEvidence;
|
|
1005
|
+
}
|
|
1006
|
+
type RecordedReadKind = 'assertValue' | 'assertAI' | 'extractAI' | 'reportValue';
|
|
1007
|
+
interface RecordedRead {
|
|
1008
|
+
/** The 1-based index of the action that read the value. */
|
|
1009
|
+
index: number;
|
|
1010
|
+
kind: RecordedReadKind;
|
|
1011
|
+
text: string;
|
|
1012
|
+
assert?: {
|
|
1013
|
+
source: string;
|
|
1014
|
+
op: string;
|
|
1015
|
+
expected?: string;
|
|
1016
|
+
passed: boolean;
|
|
1017
|
+
};
|
|
1018
|
+
claim?: string;
|
|
1019
|
+
/** An assertAI read's verdict on its claim. */
|
|
1020
|
+
judged?: boolean;
|
|
1021
|
+
mode?: string;
|
|
1022
|
+
}
|
|
1023
|
+
interface CarriedLine {
|
|
1024
|
+
stepNumber: number;
|
|
1025
|
+
actionIndex: number;
|
|
1026
|
+
text: string;
|
|
1027
|
+
}
|
|
1028
|
+
interface VerifierStepRecord {
|
|
1029
|
+
startScreen: ScreenState;
|
|
1030
|
+
actions: RecordedAction[];
|
|
1031
|
+
reads: RecordedRead[];
|
|
1032
|
+
carried: CarriedLine[];
|
|
1033
|
+
}
|
|
1034
|
+
interface RenderedStepRecord {
|
|
1035
|
+
actions: string;
|
|
1036
|
+
operationEvidence: string;
|
|
1037
|
+
reads: string;
|
|
1038
|
+
carried: string;
|
|
1039
|
+
}
|
|
1040
|
+
declare const RECORD_ACTION_LINE_CAP = 40;
|
|
1041
|
+
declare const RECORD_LINE_CHAR_CAP = 300;
|
|
1042
|
+
declare const RECORD_LIST_CAP = 50;
|
|
1043
|
+
declare class StepRecordAccumulator {
|
|
1044
|
+
private startScreen;
|
|
1045
|
+
private readonly actions;
|
|
1046
|
+
private readonly reads;
|
|
1047
|
+
private readonly carried;
|
|
1048
|
+
/** Keeps the first settled observation of the step; later calls are ignored. */
|
|
1049
|
+
start(screen: ScreenState): void;
|
|
1050
|
+
addAction(entry: Omit<RecordedAction, 'index'>): number;
|
|
1051
|
+
/** A read belongs to the action about to be recorded. */
|
|
1052
|
+
addRead(entry: Omit<RecordedRead, 'index'>): void;
|
|
1053
|
+
addCarried(line: CarriedLine): void;
|
|
1054
|
+
/** The actions recorded so far; unlike `snapshot` it needs no start screen. */
|
|
1055
|
+
recordedActions(): RecordedAction[];
|
|
1056
|
+
snapshot(): VerifierStepRecord;
|
|
1057
|
+
}
|
|
1058
|
+
/** The record as the verifier turn shows it, one string per part. */
|
|
1059
|
+
declare function renderStepRecord(record: VerifierStepRecord): RenderedStepRecord;
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* A planned action plus the frozen v45 plan envelope (D-09). Structural mirror of
|
|
1063
|
+
* core's ActionPlanSchema output; the v45 tool registry (this plan, task 3) produces it.
|
|
1064
|
+
*/
|
|
1065
|
+
interface ActionPlan {
|
|
1066
|
+
reasoning: string;
|
|
1067
|
+
action: {
|
|
1068
|
+
type: string;
|
|
1069
|
+
[key: string]: unknown;
|
|
1070
|
+
};
|
|
1071
|
+
confidence: number;
|
|
1072
|
+
stepComplete: boolean;
|
|
1073
|
+
stepFailed?: boolean;
|
|
1074
|
+
/**
|
|
1075
|
+
* D-09/GROUND-01 grounding-attempts carriage. When the planning turn grounds the
|
|
1076
|
+
* action in-turn (via the injected groundability callback), the gate's §A8
|
|
1077
|
+
* GroundingAttempt trail rides here so the resolved-descriptor evidence reaches
|
|
1078
|
+
* executeStep (threaded onto StepEvidence in Plan 07). Absent when no grounding
|
|
1079
|
+
* callback is injected or the gate recorded no attempts.
|
|
1080
|
+
*/
|
|
1081
|
+
groundingAttempts?: GroundingAttempt[];
|
|
1082
|
+
/** D-01: the element the planner meant per element lane, judged before dispatch. */
|
|
1083
|
+
statedTargets?: StatedTargets;
|
|
1084
|
+
/**
|
|
1085
|
+
* WM-01 write channel (spec §12.15, ratified 2026-07-05). Verbatim facts the planner captured
|
|
1086
|
+
* this turn via the `remember[]` plan-field; the loop hands them to the run-scoped
|
|
1087
|
+
* `WorkingMemoryStore` (Plan 08 wiring). Absent when the planner captured nothing this turn.
|
|
1088
|
+
*/
|
|
1089
|
+
remember?: WorkingMemoryEntry[];
|
|
1090
|
+
/**
|
|
1091
|
+
* D-09 bulk-loop completion predicate. On a "do X to all" step the planner declares the positive
|
|
1092
|
+
* completion predicate here and re-states it each turn; the verifier backstops. Absent for
|
|
1093
|
+
* non-bulk steps.
|
|
1094
|
+
*/
|
|
1095
|
+
bulkCompletionPredicate?: string;
|
|
1096
|
+
/**
|
|
1097
|
+
* D-04 infeasible terminal outcome (EXEC-05, spec §12.10). The additive plan-field envelope
|
|
1098
|
+
* (358 remember[] precedent) by which a turn declares the step terminally infeasible — the
|
|
1099
|
+
* requested capability/screen does not exist in this build. Closed literal union ('infeasible'
|
|
1100
|
+
* today, extensible later); machine-distinguishable from a plain `stepFailed`. When set, the
|
|
1101
|
+
* loop routes it into a typed StepEvidence.termination block and a distinct verdict outcome
|
|
1102
|
+
* (Plan 10 seam). Absent for an ordinary failure.
|
|
1103
|
+
*/
|
|
1104
|
+
failureKind?: 'infeasible';
|
|
1105
|
+
/**
|
|
1106
|
+
* D-04 infeasible reason — required whenever `failureKind` is set (enforced at the
|
|
1107
|
+
* toolCallToActionPlan parse boundary). The human-readable why, recorded verbatim in the
|
|
1108
|
+
* termination evidence block. Absent unless `failureKind` is present.
|
|
1109
|
+
*/
|
|
1110
|
+
failureReason?: string;
|
|
1111
|
+
}
|
|
1112
|
+
/** 436 D-08: debits one planner model call before it is made; false refuses the call. */
|
|
1113
|
+
interface PlanningCallGate {
|
|
1114
|
+
tryDebitPlanningCall(): boolean;
|
|
1115
|
+
}
|
|
1116
|
+
interface PlannerConfig {
|
|
1117
|
+
maxActions: number;
|
|
1118
|
+
maxPlanningTurns: number;
|
|
1119
|
+
previousStepCount: number;
|
|
1120
|
+
}
|
|
1121
|
+
interface PlanResult {
|
|
1122
|
+
plan: ActionPlan;
|
|
1123
|
+
tokenUsage?: TokenUsage;
|
|
1124
|
+
/**
|
|
1125
|
+
* Ordered non-action exchanges from this live planning decision (D-01).
|
|
1126
|
+
* Absent for cache-replay-produced results, which have no live planning turn.
|
|
1127
|
+
*/
|
|
1128
|
+
exchanges?: ToolExchangeEvidence[];
|
|
1129
|
+
/**
|
|
1130
|
+
* Observed final-response metadata for this live planning decision (D-05/D-08).
|
|
1131
|
+
* Absent when the provider exposes no call record or no live model call occurred.
|
|
1132
|
+
*/
|
|
1133
|
+
callRecord?: ModelCallRecord;
|
|
1134
|
+
}
|
|
1135
|
+
/** One condition as the verifier judged it (439 D-02, D-10); the runner decides what it counts for. */
|
|
1136
|
+
interface ConditionJudgment {
|
|
1137
|
+
quote: string;
|
|
1138
|
+
verdict: ConditionVerdict;
|
|
1139
|
+
needs: 'none' | ConditionNeed;
|
|
1140
|
+
controlRef: string;
|
|
1141
|
+
file: {
|
|
1142
|
+
name: string;
|
|
1143
|
+
match: 'none' | 'exact' | 'lines';
|
|
1144
|
+
text: string;
|
|
1145
|
+
};
|
|
1146
|
+
citations: {
|
|
1147
|
+
source: CitationSource;
|
|
1148
|
+
quote: string;
|
|
1149
|
+
}[];
|
|
1150
|
+
reasoning: string;
|
|
1151
|
+
}
|
|
1152
|
+
/** An open dialog as the verifier accounted for it (439 D-16). */
|
|
1153
|
+
interface DialogAccount {
|
|
1154
|
+
name: string;
|
|
1155
|
+
account: 'bears-on-condition' | 'requested' | 'unrelated';
|
|
1156
|
+
reasoning: string;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* The verifier's per-condition output. There is no overall success: the runner computes the pass
|
|
1160
|
+
* (439 D-10). `verifierError` is set only by the fallback when the model call failed.
|
|
1161
|
+
*/
|
|
1162
|
+
interface VerificationResult {
|
|
1163
|
+
conditions: ConditionJudgment[];
|
|
1164
|
+
dialogs: DialogAccount[];
|
|
1165
|
+
isAppError: boolean;
|
|
1166
|
+
reasoning: string;
|
|
1167
|
+
verifierError?: string;
|
|
1168
|
+
}
|
|
1169
|
+
interface VerifyResult {
|
|
1170
|
+
verification: VerificationResult;
|
|
1171
|
+
tokenUsage?: TokenUsage;
|
|
1172
|
+
}
|
|
1173
|
+
interface StepContext {
|
|
1174
|
+
stepInstruction: string;
|
|
1175
|
+
testName: string;
|
|
1176
|
+
testContext?: string;
|
|
1177
|
+
suiteContext?: string;
|
|
1178
|
+
stepNumber?: number;
|
|
1179
|
+
stepCount?: number;
|
|
1180
|
+
previousSteps: {
|
|
1181
|
+
instruction: string;
|
|
1182
|
+
outcome: string;
|
|
1183
|
+
reasoning?: string;
|
|
1184
|
+
plannedAction?: string;
|
|
1185
|
+
verifierResponse?: string;
|
|
1186
|
+
}[];
|
|
1187
|
+
contextWindow?: number;
|
|
1188
|
+
plannerModel: LanguageModel;
|
|
1189
|
+
verifierModel: LanguageModel;
|
|
1190
|
+
modelId?: string;
|
|
1191
|
+
variables?: Record<string, string>;
|
|
1192
|
+
/**
|
|
1193
|
+
* The `{{env:NAME}}` bindings that interpolated into `stepInstruction` before this step
|
|
1194
|
+
* was planned (PROC-01 · 381 D-03/D-08). DESTINATION: stamped verbatim onto
|
|
1195
|
+
* `StepEvidence.interpolatedVariables` at finalize, where the procedure-extraction emit
|
|
1196
|
+
* fold reads it to classify a trajectory value slot back to its source binding NAME.
|
|
1197
|
+
*
|
|
1198
|
+
* Carried VERBATIM end to end — never masked (unlike `variables` above, which is
|
|
1199
|
+
* secret-redacted for the planner). See the masking prohibition documented on
|
|
1200
|
+
* `StepEvidence.interpolatedVariables`: masking would break the consumer's exact-match
|
|
1201
|
+
* classification for email-valued and credential-named bindings.
|
|
1202
|
+
*
|
|
1203
|
+
* Absent/empty when the step's text interpolated nothing — finalize then stamps nothing.
|
|
1204
|
+
*/
|
|
1205
|
+
interpolatedVariables?: readonly InterpolatedVariable[];
|
|
1206
|
+
/**
|
|
1207
|
+
* D-12/D-13 compatibility slot for a directly supplied learned brief. Reader-backed runs use
|
|
1208
|
+
* the structured L1/L2/truncation fields below; buildVolatileTier applies the same hypotheses
|
|
1209
|
+
* frame to either source. Empty/undefined omits the learned section entirely.
|
|
1210
|
+
*/
|
|
1211
|
+
knowledgeBrief?: string;
|
|
1212
|
+
/**
|
|
1213
|
+
* BRIF-01 human-authored/sovereign L0 policy material. It is isolated from learned spans so
|
|
1214
|
+
* buildContextTier can keep only this byte-stable material in the cached-prefix region.
|
|
1215
|
+
*/
|
|
1216
|
+
knowledgePolicyL0?: string;
|
|
1217
|
+
/** BRIF-01 learned caution summaries rendered under the volatile hypotheses frame. */
|
|
1218
|
+
knowledgeBriefL1?: string;
|
|
1219
|
+
/** BRIF-01 learned procedure/high-trust-fact summaries rendered in the volatile tier. */
|
|
1220
|
+
knowledgeBriefL2?: string;
|
|
1221
|
+
/** BRIF-01 reader-provided clipping signal; empty/undefined means no signal is rendered. */
|
|
1222
|
+
knowledgeBriefTruncation?: string;
|
|
1223
|
+
/**
|
|
1224
|
+
* CR-08 immutable UTC date for planner prompt evidence, captured once per step as
|
|
1225
|
+
* `YYYY-MM-DD`. When absent, direct legacy prompt-builder callers retain the current
|
|
1226
|
+
* UTC-date fallback. Bound values cross this type unchanged; the production capture
|
|
1227
|
+
* boundary validates the closed date form before persistence.
|
|
1228
|
+
*/
|
|
1229
|
+
promptDate?: string;
|
|
1230
|
+
/**
|
|
1231
|
+
* D-12/D-13 working-memory channel (structured turn content). The run-scoped
|
|
1232
|
+
* `WorkingMemoryStore` set, injected verbatim as its own turn message by `composeTurnMessages`
|
|
1233
|
+
* — the second v2 channel replacing the retired single memory-context string. Phase 358
|
|
1234
|
+
* SANCTIONED BREAK (D-12/D-13): this was a plain `string` slot; it is now `WorkingMemoryEntry[]`
|
|
1235
|
+
* (no shim, no string overload) so the planner receives the structured verbatim set. Empty
|
|
1236
|
+
* array/undefined ⇒ omitted from the turn entirely (empty-slot omission).
|
|
1237
|
+
*/
|
|
1238
|
+
workingMemory?: WorkingMemoryEntry[];
|
|
1239
|
+
/**
|
|
1240
|
+
* D-09 active bulk-completion predicate (per-turn VOLATILE turn content). While a bulk
|
|
1241
|
+
* "do X to all" loop is running, the planner's declared positive predicate is re-stated each
|
|
1242
|
+
* turn with the re-evaluate/cite-positive-evidence instruction. Absent for non-bulk steps.
|
|
1243
|
+
*/
|
|
1244
|
+
bulkCompletionPredicate?: string;
|
|
1245
|
+
failureContext?: string;
|
|
1246
|
+
/**
|
|
1247
|
+
* Sub-actions already attempted in this step (multi-action mode), with each outcome and any
|
|
1248
|
+
* returned data. Screen change is not recorded here: outcomes decide (HARN-01).
|
|
1249
|
+
*/
|
|
1250
|
+
subActionHistory?: {
|
|
1251
|
+
action: string;
|
|
1252
|
+
reasoning?: string;
|
|
1253
|
+
result: 'success' | 'failure' | 'no-input';
|
|
1254
|
+
error?: string;
|
|
1255
|
+
verifierRejection?: string;
|
|
1256
|
+
data?: string;
|
|
1257
|
+
evidence?: string;
|
|
1258
|
+
}[];
|
|
1259
|
+
plannerConfig?: PlannerConfig;
|
|
1260
|
+
/** 436 D-08: the step's planning-call meter, bound by the loop before each plan() call. */
|
|
1261
|
+
planningCallGate?: PlanningCallGate;
|
|
1262
|
+
screenshot?: Buffer;
|
|
1263
|
+
/**
|
|
1264
|
+
* Vision-gate prompt note for this run (D-11). Non-empty only for text-only
|
|
1265
|
+
* models; injected into the planner prompt when present.
|
|
1266
|
+
*/
|
|
1267
|
+
promptNote?: string;
|
|
1268
|
+
/**
|
|
1269
|
+
* D-13 re-run-after-failure context (LOOP-03). A re-run carries everything (full
|
|
1270
|
+
* memory/context, no ablation) plus an explicit run-level flag and the prior
|
|
1271
|
+
* attempt's failure summary, surfaced to the planner as prompt context.
|
|
1272
|
+
*/
|
|
1273
|
+
isRerun?: boolean;
|
|
1274
|
+
rerunAttempt?: number;
|
|
1275
|
+
previousFailureSummary?: string;
|
|
1276
|
+
}
|
|
1277
|
+
interface Planner {
|
|
1278
|
+
plan(step: string, screenState: ScreenState, context: StepContext, abortSignal?: AbortSignal): Promise<PlanResult>;
|
|
1279
|
+
}
|
|
1280
|
+
interface VerifierAuthorContext {
|
|
1281
|
+
suiteContext?: string;
|
|
1282
|
+
testContext?: string;
|
|
1283
|
+
stepNumber?: number;
|
|
1284
|
+
stepCount?: number;
|
|
1285
|
+
}
|
|
1286
|
+
interface Verifier {
|
|
1287
|
+
verify(step: string, before: ScreenState, after: ScreenState, action: Action, screenshot?: Buffer, abortSignal?: AbortSignal, promptNote?: string, bulkCompletionPredicate?: string, authorContext?: VerifierAuthorContext, operationEvidence?: OperationEvidence, record?: VerifierStepRecord): Promise<VerifyResult>;
|
|
1288
|
+
/**
|
|
1289
|
+
* D-05 AI judge lane (EXEC-07, spec §9.6/§12.2) — OPTIONAL so every existing Verifier
|
|
1290
|
+
* implementor keeps compiling (D-05: no new port). Plan 09 implements this on LLMVerifier
|
|
1291
|
+
* against the injected model handle; the loop (Plan 10 seam) routes `assertAI` through it
|
|
1292
|
+
* exactly once per attempt (single-model law). A single-shot judgment over the current
|
|
1293
|
+
* observation returning the verdict + rationale. `opts` carries the requested-but-deferred
|
|
1294
|
+
* M3/M4 knobs (consensus/video/effort) so demand is recorded (D-09).
|
|
1295
|
+
*/
|
|
1296
|
+
judgeClaim?(claim: string, screen: ScreenState, opts?: Record<string, unknown>): Promise<{
|
|
1297
|
+
verdict: boolean;
|
|
1298
|
+
rationale: string;
|
|
1299
|
+
}>;
|
|
1300
|
+
/**
|
|
1301
|
+
* D-07 AI extraction lane (EXEC-07, spec §9.6) — OPTIONAL, same no-new-port rationale as
|
|
1302
|
+
* `judgeClaim`. Plan 09 implements it on LLMVerifier; the loop routes `extractAI` through it,
|
|
1303
|
+
* writes the extracted fields into run-scoped working memory (`scope:'run'`), and degrades
|
|
1304
|
+
* `scope:'global'` to the same run-scoped write (D-07). Returns the extracted field map.
|
|
1305
|
+
*/
|
|
1306
|
+
extractData?(query: string, screen: ScreenState, opts?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
1307
|
+
}
|
|
1308
|
+
type AssertionType = 'text-presence' | 'element-visibility' | 'url-match' | 'element-count' | 'ai';
|
|
1309
|
+
interface AssertionInput {
|
|
1310
|
+
type: AssertionType;
|
|
1311
|
+
value: string;
|
|
1312
|
+
expected?: string;
|
|
1313
|
+
}
|
|
1314
|
+
interface AssertionResult {
|
|
1315
|
+
passed: boolean;
|
|
1316
|
+
assertionType: AssertionType;
|
|
1317
|
+
expected: string;
|
|
1318
|
+
actual: string;
|
|
1319
|
+
reasoning: string;
|
|
1320
|
+
}
|
|
1321
|
+
interface Asserter {
|
|
1322
|
+
evaluate(assertion: AssertionInput, screenState: ScreenState): Promise<AssertionResult>;
|
|
1323
|
+
}
|
|
1324
|
+
type ExtractionMethod = 'regex' | 'selector' | 'ai';
|
|
1325
|
+
interface ExtractorInput {
|
|
1326
|
+
method: ExtractionMethod;
|
|
1327
|
+
variableName: string;
|
|
1328
|
+
pattern?: string;
|
|
1329
|
+
selector?: string;
|
|
1330
|
+
description?: string;
|
|
1331
|
+
}
|
|
1332
|
+
interface CaptureResult {
|
|
1333
|
+
success: boolean;
|
|
1334
|
+
variableName: string;
|
|
1335
|
+
value?: string;
|
|
1336
|
+
reasoning: string;
|
|
1337
|
+
}
|
|
1338
|
+
interface VariableExtractor {
|
|
1339
|
+
extract(input: ExtractorInput, screenState: ScreenState): Promise<CaptureResult>;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/** A legacy `{ success }` verdict a double may still be written with, or the full result. */
|
|
1343
|
+
type ScriptedVerdict = {
|
|
1344
|
+
success: boolean;
|
|
1345
|
+
reasoning: string;
|
|
1346
|
+
isAppError?: boolean;
|
|
1347
|
+
} | VerificationResult;
|
|
1348
|
+
/** One met condition quoting the whole step, citing the first line of the final screen. */
|
|
1349
|
+
declare function metVerification(options: {
|
|
1350
|
+
step: string;
|
|
1351
|
+
after?: ScreenState;
|
|
1352
|
+
action?: Action;
|
|
1353
|
+
reasoning?: string;
|
|
1354
|
+
}): VerificationResult;
|
|
1355
|
+
/** One not-met condition quoting the whole step, with no citations. */
|
|
1356
|
+
declare function notMetVerification(options: {
|
|
1357
|
+
step: string;
|
|
1358
|
+
reasoning: string;
|
|
1359
|
+
}): VerificationResult;
|
|
1360
|
+
/** A passing or failing scripted verdict in the per-condition shape, with `isAppError` carried. */
|
|
1361
|
+
declare function scriptedVerification(options: {
|
|
1362
|
+
step: string;
|
|
1363
|
+
after?: ScreenState;
|
|
1364
|
+
action?: Action;
|
|
1365
|
+
pass: boolean;
|
|
1366
|
+
reasoning: string;
|
|
1367
|
+
isAppError?: boolean;
|
|
1368
|
+
}): VerificationResult;
|
|
1369
|
+
/** Convert a scripted verdict to the per-condition shape against what `verify` received. */
|
|
1370
|
+
declare function toVerificationResult(verdict: ScriptedVerdict, received: {
|
|
1371
|
+
step: string;
|
|
1372
|
+
after?: ScreenState;
|
|
1373
|
+
action?: Action;
|
|
1374
|
+
}): VerificationResult;
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* A single platform's observed input for the shared spec. `buildInput` is the builder
|
|
1378
|
+
* input minus the parts the case controls (`nodes` and `platform`).
|
|
1379
|
+
*/
|
|
1380
|
+
interface ConformanceInput {
|
|
1381
|
+
platform: Platform;
|
|
1382
|
+
nodes: InterimNode[];
|
|
1383
|
+
buildInput: Omit<BuildScreenStateInput, 'nodes' | 'platform'>;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* One executable contract assertion. `run` receives a factory so cases that need
|
|
1387
|
+
* independent builds (e.g. the determinism check) can materialize the input twice.
|
|
1388
|
+
* Violations throw a plain Error — no assertion library.
|
|
1389
|
+
*/
|
|
1390
|
+
interface ScreenConformanceCase {
|
|
1391
|
+
name: string;
|
|
1392
|
+
run(make: () => ConformanceInput): void;
|
|
1393
|
+
}
|
|
1394
|
+
declare const screenConformanceCases: ScreenConformanceCase[];
|
|
1395
|
+
|
|
1396
|
+
type HookRuntime = 'node' | 'bun' | 'python' | 'bash';
|
|
1397
|
+
interface HookDefinition {
|
|
1398
|
+
id: string;
|
|
1399
|
+
name: string;
|
|
1400
|
+
runtime: HookRuntime;
|
|
1401
|
+
file: string;
|
|
1402
|
+
deps: string[];
|
|
1403
|
+
packageFile?: string;
|
|
1404
|
+
timeout: number;
|
|
1405
|
+
network: boolean;
|
|
1406
|
+
}
|
|
1407
|
+
interface HookResult {
|
|
1408
|
+
success: boolean;
|
|
1409
|
+
variables: Record<string, string>;
|
|
1410
|
+
output: string;
|
|
1411
|
+
stdout: string;
|
|
1412
|
+
stderr: string;
|
|
1413
|
+
duration: number;
|
|
1414
|
+
error?: string;
|
|
1415
|
+
}
|
|
1416
|
+
declare const RUNTIME_IMAGE_MAP: Record<HookRuntime, string>;
|
|
1417
|
+
|
|
1418
|
+
/** Structurally compatible with model-owner reports; no provider implementation dependency. */
|
|
1419
|
+
interface StepModelCallReport {
|
|
1420
|
+
inputTokens?: number;
|
|
1421
|
+
outputTokens?: number;
|
|
1422
|
+
cachedInputTokens?: number;
|
|
1423
|
+
respondingModelId?: string;
|
|
1424
|
+
}
|
|
1425
|
+
type StepModelCallEvent = {
|
|
1426
|
+
kind: 'start';
|
|
1427
|
+
} | {
|
|
1428
|
+
kind: 'success';
|
|
1429
|
+
report: StepModelCallReport;
|
|
1430
|
+
} | {
|
|
1431
|
+
kind: 'failure';
|
|
1432
|
+
};
|
|
1433
|
+
interface StepModelObservation {
|
|
1434
|
+
onCall(event: StepModelCallEvent): void;
|
|
1435
|
+
}
|
|
1436
|
+
interface StepModelScopeKey {
|
|
1437
|
+
runId: RunId;
|
|
1438
|
+
testIndex: number;
|
|
1439
|
+
stepIndex: number;
|
|
1440
|
+
}
|
|
1441
|
+
interface StepModelRoleFacts {
|
|
1442
|
+
declared?: ModelIdentity;
|
|
1443
|
+
respondingModelIds: string[];
|
|
1444
|
+
}
|
|
1445
|
+
interface StepModelAttempt {
|
|
1446
|
+
invocation: number;
|
|
1447
|
+
role: 'planner' | 'verifier';
|
|
1448
|
+
kind: StepModelCallEvent['kind'];
|
|
1449
|
+
report?: StepModelCallReport;
|
|
1450
|
+
}
|
|
1451
|
+
interface StepModelSnapshot extends StepModelScopeKey {
|
|
1452
|
+
attempts: StepModelAttempt[];
|
|
1453
|
+
started: number;
|
|
1454
|
+
succeeded: number;
|
|
1455
|
+
failed: number;
|
|
1456
|
+
inputTokens?: number;
|
|
1457
|
+
outputTokens?: number;
|
|
1458
|
+
cachedInputTokens?: number;
|
|
1459
|
+
roles: {
|
|
1460
|
+
planner: StepModelRoleFacts;
|
|
1461
|
+
verifier: StepModelRoleFacts;
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
interface StepModelConsumers {
|
|
1465
|
+
planner: Planner;
|
|
1466
|
+
verifier?: Verifier;
|
|
1467
|
+
plannerModel: LanguageModel;
|
|
1468
|
+
verifierModel: LanguageModel;
|
|
1469
|
+
}
|
|
1470
|
+
interface StepModelScope extends StepModelConsumers {
|
|
1471
|
+
readonly key: StepModelScopeKey;
|
|
1472
|
+
snapshot(): StepModelSnapshot;
|
|
1473
|
+
/** Freeze after extraction, before terminal publication. Pending attempts remain uncovered. */
|
|
1474
|
+
finish(): StepModelSnapshot;
|
|
1475
|
+
/** Called only after the final event; retained callbacks cannot affect another step. */
|
|
1476
|
+
dispose(): void;
|
|
1477
|
+
}
|
|
1478
|
+
type StepModelScopeFactory = (key: StepModelScopeKey) => StepModelScope;
|
|
1479
|
+
interface StepModelBinding {
|
|
1480
|
+
/** Resolved handle supplies metadata; it is never mutated or double-wrapped. */
|
|
1481
|
+
model: Exclude<LanguageModel, string>;
|
|
1482
|
+
/**
|
|
1483
|
+
* Bind a fresh observer for ONE SDK invocation. Reuse host conversation/accounting state,
|
|
1484
|
+
* but never a model bound to another observer. Model-owner middleware emits every actual
|
|
1485
|
+
* retry start/terminal; the runner must not synthesize dispatches around generateText.
|
|
1486
|
+
*/
|
|
1487
|
+
bind(observation: StepModelObservation): LanguageModel;
|
|
1488
|
+
}
|
|
1489
|
+
interface StepModelScopeOptions {
|
|
1490
|
+
key: StepModelScopeKey;
|
|
1491
|
+
models: {
|
|
1492
|
+
planner: StepModelBinding;
|
|
1493
|
+
verifier: StepModelBinding;
|
|
1494
|
+
};
|
|
1495
|
+
profiles?: {
|
|
1496
|
+
planner: ModelIdentity;
|
|
1497
|
+
verifier?: ModelIdentity;
|
|
1498
|
+
};
|
|
1499
|
+
mask(text: string): string;
|
|
1500
|
+
createConsumers(models: Pick<StepModelConsumers, 'plannerModel' | 'verifierModel'>): Pick<StepModelConsumers, 'planner' | 'verifier'>;
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* The two 356 A5 config digests the run actually keyed on, computed once at the CLI
|
|
1504
|
+
* composition root and passed in the resolved {@link RunConfig} (D-20). Carried verbatim
|
|
1505
|
+
* into `runStarted`/the handoff so run identity and handoff identity cannot fork.
|
|
1506
|
+
*/
|
|
1507
|
+
interface ConfigDigests {
|
|
1508
|
+
suiteConfigDigest: string;
|
|
1509
|
+
testConfigDigest: string;
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* The run-start baseline health-probe outcome (D-04, RCC-02): ONE GET against the web
|
|
1513
|
+
* target's base URL, executed at the CLI composition root (the runner bans node:* — only
|
|
1514
|
+
* the RESULT crosses this seam). ALL probe failures arrive as values on `error`; a probe
|
|
1515
|
+
* never aborts the run. Consumed by the deterministic root-cause classifier's environment
|
|
1516
|
+
* branch (run/root-cause.ts).
|
|
1517
|
+
*/
|
|
1518
|
+
interface BaselineProbeResult {
|
|
1519
|
+
status?: number;
|
|
1520
|
+
elapsedMs: number;
|
|
1521
|
+
error?: string;
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* The JSONL event-log seam (D-05/D-06). BOTH legs are SYNC and fire-and-forget — the bus
|
|
1525
|
+
* NEVER awaits telemetry (a synchronous one-line append; D-06/D-07). `append` persists one
|
|
1526
|
+
* envelope per emitted event; a throw degrades the bus to memory-only with a single
|
|
1527
|
+
* `runWarning` (D-07). `read(fromSeq)` supplies older-than-window envelopes for deep replay.
|
|
1528
|
+
* The runner-local `FsEventLog` (Plan 07) implements this against `events.jsonl`.
|
|
1529
|
+
*/
|
|
1530
|
+
interface EventLogPort {
|
|
1531
|
+
append(envelope: RunEventEnvelope): void;
|
|
1532
|
+
read(fromSeq: number): RunEventEnvelope[];
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* The per-hook execution options the orchestration threads into the {@link HookExecutorPort}
|
|
1536
|
+
* (D-06). `env` is the accumulated-variables + user-env map the orchestrator merges per hook;
|
|
1537
|
+
* `secretEnv` is the ALREADY-RESOLVED secret name→value map for container injection — the CLI
|
|
1538
|
+
* composition root resolves the SecretStore and the runner never touches the store itself. The
|
|
1539
|
+
* secret VALUES double as the output-variable filter (a hook-emitted variable equal to a secret
|
|
1540
|
+
* value is dropped). `secretRedactor` redacts stdout/stderr/error; `authState` mounts + injects
|
|
1541
|
+
* the active auth state (container-local paths only). Pitfall 3: all sandbox-side
|
|
1542
|
+
* redaction/filtering stays inside the impl — the sink mask is additive.
|
|
1543
|
+
*/
|
|
1544
|
+
interface HookExecuteOptions {
|
|
1545
|
+
env: Record<string, string>;
|
|
1546
|
+
secretEnv: Record<string, string>;
|
|
1547
|
+
secretRedactor?: SecretRedactorPort;
|
|
1548
|
+
authState?: RuntimeAuthStateConfig;
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* The D-06 hook-execution seam: a runner-owned port whose only implementation is the
|
|
1552
|
+
* runner-local `DockerHookExecutor` (the 356/361 port-in-runner/impl-in-runner-local
|
|
1553
|
+
* precedent). `execute` runs ONE hook in the sandboxed container and resolves a
|
|
1554
|
+
* {@link HookResult} — a per-hook timeout resolves a FAILED result with an exit-124-style
|
|
1555
|
+
* error, NEVER a throw (the orchestration relies on a resolved result to synthesize
|
|
1556
|
+
* fail-fast skips). node:child_process / node:fs live behind this port in runner-local;
|
|
1557
|
+
* the runner stays storageNeutral (validate:arch is the arbiter).
|
|
1558
|
+
*/
|
|
1559
|
+
interface HookExecutorPort {
|
|
1560
|
+
execute(hook: HookDefinition, opts: HookExecuteOptions): Promise<HookResult>;
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* The durable handoff seam (D-05/D-17). SYNC single-document persist of the one
|
|
1564
|
+
* `CompletedRunHandoff` at run end; the runner-local `FsHandoffStore` (Plan 07) writes
|
|
1565
|
+
* `handoff.json` beside `events.jsonl`. Failure degrades with a `runWarning`, never gates
|
|
1566
|
+
* the verdict (D-07).
|
|
1567
|
+
*/
|
|
1568
|
+
interface HandoffStorePort {
|
|
1569
|
+
save(handoff: CompletedRunHandoff): void;
|
|
1570
|
+
}
|
|
1571
|
+
/**
|
|
1572
|
+
* What the post-verdict step is told about the run whose posture it is deciding (404 D-10).
|
|
1573
|
+
*
|
|
1574
|
+
* The two markers are read off the ASSEMBLED `RunConfig` — the same values `startRun` was handed
|
|
1575
|
+
* — never off ambient state, so a gate-run skip (D-08) is decided from the run's own record.
|
|
1576
|
+
* Absent means absent: `?: true` carriers are conditionally spread, never written as `undefined`.
|
|
1577
|
+
*/
|
|
1578
|
+
interface PostVerdictStepInput {
|
|
1579
|
+
readonly runId: RunId;
|
|
1580
|
+
readonly verdict: RunStatus;
|
|
1581
|
+
readonly gateRun?: true;
|
|
1582
|
+
readonly excludedFromOutcomes?: true;
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* The step's answer: the posture, and — when the consult could not decide — the reason, by name.
|
|
1586
|
+
*
|
|
1587
|
+
* A warning rather than a fourth union arm (D-11). `detached` is the honest "nothing will be
|
|
1588
|
+
* drained from here", and the cause travels on the `runWarning` channel a surface already reads.
|
|
1589
|
+
*/
|
|
1590
|
+
interface PostVerdictOutcome {
|
|
1591
|
+
readonly disposition: PostProcessingDisposition;
|
|
1592
|
+
readonly warning?: {
|
|
1593
|
+
readonly code: string;
|
|
1594
|
+
readonly message: string;
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* The post-verdict step (404 D-10) — the `HandoffStorePort` idiom applied to the ONE decision the
|
|
1599
|
+
* shell delays `runFinished` for.
|
|
1600
|
+
*
|
|
1601
|
+
* WHAT IT MAY DO: the durable enqueue and ONE writer-lease consult. Milliseconds. The runner
|
|
1602
|
+
* names no queue, no sqlite and no lease vocabulary (validate:arch storageNeutral) — the HOST
|
|
1603
|
+
* supplies the implementation, and the shell only awaits it.
|
|
1604
|
+
*
|
|
1605
|
+
* WHAT IT MAY NOT DO: gate `done`. A throw, a resolution past
|
|
1606
|
+
* {@link POST_VERDICT_STEP_BUDGET_MS}, or an absent port all yield `detached` plus a named
|
|
1607
|
+
* `runWarning`, and `runFinished` still publishes last with `bus.close()` behind it (403-03 D-3).
|
|
1608
|
+
* The step returns a VALUE and never touches the bus: the shell keeps sole ownership of the
|
|
1609
|
+
* terminal sequence.
|
|
1610
|
+
*
|
|
1611
|
+
* THE SHELL HOLDS NO LEASE. Whatever the step acquired stays with the host, which hands it to its
|
|
1612
|
+
* own pump — acquire → pump → release is ONE lease, never re-acquired (RESEARCH Pitfall 3).
|
|
1613
|
+
*/
|
|
1614
|
+
interface PostVerdictPort {
|
|
1615
|
+
run(input: PostVerdictStepInput): Promise<PostVerdictOutcome>;
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* The hard bound the shell races the step against (404 D-10, RESEARCH Pitfall 1).
|
|
1619
|
+
*
|
|
1620
|
+
* `acquireWriterLease` has contention/deadline semantics of its own, and a busy sqlite enqueue can
|
|
1621
|
+
* stall; without a bound here `runFinished`, `done`, the SSE close and the dashboard finalize all
|
|
1622
|
+
* slide behind it. Five seconds is generous for "enqueue + one consult" and still far below any
|
|
1623
|
+
* drain budget — reaching it means the consult is not going to answer, which is `detached`.
|
|
1624
|
+
*/
|
|
1625
|
+
declare const POST_VERDICT_STEP_BUDGET_MS = 5000;
|
|
1626
|
+
/**
|
|
1627
|
+
* The D-08 run-artifact persistence seam: a runner-owned port whose only implementation is the
|
|
1628
|
+
* runner-local `FsRunArtifactStore` (the 356/361 port-in-runner/impl-in-runner-local precedent).
|
|
1629
|
+
* BOTH legs are SYNC and fire-and-forget — the shell NEVER awaits artifact persistence, and a
|
|
1630
|
+
* throw degrades with a `runWarning`, never gates the verdict (the event-log/handoff
|
|
1631
|
+
* discipline; D-07). The store writes ONE `artifact.json` into the SAME
|
|
1632
|
+
* `<root>/runs/<slug(runId)>/` family as `events.jsonl`/`handoff.json` (one self-contained run
|
|
1633
|
+
* directory). `stage(document)` persists the config + source snapshot once after `runStarted`;
|
|
1634
|
+
* `finalize(patch)` merges terminal errors + the end timestamp before `runFinished` — matching
|
|
1635
|
+
* the dashboard DB staged/finalize idiom so a crashed run still leaves a staged artifact. Per the
|
|
1636
|
+
* RESEARCH Q5 keep/drop table the FILE artifact keeps config/source/errors only; the top-level
|
|
1637
|
+
* `runtime` section is dropped (derived from events/handoff). An absent port is a silent no-op.
|
|
1638
|
+
*/
|
|
1639
|
+
interface RunArtifactStorePort {
|
|
1640
|
+
stage(document: RunArtifactStageInput): void;
|
|
1641
|
+
finalize(patch: RunArtifactFinalizeInput): void;
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Injectable timer seam for the heartbeat + run-budget clocks (spec §2.2 liveness / D-03).
|
|
1645
|
+
* `schedule(delayMs, fn)` arms a one-shot and returns a cancel handle; `now()` supplies the
|
|
1646
|
+
* envelope `ts`. The default impl wraps global setTimeout/clearTimeout (unref'd so it never
|
|
1647
|
+
* keeps the process alive); unit pins inject a deterministic fake — no real timers, no watch
|
|
1648
|
+
* mode.
|
|
1649
|
+
*/
|
|
1650
|
+
interface RunTimerPort {
|
|
1651
|
+
schedule(delayMs: number, fn: () => void): () => void;
|
|
1652
|
+
now(): number;
|
|
1653
|
+
}
|
|
1654
|
+
/**
|
|
1655
|
+
* One ordered member of the run (D-04). `failFastGroup` scopes today's suite fail-fast: a
|
|
1656
|
+
* failed/timed-out member skips the remaining SAME-group members; ungrouped members never
|
|
1657
|
+
* fail-fast, preserving the single-test path's run-all behavior.
|
|
1658
|
+
*
|
|
1659
|
+
* `testIndex` is optional: the shell owns iteration and derives the authoritative index
|
|
1660
|
+
* from array position (a caller-supplied value is a hint at most). `name` is REQUIRED so
|
|
1661
|
+
* the compiler enforces every descriptor construction site supplies the parsed test name;
|
|
1662
|
+
* handoff assembly copies it verbatim onto `PerTestOutcome.testName`.
|
|
1663
|
+
*/
|
|
1664
|
+
interface RunMemberDescriptor {
|
|
1665
|
+
testId: string;
|
|
1666
|
+
testIndex?: number;
|
|
1667
|
+
name: string;
|
|
1668
|
+
failFastGroup?: string;
|
|
1669
|
+
/**
|
|
1670
|
+
* The member test file's WORKSPACE-RELATIVE path — rooted at the config directory,
|
|
1671
|
+
* forward-slashed, no leading `./`. REQUIRED since 381-01 (ABRF-02): stable test identity =
|
|
1672
|
+
* filePath + testName, copied verbatim onto `PerTestOutcome.filePath` by the handoff assembly —
|
|
1673
|
+
* the required field makes the compiler enforce that every descriptor construction site supplies
|
|
1674
|
+
* it.
|
|
1675
|
+
*
|
|
1676
|
+
* 390-08 (CR-02 / D-01): this is IDENTITY, not a file handle. It is deliberately checkout-
|
|
1677
|
+
* independent — the same bytes on every machine — because the committed `quarantine.jsonl` keys
|
|
1678
|
+
* entries on it and matches with `===`, so an absolute path here would produce a ledger that
|
|
1679
|
+
* could only ever apply on the machine that wrote it. Nothing in the run reads bytes off disk
|
|
1680
|
+
* through this field; the CLI mints it via core's `workspaceRelativeIdentityPath` and keeps the
|
|
1681
|
+
* absolute path on the side for every read.
|
|
1682
|
+
*/
|
|
1683
|
+
filePath: string;
|
|
1684
|
+
/**
|
|
1685
|
+
* 363 D-01 parity fields (RESEARCH Q1). All OPTIONAL and additive — the shell rides them onto
|
|
1686
|
+
* the widened `testStarted` payload so reporters can render the full v1 console/JUnit/wire
|
|
1687
|
+
* surface from the bus alone. Suite grouping (`suiteId`/`suiteName`/`suiteIndex`/`suiteTotal`)
|
|
1688
|
+
* rides testStarted rather than a suite lifecycle event — the shell stays suite-agnostic and
|
|
1689
|
+
* reporters derive suite boundaries by watching the grouping change. `totalSteps` is the
|
|
1690
|
+
* per-member parsed step count. The CLI populates these at both startRun sites; absent
|
|
1691
|
+
* ⇒ omitted from the payload (no undefined-hole survives JSON round-trip).
|
|
1692
|
+
*/
|
|
1693
|
+
suiteId?: string;
|
|
1694
|
+
suiteName?: string;
|
|
1695
|
+
suiteIndex?: number;
|
|
1696
|
+
suiteTotal?: number;
|
|
1697
|
+
totalSteps?: number;
|
|
1698
|
+
/**
|
|
1699
|
+
* G-428-2 (2026-09-26): the raw authored text of each step, in order. It is the same string
|
|
1700
|
+
* member-runner announces at `stepStarted` (`memberStepText`). The shell masks it onto
|
|
1701
|
+
* `testStarted.stepTexts`; callers never pre-mask. Absent ⇒ omitted from the payload.
|
|
1702
|
+
*/
|
|
1703
|
+
stepTexts?: readonly string[];
|
|
1704
|
+
/**
|
|
1705
|
+
* THIS MEMBER'S OWN TARGET's run-start health baseline (D-04, 401-19 WR-04).
|
|
1706
|
+
*
|
|
1707
|
+
* PER MEMBER, NOT PER RUN, because a run's members may name different targets and a reading taken
|
|
1708
|
+
* against one of them says nothing about another. It lived on `RunConfig` and was handed to the
|
|
1709
|
+
* classifier for EVERY member, so a 503 observed against member B's target was recorded as member
|
|
1710
|
+
* A's environment failure. The CLI composition root fills this in behind the verdict, probing each
|
|
1711
|
+
* DISTINCT member URL once; absent means no probe applies to this member — the mobile posture,
|
|
1712
|
+
* where the device-session-create failure is the equivalent environment signal, and equally the
|
|
1713
|
+
* posture of a member whose target could not be resolved.
|
|
1714
|
+
*/
|
|
1715
|
+
baselineProbe?: BaselineProbeResult;
|
|
1716
|
+
/**
|
|
1717
|
+
* 402-02 (D-09): members declaring the SAME lane share a mutable execution resource and MUST NOT
|
|
1718
|
+
* run concurrently — the bounded member pool runs them strictly one at a time, in index order.
|
|
1719
|
+
* Absent means "this member owns everything it touches" and the pool may schedule it freely.
|
|
1720
|
+
*
|
|
1721
|
+
* This is a PRECONDITION the producer states, never something the shell infers. D-09 requires
|
|
1722
|
+
* each concurrent member its own browser/CDP handle; today's suite path does not satisfy that
|
|
1723
|
+
* (every member of a suite drives the ONE adapter its suite set up, and `createSuiteLifecycle`
|
|
1724
|
+
* admits one active suite at a time), while the direct path does (each direct test constructs
|
|
1725
|
+
* its own adapter). A producer that starts handing each member its own session drops the lane
|
|
1726
|
+
* and gets concurrency; nothing in the shell has to be re-argued for that to be safe.
|
|
1727
|
+
*/
|
|
1728
|
+
exclusiveLane?: string;
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* The run-scoped event sink handed to the executor per member test (D-02). The shell binds
|
|
1732
|
+
* `runId`/`testIndex`/masking; core curries `stepIndex` via `loopEmitFor`. `stepStarted`
|
|
1733
|
+
* fires as a step opens; `loopEmitFor(stepIndex)` yields the {@link LoopEventEmitPort} the
|
|
1734
|
+
* loop emits `stepFinalized`/`subActionRecorded` through.
|
|
1735
|
+
*/
|
|
1736
|
+
interface MemberEventSink {
|
|
1737
|
+
stepStarted(input: {
|
|
1738
|
+
stepIndex: number;
|
|
1739
|
+
stepText: string;
|
|
1740
|
+
}): void;
|
|
1741
|
+
loopEmitFor(stepIndex: number): LoopEventEmitPort;
|
|
1742
|
+
/**
|
|
1743
|
+
* 363 D-01 parity legs, each routed through its Plan 01 projection + the sink mask (never a raw
|
|
1744
|
+
* `bus.publish` from the caller — T-363-06). `hookStarted`/`hookFinished` are emitted by the
|
|
1745
|
+
* runner hook orchestration (Plan 04 populates them; `hookFinished` stdout/stderr/error and every
|
|
1746
|
+
* `variables` value are masked at the sink, T-363-05). `testRetryScheduled` is emitted by the
|
|
1747
|
+
* member-runner retry loop (Plan 10) and marks a per-test accumulation reset for subscribers.
|
|
1748
|
+
*/
|
|
1749
|
+
hookStarted(input: HookStartedPayload): void;
|
|
1750
|
+
hookFinished(input: HookFinishedPayload): void;
|
|
1751
|
+
testRetryScheduled(input: TestRetryScheduledPayload): void;
|
|
1752
|
+
}
|
|
1753
|
+
/** Immutable member-start decision context used when the shell must synthesize step evidence. */
|
|
1754
|
+
interface MemberEvidenceContext {
|
|
1755
|
+
modelProfile?: DeclaredModelProfile;
|
|
1756
|
+
verifierIdentity?: ModelIdentity;
|
|
1757
|
+
toolSchemaDigest?: string;
|
|
1758
|
+
}
|
|
1759
|
+
/** The per-member execution context threaded into the injected {@link MemberTestExecutor}. */
|
|
1760
|
+
interface MemberTestRunContext {
|
|
1761
|
+
/** Canonical shell identity. Legacy executors without a shell cannot certify scoped usage. */
|
|
1762
|
+
runId?: RunId;
|
|
1763
|
+
abortSignal: AbortSignal;
|
|
1764
|
+
testIndex: number;
|
|
1765
|
+
events: MemberEventSink;
|
|
1766
|
+
setEvidenceContext(context: MemberEvidenceContext): void;
|
|
1767
|
+
/**
|
|
1768
|
+
* CASS-02 run-scoped decision-screenshot byte store (the same instance the shell holds on
|
|
1769
|
+
* {@link RunPorts}). The member threads it onto `LoopPorts.evidenceArtifacts`; absent ⇒ a
|
|
1770
|
+
* memory-only run with no screenshot persistence.
|
|
1771
|
+
*/
|
|
1772
|
+
evidenceArtifacts?: EvidenceArtifactStorePort;
|
|
1773
|
+
/**
|
|
1774
|
+
* 403 D-05: declare (or withdraw) this member's live-stream producer. Optional on the SAME
|
|
1775
|
+
* `setEvidenceContext` precedent above — the executor pushes a capability the shell knows
|
|
1776
|
+
* nothing about, and the shell owns the per-run hub that refcounts it. Passing `undefined`
|
|
1777
|
+
* withdraws the declaration (the handle then reads `{ kind: 'none' }`). Absent on the context
|
|
1778
|
+
* ⇒ a composition that predates live view; the executor's optional call is a no-op.
|
|
1779
|
+
* `fallback` is the labelled snapshot producer the hub switches to when the primary fails to
|
|
1780
|
+
* start (428 D-12).
|
|
1781
|
+
*/
|
|
1782
|
+
registerLiveStream?(capability: LiveStreamCapability | undefined, fallback?: LiveStreamCapability): void;
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* The injected per-test execution port (D-01). The CLI composition root wraps core's
|
|
1786
|
+
* `runTest` behind this seam (core depends on runner, so runner can never import core);
|
|
1787
|
+
* Phase 363 dissolves the core side of the port when it migrates hooks/variables/reporters.
|
|
1788
|
+
* A staged migration, not a workaround — this is the exact seam 363 needs.
|
|
1789
|
+
*/
|
|
1790
|
+
type MemberTestExecutor = (member: RunMemberDescriptor, ctx: MemberTestRunContext) => Promise<TestResult>;
|
|
1791
|
+
/**
|
|
1792
|
+
* The durable/telemetry/clock seams the run wires behind ports (D-04/D-05). All optional —
|
|
1793
|
+
* an absent leg degrades gracefully (no persistence, memory-only telemetry, default clock).
|
|
1794
|
+
* Grouped under `ports` so the resolved config keeps the run identity fields flat and the
|
|
1795
|
+
* injectable I/O seams together (the Wave-0 `run-shell.test.ts` pins this nesting).
|
|
1796
|
+
*/
|
|
1797
|
+
interface RunPorts {
|
|
1798
|
+
handoffStore?: HandoffStorePort;
|
|
1799
|
+
eventLog?: EventLogPort;
|
|
1800
|
+
/**
|
|
1801
|
+
* The D-08 run-artifact store. Staged after `runStarted`, finalized before `runFinished`;
|
|
1802
|
+
* absent ⇒ no artifact.json is written (memory-only run, byte-identical pre-363-06 behavior).
|
|
1803
|
+
*/
|
|
1804
|
+
runArtifactStore?: RunArtifactStorePort;
|
|
1805
|
+
/**
|
|
1806
|
+
* The run-budget clock (D-03). The shell arms `runBudgetMs` on this timer; it is NOT
|
|
1807
|
+
* routed into the bus heartbeat (the two clocks stay distinct so firing the injected
|
|
1808
|
+
* budget timer is unambiguous — see the shell's timer note).
|
|
1809
|
+
*/
|
|
1810
|
+
timer?: RunTimerPort;
|
|
1811
|
+
/**
|
|
1812
|
+
* The CASS-02 run-scoped decision-screenshot artifact store (Plan 11). The runner-local
|
|
1813
|
+
* `FsEvidenceArtifactStore` is constructed run-scoped at the CLI composition root and injected
|
|
1814
|
+
* here; the shell hands the same instance to every member. Absent ⇒ no screenshot bytes are
|
|
1815
|
+
* persisted (memory-only run, byte-identical pre-Plan-11 behavior).
|
|
1816
|
+
*/
|
|
1817
|
+
evidenceArtifacts?: EvidenceArtifactStorePort;
|
|
1818
|
+
/**
|
|
1819
|
+
* Where a VIEWER's fault is reported (403 WR-06): a live-frame subscriber that throws during
|
|
1820
|
+
* delivery is detached by the hub and the fact lands here. NEVER the bus — the run's permanent
|
|
1821
|
+
* transcript must not depend on who happened to be watching (LIVE-03). Same signature as the
|
|
1822
|
+
* hub's own option, so the shell forwards rather than adapts; absent ⇒ reported nowhere.
|
|
1823
|
+
*/
|
|
1824
|
+
onSubscriberFault?: (code: string, message: string) => void;
|
|
1825
|
+
/**
|
|
1826
|
+
* The 404 D-10 post-verdict step, awaited between `runArtifactStore.finalize` and
|
|
1827
|
+
* `runFinished`. Absent ⇒ `disposition: 'detached'` with NO warning — a host that composed no
|
|
1828
|
+
* step is not an error, it is a host from which nothing will be drained, and the wire says so.
|
|
1829
|
+
*/
|
|
1830
|
+
postVerdict?: PostVerdictPort;
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* The fully-resolved run configuration `startRun` receives (D-04). The CLI keeps pattern
|
|
1834
|
+
* discovery, parsing, config merge, and target/device/model resolution; `startRun` never
|
|
1835
|
+
* touches fs or providers (validate:arch stays clean) — hence NO fs/provider/discovery-shaped
|
|
1836
|
+
* members here. A future cloud RPC surface reuses this same resolved-config contract.
|
|
1837
|
+
*
|
|
1838
|
+
* `members`/`ports` shapes are pinned by the Wave-0 `run-shell.test.ts` scaffold.
|
|
1839
|
+
*/
|
|
1840
|
+
interface RunConfig {
|
|
1841
|
+
runId: RunId;
|
|
1842
|
+
scope: ScopeKey;
|
|
1843
|
+
executionContext: ExecutionContext;
|
|
1844
|
+
configDigests: ConfigDigests;
|
|
1845
|
+
buildOrdinal: number;
|
|
1846
|
+
/**
|
|
1847
|
+
* D-14 §6 gate-run exclusion copied into the durable completed-run handoff. Only literal
|
|
1848
|
+
* true excludes; absence denotes an ordinary counted run and remains absent in JSON.
|
|
1849
|
+
*/
|
|
1850
|
+
excludedFromOutcomes?: true;
|
|
1851
|
+
/**
|
|
1852
|
+
* D-10 verify-before-present gate-run marker (Phase 384, 2026-07-28) copied into the
|
|
1853
|
+
* durable completed-run handoff. A DISTINCT axis from `excludedFromOutcomes` above:
|
|
1854
|
+
* C8-ratified, gate runs still feed curation. Only literal true marks a gate run;
|
|
1855
|
+
* absence denotes an ordinary run and remains absent in JSON. 384 threads the carrier
|
|
1856
|
+
* only — the exclusion semantics land in 388/393.
|
|
1857
|
+
*
|
|
1858
|
+
* ═══ 2026-08-26 — Phase 404 D-08 amends "gate runs still feed curation" ═══
|
|
1859
|
+
*
|
|
1860
|
+
* They still feed the FOLD — a gate run's handoff, folded, still emits curation candidates, and
|
|
1861
|
+
* the outcome-side exclusion still keys on `excludedFromOutcomes` alone. What changed is the
|
|
1862
|
+
* QUEUE: the post-verdict step enqueues NOTHING for a run carrying either marker, so no
|
|
1863
|
+
* `analyze-run` row exists for a `write_test` keep-filter probe. The reasons are all about the
|
|
1864
|
+
* gate loop rather than about correctness — no strong-model analysis cost per keep-gate
|
|
1865
|
+
* iteration, no draft evidence reaching curation through the chain, and no post-processing chips
|
|
1866
|
+
* about a run nobody will ever open. The marker still rides the handoff and the payload schema
|
|
1867
|
+
* still carries it, because the exclusion is a decision the enqueue takes and not a fact the
|
|
1868
|
+
* record forgets.
|
|
1869
|
+
*/
|
|
1870
|
+
gateRun?: true;
|
|
1871
|
+
members: readonly RunMemberDescriptor[];
|
|
1872
|
+
executor: MemberTestExecutor;
|
|
1873
|
+
/**
|
|
1874
|
+
* The run's redactor over the secrets its producer read at the run's start (441-12, D-06/D-11).
|
|
1875
|
+
* Every bus event, and so `events.jsonl` and the dashboard rows, is masked with it and nothing
|
|
1876
|
+
* else. Absent ⇒ the run's events are published as written.
|
|
1877
|
+
*/
|
|
1878
|
+
secretRedactor?: SecretRedactorPort;
|
|
1879
|
+
runBudgetMs?: number;
|
|
1880
|
+
ports?: RunPorts;
|
|
1881
|
+
/**
|
|
1882
|
+
* The D-08 config + source artifact snapshot the CLI composition root assembles (the
|
|
1883
|
+
* `buildRunArtifactContext` document). Staged verbatim into `artifact.json` after `runStarted`
|
|
1884
|
+
* via `ports.runArtifactStore`. Optional — absent ⇒ an empty stage (schema-version-only doc),
|
|
1885
|
+
* so the store leg stays a no-op-shaped write and the memory-only path is unchanged.
|
|
1886
|
+
*/
|
|
1887
|
+
runArtifact?: RunArtifactStageInput;
|
|
1888
|
+
/**
|
|
1889
|
+
* Phase 390 D-16: the quarantined-test set, resolved ONCE at run start by the composition layer
|
|
1890
|
+
* (the CLI folds the committed ledger before the first member executes) and threaded in as PLAIN
|
|
1891
|
+
* DATA. This is deliberately NOT a port: the runner gains zero new I/O seams and stays
|
|
1892
|
+
* storageNeutral, and the disposition is deterministic for the whole run, so a mid-run ledger
|
|
1893
|
+
* edit can never retroactively change a running build. Mirrors how `executionContext` is
|
|
1894
|
+
* resolved once at run start.
|
|
1895
|
+
*
|
|
1896
|
+
* Members are matched on `(filePath, name)` — the stable test identity — never `testId`. A match
|
|
1897
|
+
* makes the member's failure skippable in the aggregate rule (`deriveAggregateVerdict`) and
|
|
1898
|
+
* stamps the disposition beside its truthful outcome in the handoff. Absent or empty ⇒
|
|
1899
|
+
* byte-identical pre-390 behavior.
|
|
1900
|
+
*/
|
|
1901
|
+
quarantined?: readonly QuarantineAppliedEntry[];
|
|
1902
|
+
/**
|
|
1903
|
+
* 436 D-06: the planner limits the producer resolved for this run and the members whose own
|
|
1904
|
+
* limits differ, as plain data. Copied verbatim onto the handoff; absent stays absent.
|
|
1905
|
+
*/
|
|
1906
|
+
plannerLimits?: PlannerLimitsRecord;
|
|
1907
|
+
/**
|
|
1908
|
+
* 402-02 (D-09/D-12): how many members may execute CONCURRENTLY in this run's process. Defaults
|
|
1909
|
+
* to 1 — one worker is a plain sequential loop, byte-identical to the pre-402 shell, so nothing
|
|
1910
|
+
* changes until a config resolves a higher value. Effective concurrency is
|
|
1911
|
+
* `min(members.length, workers)`; values below 1 and non-finite values resolve to 1.
|
|
1912
|
+
*
|
|
1913
|
+
* CONFIG-RESOLVED, NEVER REQUEST-SUPPLIED (D-12). It arrives on the already-resolved RunConfig
|
|
1914
|
+
* the composition root builds, exactly like `runBudgetMs` — `RunsStartInput` carries no
|
|
1915
|
+
* concurrency field, so a remote token-holder cannot raise the concurrency of a target whose
|
|
1916
|
+
* owner tuned it down (the refused-request-fields posture), and unbounded concurrency
|
|
1917
|
+
* (T-402-08) has no reachable input.
|
|
1918
|
+
*/
|
|
1919
|
+
workers?: number;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
/**
|
|
1923
|
+
* The raw storage-state blob (adapter-produced; e.g. a Playwright storageState object). Opaque
|
|
1924
|
+
* to the runner — persisted and returned verbatim, never inspected or masked here (D-14).
|
|
1925
|
+
*/
|
|
1926
|
+
type StorageStateBlob = unknown;
|
|
1927
|
+
/**
|
|
1928
|
+
* The evidence-safe reference to a persisted storage state (D-14). This — and ONLY this — is
|
|
1929
|
+
* what may transit into `StepEvidence`/the handoff: the name plus a one-way fingerprint of the
|
|
1930
|
+
* stored blob. The blob itself never leaves the store.
|
|
1931
|
+
*/
|
|
1932
|
+
interface StorageStateReference {
|
|
1933
|
+
/** The caller-supplied slot name (as authored; the store slugs it before it becomes a path). */
|
|
1934
|
+
name: string;
|
|
1935
|
+
/** One-way fingerprint of the stored blob (the 343 `fingerprint()` idiom — identity, not the data). */
|
|
1936
|
+
fingerprint: string;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* The runner-facing storage-state persistence seam (D-14). A runner-local implementation owns
|
|
1940
|
+
* the `.agent-qa/storage-states/` filesystem writes; the loop injects it beside `cache` and
|
|
1941
|
+
* records only the returned reference.
|
|
1942
|
+
*/
|
|
1943
|
+
interface StorageStatePort {
|
|
1944
|
+
/**
|
|
1945
|
+
* Persist the raw storage-state blob under `name` and return the evidence-safe reference.
|
|
1946
|
+
* The name is slugged traversal-safe before it becomes a filename (T-360-03).
|
|
1947
|
+
*/
|
|
1948
|
+
save(name: string, state: StorageStateBlob): Promise<StorageStateReference>;
|
|
1949
|
+
/**
|
|
1950
|
+
* Load the raw blob previously saved under `name`, or `undefined` when no such slot exists
|
|
1951
|
+
* (a missing name is never an error).
|
|
1952
|
+
*/
|
|
1953
|
+
load(name: string): Promise<StorageStateBlob | undefined>;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
type RecordAdmission = ServingAdmissionDecision;
|
|
1957
|
+
type ServingExposureSurface = ServedAdmissionSurface;
|
|
1958
|
+
interface ServingExposure {
|
|
1959
|
+
readonly surface: ServingExposureSurface;
|
|
1960
|
+
readonly turnOrdinal: number;
|
|
1961
|
+
}
|
|
1962
|
+
interface RecordLedger {
|
|
1963
|
+
/** Preview without mutation so channel-local and combined-turn checks can commit atomically. */
|
|
1964
|
+
inspect(record: ServingRecordMetadata): RecordAdmission;
|
|
1965
|
+
admit(record: ServingRecordMetadata, exposure?: ServingExposure): RecordAdmission;
|
|
1966
|
+
resetTurn(seedRecords: readonly ServingRecordMetadata[], exposure?: ServingExposure): void;
|
|
1967
|
+
/** Optional-compatible evidence seam; production ledgers provide both methods. */
|
|
1968
|
+
beginStepEvidence?(): void;
|
|
1969
|
+
drainEvidenceEvents?(): ServedAdmissionEvent[];
|
|
1970
|
+
size(): number;
|
|
1971
|
+
spent(): number;
|
|
1972
|
+
}
|
|
1973
|
+
interface ProductionRecordLedger extends RecordLedger {
|
|
1974
|
+
beginStepEvidence(): void;
|
|
1975
|
+
drainEvidenceEvents(): ServedAdmissionEvent[];
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* One turn-time authority for the global distinct-record and combined dominance walls. Channel 1
|
|
1979
|
+
* seeds exact rendered material; Channel 3 and Channel 2 then charge every exposure against the
|
|
1980
|
+
* same source/cluster maps. Re-emphasizing an id remains free only for the DISTINCT slot — its
|
|
1981
|
+
* repeated bytes still count toward budget and dominance.
|
|
1982
|
+
*/
|
|
1983
|
+
declare function createRecordLedger(input: {
|
|
1984
|
+
cap: number;
|
|
1985
|
+
budget: number;
|
|
1986
|
+
seedRecords: readonly ServingRecordMetadata[];
|
|
1987
|
+
}): ProductionRecordLedger;
|
|
1988
|
+
|
|
1989
|
+
/** Compression output — v1's CompressResult shape (Pitfall 9). */
|
|
1990
|
+
interface CompressedScreenshot {
|
|
1991
|
+
buffer: Buffer;
|
|
1992
|
+
imageWidth: number;
|
|
1993
|
+
imageHeight: number;
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Screenshot compression + dimension port. The core-side implementation owns
|
|
1997
|
+
* `sharp` (storageNeutral forbids it in runner, Pitfall 9). `compress` covers the
|
|
1998
|
+
* LLM-planner compression call site; `dimensions` covers the action-space-width
|
|
1999
|
+
* metadata fallback done inline via `sharp(screenshot).metadata()` at v1
|
|
2000
|
+
* loop.ts:199-201. The loop applies `stampCaptureMode` itself (runner-owned since 352).
|
|
2001
|
+
*/
|
|
2002
|
+
interface ScreenshotCompressor {
|
|
2003
|
+
compress(screenshot: Buffer, options: {
|
|
2004
|
+
effectiveResolution: number;
|
|
2005
|
+
maxBytes?: number;
|
|
2006
|
+
actionSpaceWidth: number;
|
|
2007
|
+
}): Promise<CompressedScreenshot>;
|
|
2008
|
+
dimensions(screenshot: Buffer): Promise<{
|
|
2009
|
+
width: number;
|
|
2010
|
+
height: number;
|
|
2011
|
+
}>;
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Structural secret-lookup port. The concrete `SecretStore` (moved into runner in
|
|
2015
|
+
* this plan, task 2) satisfies it by construction; `resolveSecretTemplatesInValue`
|
|
2016
|
+
* consumes it. Named distinctly from the concrete class to avoid a barrel collision.
|
|
2017
|
+
*/
|
|
2018
|
+
interface SecretResolver {
|
|
2019
|
+
get(name: string): string | undefined;
|
|
2020
|
+
require(name: string): string;
|
|
2021
|
+
has(name: string): boolean;
|
|
2022
|
+
/**
|
|
2023
|
+
* OPTIONAL name enumeration (PROC-02 · 381 D-08). The loop assembles the procedure
|
|
2024
|
+
* consult's binding-NAME set from the ports already in scope; secret template names are
|
|
2025
|
+
* part of that set so secret-parameterized procedures stay replay-eligible. Names only —
|
|
2026
|
+
* the callback receives the value because the concrete `SecretStore.forEachSecret`
|
|
2027
|
+
* already carries it, but the loop only ever collects the name; no value crosses the
|
|
2028
|
+
* reader port in any form. Absent implementations simply contribute no names.
|
|
2029
|
+
*/
|
|
2030
|
+
forEachSecret?(callback: (name: string, value: string) => void): void;
|
|
2031
|
+
}
|
|
2032
|
+
/** Structural secret-redactor port — the concrete `SecretRedactor` satisfies it. */
|
|
2033
|
+
interface SecretRedactorPort {
|
|
2034
|
+
redactString(value: string): string;
|
|
2035
|
+
}
|
|
2036
|
+
/**
|
|
2037
|
+
* Structural run-scoped working-memory port (WM-01 · D-11/D-12). The concrete
|
|
2038
|
+
* `WorkingMemoryStore` (working-memory/store.ts) satisfies it by construction; declared
|
|
2039
|
+
* structurally here (the SecretResolver naming precedent) so the ports module never imports the
|
|
2040
|
+
* concrete class. `capture` writes verbatim entries (registered secrets redacted at capture);
|
|
2041
|
+
* `entries` returns the whole set in capture order; `dispose` empties it at run end.
|
|
2042
|
+
*/
|
|
2043
|
+
interface WorkingMemoryPort {
|
|
2044
|
+
capture(entries: readonly WorkingMemoryEntry[], ctx?: {
|
|
2045
|
+
test?: number;
|
|
2046
|
+
step?: number;
|
|
2047
|
+
}): void;
|
|
2048
|
+
entries(): WorkingMemoryEntry[];
|
|
2049
|
+
dispose(): void;
|
|
2050
|
+
}
|
|
2051
|
+
/** Minimal scoped-logger surface the loop uses; core's LogManager satisfies it. */
|
|
2052
|
+
interface LoopScopedLogger {
|
|
2053
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
2054
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
2055
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
2056
|
+
error?(message: string, meta?: Record<string, unknown>): void;
|
|
2057
|
+
}
|
|
2058
|
+
interface LoopLogger {
|
|
2059
|
+
createScopedLogger(scope: string): LoopScopedLogger | undefined;
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* Runner-local structural copy of model's DegradeOptions (D-05/D-07). Runner cannot
|
|
2063
|
+
* import the model package, so the shape is duplicated here; Plan 05's cli conformance
|
|
2064
|
+
* proof asserts model's `DegradeOptions` structurally matches this. Policy-free: the
|
|
2065
|
+
* caller (Phase 355) supplies the concrete budget + pin predicate.
|
|
2066
|
+
*/
|
|
2067
|
+
interface RunnerDegradeOptions {
|
|
2068
|
+
budget: number;
|
|
2069
|
+
pin?: (message: ModelMessage, index: number) => boolean;
|
|
2070
|
+
}
|
|
2071
|
+
/**
|
|
2072
|
+
* Runner-local structural copy of model's DegradeOutcome (D-09). `ok:true` carries the
|
|
2073
|
+
* fitted messages; `ok:false` is the structured cannot-fit outcome (system + final +
|
|
2074
|
+
* pinned content alone exceed the budget) — degrade never throws and never violates a pin.
|
|
2075
|
+
*/
|
|
2076
|
+
type RunnerDegradeOutcome = {
|
|
2077
|
+
ok: true;
|
|
2078
|
+
messages: ModelMessage[];
|
|
2079
|
+
} | {
|
|
2080
|
+
ok: false;
|
|
2081
|
+
reason: 'pinned-content-exceeds-budget';
|
|
2082
|
+
messages: ModelMessage[];
|
|
2083
|
+
};
|
|
2084
|
+
/**
|
|
2085
|
+
* Provider-neutral token-accounting port (D-05/D-07) — the 354↔355 seam. A types-only
|
|
2086
|
+
* structural duplicate of model's `TokenAccounting` (accounting/types.ts): runner cannot
|
|
2087
|
+
* import model, so the shape is mirrored here and message types are imported from `'ai'`
|
|
2088
|
+
* only (never `@ai-sdk/*`, PROVIDER_ORCHESTRATION_ALLOWED). Policy-free — prompt tokens
|
|
2089
|
+
* only. This plan declares the seam; Phase 355 wires estimate/degrade consumption.
|
|
2090
|
+
*/
|
|
2091
|
+
interface TokenAccounting {
|
|
2092
|
+
estimate(messages: ModelMessage[]): number;
|
|
2093
|
+
recordUsage(usage: LanguageModelUsage, messages: ModelMessage[]): void;
|
|
2094
|
+
getCeilings(): {
|
|
2095
|
+
contextWindow: number;
|
|
2096
|
+
maxOutput: number;
|
|
2097
|
+
};
|
|
2098
|
+
degrade(messages: ModelMessage[], options: RunnerDegradeOptions): RunnerDegradeOutcome;
|
|
2099
|
+
}
|
|
2100
|
+
/**
|
|
2101
|
+
* D-02 run-scoped event-emit seam. The loop emits `stepFinalized`/`subActionRecorded` at the
|
|
2102
|
+
* moment they occur (live semantics from day one) — the bus assigns `seq` centrally. Both legs
|
|
2103
|
+
* are fire-and-forget SYNCHRONOUS void: the loop must NEVER await telemetry (D-02/D-07). Follows
|
|
2104
|
+
* the optional-port idiom (masking/workingMemory): structural, optional on {@link LoopPorts},
|
|
2105
|
+
* optional-chained at the emit site, never throw-on-absent (D-05 degrade-when-absent). The
|
|
2106
|
+
* concrete curry lands at the run shell (`MemberEventSink.loopEmitFor`); the port survives 363's
|
|
2107
|
+
* migration unchanged.
|
|
2108
|
+
*/
|
|
2109
|
+
interface LoopEventEmitPort {
|
|
2110
|
+
/**
|
|
2111
|
+
* 363 D-01: the additive-optional `extra` carries the StepResult-derived
|
|
2112
|
+
* `{status, durationMs, error}` the reporters need (RESEARCH Q1 parity). Sourced from the
|
|
2113
|
+
* runner `StepResult` at the loop's single terminal `finish()` funnel — NOT fields on the §A8
|
|
2114
|
+
* `StepEvidence` DTO (Pitfall 2). `error` is masked at the sink chokepoint (T-363-05). Absent
|
|
2115
|
+
* ⇒ the base evidence-only payload, source-compatible with pre-363 callers.
|
|
2116
|
+
*/
|
|
2117
|
+
stepFinalized(evidence: StepEvidence, extra?: {
|
|
2118
|
+
status?: StepFinalizedPayload['status'];
|
|
2119
|
+
durationMs?: number;
|
|
2120
|
+
error?: string;
|
|
2121
|
+
/**
|
|
2122
|
+
* 405 D-05: the step's measured usage, from the loop's own planner + verifier tally.
|
|
2123
|
+
* Absent when nothing was measured — the loop's `tokenUsage()` already answers
|
|
2124
|
+
* `undefined` in that case, so absence is produced, never fabricated here.
|
|
2125
|
+
*/
|
|
2126
|
+
tokenUsage?: StepFinalizedPayload['tokenUsage'];
|
|
2127
|
+
/** Finished member scope, including late extraction; overrides the legacy loop tally. */
|
|
2128
|
+
modelObservations?: ModelUsageObservations;
|
|
2129
|
+
evidence?: StepFinalizedPayload['evidence'];
|
|
2130
|
+
variableSnapshot?: StepFinalizedPayload['variableSnapshot'];
|
|
2131
|
+
}): void;
|
|
2132
|
+
subActionRecorded(evidence: SubActionEvidence, subActionIndex: number,
|
|
2133
|
+
/** Capture-time ARIA state; the projector appends its ref beside the decision screenshot. */
|
|
2134
|
+
extra?: {
|
|
2135
|
+
ariaEvidence?: SubActionRecordedPayload['ariaEvidence'];
|
|
2136
|
+
recordingOffsetMs?: number;
|
|
2137
|
+
}): void;
|
|
2138
|
+
}
|
|
2139
|
+
/**
|
|
2140
|
+
* Run-scoped content-addressed evidence artifact store (CASS-02). Storage-neutral: a
|
|
2141
|
+
* runner-local `FsEvidenceArtifactStore` owns the sha-keyed blob persistence; the runner
|
|
2142
|
+
* declares only the shape. `persistDecisionScreenshot` returns the identity for the exact
|
|
2143
|
+
* byte payload so a decision screenshot becomes addressable by `ArtifactRef.sha`; `getBlob`
|
|
2144
|
+
* serves those bytes back, letting a host adapter satisfy eval's structurally identical
|
|
2145
|
+
* `ArtifactSource` without a runner-to-eval dependency. Both legs are synchronous.
|
|
2146
|
+
*/
|
|
2147
|
+
interface PersistedRecording {
|
|
2148
|
+
ref: RecordingRef;
|
|
2149
|
+
byteLength: number;
|
|
2150
|
+
}
|
|
2151
|
+
interface EvidenceArtifactStorePort {
|
|
2152
|
+
persistDecisionScreenshot(bytes: Uint8Array): ArtifactRef;
|
|
2153
|
+
/** Already live-masked JSON only. Optional for existing screenshot-only hosts. */
|
|
2154
|
+
persistEvidenceDocument?(document: Exclude<EvidenceDocument, {
|
|
2155
|
+
kind: 'variables';
|
|
2156
|
+
}>): ArtifactRef;
|
|
2157
|
+
hasBlob?(sha: string): boolean;
|
|
2158
|
+
getBlob(sha: string): Uint8Array | undefined;
|
|
2159
|
+
persistRecording?(stagingPath: string): PersistedRecording;
|
|
2160
|
+
blobSize?(sha: string): number | undefined;
|
|
2161
|
+
readBlobRange?(sha: string, start: number, end: number): Uint8Array | undefined;
|
|
2162
|
+
discardRecording?(stagingPath: string): void;
|
|
2163
|
+
}
|
|
2164
|
+
/**
|
|
2165
|
+
* The member-scoped procedure-flow carry (F-381-51-1 · 381 D-09): the replay position a
|
|
2166
|
+
* completed test step hands to the NEXT test step so a committed multi-step flow replays
|
|
2167
|
+
* exactly one body step per test step. Transports ONLY the consult match, the parsed body
|
|
2168
|
+
* payload, and the cursor — never a pre-resolved ref, never a screen: every carried body
|
|
2169
|
+
* step re-earns dispatch through the full fresh-screen descriptor/guard/page-anchor/
|
|
2170
|
+
* grounding-gate ladder at its own test step (D-10 — a stored action can never dispatch
|
|
2171
|
+
* against a screen its guards did not pass on at that step).
|
|
2172
|
+
*/
|
|
2173
|
+
interface ProcedureFlowCarry {
|
|
2174
|
+
match: ProcedureMatch;
|
|
2175
|
+
payload: ProcedureBodyPayload;
|
|
2176
|
+
nextIndex: number;
|
|
2177
|
+
}
|
|
2178
|
+
/** 439 D-04: a completed download from an earlier step of this test, labelled by its producer. */
|
|
2179
|
+
interface CarriedDownload {
|
|
2180
|
+
stepNumber: number;
|
|
2181
|
+
/** 1-based position of the producing action in its step. */
|
|
2182
|
+
actionIndex: number;
|
|
2183
|
+
evidence: DownloadEvidence;
|
|
2184
|
+
}
|
|
2185
|
+
/** 439 D-04: the latest app clipboard write from an earlier step of this test. */
|
|
2186
|
+
interface CarriedClipboard {
|
|
2187
|
+
stepNumber: number;
|
|
2188
|
+
actionIndex: number;
|
|
2189
|
+
evidence: ClipboardEvidence;
|
|
2190
|
+
}
|
|
2191
|
+
interface CarriedArtifacts {
|
|
2192
|
+
downloads: CarriedDownload[];
|
|
2193
|
+
clipboard?: CarriedClipboard;
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* The runner-owned port bundle the loop executes against — successor to v1's
|
|
2197
|
+
* AgentLoopConfig (core types.ts:66-104), minus the D-10 healing/attempt-limit
|
|
2198
|
+
* fields and minus the v1 cache-key inputs (those move behind the cache port).
|
|
2199
|
+
*/
|
|
2200
|
+
interface LoopPorts {
|
|
2201
|
+
recordingZeroMs?: number;
|
|
2202
|
+
adapter: PlatformAdapter;
|
|
2203
|
+
planner: Planner;
|
|
2204
|
+
verifier?: Verifier;
|
|
2205
|
+
/**
|
|
2206
|
+
* Redesigned per-key cache port (D-11): get/set/invalidate over a branded CacheKey,
|
|
2207
|
+
* descriptor-only value. Plan 05's FsActionCache implements it; injected at the Plan 06
|
|
2208
|
+
* CLI composition root. Absent ⇒ every step misses (no cache), never throws (D-05).
|
|
2209
|
+
*/
|
|
2210
|
+
cache?: ActionPlanCache;
|
|
2211
|
+
/**
|
|
2212
|
+
* D-05 declared decision context for both model roles. Composition roots pass the
|
|
2213
|
+
* same profile objects to the planner/verifier constructors and this recording lane;
|
|
2214
|
+
* the loop never derives model facts from a model handle. Absent preserves the
|
|
2215
|
+
* provider-neutral degrade-when-absent contract.
|
|
2216
|
+
*/
|
|
2217
|
+
modelProfiles?: {
|
|
2218
|
+
planner: DeclaredModelProfile;
|
|
2219
|
+
verifier?: DeclaredModelProfile;
|
|
2220
|
+
};
|
|
2221
|
+
/**
|
|
2222
|
+
* D-16 once-per-member digest of the complete bound tool surface. It remains a
|
|
2223
|
+
* separate optional port because tool attribution is distinct from model identity;
|
|
2224
|
+
* absent callers continue without evidence stamping and never throw.
|
|
2225
|
+
*/
|
|
2226
|
+
toolSchemaDigest?: string;
|
|
2227
|
+
compressor?: ScreenshotCompressor;
|
|
2228
|
+
/** D-05 optional read-only knowledge reader — the loop degrades gracefully when absent. */
|
|
2229
|
+
knowledgeReader?: KnowledgeHintReader;
|
|
2230
|
+
/**
|
|
2231
|
+
* 407 D-13/D-17 (2026-08-29) — the HOST's retrieval rung, resolved ONCE at the composition
|
|
2232
|
+
* root by knowledge-local's single `resolveRetrievalRung`, never re-probed per step. It
|
|
2233
|
+
* crosses as the schema type because the runner never imports knowledge-local (validate:arch
|
|
2234
|
+
* enforces that); a promise because the resolver's embedder probe can cost a model load and
|
|
2235
|
+
* overlapping it with run start keeps the probe off the critical path.
|
|
2236
|
+
*
|
|
2237
|
+
* It describes the ladder the machine was on when this run was served — NOT how this brief was
|
|
2238
|
+
* ranked. Absent ⇒ no rung is stamped (D-05 degrade-when-absent); a rejection stamps nothing
|
|
2239
|
+
* and never fails a step; and a rung is never stamped onto a step no reader served (D-16 —
|
|
2240
|
+
* "no knowledge reader bound is not a rung").
|
|
2241
|
+
*/
|
|
2242
|
+
knowledgeRetrievalRung?: Promise<RecordedRetrievalRung>;
|
|
2243
|
+
/**
|
|
2244
|
+
* F-381-51-1 · D-09 member-scoped carry cell (the recordLedger shared-mutable-object
|
|
2245
|
+
* precedent): the caller owns ONE cell per test member and threads the SAME object into
|
|
2246
|
+
* every per-step ports literal. The loop TAKES the carry at step start (reading and
|
|
2247
|
+
* immediately emptying the cell) and re-arms it only after the step's verifier accepts a
|
|
2248
|
+
* successful replayed step with body steps remaining — take-then-rearm, the D-11 one-shot
|
|
2249
|
+
* law extended across the step boundary: every other exit (guard miss, execution failure,
|
|
2250
|
+
* verifier rejection, policy denial, step failure, abort, exhaustion) leaves the cell
|
|
2251
|
+
* empty. Loop-side state only: never serialized, never recorded into evidence or the
|
|
2252
|
+
* handoff, never crosses a test boundary. Absent ⇒ no cursor crosses steps (D-05
|
|
2253
|
+
* degrade-when-absent).
|
|
2254
|
+
*/
|
|
2255
|
+
procedureFlow?: {
|
|
2256
|
+
carry?: ProcedureFlowCarry;
|
|
2257
|
+
};
|
|
2258
|
+
/**
|
|
2259
|
+
* 439 D-04 member-scoped carry cell, the `procedureFlow` precedent: the caller owns ONE cell per
|
|
2260
|
+
* test member and threads the SAME object into every per-step ports literal. The loop renders its
|
|
2261
|
+
* items into the step record's carried lines at step start, and at the step's end appends the
|
|
2262
|
+
* step's completed downloads and replaces the clipboard with its latest write. Loop-side state
|
|
2263
|
+
* only: never serialized, never recorded into evidence or the handoff, never crosses a test
|
|
2264
|
+
* boundary. Absent ⇒ nothing carries (the live editor).
|
|
2265
|
+
*/
|
|
2266
|
+
carriedArtifacts?: CarriedArtifacts;
|
|
2267
|
+
/**
|
|
2268
|
+
* D-02/D-15/D-17 knowledge budgets are computed beside model token accounting at the
|
|
2269
|
+
* composition root and arrive here as plain numbers. The provider-neutral loop never derives
|
|
2270
|
+
* model ceilings or imports token-accounting policy; absent bindings use provisional floors.
|
|
2271
|
+
*/
|
|
2272
|
+
knowledgeBudgets?: {
|
|
2273
|
+
brief: number;
|
|
2274
|
+
searchResults: number;
|
|
2275
|
+
caution: number;
|
|
2276
|
+
readBody: number;
|
|
2277
|
+
recordCap: number;
|
|
2278
|
+
};
|
|
2279
|
+
/** D-21 run-fixed build ordinal copied into ServedBrief attribution when a reader is bound. */
|
|
2280
|
+
knowledgeAsOfBuild?: number;
|
|
2281
|
+
/**
|
|
2282
|
+
* D-17 loop-controlled turn ledger. A composition root that registers Channel-2 tools binds
|
|
2283
|
+
* the same object into their closures; the loop owns reset timing and Channel 3 consults it
|
|
2284
|
+
* directly. Absent is legal and creates a step-local ledger for Channel-1 accounting only.
|
|
2285
|
+
*/
|
|
2286
|
+
recordLedger?: RecordLedger;
|
|
2287
|
+
/**
|
|
2288
|
+
* D-11 Channel-2 turn reset owned by the memory-tool wrapper. The loop invokes it only after
|
|
2289
|
+
* reseeding the shared distinct-record ledger from the frozen brief, so both ledgers begin the
|
|
2290
|
+
* live planning turn from the same boundary.
|
|
2291
|
+
*/
|
|
2292
|
+
beginMemoryTurn?: (scope?: ScopeKey, exposedIds?: readonly RecordId[]) => void;
|
|
2293
|
+
/** Consume the successful direct reads served during the current live planning turn. */
|
|
2294
|
+
drainMemoryReadTraces?: () => ReadTrace[];
|
|
2295
|
+
plannerConfig?: PlannerConfig;
|
|
2296
|
+
/** Wall-clock timeout for a single step (ms). 0 or undefined = no timeout. */
|
|
2297
|
+
stepTimeout?: number;
|
|
2298
|
+
logger?: LoopLogger;
|
|
2299
|
+
logCapture?: {
|
|
2300
|
+
console?: boolean;
|
|
2301
|
+
network?: boolean;
|
|
2302
|
+
};
|
|
2303
|
+
vision?: {
|
|
2304
|
+
screenshotSize?: number;
|
|
2305
|
+
effectiveResolution?: number;
|
|
2306
|
+
};
|
|
2307
|
+
visionGate?: {
|
|
2308
|
+
attachImage: boolean;
|
|
2309
|
+
captureMode: 'full' | 'textOnly';
|
|
2310
|
+
promptNote: string;
|
|
2311
|
+
};
|
|
2312
|
+
/** Pitfall 8 — setVariable executes via this callback, NOT the adapter. */
|
|
2313
|
+
onSetVariable?: (name: string, value: string) => void;
|
|
2314
|
+
/** Pitfall 1 — path resolution stays core-side, injected pre-resolved into the loop. */
|
|
2315
|
+
resolveUploadPath?: (rawPath: string) => string;
|
|
2316
|
+
abortSignal?: AbortSignal;
|
|
2317
|
+
secretStore?: SecretResolver;
|
|
2318
|
+
secretRedactor?: SecretRedactorPort;
|
|
2319
|
+
/**
|
|
2320
|
+
* WM-01 run-scoped working-memory seam (D-11/D-12). The concrete `WorkingMemoryStore` is
|
|
2321
|
+
* created at the CLI composition root and injected here; Phase 361 rebinds this exact seam onto
|
|
2322
|
+
* `RunSession` (the knowledgeReader idiom). Unbound ⇒ the loop runs memory-less: no capture, an
|
|
2323
|
+
* empty served set — it never throws (D-05 degrade-when-absent).
|
|
2324
|
+
*/
|
|
2325
|
+
workingMemory?: WorkingMemoryPort;
|
|
2326
|
+
/**
|
|
2327
|
+
* D-14 storage-state persistence seam (Phase 360). The runner-local `FsStorageStateStore` is
|
|
2328
|
+
* constructed run-scoped at the CLI/dashboard composition roots and injected here; the seam
|
|
2329
|
+
* strips the raw blob from the recorded result and records only `{ name, fingerprint }`. Unbound
|
|
2330
|
+
* ⇒ a `storageState` action degrades to a §7.1 persistence-unavailable error (never throws, D-05).
|
|
2331
|
+
*/
|
|
2332
|
+
storageState?: StorageStatePort;
|
|
2333
|
+
/**
|
|
2334
|
+
* D-16 injectable unique-value suffix generator (353 K=2 determinism). Absent ⇒ the seam uses
|
|
2335
|
+
* `defaultUniqueValueGenerator` (timestamp + entropy). Tests inject a fixed generator for
|
|
2336
|
+
* deterministic minted values; production leaves it unbound.
|
|
2337
|
+
*/
|
|
2338
|
+
uniqueValueGenerator?: UniqueValueGenerator;
|
|
2339
|
+
/**
|
|
2340
|
+
* D-06 optional pre-prompt blob-guard — bounds a single one-shot structured tool-output
|
|
2341
|
+
* value before it lands in the sub-action history / next planning turn. A pure signature
|
|
2342
|
+
* (no `ai`/model types, zero arch risk). Composition roots that depend on model may inject
|
|
2343
|
+
* model's canonical `guardBlob`; the loop degrades to a runner-local fallback when absent.
|
|
2344
|
+
*/
|
|
2345
|
+
guardBlob?: (value: unknown) => string | undefined;
|
|
2346
|
+
/**
|
|
2347
|
+
* D-02 run-scoped event-emit seam. Bound by the run shell (per-step curry via
|
|
2348
|
+
* `MemberEventSink.loopEmitFor`) so the loop emits `stepFinalized`/`subActionRecorded` live;
|
|
2349
|
+
* absent ⇒ the loop runs telemetry-less (no emit), never throws (D-05 degrade-when-absent).
|
|
2350
|
+
*/
|
|
2351
|
+
events?: LoopEventEmitPort;
|
|
2352
|
+
/**
|
|
2353
|
+
* D-20 real config digests, so the loop can key the cache on what the run actually keyed on
|
|
2354
|
+
* (consumed in Plan 08). Type-imported from the run layer; absent ⇒ the existing key material
|
|
2355
|
+
* is used unchanged.
|
|
2356
|
+
*/
|
|
2357
|
+
configDigests?: ConfigDigests;
|
|
2358
|
+
/**
|
|
2359
|
+
* CASS-02 run-scoped decision-screenshot byte store. The shell hands one store instance to
|
|
2360
|
+
* every member; the loop mints an `ArtifactRef` for captured screenshot bytes through it.
|
|
2361
|
+
* Absent ⇒ a legal memory-only / test-harness state (no persistence), never throws (D-05).
|
|
2362
|
+
*/
|
|
2363
|
+
evidenceArtifacts?: EvidenceArtifactStorePort;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
declare function executeStep(stepInstruction: string, ports: LoopPorts, context: StepContext): Promise<StepResult>;
|
|
2367
|
+
|
|
2368
|
+
/**
|
|
2369
|
+
* Capture-location context stamped onto each entry (D-11): the whole run set is served every
|
|
2370
|
+
* turn, so the planner weighs relevance itself — these mark where the entry was captured, they
|
|
2371
|
+
* are not a filter rule.
|
|
2372
|
+
*/
|
|
2373
|
+
interface WorkingMemoryProvenanceContext {
|
|
2374
|
+
test?: number;
|
|
2375
|
+
step?: number;
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Run-scoped verbatim working memory: a plain Map keyed by entry key, latest-wins on
|
|
2379
|
+
* re-capture (D-12 — one key ⇒ one current value, the OTP-resend reality), whole-set recall in
|
|
2380
|
+
* capture order (D-11 — no filtering, no search). Registered secrets are redacted at capture
|
|
2381
|
+
* (Pitfall 7 boundary 1); every other value stays verbatim.
|
|
2382
|
+
*/
|
|
2383
|
+
declare class WorkingMemoryStore {
|
|
2384
|
+
private readonly redactor?;
|
|
2385
|
+
private readonly store;
|
|
2386
|
+
constructor(redactor?: SecretRedactorPort | undefined);
|
|
2387
|
+
/**
|
|
2388
|
+
* Capture verbatim entries. Each item is validated against workingMemoryEntrySchema (closed
|
|
2389
|
+
* enums reject unknown kind/provenance at the boundary, V5), its value redacted for registered
|
|
2390
|
+
* secrets only, and stamped with the capture location. Re-capturing an existing key overwrites
|
|
2391
|
+
* its value (D-12 latest-wins); the key's original capture position is preserved.
|
|
2392
|
+
*/
|
|
2393
|
+
capture(entries: readonly WorkingMemoryEntry[], ctx?: WorkingMemoryProvenanceContext): void;
|
|
2394
|
+
/** The whole run-scoped set in capture order (D-11 — no filtering, no search). */
|
|
2395
|
+
entries(): WorkingMemoryEntry[];
|
|
2396
|
+
/** Empty the store at run end (WM-02 disposal; the redacted flush is composed elsewhere). */
|
|
2397
|
+
dispose(): void;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
type AgentAbortScope = 'test' | 'step';
|
|
2401
|
+
interface AgentTimeoutAbortReason {
|
|
2402
|
+
type: 'agent-qa-timeout';
|
|
2403
|
+
scope: AgentAbortScope;
|
|
2404
|
+
timeoutMs: number;
|
|
2405
|
+
message: string;
|
|
2406
|
+
}
|
|
2407
|
+
declare function createTimeoutAbortReason(scope: AgentAbortScope, timeoutMs: number): AgentTimeoutAbortReason;
|
|
2408
|
+
/**
|
|
2409
|
+
* True iff `message` is the message createTimeoutAbortReason('test', ms) mints for
|
|
2410
|
+
* some ms — the D-16 member-timeout signal. Rejects the 'step'-scope variant (a step
|
|
2411
|
+
* timeout is an ordinary failure) and any arbitrary text.
|
|
2412
|
+
*
|
|
2413
|
+
* WHY match on the message string: the AgentTimeoutAbortReason object does NOT survive
|
|
2414
|
+
* onto TestResult — the only trace that reaches verdict derivation is a failed step
|
|
2415
|
+
* whose `error` equals this minted message (RESEARCH Q3). This matcher is the one place
|
|
2416
|
+
* that knowledge lives, exported beside the sole mint site so the format stays
|
|
2417
|
+
* single-sourced.
|
|
2418
|
+
*/
|
|
2419
|
+
declare function isTestTimeoutMessage(message: string): boolean;
|
|
2420
|
+
declare function getTimeoutAbortReason(signal: AbortSignal | undefined): AgentTimeoutAbortReason | undefined;
|
|
2421
|
+
declare function withAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T>;
|
|
2422
|
+
|
|
2423
|
+
type ToolCategory = 'action' | 'file' | 'network';
|
|
2424
|
+
interface ToolDefinition<T extends z.ZodType = z.ZodType> {
|
|
2425
|
+
id: string;
|
|
2426
|
+
type: string;
|
|
2427
|
+
surface: Surface;
|
|
2428
|
+
exposed?: boolean;
|
|
2429
|
+
description: string;
|
|
2430
|
+
category: ToolCategory;
|
|
2431
|
+
schema: T;
|
|
2432
|
+
/** Advisory audit/confirmation hint only; must not gate platform slices. */
|
|
2433
|
+
destructive?: boolean;
|
|
2434
|
+
/** Advisory audit/trace hint only; must not gate platform slices. */
|
|
2435
|
+
readOnly?: boolean;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
declare const EXPOSURE: Record<Platform, ReadonlySet<Surface>>;
|
|
2439
|
+
declare class ToolRegistry {
|
|
2440
|
+
private tools;
|
|
2441
|
+
register<T extends z.ZodType>(def: ToolDefinition<T>): this;
|
|
2442
|
+
get(id: string): ToolDefinition | undefined;
|
|
2443
|
+
getAll(): ToolDefinition[];
|
|
2444
|
+
getForPlatform(platform: Platform): ToolDefinition[];
|
|
2445
|
+
getSchema(id: string): z.ZodType;
|
|
2446
|
+
generateDocs(platform?: Platform): string;
|
|
2447
|
+
}
|
|
2448
|
+
declare const defaultRegistry: ToolRegistry;
|
|
2449
|
+
|
|
2450
|
+
/**
|
|
2451
|
+
* K — the per-turn non-action tool-call budget (D-06/D-10). Channel 2's designed
|
|
2452
|
+
* search→read sequence needs two calls, and one retry pair must fit in the same turn.
|
|
2453
|
+
* This remains an internal provisional constant, injectable via
|
|
2454
|
+
* ExecutePlanningTurnOptions.budget for tests only. Do NOT promote it to user-facing
|
|
2455
|
+
* config or thread it through LoopPorts / any config schema.
|
|
2456
|
+
*/
|
|
2457
|
+
declare const DEFAULT_NON_ACTION_TOOL_BUDGET = 4;
|
|
2458
|
+
/**
|
|
2459
|
+
* A caller-registered non-action tool. `tool` is the `ai` definition merged into the
|
|
2460
|
+
* always-send-all slice; `execute` runs the call and returns a JSON-serializable
|
|
2461
|
+
* result appended to the conversation as a tool message. Zero of these ship in 353;
|
|
2462
|
+
* tests register fakes to exercise the machinery (D-07).
|
|
2463
|
+
*/
|
|
2464
|
+
interface NonActionToolHandler {
|
|
2465
|
+
tool: Tool;
|
|
2466
|
+
execute(args: Record<string, unknown>): Promise<unknown> | unknown;
|
|
2467
|
+
}
|
|
2468
|
+
type NonActionToolMap = Record<string, NonActionToolHandler>;
|
|
2469
|
+
/**
|
|
2470
|
+
* An ordered record of one executed non-action tool call within a planning turn
|
|
2471
|
+
* (§B1). Exchanges persist on SubActionEvidence for successful decisions (INST-01,
|
|
2472
|
+
* D-01) and PlanningFailureEvidence for failed turns (D-03).
|
|
2473
|
+
*/
|
|
2474
|
+
type ToolExchange = ToolExchangeEvidence;
|
|
2475
|
+
type PlanningTurnFailureReason = 'no_action_tool_called' | 'non_action_tool_budget_exhausted' | 'ungroundable_action' | 'planning_limit_reached';
|
|
2476
|
+
/**
|
|
2477
|
+
* The in-turn groundability verdict for a parsed action (D-09). Returned by the
|
|
2478
|
+
* injected `groundability` callback the caller (LLMPlanner) builds from the Plan-04
|
|
2479
|
+
* grounding gate + the in-scope screenState — the tool-loop stays gate-agnostic and
|
|
2480
|
+
* NEVER imports `grounding/` itself.
|
|
2481
|
+
*
|
|
2482
|
+
* `ok: true` may optionally carry the gate's fully-grounded execution-shape `action`
|
|
2483
|
+
* (ref, never a descriptor) and its §A8 `attempts` trail; when present the turn swaps
|
|
2484
|
+
* them onto the returned plan so the resolved action + evidence ride to executeStep.
|
|
2485
|
+
* When omitted (e.g. a test seam that only signals groundability) the parsed plan is
|
|
2486
|
+
* returned unchanged. `ok: false` carries the §7.1 error text injected back into the
|
|
2487
|
+
* SAME conversation as an error-text tool result for the model's one-shot correction.
|
|
2488
|
+
*/
|
|
2489
|
+
type GroundabilityResult = {
|
|
2490
|
+
ok: true;
|
|
2491
|
+
action?: Record<string, unknown>;
|
|
2492
|
+
attempts?: GroundingAttempt[];
|
|
2493
|
+
statedTargets?: StatedTargets;
|
|
2494
|
+
} | {
|
|
2495
|
+
ok: false;
|
|
2496
|
+
message: string;
|
|
2497
|
+
};
|
|
2498
|
+
/** The injected groundability callback: parsed action → groundability verdict (D-09). */
|
|
2499
|
+
type GroundabilityCallback = (plan: ActionPlan) => GroundabilityResult;
|
|
2500
|
+
interface PlanningTurnSuccess {
|
|
2501
|
+
ok: true;
|
|
2502
|
+
plan: ActionPlan;
|
|
2503
|
+
exchanges: ToolExchange[];
|
|
2504
|
+
tokenUsage?: TokenUsage;
|
|
2505
|
+
callRecord?: ModelCallRecord;
|
|
2506
|
+
}
|
|
2507
|
+
interface PlanningTurnFailure {
|
|
2508
|
+
ok: false;
|
|
2509
|
+
reason: PlanningTurnFailureReason;
|
|
2510
|
+
message: string;
|
|
2511
|
+
exchanges: ToolExchange[];
|
|
2512
|
+
tokenUsage?: TokenUsage;
|
|
2513
|
+
callRecord?: ModelCallRecord;
|
|
2514
|
+
}
|
|
2515
|
+
type PlanningTurnResult = PlanningTurnSuccess | PlanningTurnFailure;
|
|
2516
|
+
interface ExecutePlanningTurnOptions {
|
|
2517
|
+
model: LanguageModel;
|
|
2518
|
+
/** Registry backing toolCallToActionPlan (the turn's termination projection). */
|
|
2519
|
+
registry: ToolRegistry;
|
|
2520
|
+
/** The full v45 action slice (buildTools output) — always sent (always-send-all). */
|
|
2521
|
+
actionTools: ToolSet;
|
|
2522
|
+
/**
|
|
2523
|
+
* System prompt — a single string, or the Phase 355 [stable, context, volatile]
|
|
2524
|
+
* tier array the caller assembles (D-15). Passed through to generateText untouched;
|
|
2525
|
+
* the tool-loop applies no clamp/degrade policy of its own.
|
|
2526
|
+
*/
|
|
2527
|
+
system: string | Array<SystemModelMessage>;
|
|
2528
|
+
/** The initial user message(s): text and optional image, built by the caller. */
|
|
2529
|
+
messages: ModelMessage[];
|
|
2530
|
+
/** Caller-registered non-action tools. Empty in production (D-07). */
|
|
2531
|
+
nonActionTools?: NonActionToolMap;
|
|
2532
|
+
/** K override for tests (D-06). Defaults to DEFAULT_NON_ACTION_TOOL_BUDGET. */
|
|
2533
|
+
budget?: number;
|
|
2534
|
+
/**
|
|
2535
|
+
* D-09 in-turn grounding feedback. When present, a parsed action call is grounded by
|
|
2536
|
+
* this injected callback after `toolCallToActionPlan` succeeds. A failure appends the
|
|
2537
|
+
* §7.1 error text as an error-text tool result in the SAME conversation with EXACTLY
|
|
2538
|
+
* ONE retry (a turn-local one-shot flag, never a counter — LOOP-02); a second
|
|
2539
|
+
* consecutive grounding failure returns a structured `'ungroundable_action'` failure.
|
|
2540
|
+
* The retry neither consumes nor bypasses the ≤K non-action budget (Pitfall 8). When
|
|
2541
|
+
* absent the turn behaves exactly as the 353 bounded loop (back-compat within phase).
|
|
2542
|
+
*/
|
|
2543
|
+
groundability?: GroundabilityCallback;
|
|
2544
|
+
/**
|
|
2545
|
+
* Dynamic output cap (CTXB-04) computed by the caller's clamp helper and passed
|
|
2546
|
+
* through to generateText. `undefined` is a valid no-op passthrough — the tool-loop
|
|
2547
|
+
* computes no clamp of its own (D-15/Pitfall 5).
|
|
2548
|
+
*/
|
|
2549
|
+
maxOutputTokens?: number;
|
|
2550
|
+
/**
|
|
2551
|
+
* Optional sampling parameters applied at the model call site (D-10/D-11). Every
|
|
2552
|
+
* field is optional and absence preserves the provider default. `maxOutputTokens`
|
|
2553
|
+
* is deliberately excluded because the output ceiling remains clamp-owned.
|
|
2554
|
+
*/
|
|
2555
|
+
sampling?: SamplingParams;
|
|
2556
|
+
/**
|
|
2557
|
+
* 436 D-08: when bound, every model call of the turn is debited before it is made; a refused
|
|
2558
|
+
* debit ends the turn with `planning_limit_reached` and no model call.
|
|
2559
|
+
*/
|
|
2560
|
+
planningCallGate?: PlanningCallGate;
|
|
2561
|
+
abortSignal?: AbortSignal;
|
|
2562
|
+
logger?: LoopScopedLogger;
|
|
2563
|
+
}
|
|
2564
|
+
declare function executePlanningTurn(options: ExecutePlanningTurnOptions): Promise<PlanningTurnResult>;
|
|
2565
|
+
|
|
2566
|
+
/**
|
|
2567
|
+
* Structured planning-turn failure used to preserve failed-turn evidence across
|
|
2568
|
+
* the planner's throw-based public contract (D-03).
|
|
2569
|
+
*/
|
|
2570
|
+
declare class PlanningTurnError extends Error {
|
|
2571
|
+
readonly reason: PlanningTurnFailureReason;
|
|
2572
|
+
readonly exchanges: ToolExchangeEvidence[];
|
|
2573
|
+
readonly callRecord?: ModelCallRecord;
|
|
2574
|
+
constructor(reason: PlanningTurnFailureReason, message: string, exchanges: ToolExchangeEvidence[], callRecord?: ModelCallRecord);
|
|
2575
|
+
}
|
|
2576
|
+
/**
|
|
2577
|
+
* 436 D-08/D-12: the step's planning-turn limit refused a planner model call. Never a planning
|
|
2578
|
+
* failure; the loop ends the step with its typed limit stop.
|
|
2579
|
+
*/
|
|
2580
|
+
declare class PlanningLimitReachedError extends Error {
|
|
2581
|
+
readonly exchanges: ToolExchangeEvidence[];
|
|
2582
|
+
readonly callRecord?: ModelCallRecord;
|
|
2583
|
+
constructor(exchanges: ToolExchangeEvidence[], callRecord?: ModelCallRecord);
|
|
2584
|
+
}
|
|
2585
|
+
declare class LLMPlanner implements Planner {
|
|
2586
|
+
private model;
|
|
2587
|
+
private platform;
|
|
2588
|
+
private logger?;
|
|
2589
|
+
private capabilityNotes?;
|
|
2590
|
+
private accounting;
|
|
2591
|
+
private profile?;
|
|
2592
|
+
private nonActionTools?;
|
|
2593
|
+
constructor(model: LanguageModel, platform: Platform | undefined, logger: LoopScopedLogger | undefined, capabilityNotes: string | undefined, accounting: TokenAccounting, profile?: DeclaredModelProfile, nonActionTools?: NonActionToolMap);
|
|
2594
|
+
private buildSystemTiers;
|
|
2595
|
+
plan(step: string, screenState: ScreenState, context: StepContext, abortSignal?: AbortSignal): Promise<PlanResult>;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
declare const VerificationResultSchema: z.ZodObject<{
|
|
2599
|
+
conditions: z.ZodArray<z.ZodObject<{
|
|
2600
|
+
quote: z.ZodString;
|
|
2601
|
+
verdict: z.ZodEnum<{
|
|
2602
|
+
unverified: "unverified";
|
|
2603
|
+
met: "met";
|
|
2604
|
+
"not-met": "not-met";
|
|
2605
|
+
inconclusive: "inconclusive";
|
|
2606
|
+
}>;
|
|
2607
|
+
needs: z.ZodEnum<{
|
|
2608
|
+
visual: "visual";
|
|
2609
|
+
none: "none";
|
|
2610
|
+
file: "file";
|
|
2611
|
+
persistence: "persistence";
|
|
2612
|
+
"limit-max": "limit-max";
|
|
2613
|
+
"limit-min": "limit-min";
|
|
2614
|
+
}>;
|
|
2615
|
+
controlRef: z.ZodString;
|
|
2616
|
+
file: z.ZodObject<{
|
|
2617
|
+
name: z.ZodString;
|
|
2618
|
+
match: z.ZodEnum<{
|
|
2619
|
+
exact: "exact";
|
|
2620
|
+
none: "none";
|
|
2621
|
+
lines: "lines";
|
|
2622
|
+
}>;
|
|
2623
|
+
text: z.ZodString;
|
|
2624
|
+
}, z.core.$strip>;
|
|
2625
|
+
citations: z.ZodArray<z.ZodObject<{
|
|
2626
|
+
source: z.ZodEnum<{
|
|
2627
|
+
action: "action";
|
|
2628
|
+
"start-screen": "start-screen";
|
|
2629
|
+
"final-screen": "final-screen";
|
|
2630
|
+
read: "read";
|
|
2631
|
+
carried: "carried";
|
|
2632
|
+
screenshot: "screenshot";
|
|
2633
|
+
}>;
|
|
2634
|
+
quote: z.ZodString;
|
|
2635
|
+
}, z.core.$strip>>;
|
|
2636
|
+
reasoning: z.ZodString;
|
|
2637
|
+
}, z.core.$strip>>;
|
|
2638
|
+
dialogs: z.ZodArray<z.ZodObject<{
|
|
2639
|
+
name: z.ZodString;
|
|
2640
|
+
account: z.ZodEnum<{
|
|
2641
|
+
"bears-on-condition": "bears-on-condition";
|
|
2642
|
+
requested: "requested";
|
|
2643
|
+
unrelated: "unrelated";
|
|
2644
|
+
}>;
|
|
2645
|
+
reasoning: z.ZodString;
|
|
2646
|
+
}, z.core.$strip>>;
|
|
2647
|
+
isAppError: z.ZodBoolean;
|
|
2648
|
+
reasoning: z.ZodString;
|
|
2649
|
+
}, z.core.$strip>;
|
|
2650
|
+
/** Closed judgment schema — a boolean verdict + rationale, never free text (D-06). */
|
|
2651
|
+
declare const AiJudgmentResultSchema: z.ZodObject<{
|
|
2652
|
+
verdict: z.ZodBoolean;
|
|
2653
|
+
rationale: z.ZodString;
|
|
2654
|
+
}, z.core.$strip>;
|
|
2655
|
+
declare class LLMVerifier implements Verifier {
|
|
2656
|
+
private model;
|
|
2657
|
+
private accounting;
|
|
2658
|
+
private profile?;
|
|
2659
|
+
constructor(model: LanguageModel, accounting: TokenAccounting, profile?: DeclaredModelProfile);
|
|
2660
|
+
verify(step: string, before: ScreenState, after: ScreenState, action: Action, screenshot?: Buffer, abortSignal?: AbortSignal, promptNote?: string, bulkCompletionPredicate?: string, authorContext?: VerifierAuthorContext, operationEvidence?: OperationEvidence, record?: VerifierStepRecord): Promise<VerifyResult>;
|
|
2661
|
+
private runVerification;
|
|
2662
|
+
/**
|
|
2663
|
+
* D-05/D-06/D-09 AI judge lane. A SINGLE structured call over the current observation that
|
|
2664
|
+
* returns the verdict + rationale. `opts` carries the requested-but-deferred M3/M4 knobs
|
|
2665
|
+
* (consensus/video/effort) so demand is visible (D-09) — accepted, never honored, never an
|
|
2666
|
+
* error; the loop records them into the AiJudgment evidence block. Empty/overflow model output
|
|
2667
|
+
* degrades to a structured `verdict:false` failure (the verify() never-crash posture) — abort
|
|
2668
|
+
* is the only rethrow.
|
|
2669
|
+
*/
|
|
2670
|
+
judgeClaim(claim: string, screen: ScreenState, opts?: Record<string, unknown>): Promise<{
|
|
2671
|
+
verdict: boolean;
|
|
2672
|
+
rationale: string;
|
|
2673
|
+
}>;
|
|
2674
|
+
/**
|
|
2675
|
+
* D-07 AI extraction lane. A SINGLE structured call whose output schema is derived from the
|
|
2676
|
+
* extractAI request's declared field list (each field a string). Returns the extracted field
|
|
2677
|
+
* map; the loop writes it into run-scoped working memory and degrades a `scope:'global'`
|
|
2678
|
+
* request to the same run-scoped write (D-07 — no durable-memory port in M1). Empty/overflow
|
|
2679
|
+
* output degrades to an empty extraction (never a throw) — abort is the only rethrow.
|
|
2680
|
+
*/
|
|
2681
|
+
extractData(query: string, screen: ScreenState, opts?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
2682
|
+
private runStructured;
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
declare class AssertionEvaluator implements Asserter {
|
|
2686
|
+
evaluate(assertion: AssertionInput, screenState: ScreenState): Promise<AssertionResult>;
|
|
2687
|
+
private evaluateTextPresence;
|
|
2688
|
+
private evaluateElementVisibility;
|
|
2689
|
+
private evaluateUrlMatch;
|
|
2690
|
+
private evaluateElementCount;
|
|
2691
|
+
}
|
|
2692
|
+
declare class LLMAssertionEvaluator implements Asserter {
|
|
2693
|
+
private model;
|
|
2694
|
+
constructor(model: LanguageModel);
|
|
2695
|
+
evaluate(assertion: AssertionInput, screenState: ScreenState): Promise<AssertionResult>;
|
|
2696
|
+
}
|
|
2697
|
+
declare function createAsserter(model?: LanguageModel): Asserter;
|
|
2698
|
+
|
|
2699
|
+
type VariableSource = 'env' | 'inline' | 'suite' | 'cli' | 'capture' | 'hook' | 'step';
|
|
2700
|
+
interface VariableStoreOptions {
|
|
2701
|
+
onWarn?: (message: string) => void;
|
|
2702
|
+
}
|
|
2703
|
+
declare class VariableStore {
|
|
2704
|
+
private storage;
|
|
2705
|
+
private onWarn?;
|
|
2706
|
+
constructor(options?: VariableStoreOptions);
|
|
2707
|
+
set(name: string, value: string, source?: VariableSource): void;
|
|
2708
|
+
get(name: string): string | undefined;
|
|
2709
|
+
has(name: string): boolean;
|
|
2710
|
+
getSource(name: string): VariableSource | undefined;
|
|
2711
|
+
getAll(): Map<string, string>;
|
|
2712
|
+
setAll(vars: Record<string, string>, source?: VariableSource): void;
|
|
2713
|
+
snapshot(): Record<string, {
|
|
2714
|
+
value: string;
|
|
2715
|
+
source: VariableSource;
|
|
2716
|
+
}>;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
interface InterpolationWithProvenance {
|
|
2720
|
+
text: string;
|
|
2721
|
+
/**
|
|
2722
|
+
* The bindings that actually resolved, one entry per DISTINCT name in first-occurrence
|
|
2723
|
+
* order. Unresolved patterns and the skipped `{{secret:...}}` template contribute
|
|
2724
|
+
* nothing. Values are recorded VERBATIM — see the masking prohibition on
|
|
2725
|
+
* `StepEvidence.interpolatedVariables` (masking here would break the emit fold's
|
|
2726
|
+
* exact-match classification and reintroduce G-381-3).
|
|
2727
|
+
*/
|
|
2728
|
+
substitutions: InterpolatedVariable[];
|
|
2729
|
+
}
|
|
2730
|
+
/**
|
|
2731
|
+
* The single replacement implementation (PROC-01 · 381 D-03/D-08). Produces byte-identical
|
|
2732
|
+
* text to the historical `interpolateVariables` while reporting WHICH binding names
|
|
2733
|
+
* produced it, so the name survives the pre-planning interpolation seam into StepEvidence.
|
|
2734
|
+
*/
|
|
2735
|
+
declare function interpolateVariablesWithProvenance(template: string, store: VariableStore): InterpolationWithProvenance;
|
|
2736
|
+
declare function interpolateVariables(template: string, store: VariableStore): string;
|
|
2737
|
+
interface UnresolvedTemplate {
|
|
2738
|
+
pattern: string;
|
|
2739
|
+
message: string;
|
|
2740
|
+
}
|
|
2741
|
+
declare function findUnresolvedTemplates(text: string): UnresolvedTemplate[];
|
|
2742
|
+
declare function findBareVariables(text: string): string[];
|
|
2743
|
+
|
|
2744
|
+
declare class ExplicitExtractor implements VariableExtractor {
|
|
2745
|
+
extract(input: ExtractorInput, screenState: ScreenState): Promise<CaptureResult>;
|
|
2746
|
+
private extractByRegex;
|
|
2747
|
+
private extractBySelector;
|
|
2748
|
+
}
|
|
2749
|
+
declare function createExtractor(model?: LanguageModel, method?: string): VariableExtractor;
|
|
2750
|
+
|
|
2751
|
+
declare const ExtractionResultSchema: z.ZodObject<{
|
|
2752
|
+
value: z.ZodString;
|
|
2753
|
+
reasoning: z.ZodString;
|
|
2754
|
+
}, z.core.$strip>;
|
|
2755
|
+
declare class LLMVariableExtractor implements VariableExtractor {
|
|
2756
|
+
private model;
|
|
2757
|
+
constructor(model: LanguageModel);
|
|
2758
|
+
extract(input: ExtractorInput, screenState: ScreenState): Promise<CaptureResult>;
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
type PromptPlatform = 'web' | 'android' | 'ios';
|
|
2762
|
+
/**
|
|
2763
|
+
* v1 token estimate (chars/4). No semantic change (D-04); the TokenAccounting port
|
|
2764
|
+
* lands in Phase 355. Exported here because the prompts module is the token estimate's
|
|
2765
|
+
* sole runner-side consumer.
|
|
2766
|
+
*/
|
|
2767
|
+
declare function estimateTokens(text: string): number;
|
|
2768
|
+
declare function buildSystemPrompt(platform?: PromptPlatform): string;
|
|
2769
|
+
/**
|
|
2770
|
+
* CONTEXT tier (D-04): the app-under-test / suite / test framing. Byte-stable per run,
|
|
2771
|
+
* so it sits inside the cached prefix and carries the single Anthropic `cache_control`
|
|
2772
|
+
* ephemeral breakpoint (D-05) — everything at or above this message (stable + context)
|
|
2773
|
+
* is cached once per run at zero incremental cost. A non-Anthropic provider never parses
|
|
2774
|
+
* the `anthropic` namespace, so `providerOptions` is a transparent no-op there.
|
|
2775
|
+
*/
|
|
2776
|
+
declare function buildContextTier(context: StepContext): SystemModelMessage;
|
|
2777
|
+
/**
|
|
2778
|
+
* Render the knowledge brief (the volatile-tier ingredient, D-12) framed as hypotheses
|
|
2779
|
+
* to weigh against the live observation — never as commands (spec §5, L3). Returns ''
|
|
2780
|
+
* when the brief slot is unbound/empty so the volatile tier omits the section entirely
|
|
2781
|
+
* (no 'Knowledge brief' header, no dangling blank line).
|
|
2782
|
+
*/
|
|
2783
|
+
declare function renderBriefAsHypotheses(brief?: string): string;
|
|
2784
|
+
/**
|
|
2785
|
+
* VOLATILE tier (D-04/D-07): the knowledge brief (empty until M2 binds a
|
|
2786
|
+
* KnowledgeHintReader impl, D-12) plus a DATE-GRANULARITY timestamp. Date precision only
|
|
2787
|
+
* — minute/second precision would bust the KV cache (D-07). Carries NO cache breakpoint;
|
|
2788
|
+
* it sits after the breakpoint that lives on the context tier.
|
|
2789
|
+
*/
|
|
2790
|
+
declare function buildVolatileTier(context: StepContext): SystemModelMessage;
|
|
2791
|
+
/**
|
|
2792
|
+
* Per-turn content composer (CTXB-01/CTXB-02 — D-08/D-09/D-12). Decomposes the
|
|
2793
|
+
* per-turn state into a small ordered set of turn messages so 354's degrade() can pin
|
|
2794
|
+
* and proportionally truncate at message granularity rather than shredding one blob.
|
|
2795
|
+
* Order: re-run context → previous steps → sub-action history → failure context →
|
|
2796
|
+
* working memory → latest observation → current instruction (FINAL). Empty slots are
|
|
2797
|
+
* omitted entirely — no blank/degenerate message is pushed (D-12).
|
|
2798
|
+
*
|
|
2799
|
+
* PIN CONTRACT (D-08): the last two messages are the pinned pair — the latest
|
|
2800
|
+
* OBSERVATION (second-to-last) and the CURRENT INSTRUCTION (last, verbatim). The caller
|
|
2801
|
+
* (Plan 05) protects exactly these two via degrade's `pin`; every earlier message is
|
|
2802
|
+
* trim-eligible. All per-turn state lives here and NEVER in the cached system tiers.
|
|
2803
|
+
*/
|
|
2804
|
+
declare function composeTurnMessages(context: StepContext, screenState: ScreenState): ModelMessage[];
|
|
2805
|
+
declare function formatAction(action: Action): string;
|
|
2806
|
+
/**
|
|
2807
|
+
* VERIFIER stable tier (CTXB-01 / D-11): the byte-stable verdict-identity block, the verifier's own
|
|
2808
|
+
* cached prefix, identical on every verification call. 439 (D-01, D-05, D-06, D-11, D-18) states
|
|
2809
|
+
* the per-condition contract here: split the step into quoted conditions, judge each from cited
|
|
2810
|
+
* current evidence, and leave the pass to the runner. Arg-less: no per-call inputs; the
|
|
2811
|
+
* screenshot-analysis paragraph rides only a per-call turn that has an image (D-19).
|
|
2812
|
+
*/
|
|
2813
|
+
declare function buildVerifierStableTier(): string;
|
|
2814
|
+
/**
|
|
2815
|
+
* VERIFIER per-call turn content (D-11). Only the per-verification-call bytes: the step
|
|
2816
|
+
* goal, the enriched action description, and the before/after screen states (+ the
|
|
2817
|
+
* optional screenshot note and prompt note). The byte-stable verdict-identity/approach
|
|
2818
|
+
* prose now lives in `buildVerifierStableTier()` (the verifier's own cached prefix), so
|
|
2819
|
+
* it is no longer re-emitted per call.
|
|
2820
|
+
*/
|
|
2821
|
+
declare function buildVerificationPrompt(step: string, beforeState: ScreenState, afterState: ScreenState, action: Action, hasScreenshot?: boolean, promptNote?: string, bulkCompletionPredicate?: string, authorContext?: VerifierAuthorContext, operationEvidence?: OperationEvidence, record?: VerifierStepRecord): string;
|
|
2822
|
+
declare function buildAssertionPrompt(assertion: AssertionInput, screenState: ScreenState): string;
|
|
2823
|
+
declare function buildExtractionPrompt(input: ExtractorInput, screenState: ScreenState): string;
|
|
2824
|
+
|
|
2825
|
+
declare const CHARS_PER_TOKEN_ESTIMATE = 4;
|
|
2826
|
+
declare const OBSERVATION_LINE_PREFIX = "Observation: ";
|
|
2827
|
+
type OriginStampedEntry = IndexEntry & {
|
|
2828
|
+
origin?: 'app-observed' | 'user';
|
|
2829
|
+
originClass?: 'agent-observed' | 'agent-verified' | 'import' | 'user';
|
|
2830
|
+
};
|
|
2831
|
+
/**
|
|
2832
|
+
* Cap learned summaries using JavaScript String.length (UTF-16 code units), the
|
|
2833
|
+
* BRIF-01 length contract. The ellipsis is part of the 140-code-unit maximum.
|
|
2834
|
+
*/
|
|
2835
|
+
declare function renderCautionSummary(summary: string): string;
|
|
2836
|
+
declare function tierForEntry(entry: OriginStampedEntry): string;
|
|
2837
|
+
/** Deterministic provisional cost of the exact text handed to the planner. */
|
|
2838
|
+
declare function estimateRenderedServingCost(text: string): number;
|
|
2839
|
+
interface RenderedCautionLine {
|
|
2840
|
+
summary: string;
|
|
2841
|
+
cost: number;
|
|
2842
|
+
}
|
|
2843
|
+
/** Render and cost Channel 3 through one projection so framing can never be undercharged. */
|
|
2844
|
+
declare function renderCautionLine(entry: IndexEntry): RenderedCautionLine;
|
|
2845
|
+
declare function renderBriefLine(entry: IndexEntry): {
|
|
2846
|
+
line: string;
|
|
2847
|
+
briefLine: BriefLine;
|
|
2848
|
+
cost: number;
|
|
2849
|
+
};
|
|
2850
|
+
declare function renderObservationLine(line: BriefLine): string;
|
|
2851
|
+
declare function assembleBriefTiers(policyText: string, userFactSummaries: readonly string[], renderedLines: readonly {
|
|
2852
|
+
line: string;
|
|
2853
|
+
tier: string;
|
|
2854
|
+
}[]): {
|
|
2855
|
+
L0: string;
|
|
2856
|
+
L1: string;
|
|
2857
|
+
L2: string;
|
|
2858
|
+
};
|
|
2859
|
+
|
|
2860
|
+
declare function makeEmptyServedBrief(scope: ScopeKey, asOfBuild: number): ServedBrief;
|
|
2861
|
+
declare function foldCacheStatus(subActionCacheFlags: boolean[]): StepEvidence['cacheStatus'];
|
|
2862
|
+
declare function collapseAnnotation(action: Action, execResult: ActionResult, viewport: Viewport): ActionAnnotation;
|
|
2863
|
+
interface RecordSubActionInput {
|
|
2864
|
+
/** The planner's reasoning/plan string for this sub-action. */
|
|
2865
|
+
plan: string;
|
|
2866
|
+
plannedAction: Action$1;
|
|
2867
|
+
/**
|
|
2868
|
+
* The FULL observed ScreenState the sub-action was planned against (D-15). Pitfall 3:
|
|
2869
|
+
* v1 stored only the tree string — the forbidden shape. elements + refMap ride along.
|
|
2870
|
+
*/
|
|
2871
|
+
stateBefore: ScreenState;
|
|
2872
|
+
stateAfter?: ScreenState;
|
|
2873
|
+
/**
|
|
2874
|
+
* The execution result as the schema ActionResult (screenshot may be present here as
|
|
2875
|
+
* bytes; it is structurally excluded by toRecordedActionResult before storage — the
|
|
2876
|
+
* type-level screenshot-never guarantee, T-353-06). annotation is produced via
|
|
2877
|
+
* collapseAnnotation.
|
|
2878
|
+
*/
|
|
2879
|
+
result: ActionResult$1;
|
|
2880
|
+
/** v1 per-sub-action cache flag; folded per step by foldCacheStatus at finalize. */
|
|
2881
|
+
cached?: boolean;
|
|
2882
|
+
/**
|
|
2883
|
+
* The §A8 grounding evidence for this sub-action (GROUND-02): the resolved descriptor
|
|
2884
|
+
* plus the per-rung attempt trail (turn gate on the live path, replay resolution on the
|
|
2885
|
+
* cache path, re-ground heal entries on the D-10 path). Present only when the action
|
|
2886
|
+
* carried an element field; the loop redacts the descriptor BEFORE passing it here so a
|
|
2887
|
+
* pre-redaction descriptor never reaches evidence (T-357-02a, the redact-before-record
|
|
2888
|
+
* ordering). Typed via the schema DTO — no runner-local mirror (353 evidence-by-construction).
|
|
2889
|
+
*/
|
|
2890
|
+
grounding?: SubActionEvidence['grounding'];
|
|
2891
|
+
/**
|
|
2892
|
+
* The §A8 decision-screenshot ArtifactRef (CASS-02): the identity of the exact final image
|
|
2893
|
+
* bytes the planner saw for this live decision, minted by the run-scoped artifact store.
|
|
2894
|
+
* Absent on cache-served, text-only, unbound-store, or failed-store paths — never fabricated.
|
|
2895
|
+
*/
|
|
2896
|
+
decisionScreenshotRef?: SubActionEvidence['decisionScreenshotRef'];
|
|
2897
|
+
/**
|
|
2898
|
+
* Auxiliary §A8 ArtifactRefs already reserved by the schema DTO. Copied verbatim (order and
|
|
2899
|
+
* identity preserved) so the recorder never drops artifact references present on the input.
|
|
2900
|
+
*/
|
|
2901
|
+
artifactRefs?: SubActionEvidence['artifactRefs'];
|
|
2902
|
+
/**
|
|
2903
|
+
* Ordered exchanges from this live planning decision (D-01). The loop masks every
|
|
2904
|
+
* string leaf BEFORE passing them here (T-360-01); the recorder never sees raw values.
|
|
2905
|
+
* Present, possibly empty, for live turns and absent for cache replay.
|
|
2906
|
+
*/
|
|
2907
|
+
exchanges?: ToolExchangeEvidence[];
|
|
2908
|
+
/**
|
|
2909
|
+
* Final-response enrichment for this live decision (D-05/D-08). The loop passes it
|
|
2910
|
+
* conditionally; structural absence means no live call record was available.
|
|
2911
|
+
*/
|
|
2912
|
+
callRecord?: ModelCallRecord;
|
|
2913
|
+
/** 436 D-22: what the action produced outside the page, already masked by the loop. */
|
|
2914
|
+
operationEvidence?: SubActionEvidence['operationEvidence'];
|
|
2915
|
+
healedDispatch?: SubActionEvidence['healedDispatch'];
|
|
2916
|
+
}
|
|
2917
|
+
declare class EvidenceRecorder {
|
|
2918
|
+
private readonly subActions;
|
|
2919
|
+
private readonly cacheFlags;
|
|
2920
|
+
private servedBrief;
|
|
2921
|
+
/**
|
|
2922
|
+
* KREV-02 · D-13: the serve-time retrieval trace, recorded BESIDE `servedBrief` and never
|
|
2923
|
+
* inside it. `ServedBrief` feeds `computeBriefDigest`, so a trace channel within it would have
|
|
2924
|
+
* rewritten every cassette and acceptance digest in the tree; a sibling field leaves those
|
|
2925
|
+
* bytes untouched, which `evidence-trace.test.ts` proves by digest equality rather than by
|
|
2926
|
+
* asserting that nobody edited the brief.
|
|
2927
|
+
*
|
|
2928
|
+
* Channel 1 is REPLACED on each record (a step replans against the SAME frozen brief, so
|
|
2929
|
+
* appending would duplicate its attributions), while Channel 3 APPENDS — exactly mirroring how
|
|
2930
|
+
* `servedBrief.channel1` and `servedBrief.channel3` themselves behave.
|
|
2931
|
+
*/
|
|
2932
|
+
private channel1Trace?;
|
|
2933
|
+
private readonly channel3TraceLines;
|
|
2934
|
+
private readonly channel3Truncated;
|
|
2935
|
+
private selfHeal?;
|
|
2936
|
+
private workingMemory?;
|
|
2937
|
+
private planningFailure?;
|
|
2938
|
+
private reportedValue?;
|
|
2939
|
+
private termination?;
|
|
2940
|
+
private limitStop?;
|
|
2941
|
+
private conditionVerdicts?;
|
|
2942
|
+
private limitUsage?;
|
|
2943
|
+
private operationCapture?;
|
|
2944
|
+
private aiJudgment?;
|
|
2945
|
+
private assertOutcome?;
|
|
2946
|
+
private verdictSource?;
|
|
2947
|
+
private planSource?;
|
|
2948
|
+
private cacheMiss?;
|
|
2949
|
+
/** D-06 run-declared planner context, copied onto every finalized step when bound. */
|
|
2950
|
+
private readonly modelProfile?;
|
|
2951
|
+
/** D-07 verifier identity copied beside every finalized verdict when bound. */
|
|
2952
|
+
private readonly verifierIdentity?;
|
|
2953
|
+
/** D-16 once-per-member bound-surface digest copied onto every finalized step. */
|
|
2954
|
+
private readonly toolSchemaDigest?;
|
|
2955
|
+
/**
|
|
2956
|
+
* 407 D-13: the HOST's retrieval rung at serve time, handed in by the composition root and
|
|
2957
|
+
* copied onto every finalized step. It sits BESIDE `servedBrief` for the same reason
|
|
2958
|
+
* `retrievalTrace` does — `computeBriefDigest` keys cassettes and acceptance evidence on the
|
|
2959
|
+
* brief's bytes, so a rung channel inside the brief would invalidate every brief ever served.
|
|
2960
|
+
* The recorder never resolves it: a run's rung is a fact about serve time, not a live probe.
|
|
2961
|
+
*/
|
|
2962
|
+
private readonly retrievalRung?;
|
|
2963
|
+
/**
|
|
2964
|
+
* D-02 single sub-action chokepoint (Plan 361-08). When bound, invoked at the END of
|
|
2965
|
+
* every {@link recordSubAction} append with the recorded evidence and its index — so all
|
|
2966
|
+
* five loop call sites emit `subActionRecorded` through ONE seam, never per-call-site.
|
|
2967
|
+
* Optional-chained: unbound ⇒ the recorder is byte-identical to today (never throws, D-05).
|
|
2968
|
+
*/
|
|
2969
|
+
private readonly onSubActionRecorded?;
|
|
2970
|
+
constructor(options?: {
|
|
2971
|
+
servedBrief?: ServedBrief;
|
|
2972
|
+
scope?: ScopeKey;
|
|
2973
|
+
asOfBuild?: number;
|
|
2974
|
+
onSubActionRecorded?: (evidence: SubActionEvidence, index: number) => void;
|
|
2975
|
+
modelProfile?: DeclaredModelProfile;
|
|
2976
|
+
verifierIdentity?: ModelIdentity;
|
|
2977
|
+
toolSchemaDigest?: string;
|
|
2978
|
+
retrievalRung?: RecordedRetrievalRung;
|
|
2979
|
+
});
|
|
2980
|
+
private replaceServedBrief;
|
|
2981
|
+
private losslessProof;
|
|
2982
|
+
/**
|
|
2983
|
+
* Upgrade this production recorder from the proof-less historical shape to a live lossless
|
|
2984
|
+
* proof. The model-visible channel bytes stay unchanged; only the actual serving caps and
|
|
2985
|
+
* empty chronological journals are introduced.
|
|
2986
|
+
*/
|
|
2987
|
+
beginLosslessServedBrief(input: {
|
|
2988
|
+
recordCap: number;
|
|
2989
|
+
combinedBudget: number;
|
|
2990
|
+
}): void;
|
|
2991
|
+
/**
|
|
2992
|
+
* Append observations drained from the production admission authority. Ordinals must remain
|
|
2993
|
+
* contiguous across batches; validation occurs before mutation so a bad batch appends nothing.
|
|
2994
|
+
*/
|
|
2995
|
+
recordServedAdmissionEvents(events: readonly ServedAdmissionEvent[]): void;
|
|
2996
|
+
/** Append one exact production prompt snapshot and re-address the complete served proof. */
|
|
2997
|
+
recordServedPromptTurn(turn: ServedPromptTurn): void;
|
|
2998
|
+
/** The number of sub-actions recorded so far; the next recordSubAction returns this index. */
|
|
2999
|
+
get subActionCount(): number;
|
|
3000
|
+
/** Append a §A8 SubActionEvidence. Returns its index (for attachStateAfter). */
|
|
3001
|
+
recordSubAction(input: RecordSubActionInput): number;
|
|
3002
|
+
/**
|
|
3003
|
+
* Snapshot the run-scoped working-memory set served to this step's planning turns
|
|
3004
|
+
* (WM-01, §12.15). The loop passes the values ALREADY masked (the loop's `recordMask`, Pitfall
|
|
3005
|
+
* 7 boundary 3) — recorded BESIDE the brief, never inside `servedBrief`.
|
|
3006
|
+
*/
|
|
3007
|
+
recordWorkingMemory(entries: WorkingMemoryEntry[]): void;
|
|
3008
|
+
/**
|
|
3009
|
+
* Record the real deterministic Channel-1 brief served to this step. The recording type is the
|
|
3010
|
+
* schema-owned ServedBrief; the digest is always recomputed over the populated channels.
|
|
3011
|
+
* makeEmptyServedBrief remains exclusively the no-reader constructor fallback.
|
|
3012
|
+
*/
|
|
3013
|
+
recordServedBrief(input: {
|
|
3014
|
+
scope: ScopeKey;
|
|
3015
|
+
asOfBuild: number;
|
|
3016
|
+
budget: number;
|
|
3017
|
+
block: BriefBlock;
|
|
3018
|
+
/**
|
|
3019
|
+
* D-16's last hop, made structural. The trace is a REQUIRED parameter, so the only way to
|
|
3020
|
+
* persist a served brief without one is to change this signature — which is a compile error
|
|
3021
|
+
* at every call site rather than a silently trace-less run. `makeEmptyServedBrief` stays the
|
|
3022
|
+
* one no-reader constructor fallback and records nothing here, so an absent trace on evidence
|
|
3023
|
+
* means "no reader was bound", never "a reader served and the trace was dropped" (D-15).
|
|
3024
|
+
*/
|
|
3025
|
+
trace: ServedRetrievalTraceV1;
|
|
3026
|
+
}): void;
|
|
3027
|
+
/**
|
|
3028
|
+
* Append the exact Channel-3 CautionLines injected into a live replan turn. Refused records
|
|
3029
|
+
* never enter this method; the reserved reader overflow line does because its summary was
|
|
3030
|
+
* actually rendered. Digest recomputation keeps ServedBrief content-addressed after mutation.
|
|
3031
|
+
*
|
|
3032
|
+
* WR-10 (387-13): the LEDGER accumulates on every call, including the zero-injection one. A
|
|
3033
|
+
* turn that injected nothing is not a turn with nothing to explain — it is exactly the turn
|
|
3034
|
+
* "why didn't it mention X" is asked about, and {@link foldRetrievalTrace} carries a dedicated
|
|
3035
|
+
* arm for "no served lines, non-empty truncation ledger" that this method previously made
|
|
3036
|
+
* unreachable by returning before accumulating.
|
|
3037
|
+
*
|
|
3038
|
+
* The surviving `cautions.length === 0` return is a COST guard, not a correctness one, and the
|
|
3039
|
+
* distinction is measured rather than asserted: deleting it outright reddens nothing, because
|
|
3040
|
+
* appending an empty array and recomputing a content-addressed digest reproduces the same
|
|
3041
|
+
* bytes. It stays only to skip a whole-brief rehash on every all-cut turn. D-13's byte-stability
|
|
3042
|
+
* on this path is protected by what is NOT written here — cut candidates are never spoofed into
|
|
3043
|
+
* `channel3` as served lines — and the regression cases pin that outcome, not this branch.
|
|
3044
|
+
*/
|
|
3045
|
+
recordServedCautions(cautions: readonly CautionLine[], trace: ServedRetrievalTraceV1): void;
|
|
3046
|
+
/** Append successful Channel-2 direct reads and re-address the complete served brief. */
|
|
3047
|
+
recordServedReadTraces(traces: readonly ReadTrace[]): void;
|
|
3048
|
+
/**
|
|
3049
|
+
* Record the structurally failed planning turn (D-03). Exchanges and message arrive
|
|
3050
|
+
* ALREADY masked by the loop; the recorder remains mask-agnostic.
|
|
3051
|
+
*/
|
|
3052
|
+
recordPlanningFailure(evidence: PlanningFailureEvidence): void;
|
|
3053
|
+
/**
|
|
3054
|
+
* Record the D-03 `common_reportValue` answer block. `value` arrives already masked. Surfaced
|
|
3055
|
+
* on StepEvidence.reportedValue so the verdict layer (and Phase 361's handoff) carries the
|
|
3056
|
+
* retrieved answer first-class. reportValue is non-terminal (D-02) — this only records.
|
|
3057
|
+
*/
|
|
3058
|
+
recordReportedValue(evidence: ReportedValueEvidence): void;
|
|
3059
|
+
/**
|
|
3060
|
+
* Record the D-04 infeasible termination block — present only when a turn declared the step
|
|
3061
|
+
* infeasible via the `failureKind`/`failureReason` envelope (machine-distinguishable from a
|
|
3062
|
+
* plain `stepFailed`). Sorted keys `["kind","reason"]` are fixed by the DTO.
|
|
3063
|
+
*/
|
|
3064
|
+
recordTermination(evidence: TerminationEvidence): void;
|
|
3065
|
+
/**
|
|
3066
|
+
* Record the typed limit stop (436 D-12/D-13). Its text fields arrive already masked; it is
|
|
3067
|
+
* never a planning failure, a termination or a verdict source.
|
|
3068
|
+
*/
|
|
3069
|
+
recordLimitStop(evidence: LimitStopEvidence): void;
|
|
3070
|
+
/**
|
|
3071
|
+
* Record the per-condition verdicts (439 D-07). Text fields arrive masked and bounded; a later
|
|
3072
|
+
* judgment of the same step replaces an earlier one.
|
|
3073
|
+
*/
|
|
3074
|
+
recordConditionVerdicts(evidence: ConditionVerdictsEvidence): void;
|
|
3075
|
+
/** Record whether the step's platform could capture each operation (436 D-23). */
|
|
3076
|
+
recordOperationCapture(availability: OperationCaptureAvailability): void;
|
|
3077
|
+
/** Record the step's configured limits and usage (436 D-11); finish() calls it on every path. */
|
|
3078
|
+
recordLimitUsage(usage: StepLimitUsage): void;
|
|
3079
|
+
/**
|
|
3080
|
+
* Record the D-05/D-06/D-07/D-09 AI judge block (`assertAI`/`extractAI`): verdict, rationale
|
|
3081
|
+
* (masked), the requested-but-deferred M3/M4 knobs, and the extractAI global-scope degradation
|
|
3082
|
+
* marker. One judgment per attempt (single-model law).
|
|
3083
|
+
*/
|
|
3084
|
+
recordAiJudgment(evidence: AiJudgmentEvidence): void;
|
|
3085
|
+
/**
|
|
3086
|
+
* Record the D-10/D-11 structured `common_assertValue` outcome block: the comparator's
|
|
3087
|
+
* deterministic result with `expected`/`actual` already masked. A passing outcome drives the
|
|
3088
|
+
* verifier short-circuit (Plan 10 seam), recorded via {@link recordVerdictSource}.
|
|
3089
|
+
*/
|
|
3090
|
+
recordAssertOutcome(evidence: AssertValueEvidence): void;
|
|
3091
|
+
/**
|
|
3092
|
+
* Record which path produced this step's final verification verdict (D-11): the deterministic
|
|
3093
|
+
* assertValue comparator (`'assert-value'`) or the verifier's own model call
|
|
3094
|
+
* (`'verifier-model'`), which also decides every AI-judged conclusion (assertAI or extractAI;
|
|
3095
|
+
* quick 261004-lsa). Set with the final verdict, so a verifier rejection records nothing.
|
|
3096
|
+
*/
|
|
3097
|
+
recordVerdictSource(source: VerdictSource): void;
|
|
3098
|
+
/**
|
|
3099
|
+
* Record the typed step plan source (PROC-02 · 381 D-12). `procedure-replay` carries the
|
|
3100
|
+
* guard fold (checksPassed/total + the first miss when the fold broke off); `procedure-hint`
|
|
3101
|
+
* marks a served-without-replay exposure. Recorded BESIDE `cacheStatus` — structurally
|
|
3102
|
+
* distinct from the cached flag so eval/cassette consumers reading cached semantics are
|
|
3103
|
+
* never polluted (Pitfall 4). Last write wins: the loop updates the fold as guard checks
|
|
3104
|
+
* accumulate, and the final state is what finalize emits.
|
|
3105
|
+
*/
|
|
3106
|
+
recordPlanSource(source: StepPlanSource): void;
|
|
3107
|
+
/**
|
|
3108
|
+
* Record why a cache key hit did not replay (438 D-11), before the loop invalidates the entry.
|
|
3109
|
+
* The first reason in the step is kept.
|
|
3110
|
+
*/
|
|
3111
|
+
recordCacheMiss(miss: {
|
|
3112
|
+
reason: CacheMissReason;
|
|
3113
|
+
}): void;
|
|
3114
|
+
/**
|
|
3115
|
+
* Attach the D-15 stateAfter at the spec-locked boundaries (assert sub-actions, the
|
|
3116
|
+
* verifier boundary, the self-heal boundary) by reuse of an observation the loop
|
|
3117
|
+
* already takes — no extra capture.
|
|
3118
|
+
*/
|
|
3119
|
+
attachStateAfter(index: number, state: ScreenState): void;
|
|
3120
|
+
/** Record the §A8 contrastive self-heal pair (D-12): the failed decision and the
|
|
3121
|
+
* recovering decision at the replan-with-failure-context boundary. */
|
|
3122
|
+
recordSelfHeal(failedDecision: Decision, recoveringDecision: Decision): void;
|
|
3123
|
+
/**
|
|
3124
|
+
* Fold the per-channel serve-time attributions into the one persisted trace (D-13/D-14).
|
|
3125
|
+
*
|
|
3126
|
+
* Every value here was computed by the reader at the moment of serving and is copied through
|
|
3127
|
+
* unchanged — no score is re-derived and no rank is recomputed at persistence time. The runner
|
|
3128
|
+
* writes; the reader computed.
|
|
3129
|
+
*
|
|
3130
|
+
* Absent when nothing was served through a bound reader, which is D-15's honest no-trace arm:
|
|
3131
|
+
* an absent trace says "no reader served this step", not "a trace was lost".
|
|
3132
|
+
*/
|
|
3133
|
+
private foldRetrievalTrace;
|
|
3134
|
+
/** Finalize the accumulated evidence into a §A8 StepEvidence. cacheStatus is the
|
|
3135
|
+
* per-step fold; servedBrief is always present (empty when no reader bound).
|
|
3136
|
+
*
|
|
3137
|
+
* `options.nonCacheable` threads the D-08 bypass signal (Pitfall 7): when a step was
|
|
3138
|
+
* classified NonCacheableStepInput the loop never touched the cache, so cacheStatus is
|
|
3139
|
+
* `'bypass'` DIRECTLY — orthogonal to the boolean hit/miss fold (a cached sub-action's
|
|
3140
|
+
* flag must not fold to `'hit'` for a bypassed step). */
|
|
3141
|
+
finalize(verdict: VerifierVerdict, stepIndex: number, stepText: string, options?: {
|
|
3142
|
+
nonCacheable?: boolean;
|
|
3143
|
+
/**
|
|
3144
|
+
* The step's `{{env:NAME}}` provenance pairs (PROC-01 · 381 D-03/D-08), stamped only
|
|
3145
|
+
* when non-empty so the absent-by-default shape is preserved. The recorder is
|
|
3146
|
+
* MASK-AGNOSTIC by contract: it records exactly what its caller passes and masks
|
|
3147
|
+
* nothing itself. These pairs in particular must NOT be masked anywhere — see the
|
|
3148
|
+
* masking prohibition on `StepEvidence.interpolatedVariables`.
|
|
3149
|
+
*/
|
|
3150
|
+
interpolatedVariables?: readonly InterpolatedVariable[];
|
|
3151
|
+
}): StepEvidence;
|
|
3152
|
+
}
|
|
3153
|
+
declare function projectLegacyTrace(evidence: StepEvidence): SubActionTrace[];
|
|
3154
|
+
|
|
3155
|
+
interface GroundingStrategy {
|
|
3156
|
+
reground(locator: GroundedLocator, elements: readonly ElementInfo[]): StrictMatchResult;
|
|
3157
|
+
}
|
|
3158
|
+
declare const deterministicDescriptorMatcher: GroundingStrategy;
|
|
3159
|
+
|
|
3160
|
+
/**
|
|
3161
|
+
* Stable, non-secret, project-level env-digest salt (Open Question 2 resolution).
|
|
3162
|
+
* It MUST be a fixed versioned constant — a per-run salt would silently change the
|
|
3163
|
+
* env digest every run and disable ALL env-dependent cache hits. Never derive this
|
|
3164
|
+
* per-run and never fold a secret into it.
|
|
3165
|
+
*/
|
|
3166
|
+
declare const CACHE_ENV_DIGEST_SALT = "agent-qa/cache/env-digest/v1";
|
|
3167
|
+
interface BuildCacheableStepInputArgs {
|
|
3168
|
+
/** The declared-secret mask (441 D-06); the identity when unbound. */
|
|
3169
|
+
mask?: MaskFn;
|
|
3170
|
+
platform: Platform;
|
|
3171
|
+
suiteConfigDigest: string;
|
|
3172
|
+
testConfigDigest: string;
|
|
3173
|
+
stepText: string;
|
|
3174
|
+
stepIndex: number;
|
|
3175
|
+
testIndex: number;
|
|
3176
|
+
/** Value-free redacted env shapes only — raw env values never reach the digest. */
|
|
3177
|
+
envVars: readonly RedactedEnvVar[];
|
|
3178
|
+
/** Stable project salt; defaults to CACHE_ENV_DIGEST_SALT. Never per-run. */
|
|
3179
|
+
envSalt?: string;
|
|
3180
|
+
screenIdentityHash: string;
|
|
3181
|
+
/** Accepted for caller ergonomics but intentionally never a key component (D-14). */
|
|
3182
|
+
branch?: string;
|
|
3183
|
+
}
|
|
3184
|
+
/**
|
|
3185
|
+
* Assemble the schema kernel's 8-field CacheableStepInput. `mask()` is applied to
|
|
3186
|
+
* stepText BEFORE it enters the tuple (the MASK-FUT-01 floor, D-05); the env digest
|
|
3187
|
+
* folds only the value-free redacted shapes under the stable salt. The caller passes
|
|
3188
|
+
* the result to computeCacheKey to obtain the 16-hex CacheKey / filename (D-06).
|
|
3189
|
+
*/
|
|
3190
|
+
declare function buildCacheableStepInput(args: BuildCacheableStepInputArgs): CacheableStepInput;
|
|
3191
|
+
|
|
3192
|
+
interface CacheableClassification {
|
|
3193
|
+
cacheable: true;
|
|
3194
|
+
/** Value-free config-env shapes this step depends on (folded into the digest). */
|
|
3195
|
+
envVars: RedactedEnvVar[];
|
|
3196
|
+
}
|
|
3197
|
+
interface ClassifyStepInput {
|
|
3198
|
+
stepText: string;
|
|
3199
|
+
/** Variable names this step sets — the onSetVariable / setVariable provenance. */
|
|
3200
|
+
envSets?: readonly string[];
|
|
3201
|
+
/** Config-env values this cacheable step reads (value-free shapes). */
|
|
3202
|
+
configEnv?: readonly RedactedEnvVar[];
|
|
3203
|
+
}
|
|
3204
|
+
/**
|
|
3205
|
+
* Classify a step by resolution provenance (D-07). Env/secret read-or-set steps
|
|
3206
|
+
* return NonCacheableStepInput; a config-env-dependent step returns a
|
|
3207
|
+
* CacheableClassification carrying the value-free RedactedEnvVar shapes.
|
|
3208
|
+
*/
|
|
3209
|
+
declare function classifyStep(input: ClassifyStepInput): CacheableClassification | NonCacheableStepInput;
|
|
3210
|
+
|
|
3211
|
+
/**
|
|
3212
|
+
* Thrown by `subscribe(fromSeq, …)` when the requested range is neither in the in-memory
|
|
3213
|
+
* window nor recoverable from the (possibly degraded) event log (D-07 — report incomplete,
|
|
3214
|
+
* never silently serve a truncated stream). `earliestAvailableSeq` is the lowest seq the bus
|
|
3215
|
+
* can still replay (the SSE `Last-Event-ID` re-subscribe floor in M5).
|
|
3216
|
+
*/
|
|
3217
|
+
declare class ReplayIncompleteError extends Error {
|
|
3218
|
+
readonly earliestAvailableSeq: number;
|
|
3219
|
+
constructor(requestedSeq: number, earliestAvailableSeq: number);
|
|
3220
|
+
}
|
|
3221
|
+
/** A live/replay subscriber. Synchronous — replay-then-live per-subscriber order is total. */
|
|
3222
|
+
type RunEventHandler = (event: RunEventEnvelope) => void;
|
|
3223
|
+
/**
|
|
3224
|
+
* The bus surface. `publish` stamps `seq`/`ts` and returns the sealed envelope (the shell reads
|
|
3225
|
+
* `.seq` for `runFinished.finalSeq`); `subscribe` replays then goes live; `close` stops the
|
|
3226
|
+
* heartbeat (publish-after-close is a programming error).
|
|
3227
|
+
*/
|
|
3228
|
+
interface RunEventBus {
|
|
3229
|
+
publish(type: RunEventType, payload: RunEventPayload | Record<string, unknown>): RunEventEnvelope;
|
|
3230
|
+
subscribe(fromSeq: number | undefined, handler: RunEventHandler): () => void;
|
|
3231
|
+
readonly currentSeq: number;
|
|
3232
|
+
close(): void;
|
|
3233
|
+
}
|
|
3234
|
+
interface CreateRunEventBusOptions {
|
|
3235
|
+
runId: string;
|
|
3236
|
+
eventLog?: EventLogPort;
|
|
3237
|
+
timer?: RunTimerPort;
|
|
3238
|
+
heartbeatMs?: number;
|
|
3239
|
+
windowMaxEvents?: number;
|
|
3240
|
+
windowMaxBytes?: number;
|
|
3241
|
+
}
|
|
3242
|
+
declare function createRunEventBus(opts: CreateRunEventBusOptions): RunEventBus;
|
|
3243
|
+
|
|
3244
|
+
/** How long a handle's stream keeps running after its LAST viewer detaches (403 D-07). */
|
|
3245
|
+
declare const LIVE_STREAM_STOP_GRACE_MS = 2000;
|
|
3246
|
+
/** How long the hub waits for every subscriber to ack a frame before acking on their behalf. */
|
|
3247
|
+
declare const LIVE_STREAM_ACK_BOUND_MS = 250;
|
|
3248
|
+
/**
|
|
3249
|
+
* Default timer: the `bus.ts` idiom — unref'd so a pending grace never holds the process open.
|
|
3250
|
+
* Exported so the snapshot producer shares it rather than copying it (428 D-11).
|
|
3251
|
+
*/
|
|
3252
|
+
declare function defaultLiveStreamTimer(): RunTimerPort;
|
|
3253
|
+
interface CreateLiveStreamHubOptions {
|
|
3254
|
+
timer?: RunTimerPort;
|
|
3255
|
+
graceMs?: number;
|
|
3256
|
+
ackBoundMs?: number;
|
|
3257
|
+
/**
|
|
3258
|
+
* Facts about the RUN's producer. The shell wires this to the run's durable event log, so what
|
|
3259
|
+
* lands here is replayed to every later reader of the run.
|
|
3260
|
+
*/
|
|
3261
|
+
onWarning?: (code: string, message: string) => void;
|
|
3262
|
+
/**
|
|
3263
|
+
* Where a SUBSCRIBER's fault is reported (403 WR-06). Deliberately a DIFFERENT sink than
|
|
3264
|
+
* `onWarning`, and the difference is the whole fix: a viewer that throws — or whose socket died
|
|
3265
|
+
* between a `readyState` check and the write — is a fact about THAT VIEWER, and the run's
|
|
3266
|
+
* permanent record must not depend on who happened to be watching (LIVE-03). Absent by default,
|
|
3267
|
+
* so a host that does not care reports nothing anywhere.
|
|
3268
|
+
*/
|
|
3269
|
+
onSubscriberFault?: (code: string, message: string) => void;
|
|
3270
|
+
}
|
|
3271
|
+
/**
|
|
3272
|
+
* The run's frame fan-out. `register` is the PRODUCER side (the member executor hands its adapter's
|
|
3273
|
+
* capability in, and hands `undefined` in when the member finishes); `subscribe`/`capabilityOf` are
|
|
3274
|
+
* the CONSUMER side the `RunSession` exposes.
|
|
3275
|
+
*/
|
|
3276
|
+
interface LiveStreamHub {
|
|
3277
|
+
/**
|
|
3278
|
+
* `fallback` is the labelled snapshot producer this handle switches to when `capability` fails
|
|
3279
|
+
* to start (428 D-12). The failure is still recorded; `capabilityOf` keeps the declared kind.
|
|
3280
|
+
*/
|
|
3281
|
+
register(handle: number, capability: LiveStreamCapability | undefined, fallback?: LiveStreamCapability): void;
|
|
3282
|
+
subscribe(handle: number, onFrame: (frame: LiveFrame) => void, onEnd?: (reason: LiveFrameEndReason) => void): LiveFrameSubscription | undefined;
|
|
3283
|
+
capabilityOf(handle: number): LiveStreamDeclaration;
|
|
3284
|
+
close(): Promise<void>;
|
|
3285
|
+
}
|
|
3286
|
+
declare function createLiveStreamHub(opts?: CreateLiveStreamHubOptions): LiveStreamHub;
|
|
3287
|
+
|
|
3288
|
+
/**
|
|
3289
|
+
* The three live-preview cadences, side by side: a snapshot every second (this producer), a
|
|
3290
|
+
* keepalive frame after two quiet seconds on a static page (the web adapter's screencast, 428-06),
|
|
3291
|
+
* and the viewer's ~5s freshness window (428-14) that both of them stay inside.
|
|
3292
|
+
*/
|
|
3293
|
+
declare const LIVE_SNAPSHOT_INTERVAL_MS = 1000;
|
|
3294
|
+
declare const LIVE_KEEPALIVE_QUIET_MS = 2000;
|
|
3295
|
+
declare function createSnapshotLiveStream(input: {
|
|
3296
|
+
capture: () => Promise<Uint8Array | undefined>;
|
|
3297
|
+
timer?: RunTimerPort;
|
|
3298
|
+
intervalMs?: number;
|
|
3299
|
+
}): LiveStreamCapability;
|
|
3300
|
+
/** What a run or an editor session registers with its hub: a stream, and what replaces it on a failed start. */
|
|
3301
|
+
type LiveStreamRegistration = {
|
|
3302
|
+
capability: LiveStreamCapability;
|
|
3303
|
+
fallback?: LiveStreamCapability;
|
|
3304
|
+
};
|
|
3305
|
+
/**
|
|
3306
|
+
* THE ONE SELECTION RULE (428 D-11/D-12), used by both run paths and the editor.
|
|
3307
|
+
*
|
|
3308
|
+
* The adapter's own `liveStream` always wins. `screenshot()` is wrapped in the snapshot producer
|
|
3309
|
+
* only when the adapter's own capability is not already a snapshot: as the fallback behind a
|
|
3310
|
+
* continuous stream that may fail to start, or as the whole preview when there is no continuous
|
|
3311
|
+
* stream. An adapter whose own stream IS a snapshot (mobile) gets no fallback, which is what keeps
|
|
3312
|
+
* a mobile run off its context-switching `screenshot()`.
|
|
3313
|
+
*/
|
|
3314
|
+
declare function liveStreamFor(adapter: Pick<PlatformAdapter, 'liveStream' | 'screenshot'> | undefined, options?: {
|
|
3315
|
+
timer?: RunTimerPort;
|
|
3316
|
+
}): LiveStreamRegistration | undefined;
|
|
3317
|
+
/**
|
|
3318
|
+
* Width and height from a PNG IHDR or a JPEG SOF header, so a snapshot frame has a size before
|
|
3319
|
+
* the viewer decodes it. Anything it cannot read — another format, a truncated header — answers
|
|
3320
|
+
* 0 by 0, and every read is bounded by the byte length.
|
|
3321
|
+
*/
|
|
3322
|
+
declare function imageSizeOf(bytes: Uint8Array): {
|
|
3323
|
+
width: number;
|
|
3324
|
+
height: number;
|
|
3325
|
+
};
|
|
3326
|
+
|
|
3327
|
+
/** The explicit handle carries its own capability; no ambient current-step registry exists. */
|
|
3328
|
+
declare function withStepModelCall<T>(model: LanguageModel, call: (model: LanguageModel) => Promise<T>): Promise<{
|
|
3329
|
+
result: T;
|
|
3330
|
+
scoped: boolean;
|
|
3331
|
+
respondingModelId?: string;
|
|
3332
|
+
}>;
|
|
3333
|
+
declare function createStepModelScope(options: StepModelScopeOptions): StepModelScope;
|
|
3334
|
+
|
|
3335
|
+
declare function parseEnvFile(content: string): Record<string, string>;
|
|
3336
|
+
declare function serializeEnvFile(vars: Record<string, string>): string;
|
|
3337
|
+
|
|
3338
|
+
declare const AUTH_STATE_HOOK_JSON_ENV = "AGENT_QA_AUTH_STATE_JSON";
|
|
3339
|
+
declare const AUTH_STATE_HOOK_STORAGE_STATE_PATH_ENV = "AGENT_QA_AUTH_STATE_STORAGE_STATE_PATH";
|
|
3340
|
+
declare const AUTH_STATE_HOOK_WORKSPACE_DIR = ".agent-qa-auth-state";
|
|
3341
|
+
declare const AUTH_STATE_HOOK_STORAGE_STATE_FILENAME = "storage-state.json";
|
|
3342
|
+
declare const AUTH_STATE_HOOK_CONTAINER_STORAGE_STATE_PATH = "/workspace/.agent-qa-auth-state/storage-state.json";
|
|
3343
|
+
declare function isReservedAuthStateHookEnvKey(key: string): boolean;
|
|
3344
|
+
declare function stripReservedAuthStateHookEnv(variables: Record<string, string>): Record<string, string>;
|
|
3345
|
+
declare function buildAuthStateHookEnv(authState: RuntimeAuthStateConfig | undefined, storageStatePath?: string): Record<string, string>;
|
|
3346
|
+
|
|
3347
|
+
interface SecretTemplate {
|
|
3348
|
+
pattern: string;
|
|
3349
|
+
name: string;
|
|
3350
|
+
}
|
|
3351
|
+
interface SecretFileMetadata {
|
|
3352
|
+
count: number;
|
|
3353
|
+
}
|
|
3354
|
+
declare class SecretConfigError extends Error {
|
|
3355
|
+
constructor(message: string);
|
|
3356
|
+
}
|
|
3357
|
+
declare class MissingSecretError extends Error {
|
|
3358
|
+
constructor(name: string);
|
|
3359
|
+
}
|
|
3360
|
+
declare class SecretStore implements SecretResolver {
|
|
3361
|
+
private storage;
|
|
3362
|
+
constructor(secrets?: Record<string, string> | ReadonlyArray<readonly [string, string]>);
|
|
3363
|
+
static empty(): SecretStore;
|
|
3364
|
+
static fromEnvContent(content: string): SecretStore;
|
|
3365
|
+
private set;
|
|
3366
|
+
get(name: string): string | undefined;
|
|
3367
|
+
require(name: string): string;
|
|
3368
|
+
has(name: string): boolean;
|
|
3369
|
+
count(): number;
|
|
3370
|
+
metadata(): SecretFileMetadata;
|
|
3371
|
+
forEachSecret(callback: (name: string, value: string) => void): void;
|
|
3372
|
+
}
|
|
3373
|
+
declare function findSecretTemplates(text: string): SecretTemplate[];
|
|
3374
|
+
declare function interpolateSecretTemplates(template: string, store: SecretStore): string;
|
|
3375
|
+
declare function resolveSecretTemplatesInValue<T>(value: T, store?: SecretResolver): T;
|
|
3376
|
+
declare function redactSecretValue<T>(value: T, redactor?: SecretRedactorPort): T;
|
|
3377
|
+
/**
|
|
3378
|
+
* The cached-plan invalidation predicate (loop cache-poisoning guard): returns true
|
|
3379
|
+
* when a value contains a redacted `[secret:*]` marker. Moved from core loop.ts.
|
|
3380
|
+
*/
|
|
3381
|
+
declare function containsRedactedSecretMarker(value: unknown): boolean;
|
|
3382
|
+
declare function literalSecretMarkerRefusal(value: unknown): string | undefined;
|
|
3383
|
+
declare class SecretRedactor implements SecretRedactorPort {
|
|
3384
|
+
private readonly store;
|
|
3385
|
+
private readonly pattern;
|
|
3386
|
+
private readonly valuePattern;
|
|
3387
|
+
private readonly nameByValue;
|
|
3388
|
+
constructor(store: SecretStore);
|
|
3389
|
+
redactString(value: string): string;
|
|
3390
|
+
private maskValues;
|
|
3391
|
+
redactValue(value: unknown): unknown;
|
|
3392
|
+
}
|
|
3393
|
+
|
|
3394
|
+
/** Structural secret source (SecretStore satisfies it) — used to resolve container secretEnv. */
|
|
3395
|
+
interface SecretEnvSource {
|
|
3396
|
+
forEachSecret(callback: (name: string, value: string) => void): void;
|
|
3397
|
+
}
|
|
3398
|
+
/**
|
|
3399
|
+
* Resolve the already-materialized secret name→value map the container is injected with
|
|
3400
|
+
* (D-06). The runner-owned {@link SecretStore} satisfies the structural source; the port only
|
|
3401
|
+
* ever sees the resolved map, never the store itself.
|
|
3402
|
+
*/
|
|
3403
|
+
declare function resolveSecretEnv(store?: SecretEnvSource): Record<string, string>;
|
|
3404
|
+
/**
|
|
3405
|
+
* The threaded hook-execution carrier (Phase 363 D-06) — successor to core's
|
|
3406
|
+
* `SandboxRunnerOptions`. Threaded through the run/suite config as `sandboxOptions` and
|
|
3407
|
+
* spread into {@link RunHooksOptions} at each hook site. Carries the injected
|
|
3408
|
+
* {@link HookExecutorPort} (constructed once at the composition root) plus the secret
|
|
3409
|
+
* store / redactor / active auth-state the hook body needs. `dockerBin`/`pullPolicy` are
|
|
3410
|
+
* retained for source-compat with the old carrier (the executor owns those now).
|
|
3411
|
+
*/
|
|
3412
|
+
interface SandboxRunnerOptions {
|
|
3413
|
+
executor: HookExecutorPort;
|
|
3414
|
+
dockerBin?: string;
|
|
3415
|
+
pullPolicy?: 'always' | 'if-not-present' | 'never';
|
|
3416
|
+
envVars?: Record<string, string>;
|
|
3417
|
+
secretStore?: SecretStore;
|
|
3418
|
+
secretRedactor?: SecretRedactorPort;
|
|
3419
|
+
authState?: RuntimeAuthStateConfig;
|
|
3420
|
+
}
|
|
3421
|
+
interface HookOrchestrationResult {
|
|
3422
|
+
results: Map<string, HookResult>;
|
|
3423
|
+
variables: Record<string, string>;
|
|
3424
|
+
allPassed: boolean;
|
|
3425
|
+
duration: number;
|
|
3426
|
+
}
|
|
3427
|
+
/**
|
|
3428
|
+
* The narrow lifecycle-emit seam `runHooks` fires through — the Plan 03 sink legs
|
|
3429
|
+
* (`MemberEventSink.hookStarted`/`hookFinished`) satisfy it structurally. Both legs are
|
|
3430
|
+
* fire-and-forget; the orchestration NEVER awaits telemetry. Output detail is emitted
|
|
3431
|
+
* as-is (already sandbox-redacted by the impl); the sink applies the additive run-mask
|
|
3432
|
+
* (T-363-05) — the orchestration does not mask.
|
|
3433
|
+
*/
|
|
3434
|
+
interface HookEmitSeam {
|
|
3435
|
+
hookStarted(payload: HookStartedPayload): void;
|
|
3436
|
+
hookFinished(payload: HookFinishedPayload): void;
|
|
3437
|
+
}
|
|
3438
|
+
/** Identity fields locating this hook batch in the run tree (payload provenance). */
|
|
3439
|
+
interface HookRunIdentity {
|
|
3440
|
+
phase: HookStartedPayload['phase'];
|
|
3441
|
+
scope?: HookStartedPayload['scope'];
|
|
3442
|
+
testIndex?: number;
|
|
3443
|
+
stepIndex?: number;
|
|
3444
|
+
}
|
|
3445
|
+
interface RunHooksOptions {
|
|
3446
|
+
/** The injected D-06 executor (runner-local `DockerHookExecutor`). */
|
|
3447
|
+
executor: HookExecutorPort;
|
|
3448
|
+
/** User env + CLI/suite variables the first hook starts from. */
|
|
3449
|
+
env?: Record<string, string>;
|
|
3450
|
+
/** Carrier-compat alias for {@link env} (the old `SandboxRunnerOptions.envVars` field). */
|
|
3451
|
+
envVars?: Record<string, string>;
|
|
3452
|
+
/** Already-resolved secret name→value pairs for container injection (D-06). */
|
|
3453
|
+
secretEnv?: Record<string, string>;
|
|
3454
|
+
/** Runner-owned secret store; the orchestrator resolves {@link secretEnv} from it. */
|
|
3455
|
+
secretStore?: SecretEnvSource;
|
|
3456
|
+
/** Structural secret redactor for stdout/stderr/error redaction. */
|
|
3457
|
+
secretRedactor?: SecretRedactorPort;
|
|
3458
|
+
/** Active auth-state mounted + injected per hook. */
|
|
3459
|
+
authState?: RuntimeAuthStateConfig;
|
|
3460
|
+
/** Tolerated carrier fields (the executor owns docker config now; ignored by orchestration). */
|
|
3461
|
+
dockerBin?: string;
|
|
3462
|
+
pullPolicy?: 'always' | 'if-not-present' | 'never';
|
|
3463
|
+
/** Optional lifecycle emit seam; requires {@link identity} to emit. */
|
|
3464
|
+
emit?: HookEmitSeam;
|
|
3465
|
+
/** Payload provenance; when present alongside {@link emit}, every executed hook emits. */
|
|
3466
|
+
identity?: HookRunIdentity;
|
|
3467
|
+
}
|
|
3468
|
+
declare function runHooks(hooks: HookDefinition[], options: RunHooksOptions): Promise<HookOrchestrationResult>;
|
|
3469
|
+
|
|
3470
|
+
/**
|
|
3471
|
+
* Start a run (D-01). Constructs the session SYNCHRONOUSLY — the bus, `runStarted` at seq 1, the
|
|
3472
|
+
* run-budget arm, the run-level AbortController — then kicks the async member pool whose settled
|
|
3473
|
+
* result feeds `done`. Returns the addressable {@link RunSession} immediately.
|
|
3474
|
+
*
|
|
3475
|
+
* 402-02 (D-09): members execute through a BOUNDED CONCURRENT POOL rather than a `for` loop, but
|
|
3476
|
+
* everything after it is unchanged — the terminal sequence is still ONE code path, run once, after
|
|
3477
|
+
* the pool settles. Concurrency changes which member is in flight when; it does not change who
|
|
3478
|
+
* publishes the verdict, or when `runFinished` is last.
|
|
3479
|
+
*/
|
|
3480
|
+
declare function startRun(config: RunConfig): RunSession;
|
|
3481
|
+
|
|
3482
|
+
/**
|
|
3483
|
+
* Build the per-member {@link MemberEventSink} the shell hands to the executor context (D-02).
|
|
3484
|
+
* `testIndex` is bound here; `stepIndex` is curried by `loopEmitFor`. `mask` is the run's mask
|
|
3485
|
+
* (441-12, D-06). All three payload kinds go through the schema projections + mask.
|
|
3486
|
+
*/
|
|
3487
|
+
declare function createMemberEventSink(deps: {
|
|
3488
|
+
bus: RunEventBus;
|
|
3489
|
+
mask: MaskFn;
|
|
3490
|
+
testIndex: number;
|
|
3491
|
+
}): MemberEventSink;
|
|
3492
|
+
|
|
3493
|
+
/** A single member-test step — a bare instruction or an object carrying per-step knobs. */
|
|
3494
|
+
interface MemberStepSpec {
|
|
3495
|
+
step: string;
|
|
3496
|
+
timeout?: number;
|
|
3497
|
+
capture?: {
|
|
3498
|
+
variable: string;
|
|
3499
|
+
method?: ExtractionMethod;
|
|
3500
|
+
pattern?: string;
|
|
3501
|
+
selector?: string;
|
|
3502
|
+
description?: string;
|
|
3503
|
+
};
|
|
3504
|
+
}
|
|
3505
|
+
/**
|
|
3506
|
+
* The one derivation of the text a step is announced with at `stepStarted` (G-428-2). Every
|
|
3507
|
+
* producer that states a step's text before it starts (the CLI's `stepTexts` on each member
|
|
3508
|
+
* descriptor) derives it here, so the pending text and the announced text cannot drift.
|
|
3509
|
+
*/
|
|
3510
|
+
declare function memberStepText(step: string | MemberStepSpec): string;
|
|
3511
|
+
/** The runner-owned member-test shape (a core-independent subset of TestDefinition). */
|
|
3512
|
+
interface MemberTest {
|
|
3513
|
+
testId: string;
|
|
3514
|
+
name: string;
|
|
3515
|
+
filePath: string;
|
|
3516
|
+
url?: string;
|
|
3517
|
+
context?: string;
|
|
3518
|
+
steps: Array<string | MemberStepSpec>;
|
|
3519
|
+
meta?: {
|
|
3520
|
+
timeout?: number;
|
|
3521
|
+
retries?: number;
|
|
3522
|
+
[key: string]: unknown;
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
/** The injected runJS execution handler (Q-2). The CLI owns page.evaluate + platform checks. */
|
|
3526
|
+
type RunJsHandler = (code: string, ctx: {
|
|
3527
|
+
abortSignal: AbortSignal;
|
|
3528
|
+
}) => Promise<{
|
|
3529
|
+
value: string;
|
|
3530
|
+
error?: string;
|
|
3531
|
+
}>;
|
|
3532
|
+
/** The injected appium execution handler (Q-2). The CLI resolves secrets + calls the adapter. */
|
|
3533
|
+
type AppiumScriptHandler = (action: Action, ctx: {
|
|
3534
|
+
abortSignal: AbortSignal;
|
|
3535
|
+
}) => Promise<ActionResult>;
|
|
3536
|
+
/** The injected accessibility-check handler (Q-4). The CLI owns page acquisition + the web module. */
|
|
3537
|
+
type AccessibilityCheckHandler = (opts: {
|
|
3538
|
+
standard?: 'wcag2a' | 'wcag2aa' | 'wcag2aaa';
|
|
3539
|
+
disableRules?: string[];
|
|
3540
|
+
exclude?: string[];
|
|
3541
|
+
}, ctx: {
|
|
3542
|
+
abortSignal: AbortSignal;
|
|
3543
|
+
}) => Promise<AccessibilityViolation[]>;
|
|
3544
|
+
/** The injected failure-screenshot writer. The CLI owns the adapter screenshot capture. */
|
|
3545
|
+
type FailureScreenshotWriter = (ctx: {
|
|
3546
|
+
abortSignal: AbortSignal;
|
|
3547
|
+
}) => Promise<Buffer | undefined>;
|
|
3548
|
+
/**
|
|
3549
|
+
* Everything runMemberTest needs, all injected at the CLI composition root. The sink,
|
|
3550
|
+
* abortSignal, and testIndex are the per-member context the shell binds (so runMemberTest
|
|
3551
|
+
* slots cleanly behind {@link MemberTestExecutor}); the rest are the run-scoped host impls
|
|
3552
|
+
* the CLI already builds for the loop.
|
|
3553
|
+
*/
|
|
3554
|
+
interface MemberRunConfig {
|
|
3555
|
+
adapter: PlatformAdapter;
|
|
3556
|
+
planner: Planner;
|
|
3557
|
+
verifier?: Verifier;
|
|
3558
|
+
cache?: ActionPlanCache;
|
|
3559
|
+
modelProfiles?: {
|
|
3560
|
+
planner: DeclaredModelProfile;
|
|
3561
|
+
verifier?: DeclaredModelProfile;
|
|
3562
|
+
};
|
|
3563
|
+
onEvidenceContext?: (context: MemberEvidenceContext) => void;
|
|
3564
|
+
workingMemory?: WorkingMemoryPort;
|
|
3565
|
+
storageState?: StorageStatePort;
|
|
3566
|
+
compressor?: ScreenshotCompressor;
|
|
3567
|
+
knowledgeReader?: KnowledgeHintReader;
|
|
3568
|
+
knowledgeBudgets?: {
|
|
3569
|
+
brief: number;
|
|
3570
|
+
searchResults: number;
|
|
3571
|
+
caution: number;
|
|
3572
|
+
readBody: number;
|
|
3573
|
+
recordCap: number;
|
|
3574
|
+
};
|
|
3575
|
+
knowledgeAsOfBuild?: number;
|
|
3576
|
+
/**
|
|
3577
|
+
* 407 D-13 — the host's retrieval rung, resolved once at the composition root and threaded
|
|
3578
|
+
* verbatim onto every step's LoopPorts. Typed off LoopPorts so there is ONE spelling of the
|
|
3579
|
+
* contract between the run shell and the loop.
|
|
3580
|
+
*/
|
|
3581
|
+
knowledgeRetrievalRung?: LoopPorts['knowledgeRetrievalRung'];
|
|
3582
|
+
recordLedger?: LoopPorts['recordLedger'];
|
|
3583
|
+
beginMemoryTurn?: LoopPorts['beginMemoryTurn'];
|
|
3584
|
+
drainMemoryReadTraces?: LoopPorts['drainMemoryReadTraces'];
|
|
3585
|
+
uniqueValueGenerator?: UniqueValueGenerator;
|
|
3586
|
+
guardBlob?: (value: unknown) => string | undefined;
|
|
3587
|
+
configDigests?: ConfigDigests;
|
|
3588
|
+
evidenceArtifacts?: EvidenceArtifactStorePort;
|
|
3589
|
+
plannerModel: LanguageModel;
|
|
3590
|
+
verifierModel: LanguageModel;
|
|
3591
|
+
/** The host closes over resolved model/accounting configuration, never a current-step pointer. */
|
|
3592
|
+
stepModelScope?: {
|
|
3593
|
+
runId: StepModelScopeKey['runId'];
|
|
3594
|
+
create: StepModelScopeFactory;
|
|
3595
|
+
};
|
|
3596
|
+
envFileVars?: Record<string, string>;
|
|
3597
|
+
inlineVars?: Record<string, string>;
|
|
3598
|
+
suiteVars?: Record<string, string>;
|
|
3599
|
+
cliVars?: Record<string, string>;
|
|
3600
|
+
hookSetupVars?: Record<string, string>;
|
|
3601
|
+
hookExecutor?: HookExecutorPort;
|
|
3602
|
+
inlineHookDefs?: Map<string, HookDefinition>;
|
|
3603
|
+
hookEnvVars?: Record<string, string>;
|
|
3604
|
+
hookSecretEnv?: Record<string, string>;
|
|
3605
|
+
hookAuthState?: RuntimeAuthStateConfig;
|
|
3606
|
+
runJs?: RunJsHandler;
|
|
3607
|
+
appiumScript?: AppiumScriptHandler;
|
|
3608
|
+
accessibilityCheck?: AccessibilityCheckHandler;
|
|
3609
|
+
persistFailureScreenshot?: FailureScreenshotWriter;
|
|
3610
|
+
resolveSecrets?: (text: string) => string;
|
|
3611
|
+
secretStore?: SecretResolver;
|
|
3612
|
+
secretRedactor?: SecretRedactorPort;
|
|
3613
|
+
sink: MemberEventSink;
|
|
3614
|
+
testIndex: number;
|
|
3615
|
+
abortSignal: AbortSignal;
|
|
3616
|
+
captureScreenshots?: boolean;
|
|
3617
|
+
recording?: {
|
|
3618
|
+
enabled: boolean;
|
|
3619
|
+
keepFailuresOnly?: boolean;
|
|
3620
|
+
};
|
|
3621
|
+
accessibility?: {
|
|
3622
|
+
enabled: boolean;
|
|
3623
|
+
standard?: 'wcag2a' | 'wcag2aa' | 'wcag2aaa';
|
|
3624
|
+
runAfter?: 'every-step' | 'navigation' | 'test-end';
|
|
3625
|
+
failOnViolation?: boolean;
|
|
3626
|
+
disableRules?: string[];
|
|
3627
|
+
exclude?: string[];
|
|
3628
|
+
};
|
|
3629
|
+
timeouts?: {
|
|
3630
|
+
step?: number;
|
|
3631
|
+
test?: number;
|
|
3632
|
+
navigation?: number;
|
|
3633
|
+
};
|
|
3634
|
+
plannerConfig?: PlannerConfig;
|
|
3635
|
+
logger?: LoopLogger;
|
|
3636
|
+
logCapture?: {
|
|
3637
|
+
console?: boolean;
|
|
3638
|
+
network?: boolean;
|
|
3639
|
+
};
|
|
3640
|
+
vision?: {
|
|
3641
|
+
screenshotSize?: number;
|
|
3642
|
+
effectiveResolution?: number;
|
|
3643
|
+
};
|
|
3644
|
+
visionGate?: {
|
|
3645
|
+
attachImage: boolean;
|
|
3646
|
+
captureMode: 'full' | 'textOnly';
|
|
3647
|
+
promptNote: string;
|
|
3648
|
+
};
|
|
3649
|
+
contextWindow?: number;
|
|
3650
|
+
suiteContext?: string;
|
|
3651
|
+
resolveUploadPath?: (rawPath: string) => string;
|
|
3652
|
+
onWarn?: (message: string) => void;
|
|
3653
|
+
executeStep?: typeof executeStep;
|
|
3654
|
+
extractorFactory?: (model: LanguageModel | undefined, method: string) => VariableExtractor;
|
|
3655
|
+
}
|
|
3656
|
+
/**
|
|
3657
|
+
* Run a member test with the retry loop (D-13). The trigger stays `meta.retries`
|
|
3658
|
+
* (user-config surface unchanged). Attempts run INSIDE the member; `testRetryScheduled`
|
|
3659
|
+
* fires between attempts (a per-test accumulation reset for subscribers); only the FINAL
|
|
3660
|
+
* attempt's TestResult leaves the member carrying `retryCount` (Pitfall 9 — the shell
|
|
3661
|
+
* emits the single authoritative testStarted/testFinished pair per member).
|
|
3662
|
+
*/
|
|
3663
|
+
declare function runMemberTest(member: MemberTest, config: MemberRunConfig): Promise<TestResult>;
|
|
3664
|
+
|
|
3665
|
+
declare function isCanonicalHookIdShape(value: string): boolean;
|
|
3666
|
+
interface InlineHookCall {
|
|
3667
|
+
hookId: string;
|
|
3668
|
+
fullMatch: string;
|
|
3669
|
+
}
|
|
3670
|
+
declare function parseHookInline(text: string): InlineHookCall[];
|
|
3671
|
+
declare function stripHookInline(text: string): string;
|
|
3672
|
+
interface RunJSMatch {
|
|
3673
|
+
fullMatch: string;
|
|
3674
|
+
code: string;
|
|
3675
|
+
}
|
|
3676
|
+
declare function parseRunJSInline(text: string): RunJSMatch[];
|
|
3677
|
+
declare function coerceRunJSResult(value: unknown): string;
|
|
3678
|
+
declare function parseAppiumInline(text: string): Action | null;
|
|
3679
|
+
|
|
3680
|
+
declare function generateFailureSummary(steps: StepResult[]): string;
|
|
3681
|
+
|
|
3682
|
+
/**
|
|
3683
|
+
* Fold one completed member {@link TestResult} + its derived {@link MemberTestOutcome} into a
|
|
3684
|
+
* {@link PerTestOutcome}. Steps are built from `result.steps`: a step with §A8 evidence carries it
|
|
3685
|
+
* (with `stepIndex` OVERRIDDEN to its array position); an evidence-less step gets a synthesized
|
|
3686
|
+
* minimal StepEvidence. `testIndex` is the shell-owned array position.
|
|
3687
|
+
*
|
|
3688
|
+
* `rootCause` is the shell-classified §A9 verdict (373-07): attached ONLY when defined so clean
|
|
3689
|
+
* passes/skipped/cancelled members stay rootCause-free in the persisted JSON (the D-01 presence
|
|
3690
|
+
* rule — the key is absent, never undefined-valued).
|
|
3691
|
+
*
|
|
3692
|
+
* `deps.quarantined` is the 390 D-14 disposition, stamped BESIDE `outcome` under the same
|
|
3693
|
+
* presence rule. It never alters `outcome`: a quarantined failing member reads `'failed'` here, and
|
|
3694
|
+
* the licence to ignore that failure lives entirely in the aggregate rule and in this marker.
|
|
3695
|
+
*/
|
|
3696
|
+
declare function assemblePerTestOutcome(member: RunMemberDescriptor, result: TestResult, outcome: MemberTestOutcome, deps: {
|
|
3697
|
+
testIndex: number;
|
|
3698
|
+
scope: ScopeKey;
|
|
3699
|
+
buildOrdinal: number;
|
|
3700
|
+
mask: MaskFn;
|
|
3701
|
+
modelProfile?: DeclaredModelProfile;
|
|
3702
|
+
verifierIdentity?: ModelIdentity;
|
|
3703
|
+
toolSchemaDigest?: string;
|
|
3704
|
+
quarantined?: true;
|
|
3705
|
+
}, rootCause?: Verdict, origin?: {
|
|
3706
|
+
runId: string;
|
|
3707
|
+
outcome: 'failed' | 'timeout';
|
|
3708
|
+
rootCause?: Verdict;
|
|
3709
|
+
}): PerTestOutcome;
|
|
3710
|
+
/**
|
|
3711
|
+
* The shell-produced skip outcome (D-15): a member the shell never invoked (run aborted, or a
|
|
3712
|
+
* poisoned same-group member). `outcome: 'skipped'`, no steps — neutral in the aggregate fold.
|
|
3713
|
+
*
|
|
3714
|
+
* `quarantined` is still stamped when the ledger licensed this member (390 D-14): the disposition
|
|
3715
|
+
* describes the TEST's standing, not the outcome it happened to reach, so a reader of the handoff
|
|
3716
|
+
* sees the same truth whether the member ran or was skipped past.
|
|
3717
|
+
*/
|
|
3718
|
+
declare function skippedPerTestOutcome(member: RunMemberDescriptor, testIndex: number, quarantined?: true): PerTestOutcome;
|
|
3719
|
+
/**
|
|
3720
|
+
* Assemble the one {@link CompletedRunHandoff} a run persists (D-17). Every header field is copied
|
|
3721
|
+
* verbatim FROM the resolved {@link RunConfig} (D-04/D-20 — never re-derived); `aggregateVerdict`
|
|
3722
|
+
* is the execution verdict; `tests` are the assembled per-member outcomes in run order.
|
|
3723
|
+
*
|
|
3724
|
+
* 390 D-16: `quarantineApplied` records the ledger entries this run was operating under, copied
|
|
3725
|
+
* from the same resolved config the disposition markers were matched against — so the run's own
|
|
3726
|
+
* receipt answers "which suppressions were licensed here, and until when" without re-reading a
|
|
3727
|
+
* ledger that may since have changed. An empty set stays ABSENT, keeping every unquarantined run's
|
|
3728
|
+
* handoff bytes identical to pre-390.
|
|
3729
|
+
*/
|
|
3730
|
+
declare function assembleHandoff(config: RunConfig, outcomes: PerTestOutcome[], aggregateVerdict: RunStatus, timing: {
|
|
3731
|
+
startedAt: number;
|
|
3732
|
+
endedAt: number;
|
|
3733
|
+
}): CompletedRunHandoff;
|
|
3734
|
+
|
|
3735
|
+
/**
|
|
3736
|
+
* Derive the member-level outcome for one completed test (D-16). Pure total function.
|
|
3737
|
+
*
|
|
3738
|
+
* - status `skipped` ⇒ `'skipped'` (member-neutral; passed through)
|
|
3739
|
+
* - status `cancelled` ⇒ `'cancelled'`
|
|
3740
|
+
* - status `passed` ⇒ `'healed'` when ANY step carries selfHeal evidence, else `'passed'`.
|
|
3741
|
+
* The live heal signal is StepEvidence.selfHeal *presence* — a step-status read would
|
|
3742
|
+
* never fire (RESEARCH Pitfall 5), so we never inspect a step status string here.
|
|
3743
|
+
* - status `failed` ⇒ `'timeout'` when ANY failed step's `error` is the test-deadline
|
|
3744
|
+
* message (isTestTimeoutMessage), else `'failed'`.
|
|
3745
|
+
*
|
|
3746
|
+
* Core's TestStatus and the executor port stay untouched by this derivation (D-16).
|
|
3747
|
+
*/
|
|
3748
|
+
declare function deriveMemberOutcome(result: TestResult): MemberTestOutcome;
|
|
3749
|
+
/**
|
|
3750
|
+
* Fold member outcomes into the aggregate run verdict — the ratified spec §2.1 ordered
|
|
3751
|
+
* rule with the D-15 neutral-skipped clause. EVALUATED IN ORDER:
|
|
3752
|
+
*
|
|
3753
|
+
* 1. `runCancelled` ⇒ `'cancelled'` (a whole-run cancel wins over everything)
|
|
3754
|
+
* 2. `runBudgetExpired` ⇒ `'timeout'` (reserved for the whole-run wall-clock budget;
|
|
3755
|
+
* a member 'timeout' is NOT this — it falls to the failed branch below)
|
|
3756
|
+
* 3. any member `'failed'` or `'timeout'` ⇒ `'failed'`
|
|
3757
|
+
* 4. any member `'cancelled'` (none failed) ⇒ `'cancelled'`
|
|
3758
|
+
* 5. any member `'healed'` (none failed) ⇒ `'healed'`
|
|
3759
|
+
* 6. else ⇒ `'passed'`
|
|
3760
|
+
*
|
|
3761
|
+
* `'skipped'` members are neutral throughout — an all-skipped (or empty) run is
|
|
3762
|
+
* vacuously `'passed'` (D-15, the reconciled clause; matches every test runner).
|
|
3763
|
+
*
|
|
3764
|
+
* PHASE 390 D-14 — the quarantine skip is EXPLICIT IN THE RULE, right here in step 3 and nowhere
|
|
3765
|
+
* else. `quarantined[i] === true` marks a member whose failure the committed ledger has licensed:
|
|
3766
|
+
* step 3 passes over it, so a run whose only failures are quarantined aggregates `'passed'` and
|
|
3767
|
+
* exits 0 through the untouched {@link mapVerdictToExitCode}. Three things this deliberately does
|
|
3768
|
+
* NOT do: it does not rewrite the member outcome (`'failed'` stays `'failed'` — the projection
|
|
3769
|
+
* window and the auto-exit keep seeing reality), it does not touch any other branch (a sibling
|
|
3770
|
+
* `'cancelled'`/`'healed'` still governs, and the two run-level branches still win outright), and
|
|
3771
|
+
* it does not widen either closed union. `quarantined` absent — or shorter than `outcomes` — is
|
|
3772
|
+
* byte-identical pre-390 behavior, which is why every existing call site compiles unchanged.
|
|
3773
|
+
*
|
|
3774
|
+
* The skip covers `'timeout'` members as well as `'failed'` ones: a quarantined flaky test that
|
|
3775
|
+
* times out is the same A3 phenomenon, and a rule that suppressed one but not the other would make
|
|
3776
|
+
* relief depend on how the flake happened to manifest that build.
|
|
3777
|
+
*/
|
|
3778
|
+
declare function deriveAggregateVerdict(input: {
|
|
3779
|
+
runCancelled: boolean;
|
|
3780
|
+
runBudgetExpired: boolean;
|
|
3781
|
+
outcomes: readonly MemberTestOutcome[];
|
|
3782
|
+
quarantined?: readonly boolean[];
|
|
3783
|
+
}): RunStatus;
|
|
3784
|
+
/**
|
|
3785
|
+
* Map an aggregate verdict to a process exit code (D-14). Pure and pinned:
|
|
3786
|
+
* passed/healed ⇒ 0 (healed is ratified as a pass — the annotation lives in output and
|
|
3787
|
+
* the handoff, never the exit code), failed/timeout ⇒ 1, cancelled ⇒ 130 (existing SIGINT
|
|
3788
|
+
* convention). Exit 2 stays reserved for config/setup errors and is NOT produced here.
|
|
3789
|
+
*/
|
|
3790
|
+
declare function mapVerdictToExitCode(verdict: RunStatus): number;
|
|
3791
|
+
|
|
3792
|
+
/** The four classes the deterministic classifier can mint (RCC-02). */
|
|
3793
|
+
type DeterministicRootCauseClass = 'timeout' | 'environment' | 'flake' | 'unknown';
|
|
3794
|
+
/** The fixed unknown-class rationale template (D-02). */
|
|
3795
|
+
declare const UNKNOWN_SIGNAL_RATIONALE = "no deterministic signal matched";
|
|
3796
|
+
interface RootCauseInput {
|
|
3797
|
+
/** The member's final-attempt TestResult (carries retryCount — the flake signal source). */
|
|
3798
|
+
result: TestResult;
|
|
3799
|
+
/** The derived member outcome (deriveMemberOutcome) — the timeout + presence-rule signal. */
|
|
3800
|
+
outcome: MemberTestOutcome;
|
|
3801
|
+
/** Shell-derived member index; becomes Verdict.perTest. */
|
|
3802
|
+
testIndex: number;
|
|
3803
|
+
/** Run-start baseline probe outcome (D-04, web targets; threaded by Plan 07). */
|
|
3804
|
+
baselineProbe?: {
|
|
3805
|
+
status?: number;
|
|
3806
|
+
elapsedMs: number;
|
|
3807
|
+
error?: string;
|
|
3808
|
+
};
|
|
3809
|
+
/**
|
|
3810
|
+
* The run's mask (439 D-08). An unproven step's reason carries the planner's own words, which
|
|
3811
|
+
* the step error holds unmasked, so its rationale passes through this before it is recorded.
|
|
3812
|
+
*/
|
|
3813
|
+
mask?: (text: string) => string;
|
|
3814
|
+
}
|
|
3815
|
+
/**
|
|
3816
|
+
* The D-02 faithful VerdictBase constructor. Pure and total: every field of the LLM-shaped
|
|
3817
|
+
* base is filled deterministically — confidence keys on the class ('unknown' ⇒ 0, else 1),
|
|
3818
|
+
* the unknown rationale is always the fixed template regardless of the signal argument.
|
|
3819
|
+
*/
|
|
3820
|
+
declare function deterministicVerdict(cls: DeterministicRootCauseClass, signal: string, citedSteps: number[], perTest: number): Verdict;
|
|
3821
|
+
/**
|
|
3822
|
+
* Classify one member's root cause from deterministic signals only (RCC-02). Returns
|
|
3823
|
+
* undefined when the presence rule says no rootCause (skipped/cancelled/clean pass).
|
|
3824
|
+
* See the module header for the documented, pinned branch order.
|
|
3825
|
+
*/
|
|
3826
|
+
declare function classifyRootCause(input: RootCauseInput): Verdict | undefined;
|
|
3827
|
+
|
|
3828
|
+
declare function createMemoryTools(deps: {
|
|
3829
|
+
reader: KnowledgeHintReader;
|
|
3830
|
+
scope: ScopeKey;
|
|
3831
|
+
asOfBuild: number;
|
|
3832
|
+
searchResultBudget: number;
|
|
3833
|
+
readBodyBudget: number;
|
|
3834
|
+
recordLedger: RecordLedger;
|
|
3835
|
+
estimateTokens: (text: string) => number;
|
|
3836
|
+
onReadTrace?: (trace: ReadTrace) => void;
|
|
3837
|
+
}): {
|
|
3838
|
+
tools: NonActionToolMap;
|
|
3839
|
+
beginTurn(scope?: ScopeKey, exposedIds?: readonly RecordId[], turnOrdinal?: number): void;
|
|
3840
|
+
drainReadTraces(): ReadTrace[];
|
|
3841
|
+
};
|
|
3842
|
+
|
|
3843
|
+
declare function buildTools(registry: ToolRegistry, options: {
|
|
3844
|
+
platform: Platform;
|
|
3845
|
+
categories?: ToolCategory[];
|
|
3846
|
+
}): Record<string, Tool<any, any>>;
|
|
3847
|
+
declare function toolCallToActionPlan(toolId: string, args: Record<string, unknown>, registry: ToolRegistry): {
|
|
3848
|
+
action: {
|
|
3849
|
+
type: string;
|
|
3850
|
+
};
|
|
3851
|
+
failureReason?: string | undefined;
|
|
3852
|
+
failureKind?: "infeasible" | undefined;
|
|
3853
|
+
bulkCompletionPredicate?: string | undefined;
|
|
3854
|
+
remember?: {
|
|
3855
|
+
key: string;
|
|
3856
|
+
value: string;
|
|
3857
|
+
kind: "code" | "filename" | "extracted-value" | "progress-marker";
|
|
3858
|
+
provenance: "observation" | "toolResult" | "plannerInference";
|
|
3859
|
+
testIndex?: number | undefined;
|
|
3860
|
+
stepIndex?: number | undefined;
|
|
3861
|
+
}[] | undefined;
|
|
3862
|
+
reasoning: string;
|
|
3863
|
+
confidence: number;
|
|
3864
|
+
stepComplete: boolean;
|
|
3865
|
+
stepFailed: boolean;
|
|
3866
|
+
doesActionAdvanceUsTowardsGoal: boolean;
|
|
3867
|
+
};
|
|
3868
|
+
|
|
3869
|
+
/**
|
|
3870
|
+
* Project a resolved Action onto the schema-owned {@link RiskActionDescriptor} (D-08). An
|
|
3871
|
+
* unrecognized verb projects `recognized: false` — the classifier's fail-safe floor maps it to
|
|
3872
|
+
* `restricted` → deny. `command`/`declaredTier` are deliberately omitted: no shell-wrapper actions
|
|
3873
|
+
* exist in-test and `declaredTier` stays the registry's future channel.
|
|
3874
|
+
*/
|
|
3875
|
+
declare function projectRiskAction(action: {
|
|
3876
|
+
type: string;
|
|
3877
|
+
}): RiskActionDescriptor;
|
|
3878
|
+
|
|
3879
|
+
declare function normalizePointerActionForStep(action: Action, stepInstruction: string): Action;
|
|
3880
|
+
|
|
3881
|
+
/**
|
|
3882
|
+
* Converts "Meta" tokens in keyboard combo strings to "Control" on non-Mac platforms.
|
|
3883
|
+
*
|
|
3884
|
+
* Users write combos like "Meta+k" and we route them to Cmd+K on Mac, Ctrl+K on
|
|
3885
|
+
* Windows/Linux. Uses split/join (not regex) to avoid edge-case bugs with adjacent
|
|
3886
|
+
* tokens like "Shift+Meta+Meta+T".
|
|
3887
|
+
*
|
|
3888
|
+
* @param keys - Array of key/combo strings (e.g., ["Meta+k", "Enter"])
|
|
3889
|
+
* @param options.enabled - When false, keys are returned unchanged
|
|
3890
|
+
* @param options.isMac - When true, keys are returned unchanged (Meta is native)
|
|
3891
|
+
* @returns New array with Meta tokens replaced by Control (when applicable)
|
|
3892
|
+
*/
|
|
3893
|
+
declare function convertKeysForPlatform(keys: string[], options: {
|
|
3894
|
+
enabled: boolean;
|
|
3895
|
+
isMac: boolean;
|
|
3896
|
+
}): string[];
|
|
3897
|
+
/**
|
|
3898
|
+
* Returns true when running on macOS. Reads process.platform at call time so
|
|
3899
|
+
* tests can override it via Object.defineProperty(process, 'platform', ...).
|
|
3900
|
+
*/
|
|
3901
|
+
declare function isMacPlatform(): boolean;
|
|
3902
|
+
|
|
3903
|
+
export { AUTH_STATE_HOOK_CONTAINER_STORAGE_STATE_PATH, AUTH_STATE_HOOK_JSON_ENV, AUTH_STATE_HOOK_STORAGE_STATE_FILENAME, AUTH_STATE_HOOK_STORAGE_STATE_PATH_ENV, AUTH_STATE_HOOK_WORKSPACE_DIR, type AccessibilityCheckHandler, type AccessibilityViolation, type Action, type ActionPlan, type ActionPlanCache, type ActionResult, type ActionsFor, type AgentAbortScope, type AgentTimeoutAbortReason, type AiJudgmentMeta, AiJudgmentResultSchema, type AppRegistryEntry, type AppResolution, type AppiumScriptHandler, type AssertValueInput, type AssertValueOutcomeMeta, type AssertValueResult, type Asserter, AssertionEvaluator, type AssertionInput, type AssertionResult, type AssertionType, type BaselineProbeResult, type BrowserConfig, type BuildCacheableStepInputArgs, type BuildCachedPlanValueInput, type BuildScreenStateInput, type BuildScreenStateResult, CACHE_ENV_DIGEST_SALT, CHARS_PER_TOKEN_ESTIMATE, CLIPBOARD_TEXT_LIMIT, CLIPBOARD_TRUNCATION_MARKER, type CacheableClassification, type CacheablePlannedAction, type CachedPlanValue, type CaptureResult, type CaptureStamp, type CarriedArtifacts, type CarriedClipboard, type CarriedDownload, type CarriedLine, type ClassifyStepInput, type ClipboardWriteMeta, type CompressedScreenshot, type ConditionJudgment, type ConfigDigests, type ConformanceInput, type ConsoleLogEntry, type CreateLiveStreamHubOptions, type CreateRunEventBusOptions, DEFAULT_CANDIDATE_COUNT, DEFAULT_EDIT_DISTANCE_BOUND, DEFAULT_FUZZY_MARGIN, DEFAULT_FUZZY_THRESHOLD, DEFAULT_NON_ACTION_TOOL_BUDGET, type DesiredState, type DesiredStateAction, type DeterministicRootCauseClass, type DeviceConfig, type DialogAccount, type Direction, type DirectionScrollParams, type DocumentScrollOffsets, EXPOSURE, type EventLogPort, type EvidenceArtifactStorePort, EvidenceRecorder, type ExecuteOptions, type ExecutePlanningTurnOptions, ExplicitExtractor, type ExtractionMethod, ExtractionResultSchema, type ExtractorInput, FILE_DIFFERENCE_WINDOW, FILL_SEPARATORS, type FailureScreenshotWriter, type FillRefusalMeta, type FillRereadMeta, type FillSubmitMeta, type FillVerdictMeta, type FillVerifyInput, type FillVerifyResult, type GroundedLocator, type GroundingStrategy, type HandoffStorePort, type HookDefinition, type HookEmitSeam, type HookExecuteOptions, type HookExecutorPort, type HookOrchestrationResult, type HookResult, type HookRunIdentity, type HookRuntime, type InlineHookCall, type InterimFixture, type InterimFixtureKind, type InterimNode, type InterpolationWithProvenance, type KnowledgeHintReader, LIVE_KEEPALIVE_QUIET_MS, LIVE_SNAPSHOT_INTERVAL_MS, LIVE_STREAM_ACK_BOUND_MS, LIVE_STREAM_STOP_GRACE_MS, LLMAssertionEvaluator, LLMPlanner, LLMVariableExtractor, LLMVerifier, type LiveStreamCapability, type LiveStreamHub, type LiveStreamRegistration, type LoopEventEmitPort, type LoopLogger, type LoopPorts, type LoopScopedLogger, MAX_TO_END_SWIPES, MINT_VERBS, type MemberEventSink, type MemberEvidenceContext, type MemberRunConfig, type MemberStepSpec, type MemberTest, type MemberTestExecutor, type MemberTestRunContext, type MintVerb, MissingSecretError, type NetworkLogEntry, type NonActionToolHandler, type NonActionToolMap, OBSERVATION_LINE_PREFIX, type ObserveOptions, type OpenAppMeta, type OperationCaptureSupport, POST_VERDICT_STEP_BUDGET_MS, type PersistedRecording, type PlanResult, type Planner, type PlannerConfig, type PlanningCallGate, PlanningLimitReachedError, PlanningTurnError, type PlanningTurnFailure, type PlanningTurnFailureReason, type PlanningTurnResult, type PlanningTurnSuccess, type PlatformAdapter, type PlatformConfig, type PostVerdictOutcome, type PostVerdictPort, type PostVerdictStepInput, RECORD_ACTION_LINE_CAP, RECORD_LINE_CHAR_CAP, RECORD_LIST_CAP, ROW_CONTAINER_ROLES, RUNTIME_IMAGE_MAP, type RecordAdmission, type RecordLedger, type RecordSubActionInput, type RecordedAction, type RecordedActionOutcome, type RecordedRead, type RecordedReadKind, type RecordingCapability, type RecordingStart, type RecordingStop, type RefBinding, type RenderedCautionLine, type RenderedStepRecord, ReplayIncompleteError, type ReportValueInput, type ReportedValueMeta, type RootCauseInput, type RunArtifactStorePort, type RunConfig, type RunEventBus, type RunHooksOptions, type RunJSMatch, type RunJsHandler, type RunMemberDescriptor, type RunPorts, type RunTimerPort, type RunnerDegradeOptions, type RunnerDegradeOutcome, type RuntimeAuthStateConfig, SCROLL_OUTCOMES_READ_ONLY, SCROLL_OUTCOMES_WRITTEN, type SandboxRunnerOptions, type SavedFileComparison, type SavedFileExpectation, type ScorerOptions, type ScreenConformanceCase, type ScreenshotCompressor, type ScriptedVerdict, type ScrollEndedBy, type ScrollLoopResult, type ScrollOffsets, type ScrollPrimitives, type ScrollUntilVisibleMeta, SecretConfigError, type SecretFileMetadata, SecretRedactor, type SecretRedactorPort, type SecretResolver, SecretStore, type SecretTemplate, type ServedCautions, type ServedRunBrief, type ShellActionMeta, type StatedTarget, type StatedTargets, type StepAnnotation, type StepContext, type StepModelAttempt, type StepModelBinding, type StepModelCallEvent, type StepModelCallReport, type StepModelConsumers, type StepModelObservation, type StepModelRoleFacts, type StepModelScope, type StepModelScopeFactory, type StepModelScopeKey, type StepModelScopeOptions, type StepModelSnapshot, type StepPhaseEvent, StepRecordAccumulator, type StepResult, type StepStatus, type StepTrace, type StorageStateBlob, type StorageStatePort, type StorageStateReference, type SubActionTrace, type SwipeSignal, type TargetVisibility, type TestResult, type TestStatus, type TimeoutConfig, type TokenAccounting, type TokenUsage, type ToolCategory, type ToolDefinition, type ToolExchange, ToolRegistry, UNKNOWN_SIGNAL_RATIONALE, type UniqueValueGenerator, type UniqueValueInput, type UniqueValueMeta, type UntilVisibleParams, type VariableExtractor, type VariableSource, VariableStore, type VariableStoreOptions, type VerificationResult, VerificationResultSchema, type Verifier, type VerifierStepRecord, type VerifyResult, type WorkingMemoryPort, type WorkingMemoryProvenanceContext, WorkingMemoryStore, assembleBriefTiers, assembleHandoff, assemblePerTestOutcome, assertNever, buildAssertionPrompt, buildAuthStateHookEnv, buildCacheableStepInput, buildCachedPlanValue, buildContextTier, buildExtractionPrompt, buildScreenState, buildSystemPrompt, buildTools, buildVerificationPrompt, buildVerifierStableTier, buildVolatileTier, classifyRootCause, classifyStep, coerceRunJSResult, collapseAnnotation, compareAssertValue, compareFillReread, compareSavedFile, composeTurnMessages, containsRedactedSecretMarker, convertKeysForPlatform, createAsserter, createExtractor, createLiveStreamHub, createMemberEventSink, createMemoryTools, createRecordLedger, createRunEventBus, createSnapshotLiveStream, createStepModelScope, createTimeoutAbortReason, defaultLiveStreamTimer, defaultRegistry, defaultUniqueValueGenerator, deriveAggregateVerdict, deriveMemberOutcome, desiredStateSatisfied, deterministicDescriptorMatcher, deterministicVerdict, documentSwipeSignal, estimateRenderedServingCost, estimateTokens, executePlanningTurn, executeStep, findBareVariables, findSecretTemplates, findUnresolvedTemplates, foldCacheStatus, formatAction, generateFailureSummary, getTimeoutAbortReason, imageSizeOf, interpolateSecretTemplates, interpolateVariables, interpolateVariablesWithProvenance, isCanonicalHookIdShape, isMacPlatform, isReservedAuthStateHookEnvKey, isTestTimeoutMessage, judgeStatedTarget, literalSecretMarkerRefusal, liveStreamFor, makeEmptyServedBrief, makeInterimNodes, makeScreenState, mapVerdictToExitCode, memberStepText, metVerification, mintReportedValue, mintUniqueValue, normalizePointerActionForStep, notMetVerification, parseAppiumInline, parseEnvFile, parseHookInline, parseRunJSInline, projectLegacyTrace, projectRiskAction, readAiJudgmentMeta, readAssertValueOutcomeMeta, readClipboardMeta, readDownloadMeta, readFillRefusalMeta, readFillRereadMeta, readFillSubmitMeta, readFillVerdictMeta, readOpenAppMeta, readReportedValueMeta, readScrollUntilVisibleMeta, readShellActionMeta, readUniqueValueMeta, redactSecretValue, renderBriefAsHypotheses, renderBriefLine, renderCautionLine, renderCautionSummary, renderObservationLine, renderScreenTree, renderStepRecord, resolveAppName, resolveSecretEnv, resolveSecretTemplatesInValue, rowContains, rowTextOf, runDirectionScroll, runHooks, runMemberTest, runToEndScroll, runUntilVisibleLoop, screenConformanceCases, scriptedVerification, scrollerSetSwipeSignal, serializeEnvFile, skippedPerTestOutcome, stampCaptureMode, startRun, stripHookInline, stripReservedAuthStateHookEnv, swipeSignalFromOffsets, tierForEntry, toGroundedLocator, toVerificationResult, toolCallToActionPlan, withAbort, withStepModelCall, wordTokens };
|