@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.
Files changed (142) hide show
  1. package/README.md +391 -0
  2. package/dist/browser/browser.d.ts +24 -0
  3. package/dist/browser/browser.d.ts.map +1 -0
  4. package/dist/browser/browser.js +44 -0
  5. package/dist/browser/browser.js.map +1 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +140 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/cli/run.d.ts +9 -0
  11. package/dist/cli/run.d.ts.map +1 -0
  12. package/dist/cli/run.js +277 -0
  13. package/dist/cli/run.js.map +1 -0
  14. package/dist/config.d.ts +48 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +107 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/globals.d.ts +21 -0
  19. package/dist/globals.d.ts.map +1 -0
  20. package/dist/globals.js +24 -0
  21. package/dist/globals.js.map +1 -0
  22. package/dist/parser/loader.d.ts +7 -0
  23. package/dist/parser/loader.d.ts.map +1 -0
  24. package/dist/parser/loader.js +43 -0
  25. package/dist/parser/loader.js.map +1 -0
  26. package/dist/parser/schema.d.ts +42 -0
  27. package/dist/parser/schema.d.ts.map +1 -0
  28. package/dist/parser/schema.js +33 -0
  29. package/dist/parser/schema.js.map +1 -0
  30. package/dist/parser/steps.d.ts +13 -0
  31. package/dist/parser/steps.d.ts.map +1 -0
  32. package/dist/parser/steps.js +44 -0
  33. package/dist/parser/steps.js.map +1 -0
  34. package/dist/parser/variables.d.ts +18 -0
  35. package/dist/parser/variables.d.ts.map +1 -0
  36. package/dist/parser/variables.js +44 -0
  37. package/dist/parser/variables.js.map +1 -0
  38. package/dist/pilot/a11y-parser.d.ts +26 -0
  39. package/dist/pilot/a11y-parser.d.ts.map +1 -0
  40. package/dist/pilot/a11y-parser.js +195 -0
  41. package/dist/pilot/a11y-parser.js.map +1 -0
  42. package/dist/pilot/assertions.d.ts +30 -0
  43. package/dist/pilot/assertions.d.ts.map +1 -0
  44. package/dist/pilot/assertions.js +219 -0
  45. package/dist/pilot/assertions.js.map +1 -0
  46. package/dist/pilot/checkbox.d.ts +12 -0
  47. package/dist/pilot/checkbox.d.ts.map +1 -0
  48. package/dist/pilot/checkbox.js +104 -0
  49. package/dist/pilot/checkbox.js.map +1 -0
  50. package/dist/pilot/executor.d.ts +17 -0
  51. package/dist/pilot/executor.d.ts.map +1 -0
  52. package/dist/pilot/executor.js +462 -0
  53. package/dist/pilot/executor.js.map +1 -0
  54. package/dist/pilot/form-fields.d.ts +34 -0
  55. package/dist/pilot/form-fields.d.ts.map +1 -0
  56. package/dist/pilot/form-fields.js +139 -0
  57. package/dist/pilot/form-fields.js.map +1 -0
  58. package/dist/pilot/llm.d.ts +49 -0
  59. package/dist/pilot/llm.d.ts.map +1 -0
  60. package/dist/pilot/llm.js +188 -0
  61. package/dist/pilot/llm.js.map +1 -0
  62. package/dist/pilot/locator.d.ts +58 -0
  63. package/dist/pilot/locator.d.ts.map +1 -0
  64. package/dist/pilot/locator.js +248 -0
  65. package/dist/pilot/locator.js.map +1 -0
  66. package/dist/pilot/message-builder.d.ts +31 -0
  67. package/dist/pilot/message-builder.d.ts.map +1 -0
  68. package/dist/pilot/message-builder.js +112 -0
  69. package/dist/pilot/message-builder.js.map +1 -0
  70. package/dist/pilot/network.d.ts +23 -0
  71. package/dist/pilot/network.d.ts.map +1 -0
  72. package/dist/pilot/network.js +92 -0
  73. package/dist/pilot/network.js.map +1 -0
  74. package/dist/pilot/pilot.d.ts +27 -0
  75. package/dist/pilot/pilot.d.ts.map +1 -0
  76. package/dist/pilot/pilot.js +249 -0
  77. package/dist/pilot/pilot.js.map +1 -0
  78. package/dist/pilot/prompts.d.ts +8 -0
  79. package/dist/pilot/prompts.d.ts.map +1 -0
  80. package/dist/pilot/prompts.js +163 -0
  81. package/dist/pilot/prompts.js.map +1 -0
  82. package/dist/pilot/providers/anthropic.d.ts +6 -0
  83. package/dist/pilot/providers/anthropic.d.ts.map +1 -0
  84. package/dist/pilot/providers/anthropic.js +45 -0
  85. package/dist/pilot/providers/anthropic.js.map +1 -0
  86. package/dist/pilot/providers/gemini.d.ts +6 -0
  87. package/dist/pilot/providers/gemini.d.ts.map +1 -0
  88. package/dist/pilot/providers/gemini.js +55 -0
  89. package/dist/pilot/providers/gemini.js.map +1 -0
  90. package/dist/pilot/providers/index.d.ts +10 -0
  91. package/dist/pilot/providers/index.d.ts.map +1 -0
  92. package/dist/pilot/providers/index.js +25 -0
  93. package/dist/pilot/providers/index.js.map +1 -0
  94. package/dist/pilot/providers/openai-compatible.d.ts +7 -0
  95. package/dist/pilot/providers/openai-compatible.d.ts.map +1 -0
  96. package/dist/pilot/providers/openai-compatible.js +34 -0
  97. package/dist/pilot/providers/openai-compatible.js.map +1 -0
  98. package/dist/pilot/providers/types.d.ts +12 -0
  99. package/dist/pilot/providers/types.d.ts.map +1 -0
  100. package/dist/pilot/providers/types.js +2 -0
  101. package/dist/pilot/providers/types.js.map +1 -0
  102. package/dist/pilot/response-parser.d.ts +31 -0
  103. package/dist/pilot/response-parser.d.ts.map +1 -0
  104. package/dist/pilot/response-parser.js +188 -0
  105. package/dist/pilot/response-parser.js.map +1 -0
  106. package/dist/pilot/state.d.ts +19 -0
  107. package/dist/pilot/state.d.ts.map +1 -0
  108. package/dist/pilot/state.js +67 -0
  109. package/dist/pilot/state.js.map +1 -0
  110. package/dist/pilot/trace.d.ts +16 -0
  111. package/dist/pilot/trace.d.ts.map +1 -0
  112. package/dist/pilot/trace.js +117 -0
  113. package/dist/pilot/trace.js.map +1 -0
  114. package/dist/planner/hasher.d.ts +14 -0
  115. package/dist/planner/hasher.d.ts.map +1 -0
  116. package/dist/planner/hasher.js +21 -0
  117. package/dist/planner/hasher.js.map +1 -0
  118. package/dist/planner/plan-generator.d.ts +23 -0
  119. package/dist/planner/plan-generator.d.ts.map +1 -0
  120. package/dist/planner/plan-generator.js +56 -0
  121. package/dist/planner/plan-generator.js.map +1 -0
  122. package/dist/planner/plan-runner.d.ts +16 -0
  123. package/dist/planner/plan-runner.d.ts.map +1 -0
  124. package/dist/planner/plan-runner.js +375 -0
  125. package/dist/planner/plan-runner.js.map +1 -0
  126. package/dist/planner/plan-store.d.ts +22 -0
  127. package/dist/planner/plan-store.d.ts.map +1 -0
  128. package/dist/planner/plan-store.js +71 -0
  129. package/dist/planner/plan-store.js.map +1 -0
  130. package/dist/planner/plan-types.d.ts +64 -0
  131. package/dist/planner/plan-types.d.ts.map +1 -0
  132. package/dist/planner/plan-types.js +7 -0
  133. package/dist/planner/plan-types.js.map +1 -0
  134. package/dist/reporter/types.d.ts +130 -0
  135. package/dist/reporter/types.d.ts.map +1 -0
  136. package/dist/reporter/types.js +5 -0
  137. package/dist/reporter/types.js.map +1 -0
  138. package/dist/types.d.ts +51 -0
  139. package/dist/types.d.ts.map +1 -0
  140. package/dist/types.js +23 -0
  141. package/dist/types.js.map +1 -0
  142. package/package.json +64 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Form field capture and formatting for Playwright pages.
