pi-agent-browser-native 0.2.48 → 0.2.49

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 (185) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +16 -6
  3. package/dist/extensions/agent-browser/index.js +785 -0
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
  6. package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
  7. package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
  8. package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
  9. package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
  10. package/dist/extensions/agent-browser/lib/config.js +122 -0
  11. package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
  12. package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
  13. package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
  14. package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
  15. package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
  16. package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
  17. package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
  18. package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
  19. package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
  20. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
  21. package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
  22. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
  23. package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
  24. package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
  25. package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
  26. package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
  27. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
  28. package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
  29. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
  30. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
  31. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
  32. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
  40. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
  41. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
  42. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
  43. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
  44. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
  45. package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
  46. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
  47. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
  48. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
  49. package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
  50. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
  51. package/dist/extensions/agent-browser/lib/playbook.js +121 -0
  52. package/dist/extensions/agent-browser/lib/process.js +448 -0
  53. package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
  54. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
  55. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
  56. package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
  57. package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
  58. package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
  59. package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
  60. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
  61. package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
  62. package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
  63. package/dist/extensions/agent-browser/lib/results/network.js +73 -0
  64. package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
  65. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
  66. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
  67. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
  68. package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
  69. package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
  70. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
  71. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
  72. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
  73. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
  74. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
  75. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
  76. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
  77. package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
  78. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
  79. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
  80. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
  81. package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
  82. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
  83. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
  84. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
  85. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
  86. package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
  87. package/dist/extensions/agent-browser/lib/results/text.js +40 -0
  88. package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
  89. package/dist/extensions/agent-browser/lib/runtime.js +816 -0
  90. package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
  91. package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
  92. package/dist/extensions/agent-browser/lib/temp.js +498 -0
  93. package/dist/extensions/agent-browser/lib/web-search.js +562 -0
  94. package/docs/RELEASE.md +22 -11
  95. package/docs/SUPPORT_MATRIX.md +4 -3
  96. package/package.json +9 -5
  97. package/scripts/config.mjs +8 -2
  98. package/scripts/doctor.mjs +8 -7
  99. package/extensions/agent-browser/index.ts +0 -961
  100. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  101. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  102. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  103. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  104. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  105. package/extensions/agent-browser/lib/config-policy.js +0 -690
  106. package/extensions/agent-browser/lib/config.ts +0 -211
  107. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  108. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  109. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  110. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  111. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  112. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  113. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  114. package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
  115. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  116. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  117. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  118. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  119. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  120. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  121. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  122. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  123. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  124. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  125. package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
  126. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
  127. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
  128. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
  129. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  130. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
  131. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
  132. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
  133. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
  134. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
  135. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
  136. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
  137. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  138. package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
  139. package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
  140. package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
  141. package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
  142. package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
  143. package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
  144. package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -267
  145. package/extensions/agent-browser/lib/playbook.ts +0 -142
  146. package/extensions/agent-browser/lib/process.ts +0 -516
  147. package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
  148. package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
  149. package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
  150. package/extensions/agent-browser/lib/results/categories.ts +0 -106
  151. package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
  152. package/extensions/agent-browser/lib/results/contracts.ts +0 -241
  153. package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
  154. package/extensions/agent-browser/lib/results/envelope.ts +0 -195
  155. package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
  156. package/extensions/agent-browser/lib/results/network.ts +0 -78
  157. package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
  158. package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
  159. package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
  160. package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
  161. package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
  162. package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
  163. package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
  164. package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
  165. package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
  166. package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
  167. package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
  168. package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
  169. package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
  170. package/extensions/agent-browser/lib/results/presentation.ts +0 -257
  171. package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
  172. package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
  173. package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
  174. package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
  175. package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
  176. package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
  177. package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
  178. package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
  179. package/extensions/agent-browser/lib/results/text.ts +0 -40
  180. package/extensions/agent-browser/lib/runtime.ts +0 -988
  181. package/extensions/agent-browser/lib/session-page-state.ts +0 -512
  182. package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
  183. package/extensions/agent-browser/lib/temp.ts +0 -577
  184. package/extensions/agent-browser/lib/web-search.ts +0 -728
  185. /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
