pi-agent-browser-native 0.2.48 → 0.2.50

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