3
+ */
4
+ /**
5
+ * Capture metadata about all visible form fields on the page.
6
+ * Extracts label, placeholder, input type, required status, and select options
7
+ * directly from the DOM — information not available in the a11y tree.
8
+ */
9
+ export async function captureFormFields(page) {
10
+ return page.evaluate(() => {
11
+ const fields = [];
12
+ const inputs = document.querySelectorAll("input, textarea, select");
13
+ for (const el of inputs) {
14
+ // Skip hidden inputs and submit buttons
15
+ if (el instanceof HTMLInputElement) {
16
+ if (el.type === "hidden" || el.type === "submit" || el.type === "button")
17
+ continue;
18
+ }
19
+ // Skip invisible elements
20
+ const style = window.getComputedStyle(el);
21
+ if (style.display === "none" || style.visibility === "hidden")
22
+ continue;
23
+ // Find label: explicit <label for="id">, wrapping <label>, or aria-label
24
+ let label;
25
+ if (el.id) {
26
+ const labelEl = document.querySelector(`label[for="${el.id}"]`);
27
+ if (labelEl)
28
+ label = labelEl.textContent.trim() || undefined;
29
+ }
30
+ if (!label) {
31
+ const labelEl = el.closest("label");
32
+ if (labelEl) {
33
+ // Get label text excluding the input's own text
34
+ const clone = labelEl.cloneNode(true);
35
+ clone.querySelectorAll("input, textarea, select").forEach((c) => { c.remove(); });
36
+ label = clone.textContent.trim() || undefined;
37
+ }
38
+ }
39
+ if (!label) {
40
+ const ariaLabel = el.getAttribute("aria-label");
41
+ if (ariaLabel) {
42
+ label = ariaLabel;
43
+ }
44
+ }
45
+ if (!label) {
46
+ const ariaLabelledBy = el.getAttribute("aria-labelledby");
47
+ if (ariaLabelledBy) {
48
+ const labelledBy = document.getElementById(ariaLabelledBy);
49
+ if (labelledBy)
50
+ label = labelledBy.textContent.trim() || undefined;
51
+ }
52
+ }
53
+ // Detect autocomplete/typeahead/combobox patterns
54
+ let autocomplete = false;
55
+ const role = el.getAttribute("role");
56
+ const ariaAuto = el.getAttribute("aria-autocomplete");
57
+ const ariaExpanded = el.hasAttribute("aria-expanded");
58
+ const ariaOwns = el.getAttribute("aria-owns") ?? el.getAttribute("aria-controls");
59
+ const htmlAutocomplete = el.getAttribute("autocomplete");
60
+ // Explicit ARIA combobox or autocomplete
61
+ if (role === "combobox" || ariaAuto === "list" || ariaAuto === "both") {
62
+ autocomplete = true;
63
+ }
64
+ // Has aria-expanded (toggle pattern) or aria-owns/controls a listbox
65
+ if (ariaExpanded && ariaOwns) {
66
+ autocomplete = true;
67
+ }
68
+ // Parent or wrapper has combobox role
69
+ if (el.closest("[role='combobox']")) {
70
+ autocomplete = true;
71
+ }
72
+ // Adjacent or nearby listbox/datalist
73
+ if (el.getAttribute("list")) {
74
+ autocomplete = true; // HTML5 <datalist>
75
+ }
76
+ // Common CSS class patterns for autocomplete widgets
77
+ const classes = el.className + " " + (el.closest("[class]")?.className ?? "");
78
+ if (/autocomplete|typeahead|combobox|autosuggest|searchbox/i.test(classes)) {
79
+ autocomplete = true;
80
+ }
81
+ // Browser autocomplete="off" often indicates a custom autocomplete widget
82
+ // (the site disables native autocomplete because it has its own)
83
+ if (htmlAutocomplete === "off" && el instanceof HTMLInputElement && el.type === "text") {
84
+ // Only flag as autocomplete if there are other hints (class patterns, nearby listboxes)
85
+ const parent = el.parentElement;
86
+ if (parent && (parent.querySelector("[role='listbox'], [role='option'], .dropdown, .suggestions, .autocomplete-results") ??
87
+ /autocomplete|typeahead|combobox|autosuggest/i.test(parent.className))) {
88
+ autocomplete = true;
89
+ }
90
+ }
91
+ const field = {
92
+ label,
93
+ placeholder: el.placeholder || undefined,
94
+ inputType: el instanceof HTMLSelectElement ? "select" : (el.type || "text"),
95
+ tag: el.tagName.toLowerCase(),
96
+ required: el.required || el.getAttribute("aria-required") === "true",
97
+ };
98
+ if (autocomplete) {
99
+ field.autocomplete = true;
100
+ }
101
+ // Collect select options
102
+ if (el instanceof HTMLSelectElement) {
103
+ field.options = Array.from(el.options)
104
+ .filter((o) => o.value !== "")
105
+ .map((o) => o.text.trim());
106
+ }
107
+ fields.push(field);
108
+ }
109
+ return fields;
110
+ });
111
+ }
112
+ /**
113
+ * Format form field metadata as readable text for LLM consumption.
114
+ */
115
+ export function formatFormFields(fields) {
116
+ if (fields.length === 0)
117
+ return "(no form fields found)";
118
+ return fields
119
+ .map((f, i) => {
120
+ const parts = [`${String(i + 1)}. <${f.tag}>`];
121
+ if (f.label)
122
+ parts.push(`label="${f.label}"`);
123
+ if (f.placeholder)
124
+ parts.push(`placeholder="${f.placeholder}"`);
125
+ parts.push(`type="${f.inputType}"`);
126
+ if (f.required)
127
+ parts.push("[required]");
128
+ if (f.autocomplete)
129
+ parts.push("[autocomplete]");
130
+ if (f.ref)
131
+ parts.push(`ref=${f.ref}`);
132
+ if (f.options && f.options.length > 0) {
133
+ parts.push(`options: [${f.options.map((o) => `"${o}"`).join(", ")}]`);
134
+ }
135
+ return parts.join(" ");
136
+ })
137
+ .join("\n");
138
+ }
139
+ //# sourceMappingURL=form-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-fields.js","sourceRoot":"","sources":["../../src/pilot/form-fields.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Provider-agnostic LLM client.
3
+ * Uses pluggable providers for chat completions.
4
+ */
5
+ import type { RunConfig } from "../types.js";
6
+ import type { Action, PageState } from "../reporter/types.js";
7
+ import type { Page } from "playwright";
8
+ import type { PlannedStep } from "./response-parser.js";
9
+ import type { LLMProvider } from "./providers/index.js";
10
+ /** Re-export ChatMessage so existing imports still work. */
11
+ export type { ChatMessage } from "./providers/index.js";
12
+ /** Configuration for the LLM client. */
13
+ export interface LLMClientConfig {
14
+ apiKey: string;
15
+ provider: LLMProvider;
16
+ plannerModel: string;
17
+ pilotModel: string;
18
+ }
19
+ /** The LLM client interface. */
20
+ export interface LLMClient {
21
+ /**
22
+ * Pre-plan all steps by sending the full test spec to the LLM.
23
+ * The LLM interprets each step, potentially splitting compound steps
24
+ * into multiple atomic actions. Returns a flat list of planned steps.
25
+ */
26
+ planSteps(steps: string[]): Promise<PlannedStep[]>;
27
+ /** Resolve a single step using the page state and a11y tree. */
28
+ resolveStep(step: string, pageState: PageState): Promise<Action>;
29
+ /**
30
+ * Expand a compound step into multiple atomic actions using live page state.
31
+ * Used for steps like "fill in the form" that need to see the actual form
32
+ * fields before they can be decomposed into individual type/select/click actions.
33
+ */
34
+ expandStep(step: string, pageState: PageState, page: Page): Promise<PlannedStep[]>;
35
+ /** Reset conversation history (call between test cases). */
36
+ resetHistory(): void;
37
+ }
38
+ /** Resolve the API key from environment variables. */
39
+ export declare function resolveApiKey(): string;
40
+ /** Resolve LLM client config from RunConfig and environment. */
41
+ export declare function resolveLLMConfig(runConfig: RunConfig): LLMClientConfig;
42
+ /**
43
+ * Create an LLM client that maintains conversation history within a test case.
44
+ * The system prompt is sent once. Each step adds a user message and the LLM's
45
+ * response to the history, giving the model context about prior actions.
46
+ * Call resetHistory() between test cases.
47
+ */
48
+ export declare function createLLMClient(config: LLMClientConfig): LLMClient;
49
+ //# sourceMappingURL=llm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/pilot/llm.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAG7D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAUtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAGpE,4DAA4D;AAC5D,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAEvD,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,WAAW,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,gCAAgC;AAChC,MAAM,WAAW,SAAS;IACzB;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;IAClD,gEAAgE;IAChE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAChE;;;;OAIG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,GACR,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;IACzB,4DAA4D;IAC5D,YAAY,IAAI,IAAI,CAAA;CACpB;AAED,sDAAsD;AACtD,wBAAgB,aAAa,IAAI,MAAM,CAQtC;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,eAAe,CAYtE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA8MlE"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Provider-agnostic LLM client.
3
+ * Uses pluggable providers for chat completions.
4
+ */
5
+ import { resolveModelConfig } from "../types.js";
6
+ import { formatA11yTree } from "./a11y-parser.js";
7
+ import { captureFormFields, formatFormFields } from "./form-fields.js";
8
+ import { globals } from "../globals.js";
9
+ import { SYSTEM_PROMPT, PLAN_SYSTEM_PROMPT, EXPAND_SYSTEM_PROMPT, } from "./prompts.js";
10
+ import { buildUserMessage, buildCompactMessage } from "./message-builder.js";
11
+ import { parseActionResponse, parsePlanResponse } from "./response-parser.js";
12
+ import { createProvider } from "./providers/index.js";
13
+ /** Resolve the API key from environment variables. */
14
+ export function resolveApiKey() {
15
+ const key = process.env.LLM_API_KEY ?? process.env.OPENROUTER_API_KEY;
16
+ if (!key) {
17
+ throw new Error("No API key found. Set LLM_API_KEY or OPENROUTER_API_KEY environment variable.");
18
+ }
19
+ return key;
20
+ }
21
+ /** Resolve LLM client config from RunConfig and environment. */
22
+ export function resolveLLMConfig(runConfig) {
23
+ const modelConfig = resolveModelConfig(runConfig.model);
24
+ const provider = createProvider(runConfig.provider, runConfig.llmBaseUrl);
25
+ return {
26
+ apiKey: resolveApiKey(),
27
+ provider,
28
+ plannerModel: modelConfig.planner,
29
+ pilotModel: modelConfig.pilot,
30
+ };
31
+ }
32
+ /**
33
+ * Create an LLM client that maintains conversation history within a test case.
34
+ * The system prompt is sent once. Each step adds a user message and the LLM's
35
+ * response to the history, giving the model context about prior actions.
36
+ * Call resetHistory() between test cases.
37
+ */
38
+ export function createLLMClient(config) {
39
+ let history = [];
40
+ const cache = new Map();
41
+ let prevPageState = null;
42
+ let prevFormattedTree = "";
43
+ async function chat(messages, model) {
44
+ return config.provider.chatCompletion(messages, {
45
+ apiKey: config.apiKey,
46
+ model,
47
+ });
48
+ }
49
+ return {
50
+ resetHistory() {
51
+ history = [];
52
+ prevPageState = null;
53
+ prevFormattedTree = "";
54
+ },
55
+ async planSteps(steps) {
56
+ const userMessage = steps
57
+ .map((s, i) => `${String(i + 1)}. ${s}`)
58
+ .join("\n");
59
+ const content = await chat([
60
+ { role: "system", content: PLAN_SYSTEM_PROMPT },
61
+ { role: "user", content: userMessage },
62
+ ], config.plannerModel);
63
+ return parsePlanResponse(content);
64
+ },
65
+ async expandStep(step, pageState, page) {
66
+ const tree = formatA11yTree(pageState.a11yTree);
67
+ const formFields = await captureFormFields(page);
68
+ const formFieldsText = formatFormFields(formFields);
69
+ if (globals.debug) {
70
+ console.log(`\n [expand] Detected ${String(formFields.length)} form fields:`);
71
+ for (const f of formFields) {
72
+ const parts = [` <${f.tag}>`];
73
+ if (f.label)
74
+ parts.push(`label="${f.label}"`);
75
+ if (f.placeholder)
76
+ parts.push(`placeholder="${f.placeholder}"`);
77
+ parts.push(`type="${f.inputType}"`);
78
+ if (f.required)
79
+ parts.push("[required]");
80
+ if (f.autocomplete)
81
+ parts.push("[autocomplete]");
82
+ if (f.options && f.options.length > 0) {
83
+ parts.push(`options: [${f.options
84
+ .slice(0, 5)
85
+ .map((o) => `"${o}"`)
86
+ .join(", ")}${f.options.length > 5 ? ", ..." : ""}]`);
87
+ }
88
+ console.log(parts.join(" "));
89
+ }
90
+ const autoFields = formFields.filter((f) => f.autocomplete);
91
+ if (autoFields.length > 0) {
92
+ console.log(` [expand] ${String(autoFields.length)} autocomplete field(s) detected`);
93
+ }
94
+ }
95
+ const userMessage = [
96
+ `Original step: ${step}`,
97
+ "",
98
+ `Current URL: ${pageState.url}`,
99
+ `Page title: ${pageState.title}`,
100
+ "",
101
+ "Accessibility tree:",
102
+ tree,
103
+ "",
104
+ "Form fields on the page (with label, placeholder, type, and options):",
105
+ formFieldsText,
106
+ ].join("\n");
107
+ if (globals.debug) {
108
+ console.log(` [expand] Sending expansion request to LLM...`);
109
+ }
110
+ const content = await chat([
111
+ { role: "system", content: EXPAND_SYSTEM_PROMPT },
112
+ { role: "user", content: userMessage },
113
+ ], config.plannerModel);
114
+ if (globals.debug) {
115
+ console.log(` [expand] LLM raw response:`);
116
+ for (const line of content.trim().split("\n")) {
117
+ console.log(` ${line}`);
118
+ }
119
+ }
120
+ const expanded = parsePlanResponse(content);
121
+ if (globals.debug) {
122
+ console.log(` [expand] Parsed into ${String(expanded.length)} sub-steps:`);
123
+ for (const es of expanded) {
124
+ const label = es.action
125
+ ? JSON.stringify(es.action)
126
+ : "(needs page)";
127
+ console.log(` - ${es.step} → ${label}`);
128
+ }
129
+ }
130
+ // Add expansion exchange to history for context in subsequent steps
131
+ history.push({
132
+ role: "user",
133
+ content: `Expanded step: ${step}\nResult:\n${content}`,
134
+ }, {
135
+ role: "assistant",
136
+ content: "OK, form has been filled and submitted.",
137
+ });
138
+ return expanded;
139
+ },
140
+ async resolveStep(step, pageState) {
141
+ // Check cache: same step on same page → same action
142
+ const cacheKey = `${step}\0${pageState.url}`;
143
+ const cached = cache.get(cacheKey);
144
+ if (cached)
145
+ return cached;
146
+ // Try to build a compact message if we have prior state.
147
+ // Three modes:
148
+ // "unchanged" — page identical, skip tree + visible text
149
+ // "tree-only" — tree changed, skip visible text
150
+ // full — first step or after navigation
151
+ let userMessage;
152
+ let compactMode = "full";
153
+ if (prevPageState && history.length > 0) {
154
+ const compact = buildCompactMessage(step, pageState, prevPageState, prevFormattedTree);
155
+ if (compact) {
156
+ userMessage = compact.message;
157
+ compactMode = compact.mode;
158
+ }
159
+ else {
160
+ userMessage = buildUserMessage(step, pageState);
161
+ }
162
+ }
163
+ else {
164
+ userMessage = buildUserMessage(step, pageState);
165
+ }
166
+ if (globals.debug) {
167
+ console.log(` [resolve] Mode: ${compactMode} (${String(userMessage.length)} chars)`);
168
+ }
169
+ // Build messages: system + history + new user message
170
+ const messages = [
171
+ { role: "system", content: SYSTEM_PROMPT },
172
+ ...history,
173
+ { role: "user", content: userMessage },
174
+ ];
175
+ const content = await chat(messages, config.pilotModel);
176
+ const action = parseActionResponse(content);
177
+ // Cache the result for identical future requests
178
+ cache.set(cacheKey, action);
179
+ // Append this exchange to history for subsequent steps
180
+ history.push({ role: "user", content: userMessage }, { role: "assistant", content: content });
181
+ // Track page state for compact messages on subsequent steps
182
+ prevPageState = pageState;
183
+ prevFormattedTree = formatA11yTree(pageState.a11yTree);
184
+ return action;
185
+ },
186
+ };
187
+ }
188
+ //# sourceMappingURL=llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/pilot/llm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Locator resolution — translates a11y tree refs into Playwright locators.
3
+ */
4
+ import type { Page, Locator } from "playwright";
5
+ import type { A11yNode, Action, ResolvedSelector } from "../reporter/types.js";
6
+ export type AriaRole = Parameters<Page["getByRole"]>[0];
7
+ /**
8
+ * Find an A11yNode by its ref ID, searching the tree recursively.
9
+ */
10
+ export declare function findNodeByRef(nodes: A11yNode[], ref: string): A11yNode | undefined;
11
+ /**
12
+ * Find the path from root to a node by ref.
13
+ * Returns the chain of ancestor nodes including the target, or undefined.
14
+ */
15
+ export declare function findNodePath(nodes: A11yNode[], ref: string, path?: A11yNode[]): A11yNode[] | undefined;
16
+ /**
17
+ * Given a locator, return it if it matches exactly one element,
18
+ * or return the first visible match if there are several.
19
+ * Returns undefined if the locator matches nothing.
20
+ */
21
+ export declare function pickVisible(locator: Locator): Promise<Locator | undefined>;
22
+ export declare function roleLocator(scope: Page | Locator, node: A11yNode): Locator;
23
+ /**
24
+ * Resolve an element ref to a Playwright locator using the a11y tree hierarchy.
25
+ *
26
+ * Primary strategy: chain getByRole calls from ancestor → target using the
27
+ * same tree structure that ariaSnapshot reported. This disambiguates elements
28
+ * that share the same role+name but live under different parents.
29
+ *
30
+ * Fallback strategies (tried in order if chained locator doesn't match):
31
+ * 1. Direct getByRole (ignoring hierarchy)
32
+ * 2. getByLabel (for form inputs)
33
+ * 3. getByPlaceholder (for text inputs)
34
+ * Returns the first locator that resolves to a single visible element.
35
+ */
36
+ export declare function resolveLocator(page: Page, nodes: A11yNode[], ref: string): Promise<Locator>;
37
+ /**
38
+ * Resolve an element by its visible text content.
39
+ * Used as a fallback when the element isn't in the accessibility tree
40
+ * (e.g. due to missing ARIA roles in the page markup).
41
+ */
42
+ export declare function resolveByText(page: Page, text: string): Promise<Locator>;
43
+ /**
44
+ * Resolve a locator from an action's ref or text field.
45
+ */
46
+ export declare function resolveActionTarget(page: Page, action: Action, a11yTree: A11yNode[]): Promise<Locator>;
47
+ /**
48
+ * Extract a CSS selector from a resolved Playwright locator.
49
+ * Evaluates in-browser to build a unique path-based selector.
50
+ */
51
+ export declare function extractCssSelector(locator: Locator): Promise<string | undefined>;
52
+ /**
53
+ * Extract selector info from a resolved action for the plan recorder.
54
+ * For ref-based actions: returns role + name from the a11y node.
55
+ * For text-based actions: extracts a CSS selector from the DOM element.
56
+ */
57
+ export declare function extractSelectorInfo(page: Page, action: Action, a11yTree: A11yNode[], locator: Locator): Promise<ResolvedSelector | undefined>;
58
+ //# sourceMappingURL=locator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locator.d.ts","sourceRoot":"","sources":["../../src/pilot/locator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE9E,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAEvD;;GAEG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,QAAQ,EAAE,EACjB,GAAG,EAAE,MAAM,GACT,QAAQ,GAAG,SAAS,CAStB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,QAAQ,EAAE,EACjB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,QAAQ,EAAO,GACnB,QAAQ,EAAE,GAAG,SAAS,CAUxB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAchF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,GAAG,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAM1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CACnC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,QAAQ,EAAE,EACjB,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CA2DlB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAmB9E;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAAE,GAClB,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC7B;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAAE,EACpB,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAqCvC"}