@@ -1,527 +0,0 @@
1
- /**
2
- * Purpose: Compile constrained job and lightweight QA wrapper inputs to upstream batch commands.
3
- * Responsibilities: Validate job/QA fields, produce argv/stdin, and summarize QA diagnostic results.
4
- * Scope: Job and QA modes only.
5
- */
6
-
7
- import type { ArtifactVerificationSummary } from "../results/contracts.js";
8
- import { isRecord } from "../parsing.js";
9
- import { summarizeNetworkFailures } from "../results/network.js";
10
- import { getBatchResultItems, getCommandNameFromBatchItem, getSelectValues } from "./shared.js";
11
- import { compileAgentBrowserSemanticAction } from "./semantic-action.js";
12
- import {
13
- AGENT_BROWSER_JOB_STEP_ACTIONS,
14
- AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS,
15
- AGENT_BROWSER_QA_LOAD_STATES,
16
- type AgentBrowserJobStepAction,
17
- type AgentBrowserQaLoadState,
18
- type AgentBrowserQaPresetAnalysis,
19
- type CompiledAgentBrowserJob,
20
- type CompiledAgentBrowserJobStep,
21
- type CompiledAgentBrowserQaPreset,
22
- } from "./types.js";
23
-
24
- function getRequiredJobString(step: Record<string, unknown>, field: "path" | "selector" | "text" | "url", action: AgentBrowserJobStepAction): { value?: string; error?: string } {
25
- const value = step[field];
26
- if (typeof value !== "string" || value.trim().length === 0) {
27
- return { error: `job step ${action} requires a non-empty ${field} string.` };
28
- }
29
- return { value };
30
- }
31
-
32
- function compileJobClickOrFillStep(step: Record<string, unknown>, action: "click" | "fill"): { args?: string[]; error?: string } {
33
- const hasSelector = typeof step.selector === "string" && step.selector.trim().length > 0;
34
- const hasLocator = step.locator !== undefined || step.role !== undefined || step.name !== undefined || step.value !== undefined;
35
- if (hasSelector && hasLocator) {
36
- return { error: `job step ${action} must use either selector or semantic locator fields, not both.` };
37
- }
38
- if (hasSelector) {
39
- if (action === "click") return { args: ["click", step.selector as string] };
40
- const text = getRequiredJobString(step, "text", action);
41
- if (text.error) return { error: text.error };
42
- return { args: ["fill", step.selector as string, text.value as string] };
43
- }
44
- if (!hasLocator) {
45
- return { error: `job step ${action} requires either a non-empty selector string or semantic locator fields.` };
46
- }
47
- const compiled = compileAgentBrowserSemanticAction({
48
- action,
49
- locator: step.locator,
50
- name: step.name,
51
- role: step.role,
52
- text: step.text,
53
- value: step.value,
54
- });
55
- if (compiled.error) return { error: compiled.error.replaceAll("semanticAction", `job step ${action}`) };
56
- return { args: compiled.compiled?.args };
57
- }
58
-
59
- function getUnsupportedJobStepField(step: Record<string, unknown>, allowedFields: ReadonlySet<string>): string | undefined {
60
- return Object.keys(step).find((field) => !allowedFields.has(field));
61
- }
62
-
63
- function getUnsupportedJobStepFieldError(step: Record<string, unknown>, action: AgentBrowserJobStepAction, allowedFields: ReadonlySet<string>): string | undefined {
64
- const unsupportedField = getUnsupportedJobStepField(step, allowedFields);
65
- if (!unsupportedField) return undefined;
66
- const supportedFields = [...allowedFields].filter((field) => field !== "action");
67
- const supportedText = supportedFields.length > 0 ? `supported fields are ${supportedFields.join(", ")}.` : "no additional fields are supported.";
68
- return `job step ${action} does not support ${unsupportedField}; ${supportedText}`;
69
- }
70
-
71
- const JOB_STEP_ALLOWED_FIELDS = {
72
- assertText: new Set(["action", "text"]),
73
- assertUrl: new Set(["action", "url"]),
74
- click: new Set(["action", "locator", "name", "role", "selector", "value"]),
75
- fill: new Set(["action", "locator", "name", "role", "selector", "text", "value"]),
76
- open: new Set(["action", "loadState", "url"]),
77
- screenshot: new Set(["action", "path"]),
78
- select: new Set(["action", "selector", "value", "values"]),
79
- snapshot: new Set(["action"]),
80
- type: new Set(["action", "delayMs", "press", "selector", "text"]),
81
- wait: new Set(["action", "milliseconds"]),
82
- waitForDownload: new Set(["action", "path"]),
83
- } satisfies Record<AgentBrowserJobStepAction, ReadonlySet<string>>;
84
-
85
- type CompileJobStepResult = {
86
- args?: string[];
87
- error?: string;
88
- extraSteps?: CompiledAgentBrowserJobStep[];
89
- generatedFrom?: string;
90
- };
91
-
92
- type JobStepCompiler = (step: Record<string, unknown>, index: number) => CompileJobStepResult;
93
-
94
- type JobStepDescriptor = {
95
- allowedFields: ReadonlySet<string>;
96
- compile: JobStepCompiler;
97
- };
98
-
99
- function globUrlPatternToRegexSource(pattern: string): string {
100
- let source = "^";
101
- for (let index = 0; index < pattern.length; index += 1) {
102
- const char = pattern.charAt(index);
103
- if (char === "*") {
104
- let runLength = 1;
105
- while (pattern[index + runLength] === "*") runLength += 1;
106
- source += runLength === 1 ? "[^/]*" : ".*";
107
- index += runLength - 1;
108
- } else {
109
- source += char.replace(/[\\^$+?.()|[\]{}]/g, "\\$&");
110
- }
111
- }
112
- return `${source}$`;
113
- }
114
-
115
- function compileJobAssertUrlArgs(url: string): string[] {
116
- if (!url.includes("*")) return ["wait", "--url", url];
117
- return ["wait", "--fn", `new RegExp(${JSON.stringify(globUrlPatternToRegexSource(url))}).test(location.href)`];
118
- }
119
-
120
- function compileJobTypeSteps(step: Record<string, unknown>): { error?: string; steps?: CompiledAgentBrowserJobStep[] } {
121
- const text = getRequiredJobString(step, "text", "type");
122
- if (text.error) return { error: text.error };
123
- const selector = step.selector;
124
- if (selector !== undefined && (typeof selector !== "string" || selector.trim().length === 0)) {
125
- return { error: "job step type selector must be a non-empty string when provided." };
126
- }
127
- const delayMs = step.delayMs;
128
- if (delayMs !== undefined && (typeof delayMs !== "number" || !Number.isInteger(delayMs) || delayMs <= 0)) {
129
- return { error: "job step type delayMs must be a positive integer when provided." };
130
- }
131
- const press = step.press;
132
- if (press !== undefined && (typeof press !== "string" || press.trim().length === 0)) {
133
- return { error: "job step type press must be a non-empty key string when provided." };
134
- }
135
- const typedText = text.value as string;
136
- const typedChars = Array.from(typedText);
137
- if (typedChars.length === 0) return { error: "job step type requires non-empty text." };
138
- if (delayMs !== undefined && typedChars.length > AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS) {
139
- return { error: `job step type delayMs supports at most ${AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS} characters; split longer text into shorter calls or omit delayMs.` };
140
- }
141
- const compiledSteps: CompiledAgentBrowserJobStep[] = [];
142
- if (delayMs === undefined) {
143
- compiledSteps.push({ action: "type", args: typeof selector === "string" ? ["type", selector, typedText] : ["keyboard", "type", typedText] });
144
- } else {
145
- if (typeof selector === "string") compiledSteps.push({ action: "type", args: ["focus", selector], generatedFrom: "type.selector" });
146
- for (const [index, char] of typedChars.entries()) {
147
- compiledSteps.push({ action: "type", args: ["keyboard", "type", char], generatedFrom: "type.delayMs" });
148
- if (index < typedChars.length - 1) compiledSteps.push({ action: "wait", args: ["wait", String(delayMs)], generatedFrom: "type.delayMs" });
149
- }
150
- }
151
- if (typeof press === "string") compiledSteps.push({ action: "type", args: ["press", press], generatedFrom: "type.press" });
152
- return { steps: compiledSteps };
153
- }
154
-
155
- function compileOpenJobStep(step: Record<string, unknown>, index: number): CompileJobStepResult {
156
- const result = getRequiredJobString(step, "url", "open");
157
- if (result.error) return { error: result.error };
158
- const extraSteps: CompiledAgentBrowserJobStep[] = [];
159
- if (step.loadState !== undefined) {
160
- if (typeof step.loadState !== "string" || !AGENT_BROWSER_QA_LOAD_STATES.includes(step.loadState as AgentBrowserQaLoadState)) {
161
- return { error: `job.steps[${index}].loadState must be one of: ${AGENT_BROWSER_QA_LOAD_STATES.join(", ")}.` };
162
- }
163
- extraSteps.push({ action: "wait", args: ["wait", "--load", step.loadState], generatedFrom: "open.loadState" });
164
- }
165
- return { args: ["open", result.value as string], extraSteps };
166
- }
167
-
168
- function compileClickJobStep(step: Record<string, unknown>): CompileJobStepResult {
169
- return compileJobClickOrFillStep(step, "click");
170
- }
171
-
172
- function compileFillJobStep(step: Record<string, unknown>): CompileJobStepResult {
173
- return compileJobClickOrFillStep(step, "fill");
174
- }
175
-
176
- function compileTypeJobStep(step: Record<string, unknown>): CompileJobStepResult {
177
- const result = compileJobTypeSteps(step);
178
- if (result.error) return { error: result.error };
179
- const [firstStep, ...extraSteps] = result.steps as CompiledAgentBrowserJobStep[];
180
- return { args: firstStep.args, extraSteps, generatedFrom: firstStep.generatedFrom };
181
- }
182
-
183
- function compileSelectJobStep(step: Record<string, unknown>, index: number): CompileJobStepResult {
184
- const selector = getRequiredJobString(step, "selector", "select");
185
- if (selector.error) return { error: selector.error };
186
- const values = getSelectValues(step, `job.steps[${index}]`);
187
- if (values.error) return { error: values.error };
188
- return { args: ["select", selector.value as string, ...(values.values as string[])] };
189
- }
190
-
191
- function compileWaitJobStep(step: Record<string, unknown>): CompileJobStepResult {
192
- const milliseconds = step.milliseconds;
193
- if (typeof milliseconds !== "number" || !Number.isInteger(milliseconds) || milliseconds <= 0) {
194
- return { error: "job step wait requires a positive integer milliseconds value." };
195
- }
196
- return { args: ["wait", String(milliseconds)] };
197
- }
198
-
199
- function compileAssertTextJobStep(step: Record<string, unknown>): CompileJobStepResult {
200
- const result = getRequiredJobString(step, "text", "assertText");
201
- if (result.error) return { error: result.error };
202
- return { args: ["wait", "--text", result.value as string] };
203
- }
204
-
205
- function compileAssertUrlJobStep(step: Record<string, unknown>): CompileJobStepResult {
206
- const result = getRequiredJobString(step, "url", "assertUrl");
207
- if (result.error) return { error: result.error };
208
- return { args: compileJobAssertUrlArgs(result.value as string) };
209
- }
210
-
211
- function compilePathArtifactJobStep(step: Record<string, unknown>, action: "screenshot" | "waitForDownload"): CompileJobStepResult {
212
- const result = getRequiredJobString(step, "path", action);
213
- if (result.error) return { error: result.error };
214
- return { args: action === "waitForDownload" ? ["wait", "--download", result.value as string] : ["screenshot", result.value as string] };
215
- }
216
-
217
- const JOB_STEP_DESCRIPTORS: Record<AgentBrowserJobStepAction, JobStepDescriptor> = {
218
- assertText: { allowedFields: JOB_STEP_ALLOWED_FIELDS.assertText, compile: compileAssertTextJobStep },
219
- assertUrl: { allowedFields: JOB_STEP_ALLOWED_FIELDS.assertUrl, compile: compileAssertUrlJobStep },
220
- click: { allowedFields: JOB_STEP_ALLOWED_FIELDS.click, compile: compileClickJobStep },
221
- fill: { allowedFields: JOB_STEP_ALLOWED_FIELDS.fill, compile: compileFillJobStep },
222
- open: { allowedFields: JOB_STEP_ALLOWED_FIELDS.open, compile: compileOpenJobStep },
223
- screenshot: { allowedFields: JOB_STEP_ALLOWED_FIELDS.screenshot, compile: (step) => compilePathArtifactJobStep(step, "screenshot") },
224
- select: { allowedFields: JOB_STEP_ALLOWED_FIELDS.select, compile: compileSelectJobStep },
225
- snapshot: { allowedFields: JOB_STEP_ALLOWED_FIELDS.snapshot, compile: () => ({ args: ["snapshot", "-i"] }) },
226
- type: { allowedFields: JOB_STEP_ALLOWED_FIELDS.type, compile: compileTypeJobStep },
227
- wait: { allowedFields: JOB_STEP_ALLOWED_FIELDS.wait, compile: compileWaitJobStep },
228
- waitForDownload: { allowedFields: JOB_STEP_ALLOWED_FIELDS.waitForDownload, compile: (step) => compilePathArtifactJobStep(step, "waitForDownload") },
229
- };
230
-
231
- export function compileAgentBrowserJob(input: unknown): { compiled?: CompiledAgentBrowserJob; error?: string } {
232
- if (!isRecord(input)) {
233
- return { error: "job must be an object." };
234
- }
235
- const rawFailFast = input.failFast;
236
- if (rawFailFast !== undefined && typeof rawFailFast !== "boolean") {
237
- return { error: "job.failFast must be a boolean when provided." };
238
- }
239
- const failFast = rawFailFast !== false;
240
- const rawSteps = input.steps;
241
- if (!Array.isArray(rawSteps) || rawSteps.length === 0) {
242
- return { error: "job.steps must be a non-empty array." };
243
- }
244
- const steps: CompiledAgentBrowserJobStep[] = [];
245
- for (const [index, rawStep] of rawSteps.entries()) {
246
- if (!isRecord(rawStep)) {
247
- return { error: `job.steps[${index}] must be an object.` };
248
- }
249
- const action = rawStep.action;
250
- if (typeof action !== "string" || !AGENT_BROWSER_JOB_STEP_ACTIONS.includes(action as AgentBrowserJobStepAction)) {
251
- return { error: `job.steps[${index}].action must be one of: ${AGENT_BROWSER_JOB_STEP_ACTIONS.join(", ")}.` };
252
- }
253
- const jobAction = action as AgentBrowserJobStepAction;
254
- const descriptor = JOB_STEP_DESCRIPTORS[jobAction];
255
- const unsupportedFieldError = getUnsupportedJobStepFieldError(rawStep, jobAction, descriptor.allowedFields);
256
- if (unsupportedFieldError) return { error: `job.steps[${index}]: ${unsupportedFieldError}` };
257
- const compiledStep = descriptor.compile(rawStep, index);
258
- if (compiledStep.error) return { error: compiledStep.error.startsWith(`job.steps[${index}]`) ? compiledStep.error : `job.steps[${index}]: ${compiledStep.error}` };
259
- steps.push({ action: jobAction, args: compiledStep.args as string[], generatedFrom: compiledStep.generatedFrom }, ...(compiledStep.extraSteps ?? []));
260
- }
261
- return { compiled: { args: failFast ? ["batch", "--bail"] : ["batch"], failFast, stdin: JSON.stringify(steps.map((step) => step.args)), steps } };
262
- }
263
-
264
- export function isHttpOrHttpsUrl(url: string): boolean {
265
- try {
266
- const protocol = new URL(url).protocol;
267
- return protocol === "http:" || protocol === "https:";
268
- } catch {
269
- return false;
270
- }
271
- }
272
-
273
- function describeQaChecksRun(checks: CompiledAgentBrowserQaPreset["checks"]): string {
274
- const parts = [`load:${checks.loadState}`];
275
- if (checks.expectedText.length > 0) parts.push(`text×${checks.expectedText.length}`);
276
- if (checks.expectedSelector) parts.push("selector");
277
- if (checks.checkNetwork) parts.push("network");
278
- if (checks.checkConsole) parts.push("console");
279
- if (checks.checkErrors) parts.push("errors");
280
- if (checks.diagnosticsResetAtStart) parts.push("diagnostics-reset");
281
- else if (checks.checkNetwork || checks.checkConsole || checks.checkErrors) parts.push("attached-diagnostics-preserved");
282
- if (checks.screenshotPath) parts.push("screenshot");
283
- return parts.join(", ");
284
- }
285
-
286
- export function extractQaPageContext(options: {
287
- attachedTarget?: { title?: string; url?: string };
288
- batchData?: unknown;
289
- compiled?: CompiledAgentBrowserQaPreset;
290
- }): { title?: string; url?: string } {
291
- if (options.attachedTarget?.title || options.attachedTarget?.url) {
292
- return { title: options.attachedTarget.title, url: options.attachedTarget.url };
293
- }
294
- for (const item of getBatchResultItems(options.batchData)) {
295
- if (getCommandNameFromBatchItem(item) !== "open" || !isRecord(item.result)) continue;
296
- const url = typeof item.result.url === "string" ? item.result.url : undefined;
297
- const title = typeof item.result.title === "string" ? item.result.title : undefined;
298
- if (url || title) return { title, url };
299
- }
300
- if (options.compiled?.checks.url) {
301
- return { url: options.compiled.checks.url };
302
- }
303
- return {};
304
- }
305
-
306
- export function buildQaCompactPassText(options: {
307
- artifactVerification?: ArtifactVerificationSummary;
308
- batchStepCount: number;
309
- checks: CompiledAgentBrowserQaPreset["checks"];
310
- page?: { title?: string; url?: string };
311
- qaPreset: AgentBrowserQaPresetAnalysis;
312
- }): string {
313
- const lines = [options.qaPreset.summary];
314
- const pageParts = [options.page?.title, options.page?.url].filter((part): part is string => typeof part === "string" && part.length > 0);
315
- if (pageParts.length > 0) lines.push(`Page: ${pageParts.join(" — ")}`);
316
- lines.push(`Checks run: ${describeQaChecksRun(options.checks)} (${options.batchStepCount} batch step${options.batchStepCount === 1 ? "" : "s"})`);
317
- if (options.checks.attached && !options.checks.diagnosticsResetAtStart && (options.checks.checkNetwork || options.checks.checkConsole || options.checks.checkErrors)) {
318
- lines.push("Attached diagnostics: existing upstream session console/network/error buffers were preserved; rows may include events from before qa.attached started.");
319
- }
320
- if (options.checks.screenshotPath) {
321
- const verification = options.artifactVerification;
322
- lines.push(verification
323
- ? `Screenshot: ${options.checks.screenshotPath} (${verification.verifiedCount}/${verification.artifacts.length} verified on disk)`
324
- : `Screenshot: ${options.checks.screenshotPath}`);
325
- }
326
- lines.push("Full diagnostic matrix: see details.qaPreset and details.batchSteps.");
327
- return lines.join("\n");
328
- }
329
-
330
- const QA_VISIBLE_TEXT_TIMEOUT_MS = 5_000;
331
-
332
- function formatQaExpectedTextPreview(text: string): string {
333
- return JSON.stringify(text.length > 80 ? `${text.slice(0, 77)}...` : text);
334
- }
335
-
336
- function buildQaVisibleTextPredicate(text: string): string {
337
- return `(() => {
338
- const expected = ${JSON.stringify(text)}.replace(/\\s+/g, " ").trim();
339
- if (!expected) return false;
340
- const root = document.body || document.documentElement;
341
- if (!root) return false;
342
- const skipTags = new Set(["SCRIPT", "STYLE", "NOSCRIPT", "SVG"]);
343
- const normalize = (value) => String(value ?? "").replace(/\\s+/g, " ").trim();
344
- const isVisibleElement = (element) => {
345
- if (!(element instanceof HTMLElement)) return false;
346
- if (skipTags.has(element.tagName)) return false;
347
- const style = window.getComputedStyle(element);
348
- if (style.display === "none" || style.visibility === "hidden" || Number(style.opacity) === 0) return false;
349
- return element.getClientRects().length > 0;
350
- };
351
- const hasVisibleAncestors = (node) => {
352
- for (let element = node.parentElement; element; element = element.parentElement) {
353
- if (!isVisibleElement(element)) return false;
354
- if (element === root) break;
355
- }
356
- return true;
357
- };
358
- const textWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
359
- let visitedText = 0;
360
- for (let node = textWalker.nextNode(); node && visitedText < 6000; node = textWalker.nextNode(), visitedText += 1) {
361
- if (!hasVisibleAncestors(node)) continue;
362
- if (normalize(node.nodeValue).includes(expected)) return true;
363
- }
364
- const elementWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
365
- let visitedElements = 0;
366
- for (let node = elementWalker.nextNode(); node && visitedElements < 3000; node = elementWalker.nextNode(), visitedElements += 1) {
367
- const element = node;
368
- if (!isVisibleElement(element) || !("value" in element)) continue;
369
- if (normalize(element.value).includes(expected)) return true;
370
- }
371
- return false;
372
- })()`;
373
- }
374
-
375
- function qaVisibleTextWaitPassed(item: ReturnType<typeof getBatchResultItems>[number] | undefined, step: CompiledAgentBrowserJobStep): boolean | undefined {
376
- if (step.args[0] !== "wait" || step.args[1] !== "--fn") return undefined;
377
- if (!item || item.success === false) return false;
378
- if (typeof item.result === "boolean") return item.result;
379
- if (isRecord(item.result) && typeof item.result.result === "boolean") return item.result.result;
380
- return true;
381
- }
382
-
383
- function extractQaTextAssertionResultText(item: ReturnType<typeof getBatchResultItems>[number] | undefined): string | undefined {
384
- if (!item || item.success === false) return undefined;
385
- const result = item.result;
386
- if (typeof result === "string") return result;
387
- if (!isRecord(result)) return undefined;
388
- for (const key of ["result", "text", "value"] as const) {
389
- const value = result[key];
390
- if (typeof value === "string") return value;
391
- }
392
- return undefined;
393
- }
394
-
395
- export function analyzeQaPresetTimeout(compiled: CompiledAgentBrowserQaPreset): AgentBrowserQaPresetAnalysis | undefined {
396
- if (compiled.checks.expectedText.length === 0) return undefined;
397
- const failedChecks = compiled.checks.expectedText.map((text) => `expected text was not verified before timeout: ${formatQaExpectedTextPreview(text)}`);
398
- return {
399
- failedChecks,
400
- passed: false,
401
- summary: `QA preset failed: ${failedChecks.join("; ")}.`,
402
- warnings: ["The wrapper timed out before expected-text evidence could be verified; inspect timeoutPartialProgress and retry with a narrower readiness condition if the page was still loading."],
403
- };
404
- }
405
-
406
- export function analyzeQaPresetResults(data: unknown, compiled?: CompiledAgentBrowserQaPreset): AgentBrowserQaPresetAnalysis | undefined {
407
- const items = getBatchResultItems(data);
408
- if (items.length === 0) return undefined;
409
- const failedChecks: string[] = [];
410
- const warnings: string[] = [];
411
- for (const item of items) {
412
- if (item.success === false) {
413
- failedChecks.push(`${getCommandNameFromBatchItem(item) ?? "step"} failed`);
414
- }
415
- const result = isRecord(item.result) ? item.result : undefined;
416
- const commandName = getCommandNameFromBatchItem(item);
417
- if (commandName === "errors" && Array.isArray(result?.errors) && result.errors.length > 0) {
418
- failedChecks.push(`${result.errors.length} page error(s)`);
419
- }
420
- if (commandName === "console" && Array.isArray(result?.messages)) {
421
- const errorCount = result.messages.filter((message) => isRecord(message) && /error/i.test(String(message.type ?? message.level ?? ""))).length;
422
- if (errorCount > 0) failedChecks.push(`${errorCount} console error message(s)`);
423
- }
424
- if (commandName === "network" && Array.isArray(result?.requests)) {
425
- const networkFailures = summarizeNetworkFailures(result.requests);
426
- if (networkFailures.actionableCount > 0) failedChecks.push(`${networkFailures.actionableCount} actionable failed network request(s)`);
427
- if (networkFailures.benignCount > 0) warnings.push(`${networkFailures.benignCount} benign network request failure(s) ignored`);
428
- }
429
- }
430
- if (compiled?.checks.expectedText.length) {
431
- let expectedTextIndex = 0;
432
- compiled.steps.forEach((step, index) => {
433
- if (step.action !== "assertText") return;
434
- const expected = compiled.checks.expectedText[expectedTextIndex++];
435
- if (!expected) return;
436
- const visibleTextPassed = qaVisibleTextWaitPassed(items[index], step);
437
- if (visibleTextPassed === true) return;
438
- const actual = extractQaTextAssertionResultText(items[index]);
439
- if (!actual || !actual.includes(expected)) failedChecks.push(`expected text not found: ${formatQaExpectedTextPreview(expected)}`);
440
- });
441
- }
442
- const uniqueFailures = [...new Set(failedChecks)];
443
- const uniqueWarnings = [...new Set(warnings)];
444
- return {
445
- failedChecks: uniqueFailures,
446
- passed: uniqueFailures.length === 0,
447
- summary: uniqueFailures.length === 0
448
- ? uniqueWarnings.length === 0 ? "QA preset passed." : `QA preset passed with warnings: ${uniqueWarnings.join("; ")}.`
449
- : `QA preset failed: ${uniqueFailures.join("; ")}.`,
450
- warnings: uniqueWarnings,
451
- };
452
- }
453
-
454
- export function compileAgentBrowserQaPreset(input: unknown): { compiled?: CompiledAgentBrowserQaPreset; error?: string } {
455
- if (!isRecord(input)) {
456
- return { error: "qa must be an object." };
457
- }
458
- const attached = input.attached === true;
459
- if (input.attached !== undefined && typeof input.attached !== "boolean") {
460
- return { error: "qa.attached must be a boolean when provided." };
461
- }
462
- const url = input.url;
463
- if (attached && url !== undefined) {
464
- return { error: "qa.url must be omitted when qa.attached is true." };
465
- }
466
- if (!attached && (typeof url !== "string" || url.trim().length === 0)) {
467
- return { error: "qa.url must be a non-empty string." };
468
- }
469
- const normalizedUrl = typeof url === "string" ? url.trim() : undefined;
470
- const expectedText = input.expectedText === undefined
471
- ? []
472
- : typeof input.expectedText === "string"
473
- ? [input.expectedText]
474
- : Array.isArray(input.expectedText)
475
- ? input.expectedText
476
- : undefined;
477
- if (!expectedText || expectedText.some((text) => typeof text !== "string" || text.trim().length === 0)) {
478
- return { error: "qa.expectedText must be a non-empty string or array of non-empty strings when provided." };
479
- }
480
- const expectedSelector = input.expectedSelector;
481
- if (expectedSelector !== undefined && (typeof expectedSelector !== "string" || expectedSelector.trim().length === 0)) {
482
- return { error: "qa.expectedSelector must be a non-empty string when provided." };
483
- }
484
- const screenshotPath = input.screenshotPath;
485
- if (screenshotPath !== undefined && (typeof screenshotPath !== "string" || screenshotPath.trim().length === 0)) {
486
- return { error: "qa.screenshotPath must be a non-empty string when provided." };
487
- }
488
- for (const field of ["checkConsole", "checkErrors", "checkNetwork"] as const) {
489
- if (input[field] !== undefined && typeof input[field] !== "boolean") {
490
- return { error: `qa.${field} must be a boolean when provided.` };
491
- }
492
- }
493
- const rawLoadState = input.loadState;
494
- if (rawLoadState !== undefined && (typeof rawLoadState !== "string" || !AGENT_BROWSER_QA_LOAD_STATES.includes(rawLoadState as AgentBrowserQaLoadState))) {
495
- return { error: `qa.loadState must be one of: ${AGENT_BROWSER_QA_LOAD_STATES.join(", ")}.` };
496
- }
497
- const checkConsole = typeof input.checkConsole === "boolean" ? input.checkConsole : !attached;
498
- const checkErrors = typeof input.checkErrors === "boolean" ? input.checkErrors : !attached;
499
- const checkNetwork = typeof input.checkNetwork === "boolean" ? input.checkNetwork : !attached;
500
- const loadState = (rawLoadState as AgentBrowserQaLoadState | undefined) ?? "domcontentloaded";
501
- const diagnosticsResetAtStart = !attached;
502
- const steps: CompiledAgentBrowserJobStep[] = [];
503
- if (diagnosticsResetAtStart && checkNetwork) steps.push({ action: "wait", args: ["network", "requests", "--clear"] });
504
- if (diagnosticsResetAtStart && checkConsole) steps.push({ action: "wait", args: ["console", "--clear"] });
505
- if (diagnosticsResetAtStart && checkErrors) steps.push({ action: "wait", args: ["errors", "--clear"] });
506
- if (!attached && normalizedUrl) steps.push({ action: "open", args: ["open", normalizedUrl] });
507
- steps.push({ action: "wait", args: ["wait", "--load", loadState] });
508
- for (const text of expectedText) {
509
- steps.push({ action: "assertText", args: ["wait", "--fn", buildQaVisibleTextPredicate(text), "--timeout", String(QA_VISIBLE_TEXT_TIMEOUT_MS)] });
510
- }
511
- if (typeof expectedSelector === "string") {
512
- steps.push({ action: "wait", args: ["wait", expectedSelector] });
513
- }
514
- if (checkNetwork) steps.push({ action: "wait", args: ["network", "requests"] });
515
- if (checkConsole) steps.push({ action: "wait", args: ["console"] });
516
- if (checkErrors) steps.push({ action: "wait", args: ["errors"] });
517
- if (typeof screenshotPath === "string") steps.push({ action: "screenshot", args: ["screenshot", screenshotPath] });
518
- return {
519
- compiled: {
520
- args: ["batch", "--bail"],
521
- checks: { attached, checkConsole, checkErrors, checkNetwork, diagnosticsResetAtStart, expectedSelector, expectedText, loadState, screenshotPath, url: normalizedUrl },
522
- failFast: true,
523
- stdin: JSON.stringify(steps.map((step) => step.args)),
524
- steps,
525
- },
526
- };
527
- }