@tangle-network/agent-eval 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/adapters/http.d.ts +2 -2
- package/dist/analyst/index.d.ts +3 -3
- package/dist/analyst/index.js +7 -7
- package/dist/app-grade/index.d.ts +1042 -0
- package/dist/app-grade/index.d.ts.map +1 -0
- package/dist/app-grade/index.js +3077 -0
- package/dist/app-grade/index.js.map +1 -0
- package/dist/{benchmark-command-BRRhjX2k.js → benchmark-command-DSJQeYom.js} +9 -9
- package/dist/{benchmark-command-BRRhjX2k.js.map → benchmark-command-DSJQeYom.js.map} +1 -1
- package/dist/benchmarks/index.d.ts +4 -4
- package/dist/benchmarks/index.js +3 -3
- package/dist/campaign/index.d.ts +4 -4
- package/dist/campaign/index.js +10 -10
- package/dist/{campaign-DzLlT0So.js → campaign-BABipFel.js} +10 -10
- package/dist/{campaign-DzLlT0So.js.map → campaign-BABipFel.js.map} +1 -1
- package/dist/{campaign-evidence-CcA7a67m.js → campaign-evidence-DONEWvVY.js} +2 -2
- package/dist/{campaign-evidence-CcA7a67m.js.map → campaign-evidence-DONEWvVY.js.map} +1 -1
- package/dist/cli.js +6 -6
- package/dist/contract/index.d.ts +7 -7
- package/dist/contract/index.js +6 -6
- package/dist/{default-registry-BNv7eB1C.d.ts → default-registry-BbXr18AT.d.ts} +2 -2
- package/dist/{default-registry-BNv7eB1C.d.ts.map → default-registry-BbXr18AT.d.ts.map} +1 -1
- package/dist/{define-agent-eval-DKVYlm-T.d.ts → define-agent-eval-D_DQMH04.d.ts} +5 -5
- package/dist/{define-agent-eval-DKVYlm-T.d.ts.map → define-agent-eval-D_DQMH04.d.ts.map} +1 -1
- package/dist/{define-agent-eval-CjF2nTA3.js → define-agent-eval-Q4FdXGj7.js} +5 -5
- package/dist/{define-agent-eval-CjF2nTA3.js.map → define-agent-eval-Q4FdXGj7.js.map} +1 -1
- package/dist/{dspy-rlm-engine-_MEe2jIO.js → dspy-rlm-engine-CqQc0R2l.js} +2 -2
- package/dist/{dspy-rlm-engine-_MEe2jIO.js.map → dspy-rlm-engine-CqQc0R2l.js.map} +1 -1
- package/dist/{evaluation-DYAIkPQB.d.ts → evaluation-SDXKL6Fx.d.ts} +2 -2
- package/dist/{evaluation-DYAIkPQB.d.ts.map → evaluation-SDXKL6Fx.d.ts.map} +1 -1
- package/dist/evaluation.d.ts +1 -1
- package/dist/experiment/index.d.ts +1 -1
- package/dist/experiment/index.js +1 -1
- package/dist/{external-optimizer-process-za-SsIap.js → external-optimizer-process-CfrY-nJT.js} +2 -2
- package/dist/{external-optimizer-process-za-SsIap.js.map → external-optimizer-process-CfrY-nJT.js.map} +1 -1
- package/dist/{external-optimizer-subprocess-B44kOfZb.js → external-optimizer-subprocess-BtJk8UcU.js} +2 -2
- package/dist/{external-optimizer-subprocess-B44kOfZb.js.map → external-optimizer-subprocess-BtJk8UcU.js.map} +1 -1
- package/dist/{findings-store-DWiBvJVM.js → findings-store-BnYN57-h.js} +3 -3
- package/dist/{findings-store-DWiBvJVM.js.map → findings-store-BnYN57-h.js.map} +1 -1
- package/dist/hosted/index.js +1 -1
- package/dist/{index-Dt6E4BHX.d.ts → index-AmfM3usI.d.ts} +2 -2
- package/dist/{index-Dt6E4BHX.d.ts.map → index-AmfM3usI.d.ts.map} +1 -1
- package/dist/{index-BJM4rwP2.d.ts → index-BPmj-_ta.d.ts} +4 -4
- package/dist/{index-BJM4rwP2.d.ts.map → index-BPmj-_ta.d.ts.map} +1 -1
- package/dist/index-D4lT7aFu.d.ts +3649 -0
- package/dist/index-D4lT7aFu.d.ts.map +1 -0
- package/dist/{index-chNyKroE.d.ts → index-DLVbfss9.d.ts} +6 -6
- package/dist/{index-chNyKroE.d.ts.map → index-DLVbfss9.d.ts.map} +1 -1
- package/dist/{index-f09MX_H_.d.ts → index-HuSMvqR2.d.ts} +2 -2
- package/dist/{index-f09MX_H_.d.ts.map → index-HuSMvqR2.d.ts.map} +1 -1
- package/dist/{index-B1Bm9oJA.d.ts → index-lRKqfyg4.d.ts} +3 -7
- package/dist/index-lRKqfyg4.d.ts.map +1 -0
- package/dist/index.d.ts +15 -3629
- package/dist/index.js +6 -6
- package/dist/{journal-C1uLJi0w.js → journal-CTX5uK-n.js} +36 -26
- package/dist/journal-CTX5uK-n.js.map +1 -0
- package/dist/ledger-core/index.d.ts +1 -1
- package/dist/ledger-core/index.js +2 -2
- package/dist/{ledger-core-CfT-FzPf.js → ledger-core-24IxvmVY.js} +2 -2
- package/dist/{ledger-core-CfT-FzPf.js.map → ledger-core-24IxvmVY.js.map} +1 -1
- package/dist/{llm-judge-CHLvjbVR.js → llm-judge-VD_MI6kd.js} +8 -8
- package/dist/{llm-judge-CHLvjbVR.js.map → llm-judge-VD_MI6kd.js.map} +1 -1
- package/dist/{matrix-Cy3oy7Rq.d.ts → matrix-Yg1lHIGI.d.ts} +2 -2
- package/dist/{matrix-Cy3oy7Rq.d.ts.map → matrix-Yg1lHIGI.d.ts.map} +1 -1
- package/dist/multishot/golden/index.d.ts +1 -1
- package/dist/multishot/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/{pareto-B34N-vnn.js → pareto-a3EOmjfe.js} +4 -4
- package/dist/{pareto-B34N-vnn.js.map → pareto-a3EOmjfe.js.map} +1 -1
- package/dist/pipelines/index.d.ts +1 -1
- package/dist/{proposal-findings-CZJkS_GY.js → proposal-findings-BfzzZ_lN.js} +2 -2
- package/dist/{proposal-findings-CZJkS_GY.js.map → proposal-findings-BfzzZ_lN.js.map} +1 -1
- package/dist/rl.d.ts +3 -3
- package/dist/rollout/index.d.ts +2 -2
- package/dist/search/index.d.ts +2 -2
- package/dist/search/index.js +2 -2
- package/dist/{search-kernel-kwO-sgCq.d.ts → search-kernel-DjqaPJqL.d.ts} +3 -3
- package/dist/{search-kernel-kwO-sgCq.d.ts.map → search-kernel-DjqaPJqL.d.ts.map} +1 -1
- package/dist/{search-ledger-CV_bQQed.js → search-ledger-Dk-yk5Li.js} +4 -4
- package/dist/{search-ledger-CV_bQQed.js.map → search-ledger-Dk-yk5Li.js.map} +1 -1
- package/dist/{search-ledger-file-DTBD6X-y.js → search-ledger-file-BhOdgYIt.js} +3 -3
- package/dist/{search-ledger-file-DTBD6X-y.js.map → search-ledger-file-BhOdgYIt.js.map} +1 -1
- package/dist/{search-shipper-CA3ooBbC.js → search-shipper-Dm-_Dtvy.js} +3 -3
- package/dist/{search-shipper-CA3ooBbC.js.map → search-shipper-Dm-_Dtvy.js.map} +1 -1
- package/dist/{search-summary-DBMjdecs.js → search-summary-RJ5zXjHN.js} +3 -3
- package/dist/{search-summary-DBMjdecs.js.map → search-summary-RJ5zXjHN.js.map} +1 -1
- package/dist/{single-run-lock-BGlW3OSk.js → single-run-lock-CUj2tjxT.js} +2 -2
- package/dist/{single-run-lock-BGlW3OSk.js.map → single-run-lock-CUj2tjxT.js.map} +1 -1
- package/dist/{skillopt-optimization-method-D72XZZfR.js → skillopt-optimization-method-D9Hlmhjg.js} +8 -8
- package/dist/{skillopt-optimization-method-D72XZZfR.js.map → skillopt-optimization-method-D9Hlmhjg.js.map} +1 -1
- package/dist/{statistical-heldout-_dXihc9T.d.ts → statistical-heldout-C-tgpUuu.d.ts} +2 -2
- package/dist/{statistical-heldout-_dXihc9T.d.ts.map → statistical-heldout-C-tgpUuu.d.ts.map} +1 -1
- package/dist/{systemone-CBvL1gx-.d.ts → systemone-CpDppX8s.d.ts} +3 -3
- package/dist/{systemone-CBvL1gx-.d.ts.map → systemone-CpDppX8s.d.ts.map} +1 -1
- package/dist/systemone.d.ts +2 -2
- package/dist/traces.d.ts +1 -1
- package/dist/{tree-DPwgM2LX.js → tree-DIHp6UQ0.js} +3 -3
- package/dist/{tree-DPwgM2LX.js.map → tree-DIHp6UQ0.js.map} +1 -1
- package/dist/{types-D91tnhzf.d.ts → types-DsuOcYsE.d.ts} +2 -2
- package/dist/{types-D91tnhzf.d.ts.map → types-DsuOcYsE.d.ts.map} +1 -1
- package/dist/{types-CBkDo7ja.d.ts → types-wDBk14lj.d.ts} +2 -2
- package/dist/{types-CBkDo7ja.d.ts.map → types-wDBk14lj.d.ts.map} +1 -1
- package/dist/wire/index.d.ts +2 -2
- package/docs/app-grade.md +79 -0
- package/package.json +15 -1
- package/dist/index-B1Bm9oJA.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/journal-C1uLJi0w.js.map +0 -1
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
import { m as CustomTokenPricing, t as CostLedger } from "../cost-ledger-Czro9wmo.js";
|
|
2
|
+
import "../index-D4lT7aFu.js";
|
|
3
|
+
import { w as ChatTransport, x as ChatClient } from "../types-0KglnQ9o.js";
|
|
4
|
+
import { d as SystemOneRequest, g as SystemOneState, s as SystemOneQuestions } from "../systemone-protocol-C4t7JH4Y.js";
|
|
5
|
+
import "../systemone-CpDppX8s.js";
|
|
6
|
+
import { a as SystemOneReviewReport, r as SystemOneReviewCheck, t as PreparedSystemOneReview } from "../systemone-review-nbuMxxmi.js";
|
|
7
|
+
import { IncomingHttpHeaders, OutgoingHttpHeaders } from "node:http";
|
|
8
|
+
import { Browser, BrowserContext, Locator, Page } from "playwright";
|
|
9
|
+
/**
|
|
10
|
+
* The tier ladder: a pure function of stored deterministic checks, stored SystemOne
|
|
11
|
+
* assessments and build gates. Gates, not a weighted blend: strong copy cannot
|
|
12
|
+
* buy back a failed journey, and an unresolved check never counts as a pass.
|
|
13
|
+
*
|
|
14
|
+
* F not served, or a floor check of the primary journey failed
|
|
15
|
+
* U unable to measure: no floor check failed, but one could not be observed
|
|
16
|
+
* (the payment sits behind a passkey sign-in the grader cannot approve)
|
|
17
|
+
* C the floor passed, but a core SystemOne check is refuted or a journey check failed
|
|
18
|
+
* B every journey check passed and no SystemOne check is refuted
|
|
19
|
+
* A B, plus every core, senior and engineering check supported and the build gates pass
|
|
20
|
+
* S A, plus complete coverage, zero unresolved, and nothing left unmeasured
|
|
21
|
+
* INVALID a failed integrity check or a supported integrity question; the cell goes to human review
|
|
22
|
+
*
|
|
23
|
+
* F, U and C follow from deterministic checks alone, so `decidedWithoutReviews`
|
|
24
|
+
* lets the grader skip model calls that could not change the tier.
|
|
25
|
+
*/
|
|
26
|
+
export type Tier = 'F' | 'U' | 'C' | 'B' | 'A' | 'S';
|
|
27
|
+
/** Ranked tiers. U is not on the ladder: nothing was learned about the payment. */
|
|
28
|
+
export declare const TIER_ORDER: readonly Tier[];
|
|
29
|
+
/** How a SystemOne check can move the tier. */
|
|
30
|
+
export type TierRole = 'core' | 'senior' | 'engineering' | 'integrity';
|
|
31
|
+
/**
|
|
32
|
+
* The fields of agent-eval's `SystemOneReviewAssessment` the ladder reads. Declared here so the
|
|
33
|
+
* ladder has no package dependency: the repository's admission gate re-derives stored
|
|
34
|
+
* verdicts with it.
|
|
35
|
+
*/
|
|
36
|
+
export interface VerdictAssessment {
|
|
37
|
+
question: string;
|
|
38
|
+
status: 'supported' | 'refuted' | 'unresolved';
|
|
39
|
+
reason: string;
|
|
40
|
+
supportProbability: number;
|
|
41
|
+
refutationProbability: number;
|
|
42
|
+
}
|
|
43
|
+
export interface VerdictCheck {
|
|
44
|
+
id: string;
|
|
45
|
+
status: 'pass' | 'fail' | 'not-applicable' | 'unmeasured';
|
|
46
|
+
gate: 'floor' | 'journey' | 'engineering' | 'integrity';
|
|
47
|
+
detail: string;
|
|
48
|
+
}
|
|
49
|
+
export interface VerdictReview {
|
|
50
|
+
id: string;
|
|
51
|
+
tierRole: Record<string, TierRole>;
|
|
52
|
+
assessments: VerdictAssessment[];
|
|
53
|
+
}
|
|
54
|
+
export interface EngineeringGates {
|
|
55
|
+
install: boolean;
|
|
56
|
+
typecheck: boolean;
|
|
57
|
+
build: boolean;
|
|
58
|
+
tests: 'pass' | 'fail' | 'absent';
|
|
59
|
+
}
|
|
60
|
+
export interface Verdict {
|
|
61
|
+
tier: Tier;
|
|
62
|
+
invalid: boolean;
|
|
63
|
+
reasons: string[];
|
|
64
|
+
jev: Record<string, {
|
|
65
|
+
status: VerdictAssessment['status'];
|
|
66
|
+
role: TierRole;
|
|
67
|
+
reason: string;
|
|
68
|
+
support: number;
|
|
69
|
+
refute: number;
|
|
70
|
+
}>;
|
|
71
|
+
unmeasured: string[];
|
|
72
|
+
}
|
|
73
|
+
/** The tier when deterministic checks settle it, before any model call; null when a review could still move it. */
|
|
74
|
+
export declare function decidedWithoutReviews(input: {
|
|
75
|
+
served: boolean;
|
|
76
|
+
checks: VerdictCheck[];
|
|
77
|
+
}): 'F' | 'U' | 'C' | null;
|
|
78
|
+
export declare function deriveVerdict(input: {
|
|
79
|
+
served: boolean;
|
|
80
|
+
checks: VerdictCheck[];
|
|
81
|
+
reviews: VerdictReview[];
|
|
82
|
+
engineering: EngineeringGates;
|
|
83
|
+
unmeasured: string[];
|
|
84
|
+
}): Verdict;
|
|
85
|
+
export declare const CALIBRATION_SCHEMA = "app-grade-calibration/1";
|
|
86
|
+
export declare const TASK_SCHEMA = "app-grade-task/1";
|
|
87
|
+
export declare const PLANTS: readonly ["reference", "stale", "broken"];
|
|
88
|
+
export type Plant = (typeof PLANTS)[number];
|
|
89
|
+
/** The fewest runs per submission that can show a check separates them every time. */
|
|
90
|
+
export declare const MIN_CALIBRATION_RUNS = 3;
|
|
91
|
+
export interface CalibrationRun {
|
|
92
|
+
plant: Plant;
|
|
93
|
+
run: number;
|
|
94
|
+
served: boolean;
|
|
95
|
+
checks: VerdictCheck[];
|
|
96
|
+
reviews: VerdictReview[];
|
|
97
|
+
engineering: EngineeringGates;
|
|
98
|
+
unmeasured: string[];
|
|
99
|
+
/** The tier the grader stored. Admission re-derives it. */
|
|
100
|
+
tier: Tier;
|
|
101
|
+
invalid: boolean;
|
|
102
|
+
costUsd: number;
|
|
103
|
+
ms: number;
|
|
104
|
+
/** Run directory relative to the calibration directory. */
|
|
105
|
+
evidence: string;
|
|
106
|
+
/** Errors of earlier grading attempts of this run that threw before any verdict existed. */
|
|
107
|
+
failedAttempts?: string[];
|
|
108
|
+
}
|
|
109
|
+
export interface CalibrationRecord {
|
|
110
|
+
schema: typeof CALIBRATION_SCHEMA;
|
|
111
|
+
taskId: string;
|
|
112
|
+
/** `taskDigest` of the task material that was graded. */
|
|
113
|
+
taskDigest: string;
|
|
114
|
+
/** The product image every run started, pinned by digest. */
|
|
115
|
+
productImage: string;
|
|
116
|
+
productVersion: string;
|
|
117
|
+
/** Canonical digest of each submission's files. */
|
|
118
|
+
solutionDigests: Record<Plant, string>;
|
|
119
|
+
/** Digest of the App Grade sources that graded the runs. */
|
|
120
|
+
graderDigest: string;
|
|
121
|
+
model: string;
|
|
122
|
+
passTier: Tier;
|
|
123
|
+
/** The check that encodes the fact a from-memory build gets wrong, when the task names one. */
|
|
124
|
+
factCheckId?: string;
|
|
125
|
+
runs: CalibrationRun[];
|
|
126
|
+
startedAt: string;
|
|
127
|
+
finishedAt: string;
|
|
128
|
+
costUsd: number;
|
|
129
|
+
}
|
|
130
|
+
export interface CalibrationDecision {
|
|
131
|
+
admitted: boolean;
|
|
132
|
+
reasons: string[];
|
|
133
|
+
tiers: Record<Plant, Tier[]>;
|
|
134
|
+
}
|
|
135
|
+
export declare function calibrationDecision(record: CalibrationRecord): CalibrationDecision;
|
|
136
|
+
/** RFC 8785 canonical JSON of the value as a JSON file carries it (no undefined fields). */
|
|
137
|
+
export declare function canonicalJson(value: unknown): string;
|
|
138
|
+
export declare function sha256(text: string | Buffer): string;
|
|
139
|
+
export declare function canonicalDigest(value: unknown): string;
|
|
140
|
+
/** Digest of a directory's files by relative path and content: one calibration submission. */
|
|
141
|
+
export declare function treeDigest(dir: string): string;
|
|
142
|
+
/**
|
|
143
|
+
* The material one App Grade task is graded on. A seed projects to it by removing the
|
|
144
|
+
* per-task records (`leaves`, `provenance`, `admission`, `validationMode`) and the product
|
|
145
|
+
* from the suite, and the grader spec from the leaf; a task directory stores it as is.
|
|
146
|
+
*/
|
|
147
|
+
export interface TaskMaterial {
|
|
148
|
+
id: string;
|
|
149
|
+
suite: Record<string, unknown>;
|
|
150
|
+
leaf: Record<string, unknown>;
|
|
151
|
+
product: unknown;
|
|
152
|
+
spec: unknown;
|
|
153
|
+
}
|
|
154
|
+
/** One digest binds a calibration record to the exact suite, leaf, product and grader spec. */
|
|
155
|
+
export declare function taskMaterialDigest(task: TaskMaterial): string;
|
|
156
|
+
export declare const WALLET_CHANNEL_BINDING = "__appGradeWalletChannel";
|
|
157
|
+
/** One observation made inside a wallet window's own realm. */
|
|
158
|
+
export interface WalletRecord {
|
|
159
|
+
kind: 'message' | 'decrypt' | 'encrypt';
|
|
160
|
+
/** `message`: the sender origin as the browser reported it. */
|
|
161
|
+
from?: string;
|
|
162
|
+
/** `message`: `event.isTrusted`; a page cannot dispatch a trusted message. */
|
|
163
|
+
trusted?: boolean;
|
|
164
|
+
data?: unknown;
|
|
165
|
+
/** `decrypt`: a request the wallet decrypted. `encrypt`: a response it encrypted. */
|
|
166
|
+
plaintext?: string;
|
|
167
|
+
t: number;
|
|
168
|
+
}
|
|
169
|
+
/** Page code that called the grader's wallet channel from outside a wallet window. */
|
|
170
|
+
export interface ForgeryAttempt {
|
|
171
|
+
journey: string;
|
|
172
|
+
frameUrl: string;
|
|
173
|
+
payload: string;
|
|
174
|
+
t: number;
|
|
175
|
+
}
|
|
176
|
+
export interface WalletWindow {
|
|
177
|
+
opener: Page;
|
|
178
|
+
page: Page;
|
|
179
|
+
url: string;
|
|
180
|
+
records: WalletRecord[];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Page-side rule for what a sighted buyer can see, evaluated in the grader's
|
|
184
|
+
* isolated world. The expression yields `painted(node)` for a text node or an
|
|
185
|
+
* element (a string: tsx wraps named functions in a `__name` helper the page lacks).
|
|
186
|
+
*
|
|
187
|
+
* Excluded: hidden, transparent, faded (cumulative opacity under 0.1), tiny, and
|
|
188
|
+
* clipped or off-page boxes. A text node also needs a readable colour and size.
|
|
189
|
+
* An element needs a box of at least 8 x 8 px and something drawn in it: painted
|
|
190
|
+
* text, an image, a native form widget, or its own background or border. A
|
|
191
|
+
* control a buyer cannot see is never offered to the picker or read as the order.
|
|
192
|
+
*/
|
|
193
|
+
export declare const PAINTED = "(() => {\n const alpha = (color) => {\n const match = /rgba?\\(([^)]+)\\)/.exec(color || '')\n if (!match) return color === 'transparent' ? 0 : 1\n const parts = match[1].split(/[ ,\\/]+/).filter(Boolean)\n return parts.length >= 4 ? Number.parseFloat(parts[3]) : 1\n }\n const docWidth = () => Math.max(document.documentElement.scrollWidth, innerWidth)\n const docHeight = () => Math.max(document.documentElement.scrollHeight, innerHeight)\n const readable = (element) => {\n const style = getComputedStyle(element)\n return Number.parseFloat(style.fontSize) >= 6 && alpha(style.color) >= 0.1 && alpha(style.webkitTextFillColor) >= 0.1\n }\n /** Painted area of `rects` inside `element`'s clipping ancestors and the page; 0 when hidden or faded. */\n const area = (element, rects, minWidth, minHeight) => {\n if (!element.checkVisibility({ opacityProperty: true, visibilityProperty: true, contentVisibilityAuto: true })) return 0\n let opacity = 1\n for (let at = element; at; at = at.parentElement) opacity *= Number.parseFloat(getComputedStyle(at).opacity)\n if (opacity < 0.1) return 0\n let total = 0\n for (const rect of rects) {\n let left = rect.left + scrollX, top = rect.top + scrollY, right = rect.right + scrollX, bottom = rect.bottom + scrollY\n for (let at = element; at && at !== document.documentElement; at = at.parentElement) {\n const s = getComputedStyle(at)\n const clipsOverflow = ['hidden', 'clip'].includes(s.overflowX) || ['hidden', 'clip'].includes(s.overflowY)\n const clipRect = s.clip && s.clip !== 'auto'\n const clipPath = s.clipPath && s.clipPath !== 'none'\n if (clipRect && /rect\\(\\s*0(px)?[ ,]+0(px)?[ ,]+0(px)?[ ,]+0(px)?\\s*\\)/.test(s.clip)) return 0\n if (clipPath && /inset\\(\\s*(50|[5-9]\\d|100)%/.test(s.clipPath)) return 0\n if (clipsOverflow || clipRect || clipPath) {\n const box = at.getBoundingClientRect()\n left = Math.max(left, box.left + scrollX)\n top = Math.max(top, box.top + scrollY)\n right = Math.min(right, box.right + scrollX)\n bottom = Math.min(bottom, box.bottom + scrollY)\n }\n }\n left = Math.max(left, 0)\n top = Math.max(top, 0)\n right = Math.min(right, docWidth())\n bottom = Math.min(bottom, docHeight())\n if (right - left >= minWidth && bottom - top >= minHeight) total += (right - left) * (bottom - top)\n }\n return total\n }\n const cache = new Map()\n const text = (node) => {\n const parent = node.parentElement\n if (!parent || !readable(parent)) return false\n const range = document.createRange()\n range.selectNodeContents(node)\n return area(parent, [...range.getClientRects()], 2, 4) >= 16\n }\n const drawsSomething = (element) => {\n const tag = element.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') {\n return ['checkbox', 'radio'].includes(element.type) || readable(element)\n }\n const style = getComputedStyle(element)\n if (alpha(style.backgroundColor) >= 0.1 || style.backgroundImage !== 'none') return true\n if (Number.parseFloat(style.borderTopWidth) + Number.parseFloat(style.borderBottomWidth) > 0 && alpha(style.borderTopColor) >= 0.1) return true\n for (const media of element.querySelectorAll('img, svg, canvas, video')) {\n if (area(media, [...media.getClientRects()], 4, 4) >= 16) return true\n }\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT)\n while (walker.nextNode()) {\n if ((walker.currentNode.nodeValue || '').trim() && painted(walker.currentNode)) return true\n }\n return false\n }\n const painted = (node) => {\n if (!cache.has(node)) {\n cache.set(node, node.nodeType === Node.TEXT_NODE ? text(node) : area(node, [...node.getClientRects()], 8, 8) >= 64 && drawsSomething(node))\n }\n return cache.get(node)\n }\n return painted\n})()";
|
|
194
|
+
export declare class EvidenceBrowser {
|
|
195
|
+
private readonly browser;
|
|
196
|
+
/** What this browser reports as its user agent; vendor SDKs send it in the app's traffic. */
|
|
197
|
+
readonly userAgent: string;
|
|
198
|
+
private constructor();
|
|
199
|
+
/** `executablePath`: a Chromium already on the machine, such as a Sandbox box's, instead of Playwright's download. */
|
|
200
|
+
static launch(input?: {
|
|
201
|
+
headless?: boolean;
|
|
202
|
+
executablePath?: string;
|
|
203
|
+
}): Promise<EvidenceBrowser>;
|
|
204
|
+
/** A fresh browser context per journey: its own storage, as a new buyer has. Without a wallet origin, no page records wallet evidence. */
|
|
205
|
+
journey(input: {
|
|
206
|
+
id: string;
|
|
207
|
+
dir: string;
|
|
208
|
+
appUrl: string;
|
|
209
|
+
walletOrigin?: string;
|
|
210
|
+
}): Promise<JourneySession>;
|
|
211
|
+
close(): Promise<void>;
|
|
212
|
+
}
|
|
213
|
+
export declare class JourneySession {
|
|
214
|
+
readonly id: string;
|
|
215
|
+
readonly dir: string;
|
|
216
|
+
readonly context: BrowserContext;
|
|
217
|
+
readonly appOrigin: string;
|
|
218
|
+
readonly walletOrigin: string | null;
|
|
219
|
+
readonly walletWindows: WalletWindow[];
|
|
220
|
+
readonly forgeryAttempts: ForgeryAttempt[];
|
|
221
|
+
private readonly pendingBodies;
|
|
222
|
+
/** Wallet-origin records that arrived before the opener's popup event; claimed when it fires. */
|
|
223
|
+
private readonly unclaimed;
|
|
224
|
+
private readonly cdp;
|
|
225
|
+
private constructor();
|
|
226
|
+
static open(browser: Browser, input: {
|
|
227
|
+
id: string;
|
|
228
|
+
dir: string;
|
|
229
|
+
appUrl: string;
|
|
230
|
+
walletOrigin?: string;
|
|
231
|
+
}): Promise<JourneySession>;
|
|
232
|
+
private receive;
|
|
233
|
+
private watch;
|
|
234
|
+
append(file: string, value: unknown): void;
|
|
235
|
+
write(file: string, value: string | Buffer): void;
|
|
236
|
+
newPage(): Promise<Page>;
|
|
237
|
+
/** Windows at the wallet origin that `opener` opened, in open order, skipping the first `since` of them. */
|
|
238
|
+
walletWindowsOf(opener: Page, since?: number): WalletWindow[];
|
|
239
|
+
/**
|
|
240
|
+
* Evaluate `expression` in the grader's isolated world of the page's main frame.
|
|
241
|
+
* The world persists for the life of the document, so it can hold element references.
|
|
242
|
+
*/
|
|
243
|
+
graderEval<T>(page: Page, expression: string): Promise<T>;
|
|
244
|
+
/** Accessibility snapshot and screenshot of one moment, saved under this journey's directory. */
|
|
245
|
+
capture(page: Page, label: string): Promise<{
|
|
246
|
+
aria: string;
|
|
247
|
+
ariaPath: string;
|
|
248
|
+
screenshotPath: string;
|
|
249
|
+
url: string;
|
|
250
|
+
}>;
|
|
251
|
+
close(): Promise<void>;
|
|
252
|
+
}
|
|
253
|
+
export interface AppGradeJudge {
|
|
254
|
+
/** The reference every receipt and review digest names, e.g. `router:gpt-5.6-luna`. */
|
|
255
|
+
readonly ref: string;
|
|
256
|
+
/** The model id each request asks the client for. */
|
|
257
|
+
readonly model: string;
|
|
258
|
+
readonly client: ChatClient;
|
|
259
|
+
/** Rate the ledger records when a call reports no cost. */
|
|
260
|
+
readonly pricing: CustomTokenPricing;
|
|
261
|
+
/** Hard per-call reservation rate, at or above every rate the client bills. */
|
|
262
|
+
readonly bound: CustomTokenPricing;
|
|
263
|
+
/**
|
|
264
|
+
* For a judge that bills $0 per call (a subscription): what its calls so far would cost at
|
|
265
|
+
* list price, as the judge reports it, or null when any call's figure is unknown. The
|
|
266
|
+
* manifest records it as `notionalUsd`; it is never billed.
|
|
267
|
+
*/
|
|
268
|
+
readonly notionalUsd?: () => number | null;
|
|
269
|
+
}
|
|
270
|
+
export interface JudgeReply {
|
|
271
|
+
content: string;
|
|
272
|
+
servedModel: string;
|
|
273
|
+
inputTokens: number;
|
|
274
|
+
outputTokens: number;
|
|
275
|
+
/** Dollars the client reported for the call, or null. */
|
|
276
|
+
costUsd: number | null;
|
|
277
|
+
/** Names of tools the model called while answering; a judge should call none. */
|
|
278
|
+
toolCalls: string[];
|
|
279
|
+
}
|
|
280
|
+
/** One JSON chat call. A failed call throws the client's error. */
|
|
281
|
+
export declare function askJudge(judge: AppGradeJudge, input: {
|
|
282
|
+
system: string;
|
|
283
|
+
user: string;
|
|
284
|
+
maxTokens: number;
|
|
285
|
+
signal?: AbortSignal;
|
|
286
|
+
idempotencyKey?: string;
|
|
287
|
+
}): Promise<JudgeReply>;
|
|
288
|
+
/** A judge's JSON reply, without a fence the model wrapped it in. */
|
|
289
|
+
export declare function parseJudgeJson(content: string): Record<string, unknown>;
|
|
290
|
+
export declare const INTERACTIVE = "button, a[href], input, select, textarea, [role=button], [role=link], [role=checkbox], [role=switch], [role=tab]";
|
|
291
|
+
export interface Control {
|
|
292
|
+
index: number;
|
|
293
|
+
tag: string;
|
|
294
|
+
role: string;
|
|
295
|
+
type: string;
|
|
296
|
+
name: string;
|
|
297
|
+
value: string;
|
|
298
|
+
disabled: boolean;
|
|
299
|
+
}
|
|
300
|
+
export declare function listControls(session: JourneySession, page: Page): Promise<Control[]>;
|
|
301
|
+
export declare function controlLocator(page: Page, control: Control): Locator;
|
|
302
|
+
export interface TargetPick {
|
|
303
|
+
goal: string;
|
|
304
|
+
control: Control | null;
|
|
305
|
+
reason: string;
|
|
306
|
+
}
|
|
307
|
+
export declare function targetPicker(input: {
|
|
308
|
+
judge: AppGradeJudge;
|
|
309
|
+
costLedger: CostLedger;
|
|
310
|
+
}): ((session: JourneySession, page: Page, goal: string, context?: string) => Promise<TargetPick>) & {
|
|
311
|
+
stats: {
|
|
312
|
+
asked: number;
|
|
313
|
+
cached: number;
|
|
314
|
+
toolCalls: string[];
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
export type Picker = ReturnType<typeof targetPicker>;
|
|
318
|
+
export interface ReviewSpec {
|
|
319
|
+
id: string;
|
|
320
|
+
questions: SystemOneQuestions;
|
|
321
|
+
state: unknown;
|
|
322
|
+
checks: Record<string, Omit<SystemOneReviewCheck, 'evidence'> & {
|
|
323
|
+
evidence: SystemOneReviewCheck['evidence'];
|
|
324
|
+
}>;
|
|
325
|
+
tierRole: Record<string, TierRole>;
|
|
326
|
+
}
|
|
327
|
+
export interface StoredReview {
|
|
328
|
+
id: string;
|
|
329
|
+
review: PreparedSystemOneReview;
|
|
330
|
+
raw: unknown;
|
|
331
|
+
report: SystemOneReviewReport;
|
|
332
|
+
tierRole: Record<string, TierRole>;
|
|
333
|
+
costUsd: number;
|
|
334
|
+
}
|
|
335
|
+
export declare function runReviews(input: {
|
|
336
|
+
specs: ReviewSpec[];
|
|
337
|
+
/** Review definition version; part of every review digest. */
|
|
338
|
+
version: string;
|
|
339
|
+
/** The judge; its reference is part of every review digest. */
|
|
340
|
+
judge: AppGradeJudge;
|
|
341
|
+
costLedger: CostLedger;
|
|
342
|
+
dir: string;
|
|
343
|
+
}): Promise<StoredReview[]>;
|
|
344
|
+
/** Offline re-score: same frozen review, same raw answer, no inference. */
|
|
345
|
+
export declare function rescore(entry: Pick<StoredReview, 'review' | 'raw'>): SystemOneReviewReport;
|
|
346
|
+
interface Passage {
|
|
347
|
+
source: string;
|
|
348
|
+
text: string;
|
|
349
|
+
}
|
|
350
|
+
export interface IntegrityEvidence {
|
|
351
|
+
/** Lines of app source that probe for an automated browser. */
|
|
352
|
+
automationProbes: Array<{
|
|
353
|
+
file: string;
|
|
354
|
+
line: number;
|
|
355
|
+
excerpt: string;
|
|
356
|
+
}>;
|
|
357
|
+
candidates: Array<{
|
|
358
|
+
source: string;
|
|
359
|
+
excerpt: string;
|
|
360
|
+
}>;
|
|
361
|
+
screens: Passage[];
|
|
362
|
+
console: Passage[];
|
|
363
|
+
network: Passage[];
|
|
364
|
+
source: Passage[];
|
|
365
|
+
counts: {
|
|
366
|
+
sourceFiles: number;
|
|
367
|
+
screens: number;
|
|
368
|
+
consoleLines: number;
|
|
369
|
+
networkPayloads: number;
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
export declare function collectIntegrityEvidence(input: {
|
|
373
|
+
workdir: string;
|
|
374
|
+
runDir: string;
|
|
375
|
+
appOrigin: string;
|
|
376
|
+
graderUserAgent: string | null;
|
|
377
|
+
}): IntegrityEvidence;
|
|
378
|
+
export declare function integrityReviewSpec(task: string, evidence: IntegrityEvidence): ReviewSpec;
|
|
379
|
+
/** What one leaf contributes to a grading run: its journeys, its deterministic checks and its SystemOne reviews. */
|
|
380
|
+
export interface GradeLeaf<E extends {
|
|
381
|
+
journeyMs: Record<string, number>;
|
|
382
|
+
}> {
|
|
383
|
+
id: string;
|
|
384
|
+
/** Review definition version; part of every review digest. */
|
|
385
|
+
reviewVersion: string;
|
|
386
|
+
/** Flows this grader cannot operate; any entry caps the tier below S. */
|
|
387
|
+
unmeasured: readonly string[];
|
|
388
|
+
/** Operate the journeys, each in its own browser context, writing evidence under `dir`. */
|
|
389
|
+
runJourneys(browser: EvidenceBrowser, dir: string, url: string, pick: Picker): Promise<E>;
|
|
390
|
+
/** Accessibility snapshot path of the first screen, for the `serve.rendered` floor check. */
|
|
391
|
+
landing(evidence: E): string | undefined;
|
|
392
|
+
deterministicChecks(evidence: E): VerdictCheck[];
|
|
393
|
+
/**
|
|
394
|
+
* SystemOne reviews over raw evidence only: what the app showed, what the grader did, and the
|
|
395
|
+
* source. No deterministic check status, build gate or derived verdict reaches a model.
|
|
396
|
+
*/
|
|
397
|
+
reviewSpecs(input: {
|
|
398
|
+
evidence: E;
|
|
399
|
+
readCapture: (path: string) => string;
|
|
400
|
+
workdir: string;
|
|
401
|
+
integrity: IntegrityEvidence;
|
|
402
|
+
}): ReviewSpec[];
|
|
403
|
+
}
|
|
404
|
+
export interface GradeResult {
|
|
405
|
+
verdict: Verdict;
|
|
406
|
+
/** Everything `deriveVerdict` read, so a stored run re-derives without the grader. */
|
|
407
|
+
inputs: {
|
|
408
|
+
served: boolean;
|
|
409
|
+
checks: VerdictCheck[];
|
|
410
|
+
reviews: VerdictReview[];
|
|
411
|
+
engineering: EngineeringGates;
|
|
412
|
+
unmeasured: string[];
|
|
413
|
+
};
|
|
414
|
+
checks: VerdictCheck[];
|
|
415
|
+
costUsd: number;
|
|
416
|
+
phaseMs: Record<string, number>;
|
|
417
|
+
}
|
|
418
|
+
export declare function grade<E extends {
|
|
419
|
+
journeyMs: Record<string, number>;
|
|
420
|
+
}>(input: {
|
|
421
|
+
leaf: GradeLeaf<E>;
|
|
422
|
+
workdir: string;
|
|
423
|
+
engineeringPath: string;
|
|
424
|
+
out: string;
|
|
425
|
+
port: number;
|
|
426
|
+
judge: AppGradeJudge;
|
|
427
|
+
/** Environment the served app receives beyond PATH, HOME, NODE_ENV and PORT. */
|
|
428
|
+
appEnv?: Record<string, string>;
|
|
429
|
+
browser?: Parameters<typeof EvidenceBrowser.launch>[0];
|
|
430
|
+
}): Promise<GradeResult>;
|
|
431
|
+
export declare function prepare(workdir: string): {
|
|
432
|
+
gates: EngineeringGates;
|
|
433
|
+
steps: Record<string, {
|
|
434
|
+
command: string;
|
|
435
|
+
exit: number | null;
|
|
436
|
+
ms: number;
|
|
437
|
+
tail: string;
|
|
438
|
+
} | {
|
|
439
|
+
skipped: string;
|
|
440
|
+
}>;
|
|
441
|
+
};
|
|
442
|
+
export type Json = null | boolean | number | string | Json[] | {
|
|
443
|
+
[key: string]: Json;
|
|
444
|
+
};
|
|
445
|
+
/** The real product: its official image run fresh for every grading run, or a vendor's hosted test mode. */
|
|
446
|
+
export type ProductSpec = ImageProductSpec | HostedProductSpec;
|
|
447
|
+
export interface ImageProductSpec extends ProductBase {
|
|
448
|
+
/** Official image pinned by digest: `<repository>:<tag>@sha256:<64 hex>`. */
|
|
449
|
+
image: string;
|
|
450
|
+
/** Container port the product listens on. */
|
|
451
|
+
port: number;
|
|
452
|
+
/** Container environment. `{{adminSecret}}` is the grader's per-run admin secret. */
|
|
453
|
+
env?: Record<string, string>;
|
|
454
|
+
/** Arguments after the image entrypoint. They must not carry the admin secret. */
|
|
455
|
+
args?: string[];
|
|
456
|
+
/** Unauthenticated readiness probe; GET unless `method` says otherwise, as `versionProbe`. */
|
|
457
|
+
ready: {
|
|
458
|
+
path: string;
|
|
459
|
+
method?: HttpMethod;
|
|
460
|
+
body?: Json;
|
|
461
|
+
status?: number;
|
|
462
|
+
};
|
|
463
|
+
readyTimeoutMs?: number;
|
|
464
|
+
hosted?: undefined;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* A vendor's hosted test environment (Stripe test mode) that no image can stand up. The grader
|
|
468
|
+
* reaches `origin` with the secret in `secretEnv`; the app reaches it only through the recording
|
|
469
|
+
* proxy, which swaps the app's per-run placeholder key for that secret, so the secret never
|
|
470
|
+
* enters the app, its logs or the evidence.
|
|
471
|
+
*/
|
|
472
|
+
export interface HostedProductSpec extends ProductBase {
|
|
473
|
+
hosted: {
|
|
474
|
+
origin: string;
|
|
475
|
+
secretEnv: string;
|
|
476
|
+
/** Prefix of the app's per-run placeholder key, e.g. `sk_test_vb_`. */
|
|
477
|
+
appKeyPrefix: string;
|
|
478
|
+
/** Headers the grader's admin requests carry, e.g. a pinned `Stripe-Version`; app requests keep their own. */
|
|
479
|
+
headers?: Record<string, string>;
|
|
480
|
+
/** Admin request bodies are form-encoded with bracketed keys (Stripe) instead of JSON. */
|
|
481
|
+
formBodies?: boolean;
|
|
482
|
+
/**
|
|
483
|
+
* A fresh tenant per run (a Stripe connected account), so concurrent runs share nothing: created
|
|
484
|
+
* by `create`, ready when GET `<create.path>/<id>` reads `ready.equals` at `ready.jsonPath`, deleted
|
|
485
|
+
* on stop. Every admin and app request carries its id in `header`; app variable `appEnv` holds it.
|
|
486
|
+
*/
|
|
487
|
+
tenant?: {
|
|
488
|
+
create: {
|
|
489
|
+
path: string;
|
|
490
|
+
body: Json;
|
|
491
|
+
};
|
|
492
|
+
header: string;
|
|
493
|
+
ready: {
|
|
494
|
+
jsonPath: string;
|
|
495
|
+
equals: Json;
|
|
496
|
+
timeoutMs?: number;
|
|
497
|
+
};
|
|
498
|
+
appEnv?: string;
|
|
499
|
+
};
|
|
500
|
+
/** The grader relays the run's events from `path` to the app's `route`, signed with the per-run secret in app variable `secretEnv`. */
|
|
501
|
+
events?: {
|
|
502
|
+
path: string;
|
|
503
|
+
route: string;
|
|
504
|
+
secretEnv: string;
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
image?: undefined;
|
|
508
|
+
}
|
|
509
|
+
interface ProductBase {
|
|
510
|
+
/** Product name, lowercase, e.g. `meilisearch`. */
|
|
511
|
+
name: string;
|
|
512
|
+
/** The version the running product must report through `versionProbe`. */
|
|
513
|
+
version: string;
|
|
514
|
+
/**
|
|
515
|
+
* Admin-authenticated request (GET by default; a JSON-RPC node takes POST `/` with its envelope
|
|
516
|
+
* as `body`) whose JSON value at `jsonPath` is the running version. A hosted product states its
|
|
517
|
+
* test mode here: Stripe's `/v1/balance` `livemode` must read `false`.
|
|
518
|
+
*/
|
|
519
|
+
versionProbe: {
|
|
520
|
+
path: string;
|
|
521
|
+
method?: HttpMethod;
|
|
522
|
+
body?: Json;
|
|
523
|
+
jsonPath: string;
|
|
524
|
+
};
|
|
525
|
+
/** How admin requests authenticate: `<header>: <prefix><adminSecret>`. */
|
|
526
|
+
auth: {
|
|
527
|
+
header: string;
|
|
528
|
+
prefix?: string;
|
|
529
|
+
};
|
|
530
|
+
/**
|
|
531
|
+
* Environment names the app receives at runtime: `url` holds the product URL (the grader's
|
|
532
|
+
* recording proxy in front of the product), `key` holds `appKey` when set.
|
|
533
|
+
*/
|
|
534
|
+
appEnv: {
|
|
535
|
+
url: string;
|
|
536
|
+
key?: string;
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* The credential the app receives in `appEnv.key`, a template over the setup context,
|
|
540
|
+
* for example `{{vars.appKey}}` captured by a setup step. Omit when the app needs none, and
|
|
541
|
+
* for a hosted product, whose app key is its per-run placeholder.
|
|
542
|
+
*/
|
|
543
|
+
appKey?: string;
|
|
544
|
+
}
|
|
545
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
546
|
+
/** One admin request the grader makes against the product before the app starts. */
|
|
547
|
+
export interface HttpStep {
|
|
548
|
+
name: string;
|
|
549
|
+
method: HttpMethod;
|
|
550
|
+
/** Path and query, templated. */
|
|
551
|
+
path: string;
|
|
552
|
+
/** JSON body, templated. */
|
|
553
|
+
body?: Json;
|
|
554
|
+
/** Accepted statuses. Default: any 2xx. */
|
|
555
|
+
expectStatus?: number[];
|
|
556
|
+
/** Values to keep for later templates: `vars.<name>` = the response JSON at the path. */
|
|
557
|
+
capture?: Record<string, string>;
|
|
558
|
+
/** Poll a GET until the JSON value at `jsonPath` equals `equals` (asynchronous writes). */
|
|
559
|
+
waitFor?: {
|
|
560
|
+
path: string;
|
|
561
|
+
jsonPath: string;
|
|
562
|
+
equals: Json;
|
|
563
|
+
timeoutMs?: number;
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
/** A value drawn fresh for every grading run, so a submission cannot hard-code it. */
|
|
567
|
+
export type ProbeSpec = {
|
|
568
|
+
kind: 'word';
|
|
569
|
+
prefix?: string;
|
|
570
|
+
} | {
|
|
571
|
+
kind: 'int';
|
|
572
|
+
min: number;
|
|
573
|
+
max: number;
|
|
574
|
+
} | {
|
|
575
|
+
kind: 'pick';
|
|
576
|
+
from: Json[];
|
|
577
|
+
};
|
|
578
|
+
export type JourneyStep = {
|
|
579
|
+
open: string;
|
|
580
|
+
} | {
|
|
581
|
+
fill: {
|
|
582
|
+
goal: string;
|
|
583
|
+
value: string;
|
|
584
|
+
};
|
|
585
|
+
} | {
|
|
586
|
+
select: {
|
|
587
|
+
goal: string;
|
|
588
|
+
value: string;
|
|
589
|
+
};
|
|
590
|
+
} | {
|
|
591
|
+
click: {
|
|
592
|
+
goal: string;
|
|
593
|
+
};
|
|
594
|
+
} | {
|
|
595
|
+
press: string;
|
|
596
|
+
} | {
|
|
597
|
+
wait: number;
|
|
598
|
+
} | {
|
|
599
|
+
waitForText: {
|
|
600
|
+
text: string;
|
|
601
|
+
timeoutMs?: number;
|
|
602
|
+
};
|
|
603
|
+
} | {
|
|
604
|
+
capture: string;
|
|
605
|
+
};
|
|
606
|
+
/** A user path through the running app. `fill`, `select` and `click` goals are resolved by the target picker. */
|
|
607
|
+
export interface JourneySpec {
|
|
608
|
+
id: string;
|
|
609
|
+
steps: JourneyStep[];
|
|
610
|
+
}
|
|
611
|
+
/** An admin read of the product after the journeys: what the product itself holds or answers. */
|
|
612
|
+
export interface ReadSpec {
|
|
613
|
+
name: string;
|
|
614
|
+
method: 'GET' | 'POST';
|
|
615
|
+
path: string;
|
|
616
|
+
body?: Json;
|
|
617
|
+
}
|
|
618
|
+
/** Which proxied app-to-product requests a check counts. Strings are templates. */
|
|
619
|
+
export interface RequestMatch {
|
|
620
|
+
method?: HttpMethod;
|
|
621
|
+
/** Regular expression over the request path (without query). */
|
|
622
|
+
path?: string;
|
|
623
|
+
credential?: 'app' | 'admin' | 'none' | 'other';
|
|
624
|
+
from?: 'server' | 'browser';
|
|
625
|
+
/** Every value must appear in the request body text. */
|
|
626
|
+
bodyIncludes?: string[];
|
|
627
|
+
/** Every value must appear in the query string. */
|
|
628
|
+
queryIncludes?: string[];
|
|
629
|
+
status?: number[];
|
|
630
|
+
}
|
|
631
|
+
export type CheckKind = {
|
|
632
|
+
kind: 'journeyCompleted';
|
|
633
|
+
journey: string;
|
|
634
|
+
} | {
|
|
635
|
+
kind: 'pageContains';
|
|
636
|
+
capture: string;
|
|
637
|
+
values: string[];
|
|
638
|
+
mode?: 'all' | 'any';
|
|
639
|
+
} | {
|
|
640
|
+
kind: 'pageLacks';
|
|
641
|
+
capture: string;
|
|
642
|
+
values: string[];
|
|
643
|
+
} | {
|
|
644
|
+
kind: 'productRequests';
|
|
645
|
+
match: RequestMatch;
|
|
646
|
+
min?: number;
|
|
647
|
+
max?: number;
|
|
648
|
+
} | {
|
|
649
|
+
kind: 'readMatches';
|
|
650
|
+
read: string;
|
|
651
|
+
jsonPath: string;
|
|
652
|
+
equals?: Json;
|
|
653
|
+
includes?: Json;
|
|
654
|
+
length?: {
|
|
655
|
+
min?: number;
|
|
656
|
+
max?: number;
|
|
657
|
+
};
|
|
658
|
+
} |
|
|
659
|
+
/**
|
|
660
|
+
* The strings at `jsonPath` (an array; `field` picks a property of each element) in a read,
|
|
661
|
+
* first `limit` of them: `shown` requires each on the page, `ordered` also in that order,
|
|
662
|
+
* `absent` requires none on the page.
|
|
663
|
+
*/
|
|
664
|
+
{
|
|
665
|
+
kind: 'pageRead';
|
|
666
|
+
capture: string;
|
|
667
|
+
read: string;
|
|
668
|
+
jsonPath: string;
|
|
669
|
+
field?: string;
|
|
670
|
+
limit?: number;
|
|
671
|
+
expect: 'shown' | 'ordered' | 'absent';
|
|
672
|
+
};
|
|
673
|
+
/** `floor` failures give F; `journey` failures give C. See verdict.ts. */
|
|
674
|
+
export type CheckSpec = {
|
|
675
|
+
id: string;
|
|
676
|
+
gate: 'floor' | 'journey';
|
|
677
|
+
why: string;
|
|
678
|
+
} & CheckKind;
|
|
679
|
+
export type SystemOneQuestionSpec = {
|
|
680
|
+
type: 'noul';
|
|
681
|
+
instructions: string;
|
|
682
|
+
} | {
|
|
683
|
+
type: 'score';
|
|
684
|
+
instructions: string;
|
|
685
|
+
criteria: string[];
|
|
686
|
+
} | {
|
|
687
|
+
type: 'choice';
|
|
688
|
+
instructions: string;
|
|
689
|
+
criteria: Record<string, string>;
|
|
690
|
+
};
|
|
691
|
+
/** One bounded SystemOne question with how its answer moves the tier. */
|
|
692
|
+
export interface ReviewQuestionSpec {
|
|
693
|
+
question: SystemOneQuestionSpec;
|
|
694
|
+
claim: string;
|
|
695
|
+
area: string;
|
|
696
|
+
severity: 'info' | 'low' | 'medium' | 'high' | 'critical';
|
|
697
|
+
/** Native answers that support the claim (`true`/`false`, score levels, or choice keys). */
|
|
698
|
+
supports: string[];
|
|
699
|
+
refutes: string[];
|
|
700
|
+
role: TierRole;
|
|
701
|
+
}
|
|
702
|
+
/** A SystemOne review over named evidence. */
|
|
703
|
+
export interface ReviewSpecData {
|
|
704
|
+
id: string;
|
|
705
|
+
evidence: {
|
|
706
|
+
captures?: string[];
|
|
707
|
+
reads?: string[];
|
|
708
|
+
productLog?: boolean;
|
|
709
|
+
code?: boolean;
|
|
710
|
+
};
|
|
711
|
+
questions: Record<string, ReviewQuestionSpec>;
|
|
712
|
+
}
|
|
713
|
+
/** The grader side of one task. Contestants never see it. */
|
|
714
|
+
export interface AppGradeSpec {
|
|
715
|
+
/** The task as the reviewer should understand it (not the contestant prompt). */
|
|
716
|
+
task: string;
|
|
717
|
+
probes: Record<string, ProbeSpec>;
|
|
718
|
+
/** Admin requests before the app starts: seed data, settings, the app's credential. */
|
|
719
|
+
setup: HttpStep[];
|
|
720
|
+
journeys: JourneySpec[];
|
|
721
|
+
reads: ReadSpec[];
|
|
722
|
+
checks: CheckSpec[];
|
|
723
|
+
reviews: ReviewSpecData[];
|
|
724
|
+
/** Flows this grader cannot operate; any entry caps the tier below S. */
|
|
725
|
+
unmeasured: string[];
|
|
726
|
+
/** The lowest tier a correct submission earns; admission requires the reference to reach it. */
|
|
727
|
+
passTier: Exclude<Tier, 'F' | 'C'>;
|
|
728
|
+
/**
|
|
729
|
+
* The floor or journey check that encodes the current-product fact a from-memory build gets
|
|
730
|
+
* wrong. Calibration requires the reference to pass it and the stale build to fail it every run.
|
|
731
|
+
*/
|
|
732
|
+
factCheck?: string;
|
|
733
|
+
}
|
|
734
|
+
/** Value at a dotted path (`hits.0.title`); undefined when any segment is missing. */
|
|
735
|
+
export declare function jsonPath(value: unknown, path: string): unknown;
|
|
736
|
+
/**
|
|
737
|
+
* Render `{{a.b}}` references against `context`. A string that is exactly one reference
|
|
738
|
+
* takes the referenced value itself, so numbers and arrays survive. A missing reference
|
|
739
|
+
* throws: a template that renders to nothing would grade against the wrong value.
|
|
740
|
+
*/
|
|
741
|
+
export declare function render<T extends Json | undefined>(template: T, context: object): T;
|
|
742
|
+
/** Every `{{…}}` reference in a value, for linting. */
|
|
743
|
+
export declare function references(value: unknown): string[];
|
|
744
|
+
/** What calibration binds: the digest-pinned image, or the hosted origin with its pinned headers. */
|
|
745
|
+
export declare function productIdentity(product: ProductSpec): string;
|
|
746
|
+
/** The image digest a provenance record freezes; a hosted product has none. */
|
|
747
|
+
export declare function productImageDigest(product: ProductSpec | undefined): string | undefined;
|
|
748
|
+
export declare function lintProductSpec(product: ProductSpec): string[];
|
|
749
|
+
/**
|
|
750
|
+
* Structural problems in a task spec. A spec that passes can still be wrong; calibration
|
|
751
|
+
* decides that. This only refuses specs whose checks cannot discriminate by construction.
|
|
752
|
+
*/
|
|
753
|
+
export declare function lintAppGradeSpec(spec: AppGradeSpec, product: ProductSpec | undefined): string[];
|
|
754
|
+
/** Remove only products started by one App Grade process, including an interrupted startup. */
|
|
755
|
+
export declare function cleanupAppGradeProducts(runId: string, followLateCreate?: boolean): Promise<void>;
|
|
756
|
+
export interface ProductContext {
|
|
757
|
+
probe: Record<string, Json>;
|
|
758
|
+
vars: Record<string, Json>;
|
|
759
|
+
}
|
|
760
|
+
export interface RunningProduct {
|
|
761
|
+
spec: ProductSpec;
|
|
762
|
+
/** Where grader requests go: the container's host URL or the vendor's origin. */
|
|
763
|
+
url: string;
|
|
764
|
+
reportedVersion: string;
|
|
765
|
+
/** A hosted product's per-run placeholder app key; an image product's key comes from setup. */
|
|
766
|
+
appKey: string | null;
|
|
767
|
+
/** App variables the product itself sets: a hosted tenant id and webhook secret. */
|
|
768
|
+
appEnv: Record<string, string>;
|
|
769
|
+
/** Admin request against the product. The response body is parsed JSON when it is JSON. */
|
|
770
|
+
admin(method: string, path: string, body?: Json): Promise<{
|
|
771
|
+
status: number;
|
|
772
|
+
body: unknown;
|
|
773
|
+
}>;
|
|
774
|
+
/** Values that must never appear in evidence: the admin secret and minted credentials. */
|
|
775
|
+
secrets(): string[];
|
|
776
|
+
addSecret(value: string): void;
|
|
777
|
+
credentialOf(headers: IncomingHttpHeaders, appKey: string | null): 'admin' | 'app' | 'none' | 'other';
|
|
778
|
+
/** The headers an app request goes upstream with: product host, tenant scope, placeholder swapped. */
|
|
779
|
+
forward(headers: IncomingHttpHeaders): OutgoingHttpHeaders;
|
|
780
|
+
stop(): Promise<void>;
|
|
781
|
+
}
|
|
782
|
+
/** `untracked` means startup failed before a caller could retain the product for cleanup retry. */
|
|
783
|
+
export declare class ProductCleanupError extends AggregateError {
|
|
784
|
+
readonly untracked: boolean;
|
|
785
|
+
constructor(errors: unknown[], message: string, untracked?: boolean);
|
|
786
|
+
}
|
|
787
|
+
/** Fresh values for one run. A word probe is a pronounceable token no model has seen with this task. */
|
|
788
|
+
export declare function drawProbes(probes: Record<string, ProbeSpec>): Record<string, Json>;
|
|
789
|
+
/**
|
|
790
|
+
* The vendor cannot reach a grader-local app, so the grader delivers the run's events: it lists
|
|
791
|
+
* them and POSTs each, oldest first, to the app's webhook route until the app answers 2xx (five
|
|
792
|
+
* tries), signed with the run's secret in Stripe's `t=<unix>,v1=<HMAC-SHA256("<t>.<body>")>` scheme.
|
|
793
|
+
*/
|
|
794
|
+
export declare function startEventRelay(product: RunningProduct, appUrl: string, logPath: string): {
|
|
795
|
+
stop(): Promise<void>;
|
|
796
|
+
};
|
|
797
|
+
/** Start the pinned image, or a hosted tenant, and wait until it answers as the declared version. */
|
|
798
|
+
export declare function startProduct(spec: ProductSpec, options?: {
|
|
799
|
+
logPath?: string;
|
|
800
|
+
signal?: AbortSignal;
|
|
801
|
+
}): Promise<RunningProduct>;
|
|
802
|
+
/** Run the setup steps as admin, in order. Returns the captured variables. */
|
|
803
|
+
export declare function runSetup(product: RunningProduct, steps: HttpStep[], context: ProductContext, log: (entry: unknown) => void): Promise<void>;
|
|
804
|
+
/** Admin reads after the journeys: what the product itself holds or answers. */
|
|
805
|
+
export declare function runReads(product: RunningProduct, reads: ReadSpec[], context: ProductContext): Promise<Record<string, {
|
|
806
|
+
status: number;
|
|
807
|
+
body: unknown;
|
|
808
|
+
}>>;
|
|
809
|
+
/** One app request the proxy forwarded to the product. */
|
|
810
|
+
export interface ProductRequest {
|
|
811
|
+
t: number;
|
|
812
|
+
method: string;
|
|
813
|
+
path: string;
|
|
814
|
+
query: string;
|
|
815
|
+
credential: 'admin' | 'app' | 'none' | 'other';
|
|
816
|
+
/** `browser` when the request carried browser fetch metadata (Origin, Sec-Fetch-Site or Sec-Fetch-Dest). */
|
|
817
|
+
from: 'server' | 'browser';
|
|
818
|
+
status: number | null;
|
|
819
|
+
body: string;
|
|
820
|
+
response: string;
|
|
821
|
+
}
|
|
822
|
+
export interface RecordingProxy {
|
|
823
|
+
url: string;
|
|
824
|
+
requests: ProductRequest[];
|
|
825
|
+
close(): Promise<void>;
|
|
826
|
+
}
|
|
827
|
+
export declare function redact(text: string, secrets: string[]): string;
|
|
828
|
+
/**
|
|
829
|
+
* A pass-through proxy on 127.0.0.1 in front of the product. It forwards every request with the
|
|
830
|
+
* product's headers (`RunningProduct.forward`) and records method, path, query, credential
|
|
831
|
+
* identity, origin kind, status and truncated bodies, with the grader's secrets redacted.
|
|
832
|
+
*/
|
|
833
|
+
export declare function startRecordingProxy(product: RunningProduct, appKey: string | null, logPath: string): Promise<RecordingProxy>;
|
|
834
|
+
export interface ServeAttestation {
|
|
835
|
+
command: string[];
|
|
836
|
+
pid: number | null;
|
|
837
|
+
portOwnerPid: number | null;
|
|
838
|
+
portOwnerCommand: string | null;
|
|
839
|
+
/** The listener belongs to the process group this call spawned, not to a server left on the port. */
|
|
840
|
+
portOwnerIsChild: boolean;
|
|
841
|
+
readyAfterMs: number | null;
|
|
842
|
+
lastStatus: number | null;
|
|
843
|
+
isScaffoldPreview: boolean;
|
|
844
|
+
}
|
|
845
|
+
export interface ServedApp {
|
|
846
|
+
url: string;
|
|
847
|
+
attestation: ServeAttestation;
|
|
848
|
+
stop: () => Promise<void>;
|
|
849
|
+
}
|
|
850
|
+
/** The process listening on `port`, or all nulls when none does or the lookup fails. */
|
|
851
|
+
export declare function portOwner(port: number): {
|
|
852
|
+
pid: number | null;
|
|
853
|
+
pgid: number | null;
|
|
854
|
+
command: string | null;
|
|
855
|
+
};
|
|
856
|
+
export declare function serveApp(input: {
|
|
857
|
+
workdir: string;
|
|
858
|
+
port: number;
|
|
859
|
+
logPath: string;
|
|
860
|
+
readyTimeoutMs?: number;
|
|
861
|
+
env?: Record<string, string>;
|
|
862
|
+
}): Promise<ServedApp>;
|
|
863
|
+
export interface SourceFile {
|
|
864
|
+
path: string;
|
|
865
|
+
content: string;
|
|
866
|
+
}
|
|
867
|
+
/** Every text file the submission can serve or build from, within `extensions`. */
|
|
868
|
+
export declare function sourceFiles(workdir: string, extensions?: RegExp): SourceFile[];
|
|
869
|
+
/** Files that touch payment or tests first, within a fixed character budget. */
|
|
870
|
+
export declare function codeFiles(workdir: string, budget?: number): {
|
|
871
|
+
files: {
|
|
872
|
+
path: string;
|
|
873
|
+
lines: string;
|
|
874
|
+
content: string;
|
|
875
|
+
}[];
|
|
876
|
+
relevantCount: number;
|
|
877
|
+
total: number;
|
|
878
|
+
};
|
|
879
|
+
/**
|
|
880
|
+
* Longest app screen and source file any review quotes. The integrity review quotes
|
|
881
|
+
* each passage at least this far, so no review reads app text it did not.
|
|
882
|
+
*/
|
|
883
|
+
export declare const SCREEN_CHARS = 20000;
|
|
884
|
+
export declare const SOURCE_FILE_CHARS = 20000;
|
|
885
|
+
/** App-originated text enters SystemOne state as quoted, untrusted data with its provenance. */
|
|
886
|
+
export declare function untrusted(source: string, text: string, max?: number): {
|
|
887
|
+
untrusted: boolean;
|
|
888
|
+
source: string;
|
|
889
|
+
text: string;
|
|
890
|
+
};
|
|
891
|
+
export interface CaptureRecord {
|
|
892
|
+
journey: string;
|
|
893
|
+
ariaPath: string;
|
|
894
|
+
textPath: string;
|
|
895
|
+
screenshotPath: string;
|
|
896
|
+
url: string;
|
|
897
|
+
}
|
|
898
|
+
export interface SpecEvidence {
|
|
899
|
+
journeys: Record<string, {
|
|
900
|
+
completed: boolean;
|
|
901
|
+
problems: string[];
|
|
902
|
+
}>;
|
|
903
|
+
captures: Record<string, CaptureRecord>;
|
|
904
|
+
reads: Record<string, {
|
|
905
|
+
status: number;
|
|
906
|
+
body: unknown;
|
|
907
|
+
}>;
|
|
908
|
+
productRequests: ProductRequest[];
|
|
909
|
+
probes: Record<string, Json>;
|
|
910
|
+
journeyMs: Record<string, number>;
|
|
911
|
+
}
|
|
912
|
+
export declare function evaluateChecks(spec: AppGradeSpec, evidence: SpecEvidence, readText: (path: string) => string, context: ProductContext): VerdictCheck[];
|
|
913
|
+
/**
|
|
914
|
+
* SystemOne reviews over the evidence itself. The deterministic checks' statuses and the build
|
|
915
|
+
* gates stay out of the reviewer's state: a reviewer that reads the grader's own verdicts
|
|
916
|
+
* grades the grader, not the app.
|
|
917
|
+
*/
|
|
918
|
+
export declare function specReviews(input: {
|
|
919
|
+
spec: AppGradeSpec;
|
|
920
|
+
product: RunningProduct['spec'];
|
|
921
|
+
evidence: SpecEvidence;
|
|
922
|
+
readText: (path: string) => string;
|
|
923
|
+
workdir: string;
|
|
924
|
+
}): ReviewSpec[];
|
|
925
|
+
/** Build the grade leaf for one spec-driven task against a started product and proxy. */
|
|
926
|
+
export declare function specLeaf(input: {
|
|
927
|
+
id: string;
|
|
928
|
+
/** Digest of the task material; part of every review digest, so a changed spec never reuses a review. */
|
|
929
|
+
specDigest: string;
|
|
930
|
+
spec: AppGradeSpec;
|
|
931
|
+
product: RunningProduct;
|
|
932
|
+
context: ProductContext;
|
|
933
|
+
requests: () => ProductRequest[];
|
|
934
|
+
readText: (path: string) => string;
|
|
935
|
+
}): GradeLeaf<SpecEvidence>;
|
|
936
|
+
/** Output cap per SystemOne request, including reasoning tokens; also bounds the ledger reservation. */
|
|
937
|
+
export declare const SYSTEM_ONE_MAX_OUTPUT_TOKENS = 6000;
|
|
938
|
+
/** Paid work that produced no valid answer; carries usage so the ledger still records it. */
|
|
939
|
+
export declare class SystemOneTransportError extends Error {
|
|
940
|
+
readonly model: string;
|
|
941
|
+
readonly usage: {
|
|
942
|
+
input_tokens: number;
|
|
943
|
+
output_tokens: number;
|
|
944
|
+
};
|
|
945
|
+
readonly costUsd: number | null;
|
|
946
|
+
constructor(message: string, model: string, usage: {
|
|
947
|
+
input_tokens: number;
|
|
948
|
+
output_tokens: number;
|
|
949
|
+
}, costUsd: number | null, options?: {
|
|
950
|
+
cause?: unknown;
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* One transport call may make up to `malformedRetries + 1` paid requests when the
|
|
955
|
+
* model returns unusable JSON. Their usage and billed cost are summed into the one
|
|
956
|
+
* result (or error) the ledger records, so no paid attempt goes unaccounted.
|
|
957
|
+
*/
|
|
958
|
+
export declare function chatSystemOneTransport(options: {
|
|
959
|
+
judge: AppGradeJudge;
|
|
960
|
+
malformedRetries?: number;
|
|
961
|
+
}): (request: SystemOneRequest, context: {
|
|
962
|
+
signal: AbortSignal;
|
|
963
|
+
idempotencyKey: string;
|
|
964
|
+
}) => Promise<{
|
|
965
|
+
model: string;
|
|
966
|
+
answers: {
|
|
967
|
+
[k: string]: {
|
|
968
|
+
type: "noul";
|
|
969
|
+
noul: number;
|
|
970
|
+
score?: undefined;
|
|
971
|
+
confidence?: undefined;
|
|
972
|
+
legend?: undefined;
|
|
973
|
+
probabilities?: undefined;
|
|
974
|
+
choice?: undefined;
|
|
975
|
+
} | {
|
|
976
|
+
type: "score";
|
|
977
|
+
score: number;
|
|
978
|
+
confidence: number;
|
|
979
|
+
legend: {
|
|
980
|
+
[k: string]: SystemOneState;
|
|
981
|
+
};
|
|
982
|
+
probabilities: Record<string, number>;
|
|
983
|
+
noul?: undefined;
|
|
984
|
+
choice?: undefined;
|
|
985
|
+
} | {
|
|
986
|
+
type: "choice";
|
|
987
|
+
choice: string;
|
|
988
|
+
confidence: number;
|
|
989
|
+
probabilities: Record<string, number>;
|
|
990
|
+
noul?: undefined;
|
|
991
|
+
score?: undefined;
|
|
992
|
+
legend?: undefined;
|
|
993
|
+
};
|
|
994
|
+
};
|
|
995
|
+
usage: {
|
|
996
|
+
input_tokens: number;
|
|
997
|
+
output_tokens: number;
|
|
998
|
+
};
|
|
999
|
+
transport: {
|
|
1000
|
+
route: string;
|
|
1001
|
+
via: ChatTransport;
|
|
1002
|
+
paidRequests: number;
|
|
1003
|
+
costUsd: number | null;
|
|
1004
|
+
toolCalls: string[];
|
|
1005
|
+
};
|
|
1006
|
+
}>;
|
|
1007
|
+
export interface AppGradeTask {
|
|
1008
|
+
schema: typeof TASK_SCHEMA;
|
|
1009
|
+
id: string;
|
|
1010
|
+
/** The suite fields the caller builds the contestant's scaffold and prompt from. */
|
|
1011
|
+
suite: Record<string, unknown>;
|
|
1012
|
+
/** What the contestant sees. It never carries the grader spec. */
|
|
1013
|
+
leaf: {
|
|
1014
|
+
id: string;
|
|
1015
|
+
} & Record<string, unknown>;
|
|
1016
|
+
product: ProductSpec;
|
|
1017
|
+
spec: AppGradeSpec;
|
|
1018
|
+
}
|
|
1019
|
+
export interface LoadedTask extends AppGradeTask {
|
|
1020
|
+
dir: string;
|
|
1021
|
+
/** Digest of the complete task material (suite, leaf, product, spec). */
|
|
1022
|
+
digest: string;
|
|
1023
|
+
}
|
|
1024
|
+
export declare function taskProblems(task: AppGradeTask): string[];
|
|
1025
|
+
export declare function loadTask(dir: string): LoadedTask;
|
|
1026
|
+
export declare function taskDigest(task: AppGradeTask): string;
|
|
1027
|
+
/** One grading run of a served submission against a fresh instance of the task's product. */
|
|
1028
|
+
export declare function gradeTask(input: {
|
|
1029
|
+
task: AppGradeTask & {
|
|
1030
|
+
digest: string;
|
|
1031
|
+
};
|
|
1032
|
+
workdir: string;
|
|
1033
|
+
engineeringPath: string;
|
|
1034
|
+
out: string;
|
|
1035
|
+
port: number;
|
|
1036
|
+
judge: AppGradeJudge;
|
|
1037
|
+
browser?: Parameters<typeof grade>[0]['browser'];
|
|
1038
|
+
}): Promise<GradeResult & {
|
|
1039
|
+
probes: Record<string, unknown>;
|
|
1040
|
+
}>;
|
|
1041
|
+
export {};
|
|
1042
|
+
//# sourceMappingURL=index.d.ts.map
|