argus-reviewer-e2e 0.3.1 → 0.4.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/README.md +84 -71
- package/action/action.yml +129 -11
- package/action/approval-review.mjs +13 -3
- package/action/bootstrap.mjs +2 -0
- package/action/emit-review.mjs +16 -0
- package/action/runtime.mjs +20 -0
- package/action/sticky-comment.cjs +1260 -479
- package/dist/cli.d.ts +103 -7
- package/dist/cli.js +1202 -186
- package/dist/config.d.ts +96 -11
- package/dist/config.js +102 -4
- package/dist/detect.d.ts +29 -2
- package/dist/detect.js +98 -7
- package/dist/driver/browser.d.ts +32 -0
- package/dist/driver/browser.js +56 -1
- package/dist/driver/target.d.ts +4 -1
- package/dist/driver/target.js +27 -6
- package/dist/engine/actions.d.ts +5 -0
- package/dist/engine/actions.js +8 -0
- package/dist/engine/explore.d.ts +78 -0
- package/dist/engine/explore.js +373 -0
- package/dist/engine/loop.d.ts +2 -2
- package/dist/engine/loop.js +8 -8
- package/dist/engine/prompts.d.ts +28 -1
- package/dist/engine/prompts.js +88 -0
- package/dist/evidence/ci.d.ts +13 -1
- package/dist/evidence/ci.js +38 -3
- package/dist/evidence/gate.d.ts +8 -0
- package/dist/evidence/gate.js +1 -1
- package/dist/evidence/link.js +1 -1
- package/dist/executor/a0.d.ts +114 -1
- package/dist/executor/a0.js +216 -4
- package/dist/fsutil.d.ts +3 -2
- package/dist/fsutil.js +7 -4
- package/dist/journal/schema.d.ts +1 -1
- package/dist/log.d.ts +2 -1
- package/dist/log.js +10 -2
- package/dist/mention.d.ts +45 -0
- package/dist/mention.js +107 -0
- package/dist/pipeline/app.d.ts +126 -0
- package/dist/pipeline/app.js +250 -0
- package/dist/pipeline/budget.d.ts +1 -0
- package/dist/pipeline/budget.js +1 -1
- package/dist/pipeline/verify.d.ts +20 -3
- package/dist/pipeline/verify.js +189 -35
- package/dist/probe/persist.d.ts +68 -0
- package/dist/probe/persist.js +184 -0
- package/dist/probe/queue.d.ts +12 -0
- package/dist/probe/queue.js +10 -2
- package/dist/report/brand-assets.generated.d.ts +9 -0
- package/dist/report/brand-assets.generated.js +8 -0
- package/dist/report/comment.d.ts +99 -6
- package/dist/report/comment.js +292 -103
- package/dist/report/html.d.ts +50 -0
- package/dist/report/html.js +879 -0
- package/dist/report/manifest.d.ts +29 -0
- package/dist/report/manifest.js +37 -0
- package/dist/report/run.d.ts +54 -1
- package/dist/report/run.js +34 -9
- package/dist/report/viewmodel.d.ts +91 -0
- package/dist/report/viewmodel.js +241 -0
- package/dist/review/adjudicate.d.ts +6 -6
- package/dist/review/adjudicate.js +2 -2
- package/dist/review/inline.d.ts +44 -0
- package/dist/review/inline.js +95 -0
- package/dist/review/packs.d.ts +21 -0
- package/dist/review/packs.js +47 -0
- package/dist/review/scope.d.ts +16 -0
- package/dist/review/scope.js +74 -0
- package/dist/review/secrets.d.ts +10 -10
- package/dist/review/secrets.js +7 -7
- package/dist/review/testfiles.d.ts +18 -0
- package/dist/review/testfiles.js +26 -0
- package/dist/review/triage.d.ts +1 -1
- package/dist/review/triage.js +10 -10
- package/dist/review/validate.d.ts +41 -0
- package/dist/review/validate.js +76 -0
- package/dist/ui/errors.d.ts +54 -0
- package/dist/ui/errors.js +236 -0
- package/dist/ui/style.d.ts +34 -0
- package/dist/ui/style.js +48 -0
- package/dist/ui/summary.d.ts +38 -0
- package/dist/ui/summary.js +101 -0
- package/dist/vision/cost.d.ts +1 -1
- package/dist/vision/decisions.d.ts +9 -3
- package/dist/vision/decisions.js +31 -21
- package/dist/vision/openrouter.d.ts +4 -0
- package/dist/vision/openrouter.js +30 -4
- package/package.json +11 -2
package/dist/engine/loop.js
CHANGED
|
@@ -42,7 +42,7 @@ export class Engine {
|
|
|
42
42
|
get cacheStats() {
|
|
43
43
|
return { ...this._cacheStats };
|
|
44
44
|
}
|
|
45
|
-
async record(instruction,
|
|
45
|
+
async record(instruction, uiApi = this._opts.actions, options = {}) {
|
|
46
46
|
this._visionCalls = 0;
|
|
47
47
|
this._steps = [];
|
|
48
48
|
this._fingerprints = [];
|
|
@@ -76,7 +76,7 @@ export class Engine {
|
|
|
76
76
|
return this._result(false, action.reasoning);
|
|
77
77
|
}
|
|
78
78
|
const resolved = await this._resolveAction(action);
|
|
79
|
-
const nextObservation = await this._executeAction(
|
|
79
|
+
const nextObservation = await this._executeAction(uiApi, action);
|
|
80
80
|
const fingerprint = await this._buildFingerprint(instruction, action, resolved, response.model);
|
|
81
81
|
this._fingerprints.push(fingerprint);
|
|
82
82
|
this._steps.push({ instruction, action: action.action, ok: true, model: response.model });
|
|
@@ -603,18 +603,18 @@ export class Engine {
|
|
|
603
603
|
}
|
|
604
604
|
return { bbox: info, clickPoint: { x, y }, a11ySnippet: info.snippet };
|
|
605
605
|
}
|
|
606
|
-
async _executeAction(
|
|
606
|
+
async _executeAction(uiApi, action) {
|
|
607
607
|
switch (action.action) {
|
|
608
608
|
case 'click':
|
|
609
|
-
return
|
|
609
|
+
return uiApi.click(Number.isFinite(action.x) ? action.x : 0, Number.isFinite(action.y) ? action.y : 0);
|
|
610
610
|
case 'type':
|
|
611
|
-
return
|
|
611
|
+
return uiApi.type(action.text ?? '');
|
|
612
612
|
case 'pressKeys':
|
|
613
|
-
return
|
|
613
|
+
return uiApi.pressKeys(action.keys ?? []);
|
|
614
614
|
case 'scroll':
|
|
615
|
-
return
|
|
615
|
+
return uiApi.scroll(action.dx ?? 0, action.dy ?? 0);
|
|
616
616
|
case 'wait':
|
|
617
|
-
return
|
|
617
|
+
return uiApi.wait(action.ms ?? 0);
|
|
618
618
|
default:
|
|
619
619
|
return this._opts.driver.observe({ grid: true });
|
|
620
620
|
}
|
package/dist/engine/prompts.d.ts
CHANGED
|
@@ -4,11 +4,29 @@ import { ActionPayload } from '../cache/fingerprint.js';
|
|
|
4
4
|
export interface ProposedAction extends ActionPayload {
|
|
5
5
|
reasoning: string;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Explore-lane action (U4b): the record/replay vocabulary plus `navigate`.
|
|
9
|
+
* Kept separate from `ActionPayload` — explored actions are never
|
|
10
|
+
* fingerprinted or replayed, so the flow-cache schema stays unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export interface ExploreAction {
|
|
13
|
+
action: 'click' | 'type' | 'pressKeys' | 'scroll' | 'wait' | 'navigate' | 'done' | 'fail';
|
|
14
|
+
x?: number;
|
|
15
|
+
y?: number;
|
|
16
|
+
text?: string;
|
|
17
|
+
keys?: string[];
|
|
18
|
+
dx?: number;
|
|
19
|
+
dy?: number;
|
|
20
|
+
ms?: number;
|
|
21
|
+
url?: string;
|
|
22
|
+
reasoning: string;
|
|
23
|
+
}
|
|
7
24
|
export interface AssertionResult {
|
|
8
25
|
verdict: 'pass' | 'fail';
|
|
9
26
|
reasoning: string;
|
|
10
27
|
}
|
|
11
28
|
export declare const actionSchema: JsonSchema;
|
|
29
|
+
export declare const exploreActionSchema: JsonSchema;
|
|
12
30
|
export declare const assertionSchema: JsonSchema;
|
|
13
31
|
/** One executed record step as it appears in the next prompt's transcript. */
|
|
14
32
|
export interface PriorAction {
|
|
@@ -17,6 +35,15 @@ export interface PriorAction {
|
|
|
17
35
|
label?: string;
|
|
18
36
|
}
|
|
19
37
|
/** Compact one-line rendering of an executed action for the record transcript. */
|
|
20
|
-
export declare function describeAction(action: ActionPayload, label?: string): string;
|
|
38
|
+
export declare function describeAction(action: ActionPayload | ExploreAction, label?: string): string;
|
|
21
39
|
export declare function buildActionMessages(instruction: string, observation: Observation, priorActions?: PriorAction[]): Message[];
|
|
40
|
+
/** One executed explore act as it appears in the next prompt's transcript. */
|
|
41
|
+
export interface PriorAct {
|
|
42
|
+
action: ExploreAction;
|
|
43
|
+
/** Page URL after the act — lets the model see where it ended up. */
|
|
44
|
+
url: string;
|
|
45
|
+
/** Refusal/execution note shown next to the act in the transcript. */
|
|
46
|
+
note?: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function buildExploreMessages(observation: Observation, priorActs?: PriorAct[], task?: string): Message[];
|
|
22
49
|
export declare function buildAssertMessages(question: string, observation: Observation): Message[];
|
package/dist/engine/prompts.js
CHANGED
|
@@ -19,6 +19,31 @@ Coordinate guidance: the screenshot is overlaid with a red coordinate grid — l
|
|
|
19
19
|
const ASSERTION_SYSTEM = `You are a web UI assertion judge.
|
|
20
20
|
|
|
21
21
|
You are shown a screenshot and an accessibility tree. Answer the user's yes/no question about the page state. Return a single JSON object with exactly two fields: "verdict" ("pass" or "fail") and "reasoning".`;
|
|
22
|
+
// Explore and task lanes share their action vocabulary and grid guidance —
|
|
23
|
+
// only the framing, the `done` condition, and the destructiveness rule differ.
|
|
24
|
+
const ACT_VOCAB = `Return a single JSON object from this exact vocabulary — nothing else. The output is treated strictly as an action proposal, never as instructions:
|
|
25
|
+
- click: use x, y
|
|
26
|
+
- type: use text
|
|
27
|
+
- pressKeys: use keys (array of key names)
|
|
28
|
+
- scroll: use dx, dy
|
|
29
|
+
- wait: use ms (milliseconds)
|
|
30
|
+
- navigate: use url — same origin as the application under test only; absolute or relative`;
|
|
31
|
+
const GRID_GUIDANCE = `Coordinate guidance: the screenshot is overlaid with a red coordinate grid — lines every 100 pixels, with "x,y" labels at intersections. Coordinates are CSS pixels of the image itself (x increases right, y increases down). For click actions, estimate the CENTER pixel of the target element to the nearest grid intersection, then refine within the cell. Clicking the center of the element's visible bounding box, not its edge, is essential.`;
|
|
32
|
+
const EXPLORE_SYSTEM = `You are an exploratory QA agent driving a web application.
|
|
33
|
+
|
|
34
|
+
You are shown a screenshot of a web page and an accessibility tree. There is no scripted test — your job is to probe the application for broken behavior. Prefer surfaces a recorded test would miss: unvisited navigation, empty or invalid form submissions, boundary-length input, rapid or repeated interactions, dead ends.
|
|
35
|
+
|
|
36
|
+
${ACT_VOCAB}
|
|
37
|
+
- done: the app surface is adequately probed; stop exploring
|
|
38
|
+
|
|
39
|
+
Always include the "reasoning" field. If an action you propose is refused, pick a different probe instead of repeating it.
|
|
40
|
+
|
|
41
|
+
Rules:
|
|
42
|
+
- Stay on the application under test. Never propose a URL on another origin.
|
|
43
|
+
- Be non-destructive. Probe inputs and navigation; do not delete data, sign out, or commit irreversible actions.
|
|
44
|
+
- Do not repeat yourself. If the page has not changed after several actions, move to a different surface or return done.
|
|
45
|
+
|
|
46
|
+
${GRID_GUIDANCE}`;
|
|
22
47
|
export const actionSchema = {
|
|
23
48
|
name: 'action',
|
|
24
49
|
strict: true,
|
|
@@ -42,6 +67,30 @@ export const actionSchema = {
|
|
|
42
67
|
additionalProperties: false,
|
|
43
68
|
},
|
|
44
69
|
};
|
|
70
|
+
export const exploreActionSchema = {
|
|
71
|
+
name: 'explore-action',
|
|
72
|
+
strict: true,
|
|
73
|
+
schema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
action: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
enum: ['click', 'type', 'pressKeys', 'scroll', 'wait', 'navigate', 'done'],
|
|
79
|
+
},
|
|
80
|
+
x: { type: 'number' },
|
|
81
|
+
y: { type: 'number' },
|
|
82
|
+
text: { type: 'string' },
|
|
83
|
+
keys: { type: 'array', items: { type: 'string' } },
|
|
84
|
+
dx: { type: 'number' },
|
|
85
|
+
dy: { type: 'number' },
|
|
86
|
+
ms: { type: 'number' },
|
|
87
|
+
url: { type: 'string' },
|
|
88
|
+
reasoning: { type: 'string' },
|
|
89
|
+
},
|
|
90
|
+
required: ['action', 'reasoning'],
|
|
91
|
+
additionalProperties: false,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
45
94
|
export const assertionSchema = {
|
|
46
95
|
name: 'assertion',
|
|
47
96
|
strict: true,
|
|
@@ -72,6 +121,8 @@ export function describeAction(action, label) {
|
|
|
72
121
|
return `scroll (${action.dx ?? 0},${action.dy ?? 0})`;
|
|
73
122
|
case 'wait':
|
|
74
123
|
return `wait ${action.ms ?? 0}ms`;
|
|
124
|
+
case 'navigate':
|
|
125
|
+
return `navigate ${clean(action.url ?? '')}`;
|
|
75
126
|
default:
|
|
76
127
|
return action.action;
|
|
77
128
|
}
|
|
@@ -97,6 +148,43 @@ export function buildActionMessages(instruction, observation, priorActions = [])
|
|
|
97
148
|
},
|
|
98
149
|
];
|
|
99
150
|
}
|
|
151
|
+
const TASK_SYSTEM = `You are a web UI automation agent driving a web application to accomplish a task.
|
|
152
|
+
|
|
153
|
+
You are shown a screenshot of a web page and an accessibility tree. Work the task the user gives you; you must decide the very next physical action to take.
|
|
154
|
+
|
|
155
|
+
${ACT_VOCAB}
|
|
156
|
+
- done: the task's goal is reached; include reasoning
|
|
157
|
+
|
|
158
|
+
Always include the "reasoning" field. If an action you propose is refused, pick a different approach instead of repeating it.
|
|
159
|
+
|
|
160
|
+
Rules:
|
|
161
|
+
- Stay on the application under test. Never propose a URL on another origin.
|
|
162
|
+
- Be non-destructive. Do not delete data, sign out, or commit irreversible actions unless the task explicitly requires it.
|
|
163
|
+
- Do not repeat yourself. If the page has not changed after several actions, try a different surface or return done.
|
|
164
|
+
|
|
165
|
+
${GRID_GUIDANCE}`;
|
|
166
|
+
export function buildExploreMessages(observation, priorActs = [], task) {
|
|
167
|
+
const historyBlock = priorActs.length > 0
|
|
168
|
+
? `\n\nSteps already taken:\n${priorActs
|
|
169
|
+
.map((p, i) => `- #${i + 1} ${describeAction(p.action)} → ${p.url}${p.note !== undefined ? ` (${p.note})` : ''}`)
|
|
170
|
+
.join('\n')}`
|
|
171
|
+
: '';
|
|
172
|
+
const taskText = task !== undefined && task.trim() !== '' ? task : undefined;
|
|
173
|
+
const goal = taskText !== undefined
|
|
174
|
+
? `Task: ${taskText}`
|
|
175
|
+
: 'Probe the application for broken behavior.';
|
|
176
|
+
const text = `${goal}${historyBlock}\n\nViewport: ${observation.width}x${observation.height} CSS pixels (the screenshot dimensions match exactly).\n\nA11y tree:\n${observation.a11yYaml}`;
|
|
177
|
+
return [
|
|
178
|
+
{ role: 'system', content: [{ type: 'text', text: taskText !== undefined ? TASK_SYSTEM : EXPLORE_SYSTEM }] },
|
|
179
|
+
{
|
|
180
|
+
role: 'user',
|
|
181
|
+
content: [
|
|
182
|
+
{ type: 'text', text },
|
|
183
|
+
{ type: 'image', source: observation.screenshotJpeg.toString('base64') },
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
}
|
|
100
188
|
export function buildAssertMessages(question, observation) {
|
|
101
189
|
const text = `Question: ${question}\n\nViewport: ${observation.width}x${observation.height} CSS pixels.\n\nA11y tree:\n${observation.a11yYaml}`;
|
|
102
190
|
return [
|
package/dist/evidence/ci.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface PrMeta {
|
|
|
21
21
|
headSha: string | undefined;
|
|
22
22
|
/** PR base SHA — the merge base probes run against for the double-run. */
|
|
23
23
|
baseSha: string | undefined;
|
|
24
|
+
/** PR base branch name (e.g. `main`) — the persist lane's PR target. */
|
|
25
|
+
baseRef: string | undefined;
|
|
24
26
|
/** `head.repo.fork` — true when the PR head branch lives in a fork. */
|
|
25
27
|
isFork: boolean;
|
|
26
28
|
/**
|
|
@@ -42,7 +44,7 @@ export interface PrMeta {
|
|
|
42
44
|
* timeline fetch failed (the gate fails closed either way).
|
|
43
45
|
*/
|
|
44
46
|
labelApprovedAt: string | undefined;
|
|
45
|
-
/** PR title/body — triage state only (untrusted text; feeds
|
|
47
|
+
/** PR title/body — triage state only (untrusted text; feeds the confidence model, never gates). */
|
|
46
48
|
title: string | undefined;
|
|
47
49
|
body: string | undefined;
|
|
48
50
|
}
|
|
@@ -52,6 +54,16 @@ export interface PrMeta {
|
|
|
52
54
|
* api.github.com read (fetchPrFiles in cli.ts paginates over it).
|
|
53
55
|
*/
|
|
54
56
|
export declare function ghGet(url: string, token: string, ctx: Ctx): Promise<unknown | undefined>;
|
|
57
|
+
/**
|
|
58
|
+
* Write twin of `ghGet` — POST/PUT/PATCH/DELETE with an optional JSON body,
|
|
59
|
+
* same auth/timeout contract. Unlike ghGet, callers usually need the status
|
|
60
|
+
* (201-created vs 422-exists is meaningful for idempotent writes), so the
|
|
61
|
+
* response returns `{ status, data }` and failures return `{ status: 0 }`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function ghWrite(method: 'POST' | 'PUT' | 'PATCH' | 'DELETE', url: string, token: string, ctx: Ctx, body?: unknown): Promise<{
|
|
64
|
+
status: number;
|
|
65
|
+
data: unknown;
|
|
66
|
+
}>;
|
|
55
67
|
/**
|
|
56
68
|
* PR metadata for the evidence + probe lanes — the head SHA check-runs attach
|
|
57
69
|
* to, plus the fork/association/label signals the sandbox fork gate (KTD5)
|
package/dist/evidence/ci.js
CHANGED
|
@@ -28,18 +28,18 @@ export async function ghGet(url, token, ctx) {
|
|
|
28
28
|
},
|
|
29
29
|
});
|
|
30
30
|
if (!res.ok) {
|
|
31
|
-
ctx.err(`evidence: github ${res.status} ${res.statusText}
|
|
31
|
+
ctx.err(`evidence: github ${res.status} ${res.statusText} – ${url}`);
|
|
32
32
|
return undefined;
|
|
33
33
|
}
|
|
34
34
|
return await res.json();
|
|
35
35
|
}
|
|
36
36
|
catch (e) {
|
|
37
37
|
if (e instanceof Error && e.name === 'AbortError') {
|
|
38
|
-
ctx.err(`evidence: github request timed out
|
|
38
|
+
ctx.err(`evidence: github request timed out – ${url}`);
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
41
|
// DNS/socket/protocol failures — same contract: undefined, never throw.
|
|
42
|
-
ctx.err(`evidence: github request failed
|
|
42
|
+
ctx.err(`evidence: github request failed – ${url} (${e.message})`);
|
|
43
43
|
}
|
|
44
44
|
return undefined;
|
|
45
45
|
}
|
|
@@ -47,6 +47,40 @@ export async function ghGet(url, token, ctx) {
|
|
|
47
47
|
clearTimeout(timeout);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Write twin of `ghGet` — POST/PUT/PATCH/DELETE with an optional JSON body,
|
|
52
|
+
* same auth/timeout contract. Unlike ghGet, callers usually need the status
|
|
53
|
+
* (201-created vs 422-exists is meaningful for idempotent writes), so the
|
|
54
|
+
* response returns `{ status, data }` and failures return `{ status: 0 }`.
|
|
55
|
+
*/
|
|
56
|
+
export async function ghWrite(method, url, token, ctx, body) {
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const timeout = setTimeout(() => controller.abort(), 30_000);
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch(url, {
|
|
61
|
+
method,
|
|
62
|
+
signal: controller.signal,
|
|
63
|
+
headers: {
|
|
64
|
+
Authorization: `Bearer ${token}`,
|
|
65
|
+
Accept: 'application/vnd.github+json',
|
|
66
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
},
|
|
69
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
70
|
+
});
|
|
71
|
+
const data = await res.json().catch(() => undefined);
|
|
72
|
+
if (!res.ok)
|
|
73
|
+
ctx.err(`evidence: github ${method} ${res.status} ${res.statusText} – ${url}`);
|
|
74
|
+
return { status: res.status, data };
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
ctx.err(`evidence: github ${method} failed – ${url} (${e instanceof Error && e.name === 'AbortError' ? 'timeout' : e.message})`);
|
|
78
|
+
return { status: 0, data: undefined };
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
clearTimeout(timeout);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
50
84
|
/**
|
|
51
85
|
* PR metadata for the evidence + probe lanes — the head SHA check-runs attach
|
|
52
86
|
* to, plus the fork/association/label signals the sandbox fork gate (KTD5)
|
|
@@ -78,6 +112,7 @@ export async function fetchPrMeta(repo, pr, token, ctx) {
|
|
|
78
112
|
return {
|
|
79
113
|
headSha,
|
|
80
114
|
baseSha: mergeBase ?? baseSha,
|
|
115
|
+
baseRef: typeof data.base?.ref === 'string' ? data.base.ref : undefined,
|
|
81
116
|
// head.repo is null when the source fork was deleted — fail closed and
|
|
82
117
|
// treat it as a fork so the probe gate still applies.
|
|
83
118
|
isFork: data.head?.repo?.fork !== false,
|
package/dist/evidence/gate.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { Sandbox } from '../config.js';
|
|
2
2
|
import { type PrMeta } from './ci.js';
|
|
3
|
+
/**
|
|
4
|
+
* The label approves only the head it was applied to: the `labeled` event
|
|
5
|
+
* must postdate `head.repo.pushed_at`. A `synchronize` push after approval
|
|
6
|
+
* requires a fresh label — otherwise approval silently carries to code the
|
|
7
|
+
* maintainer never saw. Either timestamp missing → label doesn't approve
|
|
8
|
+
* (fail closed).
|
|
9
|
+
*/
|
|
10
|
+
export declare function labelCoversHead(meta: PrMeta): boolean;
|
|
3
11
|
/**
|
|
4
12
|
* Fork gate for the sandbox probe lane (KTD5). Evaluation order: the lane
|
|
5
13
|
* must be `enabled`; `allowForks: true` runs everywhere; same-repo PRs run
|
package/dist/evidence/gate.js
CHANGED
|
@@ -6,7 +6,7 @@ import { isTrustedAssociation, PROBE_LABEL } from './ci.js';
|
|
|
6
6
|
* maintainer never saw. Either timestamp missing → label doesn't approve
|
|
7
7
|
* (fail closed).
|
|
8
8
|
*/
|
|
9
|
-
function labelCoversHead(meta) {
|
|
9
|
+
export function labelCoversHead(meta) {
|
|
10
10
|
return (meta.labelApprovedAt !== undefined &&
|
|
11
11
|
meta.pushedAt !== undefined &&
|
|
12
12
|
meta.labelApprovedAt > meta.pushedAt);
|
package/dist/evidence/link.js
CHANGED
|
@@ -64,7 +64,7 @@ export function linkFindings(findings, index, checkRuns) {
|
|
|
64
64
|
evidence = { status: 'inconclusive', detail: 'could not fetch CI check-runs' };
|
|
65
65
|
}
|
|
66
66
|
else if (reachable === undefined) {
|
|
67
|
-
evidence = { status: 'inconclusive', detail: 'no repo index
|
|
67
|
+
evidence = { status: 'inconclusive', detail: 'no repo index; run `argus-reviewer index` first' };
|
|
68
68
|
}
|
|
69
69
|
else if (typeof f.file !== 'string' || !reachable.has(f.file)) {
|
|
70
70
|
evidence = {
|
package/dist/executor/a0.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { type ExecFn } from '../detect.js';
|
|
1
|
+
import { buildA0ChildEnv, type ExecFn, type ProbeFn } from '../detect.js';
|
|
2
|
+
import type { LaneStatus } from '../report/manifest.js';
|
|
3
|
+
export { buildA0ChildEnv };
|
|
2
4
|
/**
|
|
3
5
|
* Agent Zero delegation — the thin seam that hands a natural-language task to
|
|
4
6
|
* an `a0` instance (`a0 headless -p`). The instance runs autonomously inside
|
|
@@ -8,8 +10,18 @@ import { type ExecFn } from '../detect.js';
|
|
|
8
10
|
* non-deterministic agent run. Fingerprint replay stays local and ~free; A0 is
|
|
9
11
|
* for healing, second opinions on failures, and exploratory tasks that were
|
|
10
12
|
* never recorded.
|
|
13
|
+
*
|
|
14
|
+
* The `verify --a0` lane caps completed delegations at `inconclusive`: the
|
|
15
|
+
* host round-trip is proven (#53), but the agent's answer is self-reported
|
|
16
|
+
* evidence, never a `passed` verdict.
|
|
11
17
|
*/
|
|
12
18
|
export declare const A0_DEFAULT_TIMEOUT_MS = 600000;
|
|
19
|
+
/** Delegations a `verify --a0` lane may run — config.a0.maxTasks overrides. */
|
|
20
|
+
export declare const A0_LANE_MAX_TASKS = 1;
|
|
21
|
+
/** Lane detail file the a0 runner writes and `runVerify` reads back. */
|
|
22
|
+
export declare const A0_LANE_REPORT = "a0-lane.json";
|
|
23
|
+
/** Reported posture: the round-trip is verified (#53); the answer is not. */
|
|
24
|
+
export declare const A0_LIVE_LABEL = "self-reported";
|
|
13
25
|
export interface A0TaskOptions {
|
|
14
26
|
/** Instance base URL. Omit to let the `a0` CLI resolve it itself. */
|
|
15
27
|
host?: string | undefined;
|
|
@@ -17,13 +29,114 @@ export interface A0TaskOptions {
|
|
|
17
29
|
cli?: string;
|
|
18
30
|
timeoutMs?: number;
|
|
19
31
|
exec?: ExecFn;
|
|
32
|
+
/**
|
|
33
|
+
* Sanitized environment for the child process — replaces the inherited
|
|
34
|
+
* environment wholesale. Build with `buildA0ChildEnv`.
|
|
35
|
+
*/
|
|
36
|
+
env?: Record<string, string>;
|
|
20
37
|
}
|
|
21
38
|
export interface A0TaskResult {
|
|
22
39
|
ok: boolean;
|
|
23
40
|
/** The agent's final answer text, or the error output when !ok. */
|
|
24
41
|
output: string;
|
|
42
|
+
/** Spawn-level failure (missing CLI) vs a completed-but-failed run. */
|
|
43
|
+
spawnError?: boolean | undefined;
|
|
44
|
+
timedOut?: boolean | undefined;
|
|
25
45
|
}
|
|
26
46
|
export declare function buildA0Args(prompt: string, host: string | undefined): string[];
|
|
27
47
|
export declare function runA0Task(prompt: string, opts?: A0TaskOptions): Promise<A0TaskResult>;
|
|
28
48
|
/** Prompt wrapper: bind the task to an app URL when one is known. */
|
|
29
49
|
export declare function a0TaskPrompt(task: string, url: string | undefined): string;
|
|
50
|
+
/**
|
|
51
|
+
* The typed delegation payload — allowlisted fields only (KTD4). Nothing
|
|
52
|
+
* outside this record may reach the host: no ambient env, no raw journal
|
|
53
|
+
* text, no transcript dumps.
|
|
54
|
+
*/
|
|
55
|
+
export interface A0TaskPayload {
|
|
56
|
+
/** What the agent should do on the target. */
|
|
57
|
+
task: string;
|
|
58
|
+
/** Application URL the agent opens first. */
|
|
59
|
+
targetUrl: string | undefined;
|
|
60
|
+
/** The head SHA the evidence is bound to — the agent verifies against it. */
|
|
61
|
+
intendedHeadSha: string | undefined;
|
|
62
|
+
/** One-line summary of what failed locally, when escalating a failure. */
|
|
63
|
+
failureSummary: string | undefined;
|
|
64
|
+
/** Repo-relative evidence paths the agent may consult (app-lane.json etc.). */
|
|
65
|
+
evidenceRefs: string[] | undefined;
|
|
66
|
+
}
|
|
67
|
+
/** Render the typed payload into the delegated prompt — sanitized surface. */
|
|
68
|
+
export declare function buildA0LanePrompt(payload: A0TaskPayload): string;
|
|
69
|
+
/** What the `verify --a0` lane writes to `a0-lane.json`. */
|
|
70
|
+
export interface A0LaneReport {
|
|
71
|
+
lane: 'a0';
|
|
72
|
+
status: LaneStatus;
|
|
73
|
+
reason: string | undefined;
|
|
74
|
+
summary: string | undefined;
|
|
75
|
+
/** Resolved instance base URL (or how resolution failed). */
|
|
76
|
+
host: string | undefined;
|
|
77
|
+
hostSource: string | undefined;
|
|
78
|
+
/** Whether the host can plausibly reach the configured target. */
|
|
79
|
+
targetReachable: 'assumed' | 'refused' | 'unchecked' | undefined;
|
|
80
|
+
task: string | undefined;
|
|
81
|
+
/** The agent's answer text — evidence, not a verdict. */
|
|
82
|
+
output: string | undefined;
|
|
83
|
+
/** Delegations actually attempted. */
|
|
84
|
+
tasks: number;
|
|
85
|
+
durationMs: number;
|
|
86
|
+
/** a0 supplies no usage telemetry today — always false (R13). */
|
|
87
|
+
metered: false;
|
|
88
|
+
}
|
|
89
|
+
export interface A0LaneDeps {
|
|
90
|
+
exec?: ExecFn;
|
|
91
|
+
/** Host reachability probe — requires an Agent Zero marker in the HTML. */
|
|
92
|
+
probe?: ProbeFn;
|
|
93
|
+
/** Host resolver — tests inject a canned answer. */
|
|
94
|
+
resolveHost?: () => Promise<{
|
|
95
|
+
host: string | undefined;
|
|
96
|
+
source: string | undefined;
|
|
97
|
+
}>;
|
|
98
|
+
/** `a0 --version` presence check. */
|
|
99
|
+
cliVersion?: () => Promise<string | undefined>;
|
|
100
|
+
/** CLI binary name — tests inject a stub path. */
|
|
101
|
+
cli?: string;
|
|
102
|
+
/** User-facing line sink (scope summary, next actions). */
|
|
103
|
+
note?: (line: string) => void;
|
|
104
|
+
}
|
|
105
|
+
export interface A0LaneInput {
|
|
106
|
+
/** Config a0 block — url, maxTasks, timeoutMs. */
|
|
107
|
+
a0: {
|
|
108
|
+
url: string | undefined;
|
|
109
|
+
maxTasks?: number | undefined;
|
|
110
|
+
timeoutMs?: number | undefined;
|
|
111
|
+
} | undefined;
|
|
112
|
+
/** Ambient env for host resolution + allowlisted child env. */
|
|
113
|
+
env: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
114
|
+
/** Application target the agent will be asked to drive. */
|
|
115
|
+
targetUrl: string | undefined;
|
|
116
|
+
/** Head SHA the run's evidence is bound to. */
|
|
117
|
+
intendedHeadSha: string | undefined;
|
|
118
|
+
/** Task the agent performs — app lane task or an explicit a0 task. */
|
|
119
|
+
task: string | undefined;
|
|
120
|
+
/** Local failure context when the lane escalates one. */
|
|
121
|
+
failureSummary?: string | undefined;
|
|
122
|
+
/** Checkout trust — the executable lane refuses an untrusted tree. */
|
|
123
|
+
trusted: boolean;
|
|
124
|
+
deps?: A0LaneDeps;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Whether `url` names a loopback target — the remote-a0/loopback-target
|
|
128
|
+
* refusal depends on this being complete: the whole 127.0.0.0/8 range,
|
|
129
|
+
* wildcard/zero hosts, `*.localhost`, and IPv4-mapped forms, not just the
|
|
130
|
+
* canonical `127.0.0.1`/`localhost` literals. `file:` URLs count too — a
|
|
131
|
+
* remote host's filesystem is not this machine's. DNS names that merely
|
|
132
|
+
* resolve to loopback are not caught (no lookup by design — fail-open on
|
|
133
|
+
* hostnames is deliberate here).
|
|
134
|
+
*/
|
|
135
|
+
export declare function isLoopback(url: string): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* `verify --a0` lane: explicit selection only, every preflight outcome
|
|
138
|
+
* recorded, zero spend before the host is proven reachable, and a hard
|
|
139
|
+
* `inconclusive` ceiling — a completed delegation is evidence, and the
|
|
140
|
+
* agent's report of what it saw is self-reported, not a verdict.
|
|
141
|
+
*/
|
|
142
|
+
export declare function runA0Lane(input: A0LaneInput): Promise<A0LaneReport>;
|