@eidra-umain/greenlight 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +391 -0
- package/dist/browser/browser.d.ts +24 -0
- package/dist/browser/browser.d.ts.map +1 -0
- package/dist/browser/browser.js +44 -0
- package/dist/browser/browser.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +140 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +9 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +277 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +107 -0
- package/dist/config.js.map +1 -0
- package/dist/globals.d.ts +21 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/globals.js +24 -0
- package/dist/globals.js.map +1 -0
- package/dist/parser/loader.d.ts +7 -0
- package/dist/parser/loader.d.ts.map +1 -0
- package/dist/parser/loader.js +43 -0
- package/dist/parser/loader.js.map +1 -0
- package/dist/parser/schema.d.ts +42 -0
- package/dist/parser/schema.d.ts.map +1 -0
- package/dist/parser/schema.js +33 -0
- package/dist/parser/schema.js.map +1 -0
- package/dist/parser/steps.d.ts +13 -0
- package/dist/parser/steps.d.ts.map +1 -0
- package/dist/parser/steps.js +44 -0
- package/dist/parser/steps.js.map +1 -0
- package/dist/parser/variables.d.ts +18 -0
- package/dist/parser/variables.d.ts.map +1 -0
- package/dist/parser/variables.js +44 -0
- package/dist/parser/variables.js.map +1 -0
- package/dist/pilot/a11y-parser.d.ts +26 -0
- package/dist/pilot/a11y-parser.d.ts.map +1 -0
- package/dist/pilot/a11y-parser.js +195 -0
- package/dist/pilot/a11y-parser.js.map +1 -0
- package/dist/pilot/assertions.d.ts +30 -0
- package/dist/pilot/assertions.d.ts.map +1 -0
- package/dist/pilot/assertions.js +219 -0
- package/dist/pilot/assertions.js.map +1 -0
- package/dist/pilot/checkbox.d.ts +12 -0
- package/dist/pilot/checkbox.d.ts.map +1 -0
- package/dist/pilot/checkbox.js +104 -0
- package/dist/pilot/checkbox.js.map +1 -0
- package/dist/pilot/executor.d.ts +17 -0
- package/dist/pilot/executor.d.ts.map +1 -0
- package/dist/pilot/executor.js +462 -0
- package/dist/pilot/executor.js.map +1 -0
- package/dist/pilot/form-fields.d.ts +34 -0
- package/dist/pilot/form-fields.d.ts.map +1 -0
- package/dist/pilot/form-fields.js +139 -0
- package/dist/pilot/form-fields.js.map +1 -0
- package/dist/pilot/llm.d.ts +49 -0
- package/dist/pilot/llm.d.ts.map +1 -0
- package/dist/pilot/llm.js +188 -0
- package/dist/pilot/llm.js.map +1 -0
- package/dist/pilot/locator.d.ts +58 -0
- package/dist/pilot/locator.d.ts.map +1 -0
- package/dist/pilot/locator.js +248 -0
- package/dist/pilot/locator.js.map +1 -0
- package/dist/pilot/message-builder.d.ts +31 -0
- package/dist/pilot/message-builder.d.ts.map +1 -0
- package/dist/pilot/message-builder.js +112 -0
- package/dist/pilot/message-builder.js.map +1 -0
- package/dist/pilot/network.d.ts +23 -0
- package/dist/pilot/network.d.ts.map +1 -0
- package/dist/pilot/network.js +92 -0
- package/dist/pilot/network.js.map +1 -0
- package/dist/pilot/pilot.d.ts +27 -0
- package/dist/pilot/pilot.d.ts.map +1 -0
- package/dist/pilot/pilot.js +249 -0
- package/dist/pilot/pilot.js.map +1 -0
- package/dist/pilot/prompts.d.ts +8 -0
- package/dist/pilot/prompts.d.ts.map +1 -0
- package/dist/pilot/prompts.js +163 -0
- package/dist/pilot/prompts.js.map +1 -0
- package/dist/pilot/providers/anthropic.d.ts +6 -0
- package/dist/pilot/providers/anthropic.d.ts.map +1 -0
- package/dist/pilot/providers/anthropic.js +45 -0
- package/dist/pilot/providers/anthropic.js.map +1 -0
- package/dist/pilot/providers/gemini.d.ts +6 -0
- package/dist/pilot/providers/gemini.d.ts.map +1 -0
- package/dist/pilot/providers/gemini.js +55 -0
- package/dist/pilot/providers/gemini.js.map +1 -0
- package/dist/pilot/providers/index.d.ts +10 -0
- package/dist/pilot/providers/index.d.ts.map +1 -0
- package/dist/pilot/providers/index.js +25 -0
- package/dist/pilot/providers/index.js.map +1 -0
- package/dist/pilot/providers/openai-compatible.d.ts +7 -0
- package/dist/pilot/providers/openai-compatible.d.ts.map +1 -0
- package/dist/pilot/providers/openai-compatible.js +34 -0
- package/dist/pilot/providers/openai-compatible.js.map +1 -0
- package/dist/pilot/providers/types.d.ts +12 -0
- package/dist/pilot/providers/types.d.ts.map +1 -0
- package/dist/pilot/providers/types.js +2 -0
- package/dist/pilot/providers/types.js.map +1 -0
- package/dist/pilot/response-parser.d.ts +31 -0
- package/dist/pilot/response-parser.d.ts.map +1 -0
- package/dist/pilot/response-parser.js +188 -0
- package/dist/pilot/response-parser.js.map +1 -0
- package/dist/pilot/state.d.ts +19 -0
- package/dist/pilot/state.d.ts.map +1 -0
- package/dist/pilot/state.js +67 -0
- package/dist/pilot/state.js.map +1 -0
- package/dist/pilot/trace.d.ts +16 -0
- package/dist/pilot/trace.d.ts.map +1 -0
- package/dist/pilot/trace.js +117 -0
- package/dist/pilot/trace.js.map +1 -0
- package/dist/planner/hasher.d.ts +14 -0
- package/dist/planner/hasher.d.ts.map +1 -0
- package/dist/planner/hasher.js +21 -0
- package/dist/planner/hasher.js.map +1 -0
- package/dist/planner/plan-generator.d.ts +23 -0
- package/dist/planner/plan-generator.d.ts.map +1 -0
- package/dist/planner/plan-generator.js +56 -0
- package/dist/planner/plan-generator.js.map +1 -0
- package/dist/planner/plan-runner.d.ts +16 -0
- package/dist/planner/plan-runner.d.ts.map +1 -0
- package/dist/planner/plan-runner.js +375 -0
- package/dist/planner/plan-runner.js.map +1 -0
- package/dist/planner/plan-store.d.ts +22 -0
- package/dist/planner/plan-store.d.ts.map +1 -0
- package/dist/planner/plan-store.js +71 -0
- package/dist/planner/plan-store.js.map +1 -0
- package/dist/planner/plan-types.d.ts +64 -0
- package/dist/planner/plan-types.d.ts.map +1 -0
- package/dist/planner/plan-types.js +7 -0
- package/dist/planner/plan-types.js.map +1 -0
- package/dist/reporter/types.d.ts +130 -0
- package/dist/reporter/types.d.ts.map +1 -0
- package/dist/reporter/types.js +5 -0
- package/dist/reporter/types.js.map +1 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locator resolution — translates a11y tree refs into Playwright locators.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Find an A11yNode by its ref ID, searching the tree recursively.
|
|
6
|
+
*/
|
|
7
|
+
export function findNodeByRef(nodes, ref) {
|
|
8
|
+
for (const node of nodes) {
|
|
9
|
+
if (node.ref === ref)
|
|
10
|
+
return node;
|
|
11
|
+
if (node.children) {
|
|
12
|
+
const found = findNodeByRef(node.children, ref);
|
|
13
|
+
if (found)
|
|
14
|
+
return found;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Find the path from root to a node by ref.
|
|
21
|
+
* Returns the chain of ancestor nodes including the target, or undefined.
|
|
22
|
+
*/
|
|
23
|
+
export function findNodePath(nodes, ref, path = []) {
|
|
24
|
+
for (const node of nodes) {
|
|
25
|
+
const currentPath = [...path, node];
|
|
26
|
+
if (node.ref === ref)
|
|
27
|
+
return currentPath;
|
|
28
|
+
if (node.children) {
|
|
29
|
+
const found = findNodePath(node.children, ref, currentPath);
|
|
30
|
+
if (found)
|
|
31
|
+
return found;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Given a locator, return it if it matches exactly one element,
|
|
38
|
+
* or return the first visible match if there are several.
|
|
39
|
+
* Returns undefined if the locator matches nothing.
|
|
40
|
+
*/
|
|
41
|
+
export async function pickVisible(locator) {
|
|
42
|
+
try {
|
|
43
|
+
const count = await locator.count();
|
|
44
|
+
if (count === 1)
|
|
45
|
+
return locator;
|
|
46
|
+
if (count > 1) {
|
|
47
|
+
for (let i = 0; i < count; i++) {
|
|
48
|
+
const nth = locator.nth(i);
|
|
49
|
+
if (await nth.isVisible())
|
|
50
|
+
return nth;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Locator failed entirely
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
export function roleLocator(scope, node) {
|
|
60
|
+
const role = node.role;
|
|
61
|
+
if (node.name) {
|
|
62
|
+
return scope.getByRole(role, { name: node.name, exact: true });
|
|
63
|
+
}
|
|
64
|
+
return scope.getByRole(role);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve an element ref to a Playwright locator using the a11y tree hierarchy.
|
|
68
|
+
*
|
|
69
|
+
* Primary strategy: chain getByRole calls from ancestor → target using the
|
|
70
|
+
* same tree structure that ariaSnapshot reported. This disambiguates elements
|
|
71
|
+
* that share the same role+name but live under different parents.
|
|
72
|
+
*
|
|
73
|
+
* Fallback strategies (tried in order if chained locator doesn't match):
|
|
74
|
+
* 1. Direct getByRole (ignoring hierarchy)
|
|
75
|
+
* 2. getByLabel (for form inputs)
|
|
76
|
+
* 3. getByPlaceholder (for text inputs)
|
|
77
|
+
* Returns the first locator that resolves to a single visible element.
|
|
78
|
+
*/
|
|
79
|
+
export async function resolveLocator(page, nodes, ref) {
|
|
80
|
+
const path = findNodePath(nodes, ref);
|
|
81
|
+
if (!path || path.length === 0) {
|
|
82
|
+
throw new Error(`Element ref "${ref}" not found in accessibility tree`);
|
|
83
|
+
}
|
|
84
|
+
const target = path[path.length - 1];
|
|
85
|
+
const role = target.role;
|
|
86
|
+
// Build candidates list, best to worst
|
|
87
|
+
const candidates = [];
|
|
88
|
+
// 1. Chained locator using ancestor hierarchy
|
|
89
|
+
// Use named ancestors to scope the search progressively
|
|
90
|
+
if (path.length > 1) {
|
|
91
|
+
let scoped;
|
|
92
|
+
for (const ancestor of path.slice(0, -1)) {
|
|
93
|
+
// Only chain through named nodes — unnamed structural nodes
|
|
94
|
+
// (like bare "list" or "main") add noise without disambiguation
|
|
95
|
+
if (!ancestor.name)
|
|
96
|
+
continue;
|
|
97
|
+
scoped = roleLocator(scoped ?? page, ancestor);
|
|
98
|
+
}
|
|
99
|
+
if (scoped) {
|
|
100
|
+
candidates.push(roleLocator(scoped, target));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// 2. Direct getByRole with exact name
|
|
104
|
+
if (target.name) {
|
|
105
|
+
candidates.push(page.getByRole(role, { name: target.name, exact: true }));
|
|
106
|
+
}
|
|
107
|
+
// 3. getByLabel (finds inputs by associated label text)
|
|
108
|
+
if (target.name) {
|
|
109
|
+
candidates.push(page.getByLabel(target.name, { exact: true }));
|
|
110
|
+
}
|
|
111
|
+
// 4. getByPlaceholder (finds inputs by placeholder attribute)
|
|
112
|
+
if (target.name) {
|
|
113
|
+
candidates.push(page.getByPlaceholder(target.name, { exact: true }));
|
|
114
|
+
}
|
|
115
|
+
// 5. Direct getByRole with loose name match
|
|
116
|
+
if (target.name) {
|
|
117
|
+
candidates.push(page.getByRole(role, { name: target.name }));
|
|
118
|
+
}
|
|
119
|
+
// Try each candidate: return the first that matches exactly one element,
|
|
120
|
+
// or the first visible element when there are multiple matches.
|
|
121
|
+
for (const locator of candidates) {
|
|
122
|
+
const match = await pickVisible(locator);
|
|
123
|
+
if (match)
|
|
124
|
+
return match;
|
|
125
|
+
}
|
|
126
|
+
// Last resort — return the basic locator and let Playwright handle errors
|
|
127
|
+
if (target.name) {
|
|
128
|
+
return page.getByRole(role, { name: target.name });
|
|
129
|
+
}
|
|
130
|
+
return page.getByRole(role);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolve an element by its visible text content.
|
|
134
|
+
* Used as a fallback when the element isn't in the accessibility tree
|
|
135
|
+
* (e.g. due to missing ARIA roles in the page markup).
|
|
136
|
+
*/
|
|
137
|
+
export async function resolveByText(page, text) {
|
|
138
|
+
const candidates = [
|
|
139
|
+
// Exact text match (link, button, or any element)
|
|
140
|
+
page.getByRole("link", { name: text, exact: true }),
|
|
141
|
+
page.getByRole("button", { name: text, exact: true }),
|
|
142
|
+
page.getByText(text, { exact: true }),
|
|
143
|
+
// Loose match
|
|
144
|
+
page.getByRole("link", { name: text }),
|
|
145
|
+
page.getByRole("button", { name: text }),
|
|
146
|
+
page.getByText(text),
|
|
147
|
+
];
|
|
148
|
+
for (const locator of candidates) {
|
|
149
|
+
const match = await pickVisible(locator);
|
|
150
|
+
if (match)
|
|
151
|
+
return match;
|
|
152
|
+
}
|
|
153
|
+
// Last resort — let Playwright handle the error
|
|
154
|
+
return page.getByText(text);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Resolve a locator from an action's ref or text field.
|
|
158
|
+
*/
|
|
159
|
+
export async function resolveActionTarget(page, action, a11yTree) {
|
|
160
|
+
if (action.ref) {
|
|
161
|
+
return resolveLocator(page, a11yTree, action.ref);
|
|
162
|
+
}
|
|
163
|
+
if (action.text) {
|
|
164
|
+
return resolveByText(page, action.text);
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`${action.action} action requires a ref or text target`);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Extract a CSS selector from a resolved Playwright locator.
|
|
170
|
+
* Evaluates in-browser to build a unique path-based selector.
|
|
171
|
+
*/
|
|
172
|
+
export async function extractCssSelector(locator) {
|
|
173
|
+
try {
|
|
174
|
+
return await locator.evaluate((el) => {
|
|
175
|
+
const escape = (s) => CSS.escape(s);
|
|
176
|
+
if (el.id)
|
|
177
|
+
return "#" + escape(el.id);
|
|
178
|
+
const parts = [];
|
|
179
|
+
let current = el;
|
|
180
|
+
while (current &&
|
|
181
|
+
current !== document.body &&
|
|
182
|
+
current !== document.documentElement) {
|
|
183
|
+
let sel = current.tagName.toLowerCase();
|
|
184
|
+
if (current.id) {
|
|
185
|
+
parts.unshift("#" + escape(current.id));
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
const parent = current.parentElement;
|
|
189
|
+
if (parent) {
|
|
190
|
+
const cur = current;
|
|
191
|
+
const sameTag = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
|
|
192
|
+
if (sameTag.length > 1) {
|
|
193
|
+
sel += `:nth-of-type(${String(sameTag.indexOf(current) + 1)})`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
parts.unshift(sel);
|
|
197
|
+
current = current.parentElement;
|
|
198
|
+
}
|
|
199
|
+
return parts.join(" > ");
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Extract selector info from a resolved action for the plan recorder.
|
|
208
|
+
* For ref-based actions: returns role + name from the a11y node.
|
|
209
|
+
* For text-based actions: extracts a CSS selector from the DOM element.
|
|
210
|
+
*/
|
|
211
|
+
export async function extractSelectorInfo(page, action, a11yTree, locator) {
|
|
212
|
+
if (action.ref) {
|
|
213
|
+
const node = findNodeByRef(a11yTree, action.ref);
|
|
214
|
+
if (node) {
|
|
215
|
+
const selector = { role: node.role, name: node.name };
|
|
216
|
+
// Check if multiple elements match this role+name.
|
|
217
|
+
// If so, record which one was acted on (nth index).
|
|
218
|
+
try {
|
|
219
|
+
const allMatches = node.name
|
|
220
|
+
? page.getByRole(node.role, { name: node.name })
|
|
221
|
+
: page.getByRole(node.role);
|
|
222
|
+
const count = await allMatches.count();
|
|
223
|
+
if (count > 1) {
|
|
224
|
+
// Find which nth match our locator corresponds to
|
|
225
|
+
const targetEl = await locator.elementHandle();
|
|
226
|
+
for (let i = 0; i < count; i++) {
|
|
227
|
+
const matchEl = await allMatches.nth(i).elementHandle();
|
|
228
|
+
if (targetEl && matchEl && await targetEl.evaluate((a, b) => a === b, matchEl)) {
|
|
229
|
+
selector.nth = i;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// If counting fails, proceed without nth
|
|
237
|
+
}
|
|
238
|
+
return selector;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (action.text) {
|
|
242
|
+
const css = await extractCssSelector(locator);
|
|
243
|
+
if (css)
|
|
244
|
+
return { css };
|
|
245
|
+
}
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=locator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.js","sourceRoot":"","sources":["../../src/pilot/locator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message construction helpers for the LLM client.
|
|
3
|
+
*/
|
|
4
|
+
import type { PageState } from "../reporter/types.js";
|
|
5
|
+
import type { ChatMessage } from "./providers/types.js";
|
|
6
|
+
/** Build the user message containing the step and full page state. */
|
|
7
|
+
export declare function buildUserMessage(step: string, pageState: PageState): string;
|
|
8
|
+
/**
|
|
9
|
+
* Compute a line-level diff between two tree strings.
|
|
10
|
+
* Returns added and removed lines, preserving order.
|
|
11
|
+
*/
|
|
12
|
+
export declare function computeTreeDiff(oldTree: string, newTree: string): {
|
|
13
|
+
added: string[];
|
|
14
|
+
removed: string[];
|
|
15
|
+
changedRatio: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Build a compact message for subsequent steps on the same page.
|
|
19
|
+
* Refs are stable across captures (derived from structural identity), so:
|
|
20
|
+
* - If the a11y tree is identical: skip both tree and visible text ("unchanged").
|
|
21
|
+
* - If < 30% of tree lines changed: send only the diff ("tree-diff").
|
|
22
|
+
* - If >= 30% changed: send the full tree without visible text ("tree-only").
|
|
23
|
+
* Returns null if we should send full state instead (e.g. after navigation).
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildCompactMessage(step: string, pageState: PageState, prevState: PageState, prevTree: string): {
|
|
26
|
+
message: string;
|
|
27
|
+
mode: "unchanged" | "tree-diff" | "tree-only";
|
|
28
|
+
} | null;
|
|
29
|
+
/** Build the full messages array for a chat completion request. */
|
|
30
|
+
export declare function buildMessages(step: string, pageState: PageState): ChatMessage[];
|
|
31
|
+
//# sourceMappingURL=message-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-builder.d.ts","sourceRoot":"","sources":["../../src/pilot/message-builder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAGrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAEvD,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACb;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAa9D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,GAAG,WAAW,GAAG,WAAW,CAAA;CAAE,GAAG,IAAI,CAyD3E;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,SAAS,GAClB,WAAW,EAAE,CAKf"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message construction helpers for the LLM client.
|
|
3
|
+
*/
|
|
4
|
+
import { formatA11yTree } from "./a11y-parser.js";
|
|
5
|
+
import { SYSTEM_PROMPT } from "./prompts.js";
|
|
6
|
+
/** Build the user message containing the step and full page state. */
|
|
7
|
+
export function buildUserMessage(step, pageState) {
|
|
8
|
+
const tree = formatA11yTree(pageState.a11yTree);
|
|
9
|
+
const parts = [
|
|
10
|
+
`Current URL: ${pageState.url}`,
|
|
11
|
+
`Page title: ${pageState.title}`,
|
|
12
|
+
"",
|
|
13
|
+
"Accessibility tree:",
|
|
14
|
+
tree,
|
|
15
|
+
];
|
|
16
|
+
if (pageState.visibleText) {
|
|
17
|
+
parts.push("", "Visible page text:", pageState.visibleText);
|
|
18
|
+
}
|
|
19
|
+
parts.push("", `Step to execute: ${step}`);
|
|
20
|
+
return parts.join("\n");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compute a line-level diff between two tree strings.
|
|
24
|
+
* Returns added and removed lines, preserving order.
|
|
25
|
+
*/
|
|
26
|
+
export function computeTreeDiff(oldTree, newTree) {
|
|
27
|
+
const oldLines = oldTree.split("\n");
|
|
28
|
+
const newLines = newTree.split("\n");
|
|
29
|
+
const oldSet = new Set(oldLines);
|
|
30
|
+
const newSet = new Set(newLines);
|
|
31
|
+
const added = newLines.filter((l) => !oldSet.has(l));
|
|
32
|
+
const removed = oldLines.filter((l) => !newSet.has(l));
|
|
33
|
+
const total = Math.max(oldLines.length, newLines.length);
|
|
34
|
+
return {
|
|
35
|
+
added,
|
|
36
|
+
removed,
|
|
37
|
+
changedRatio: total > 0 ? (added.length + removed.length) / total : 0,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build a compact message for subsequent steps on the same page.
|
|
42
|
+
* Refs are stable across captures (derived from structural identity), so:
|
|
43
|
+
* - If the a11y tree is identical: skip both tree and visible text ("unchanged").
|
|
44
|
+
* - If < 30% of tree lines changed: send only the diff ("tree-diff").
|
|
45
|
+
* - If >= 30% changed: send the full tree without visible text ("tree-only").
|
|
46
|
+
* Returns null if we should send full state instead (e.g. after navigation).
|
|
47
|
+
*/
|
|
48
|
+
export function buildCompactMessage(step, pageState, prevState, prevTree) {
|
|
49
|
+
// If the URL path changed, the page is fundamentally different — send full state
|
|
50
|
+
try {
|
|
51
|
+
const oldPath = new URL(prevState.url).pathname;
|
|
52
|
+
const newPath = new URL(pageState.url).pathname;
|
|
53
|
+
if (oldPath !== newPath)
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const tree = formatA11yTree(pageState.a11yTree);
|
|
60
|
+
const treeUnchanged = tree === prevTree;
|
|
61
|
+
if (treeUnchanged) {
|
|
62
|
+
const parts = [
|
|
63
|
+
`Current URL: ${pageState.url}`,
|
|
64
|
+
"",
|
|
65
|
+
"Page state is unchanged from the previous step. All element refs remain the same.",
|
|
66
|
+
"",
|
|
67
|
+
`Step to execute: ${step}`,
|
|
68
|
+
];
|
|
69
|
+
return { message: parts.join("\n"), mode: "unchanged" };
|
|
70
|
+
}
|
|
71
|
+
const { added, removed, changedRatio } = computeTreeDiff(prevTree, tree);
|
|
72
|
+
// Small change — send just the diff. Refs are stable so the LLM can
|
|
73
|
+
// combine this with the full tree it saw earlier.
|
|
74
|
+
if (changedRatio < 0.3) {
|
|
75
|
+
const parts = [
|
|
76
|
+
`Current URL: ${pageState.url}`,
|
|
77
|
+
"",
|
|
78
|
+
"Accessibility tree changes (refs are stable — unchanged elements keep their refs from the previous message):",
|
|
79
|
+
];
|
|
80
|
+
if (removed.length > 0) {
|
|
81
|
+
parts.push("Removed elements:");
|
|
82
|
+
for (const line of removed)
|
|
83
|
+
parts.push(` - ${line.trim()}`);
|
|
84
|
+
}
|
|
85
|
+
if (added.length > 0) {
|
|
86
|
+
parts.push("New/changed elements:");
|
|
87
|
+
for (const line of added)
|
|
88
|
+
parts.push(` + ${line.trim()}`);
|
|
89
|
+
}
|
|
90
|
+
parts.push("", `Step to execute: ${step}`);
|
|
91
|
+
return { message: parts.join("\n"), mode: "tree-diff" };
|
|
92
|
+
}
|
|
93
|
+
// Large change — send full tree without visible text
|
|
94
|
+
const parts = [
|
|
95
|
+
`Current URL: ${pageState.url}`,
|
|
96
|
+
`Page title: ${pageState.title}`,
|
|
97
|
+
"",
|
|
98
|
+
"Accessibility tree (updated — refs are stable, only changed elements have new/removed entries):",
|
|
99
|
+
tree,
|
|
100
|
+
"",
|
|
101
|
+
`Step to execute: ${step}`,
|
|
102
|
+
];
|
|
103
|
+
return { message: parts.join("\n"), mode: "tree-only" };
|
|
104
|
+
}
|
|
105
|
+
/** Build the full messages array for a chat completion request. */
|
|
106
|
+
export function buildMessages(step, pageState) {
|
|
107
|
+
return [
|
|
108
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
109
|
+
{ role: "user", content: buildUserMessage(step, pageState) },
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=message-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-builder.js","sourceRoot":"","sources":["../../src/pilot/message-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network idle tracking and console log collection for Playwright pages.
|
|
3
|
+
*/
|
|
4
|
+
import type { Page } from "playwright";
|
|
5
|
+
import type { ConsoleEntry } from "../reporter/types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Attach a network request tracker to a page.
|
|
8
|
+
* Call once after page creation. The returned `waitForNetworkIdle` function
|
|
9
|
+
* waits until all in-flight requests have completed (with a grace period
|
|
10
|
+
* to allow follow-up requests to start).
|
|
11
|
+
*/
|
|
12
|
+
export declare function attachNetworkTracker(page: Page): {
|
|
13
|
+
waitForNetworkIdle: (timeoutMs?: number) => Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Collect console messages from a page.
|
|
17
|
+
* Call attachConsoleCollector(page) once after page creation,
|
|
18
|
+
* then drainConsoleLogs() to retrieve and clear collected entries.
|
|
19
|
+
*/
|
|
20
|
+
export declare function attachConsoleCollector(page: Page): {
|
|
21
|
+
drain: () => ConsoleEntry[];
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/pilot/network.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG;IACjD,kBAAkB,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACzD,CA6DA;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG;IACnD,KAAK,EAAE,MAAM,YAAY,EAAE,CAAA;CAC3B,CAcA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network idle tracking and console log collection for Playwright pages.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Attach a network request tracker to a page.
|
|
6
|
+
* Call once after page creation. The returned `waitForNetworkIdle` function
|
|
7
|
+
* waits until all in-flight requests have completed (with a grace period
|
|
8
|
+
* to allow follow-up requests to start).
|
|
9
|
+
*/
|
|
10
|
+
export function attachNetworkTracker(page) {
|
|
11
|
+
const pending = new Set();
|
|
12
|
+
page.on("request", (req) => pending.add(req));
|
|
13
|
+
page.on("requestfinished", (req) => pending.delete(req));
|
|
14
|
+
page.on("requestfailed", (req) => pending.delete(req));
|
|
15
|
+
return {
|
|
16
|
+
/**
|
|
17
|
+
* Wait until the page has settled: network requests done AND
|
|
18
|
+
* rendered content stable. Two phases:
|
|
19
|
+
* 1. Wait for zero in-flight requests (with grace period for chained requests)
|
|
20
|
+
* 2. Wait for innerText to stop changing (catches CSS transitions/animations)
|
|
21
|
+
*/
|
|
22
|
+
async waitForNetworkIdle(timeoutMs = 5000) {
|
|
23
|
+
const deadline = performance.now() + timeoutMs;
|
|
24
|
+
// Phase 1: wait for network requests to complete
|
|
25
|
+
const networkGrace = 200;
|
|
26
|
+
let quietSince = pending.size === 0 ? performance.now() : 0;
|
|
27
|
+
while (performance.now() < deadline) {
|
|
28
|
+
if (pending.size === 0) {
|
|
29
|
+
if (!quietSince)
|
|
30
|
+
quietSince = performance.now();
|
|
31
|
+
if (performance.now() - quietSince >= networkGrace)
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
quietSince = 0;
|
|
36
|
+
}
|
|
37
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
38
|
+
}
|
|
39
|
+
// Phase 2: wait for DOM content to stabilize.
|
|
40
|
+
// Use textContent (not innerText) because some frameworks
|
|
41
|
+
// render content that CSS hides from innerText during animations.
|
|
42
|
+
// textContent sees all DOM text regardless of CSS.
|
|
43
|
+
const contentGrace = 300;
|
|
44
|
+
let previous;
|
|
45
|
+
try {
|
|
46
|
+
previous =
|
|
47
|
+
(await page.locator("body").textContent()) ?? "";
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
let stableSince = performance.now();
|
|
53
|
+
while (performance.now() < deadline) {
|
|
54
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
55
|
+
let current;
|
|
56
|
+
try {
|
|
57
|
+
current =
|
|
58
|
+
(await page.locator("body").textContent()) ?? "";
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (current !== previous) {
|
|
64
|
+
previous = current;
|
|
65
|
+
stableSince = performance.now();
|
|
66
|
+
}
|
|
67
|
+
else if (performance.now() - stableSince >= contentGrace) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Collect console messages from a page.
|
|
76
|
+
* Call attachConsoleCollector(page) once after page creation,
|
|
77
|
+
* then drainConsoleLogs() to retrieve and clear collected entries.
|
|
78
|
+
*/
|
|
79
|
+
export function attachConsoleCollector(page) {
|
|
80
|
+
const logs = [];
|
|
81
|
+
page.on("console", (msg) => {
|
|
82
|
+
logs.push({ type: msg.type(), text: msg.text() });
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
drain() {
|
|
86
|
+
const snapshot = [...logs];
|
|
87
|
+
logs.length = 0;
|
|
88
|
+
return snapshot;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/pilot/network.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Pilot — core AI agent loop.
|
|
3
|
+
* Iterates through test steps: capture state → LLM → execute → record result.
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from "playwright";
|
|
6
|
+
import type { ConsoleEntry, TestCaseResult } from "../reporter/types.js";
|
|
7
|
+
import type { LLMClient } from "./llm.js";
|
|
8
|
+
import type { PlanRecorder } from "../planner/plan-generator.js";
|
|
9
|
+
export interface PilotOptions {
|
|
10
|
+
/** Per-step timeout in ms. */
|
|
11
|
+
timeout: number;
|
|
12
|
+
/** Console log drain function. */
|
|
13
|
+
consoleDrain: () => ConsoleEntry[];
|
|
14
|
+
/** Optional plan recorder for capturing heuristic plans during discovery. */
|
|
15
|
+
recorder?: PlanRecorder;
|
|
16
|
+
/** Wait for network requests to settle before capturing page state. */
|
|
17
|
+
waitForNetworkIdle?: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Run all steps of a test case sequentially.
|
|
21
|
+
* Fails fast: stops on the first failed step.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runTestCase(page: Page, testCase: {
|
|
24
|
+
name: string;
|
|
25
|
+
steps: string[];
|
|
26
|
+
}, llm: LLMClient, options: PilotOptions): Promise<TestCaseResult>;
|
|
27
|
+
//# sourceMappingURL=pilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pilot.d.ts","sourceRoot":"","sources":["../../src/pilot/pilot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAGX,YAAY,EAGZ,cAAc,EACd,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAMzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAGhE,MAAM,WAAW,YAAY;IAC5B,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,kCAAkC;IAClC,YAAY,EAAE,MAAM,YAAY,EAAE,CAAA;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACxC;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAChC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,EAC3C,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CAwQzB"}
|