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
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Purpose: Compile and analyze experimental DOM/source and network source lookup input modes.
3
+ * Responsibilities: Build lookup batches, collect DOM/React/workspace candidates, and redact network lookup surfaces.
4
+ * Scope: Source lookup and network source lookup only.
5
+ */
6
+ import { readFile, readdir } from "node:fs/promises";
7
+ import { extname, join } from "node:path";
8
+ import { isRecord } from "../parsing.js";
9
+ import { redactInvocationArgs, redactSensitiveText } from "../runtime.js";
10
+ import { getBatchResultItems, validateLookupMaxWorkspaceFiles } from "./shared.js";
11
+ import { SOURCE_LOOKUP_DEFAULT_MAX_WORKSPACE_FILES, SOURCE_LOOKUP_IGNORED_DIRECTORIES, SOURCE_LOOKUP_MAX_WORKSPACE_FILES, SOURCE_LOOKUP_WORKSPACE_EXTENSIONS, } from "./types.js";
12
+ export function compileAgentBrowserSourceLookup(input) {
13
+ if (!isRecord(input)) {
14
+ return { error: "sourceLookup must be an object." };
15
+ }
16
+ const selector = input.selector;
17
+ const reactFiberId = input.reactFiberId;
18
+ const componentName = input.componentName;
19
+ if (selector !== undefined && (typeof selector !== "string" || selector.trim().length === 0)) {
20
+ return { error: "sourceLookup.selector must be a non-empty string when provided." };
21
+ }
22
+ if (reactFiberId !== undefined && (typeof reactFiberId !== "string" || reactFiberId.trim().length === 0)) {
23
+ return { error: "sourceLookup.reactFiberId must be a non-empty string when provided." };
24
+ }
25
+ if (componentName !== undefined && (typeof componentName !== "string" || componentName.trim().length === 0)) {
26
+ return { error: "sourceLookup.componentName must be a non-empty string when provided." };
27
+ }
28
+ if (selector === undefined && reactFiberId === undefined && componentName === undefined) {
29
+ return { error: "sourceLookup requires selector, reactFiberId, or componentName." };
30
+ }
31
+ if (input.includeDomHints !== undefined && typeof input.includeDomHints !== "boolean") {
32
+ return { error: "sourceLookup.includeDomHints must be a boolean when provided." };
33
+ }
34
+ const rawMaxWorkspaceFiles = input.maxWorkspaceFiles;
35
+ if (rawMaxWorkspaceFiles !== undefined && (typeof rawMaxWorkspaceFiles !== "number" || !Number.isInteger(rawMaxWorkspaceFiles) || rawMaxWorkspaceFiles <= 0)) {
36
+ return { error: "sourceLookup.maxWorkspaceFiles must be a positive integer when provided." };
37
+ }
38
+ if (typeof rawMaxWorkspaceFiles === "number" && rawMaxWorkspaceFiles > SOURCE_LOOKUP_MAX_WORKSPACE_FILES) {
39
+ return { error: `sourceLookup.maxWorkspaceFiles must be ${SOURCE_LOOKUP_MAX_WORKSPACE_FILES} or less.` };
40
+ }
41
+ const includeDomHints = input.includeDomHints !== false;
42
+ const maxWorkspaceFiles = rawMaxWorkspaceFiles ?? SOURCE_LOOKUP_DEFAULT_MAX_WORKSPACE_FILES;
43
+ const steps = [];
44
+ if (typeof selector === "string") {
45
+ steps.push({ action: "dom", args: ["is", "visible", selector] });
46
+ if (includeDomHints) {
47
+ steps.push({ action: "dom", args: ["get", "html", selector] });
48
+ }
49
+ }
50
+ if (typeof reactFiberId === "string") {
51
+ steps.push({ action: "react", args: ["react", "inspect", reactFiberId] });
52
+ }
53
+ if (typeof componentName === "string") {
54
+ steps.push({ action: "react", args: ["react", "tree"] });
55
+ }
56
+ return {
57
+ compiled: {
58
+ args: ["batch"],
59
+ query: { componentName, includeDomHints, maxWorkspaceFiles, reactFiberId, selector },
60
+ stdin: JSON.stringify(steps.map((step) => step.args)),
61
+ steps,
62
+ },
63
+ };
64
+ }
65
+ function extractStringField(value, names) {
66
+ for (const name of names) {
67
+ const field = value[name];
68
+ if (typeof field === "string" && field.trim().length > 0)
69
+ return field;
70
+ }
71
+ return undefined;
72
+ }
73
+ function extractNumberField(value, names) {
74
+ for (const name of names) {
75
+ const field = value[name];
76
+ if (typeof field === "number" && Number.isFinite(field))
77
+ return field;
78
+ if (typeof field === "string" && /^\d+$/.test(field))
79
+ return Number(field);
80
+ }
81
+ return undefined;
82
+ }
83
+ function candidateKey(candidate) {
84
+ return [candidate.source, candidate.file ?? "", candidate.line ?? "", candidate.column ?? "", candidate.componentName ?? ""].join(":");
85
+ }
86
+ function addSourceLookupCandidate(candidates, candidate) {
87
+ if (!candidates.some((existing) => candidateKey(existing) === candidateKey(candidate))) {
88
+ candidates.push(candidate);
89
+ }
90
+ }
91
+ function collectSourceCandidatesFromValue(value, source, candidates, evidence, depth = 0) {
92
+ if (depth > 6 || value === undefined || value === null)
93
+ return;
94
+ if (typeof value === "string") {
95
+ const sourcePattern = /([A-Za-z0-9_./@-]+\.(?:tsx|jsx|ts|js))(?:[:#](\d+))?(?:[:#](\d+))?/g;
96
+ for (const match of value.matchAll(sourcePattern)) {
97
+ addSourceLookupCandidate(candidates, {
98
+ column: match[3] ? Number(match[3]) : undefined,
99
+ confidence: source === "react-inspect" ? "high" : "medium",
100
+ evidence,
101
+ file: match[1],
102
+ line: match[2] ? Number(match[2]) : undefined,
103
+ source,
104
+ });
105
+ }
106
+ return;
107
+ }
108
+ if (Array.isArray(value)) {
109
+ for (const item of value)
110
+ collectSourceCandidatesFromValue(item, source, candidates, evidence, depth + 1);
111
+ return;
112
+ }
113
+ if (!isRecord(value))
114
+ return;
115
+ const file = extractStringField(value, ["file", "fileName", "filename", "filePath", "path", "source", "url"]);
116
+ if (file && /\.(?:tsx|jsx|ts|js)(?:$|[:?#])/.test(file)) {
117
+ addSourceLookupCandidate(candidates, {
118
+ column: extractNumberField(value, ["column", "columnNumber", "col"]),
119
+ confidence: source === "react-inspect" ? "high" : "medium",
120
+ evidence,
121
+ file,
122
+ line: extractNumberField(value, ["line", "lineNumber"]),
123
+ source,
124
+ });
125
+ }
126
+ for (const nested of Object.values(value)) {
127
+ collectSourceCandidatesFromValue(nested, source, candidates, evidence, depth + 1);
128
+ }
129
+ }
130
+ function getHtmlAttributeValue(html, name) {
131
+ const pattern = new RegExp(`${name}=["']([^"']+)["']`, "i");
132
+ return pattern.exec(html)?.[1];
133
+ }
134
+ function collectDomSourceCandidates(html, candidates) {
135
+ if (typeof html !== "string")
136
+ return;
137
+ const file = getHtmlAttributeValue(html, "(?:data-source-file|data-file|data-component-file|data-source)");
138
+ if (file && /\.(?:tsx|jsx|ts|js)$/.test(file)) {
139
+ const line = getHtmlAttributeValue(html, "(?:data-source-line|data-line)");
140
+ const column = getHtmlAttributeValue(html, "(?:data-source-column|data-column)");
141
+ addSourceLookupCandidate(candidates, {
142
+ column: column && /^\d+$/.test(column) ? Number(column) : undefined,
143
+ confidence: "medium",
144
+ evidence: ["selector HTML contained source-like data attributes"],
145
+ file,
146
+ line: line && /^\d+$/.test(line) ? Number(line) : undefined,
147
+ source: "dom-attribute",
148
+ });
149
+ }
150
+ collectSourceCandidatesFromValue(html, "dom-attribute", candidates, ["selector HTML contained source-like text"]);
151
+ }
152
+ async function walkWorkspaceSourceFiles(root, maxFiles) {
153
+ const files = [];
154
+ async function visit(directory) {
155
+ if (files.length >= maxFiles)
156
+ return;
157
+ let entries;
158
+ try {
159
+ entries = await readdir(directory, { withFileTypes: true });
160
+ }
161
+ catch {
162
+ return;
163
+ }
164
+ for (const entry of entries) {
165
+ if (files.length >= maxFiles)
166
+ return;
167
+ const path = join(directory, entry.name);
168
+ if (entry.isDirectory()) {
169
+ if (!SOURCE_LOOKUP_IGNORED_DIRECTORIES.has(entry.name))
170
+ await visit(path);
171
+ }
172
+ else if (entry.isFile() && SOURCE_LOOKUP_WORKSPACE_EXTENSIONS.has(extname(entry.name))) {
173
+ files.push(path);
174
+ }
175
+ }
176
+ }
177
+ await visit(root);
178
+ return files;
179
+ }
180
+ function escapeRegExp(value) {
181
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
182
+ }
183
+ async function collectWorkspaceComponentCandidates(query, cwd, candidates, limitations) {
184
+ if (!query.componentName)
185
+ return;
186
+ const files = await walkWorkspaceSourceFiles(cwd, query.maxWorkspaceFiles);
187
+ if (files.length >= query.maxWorkspaceFiles) {
188
+ limitations.push(`Workspace source scan stopped at ${query.maxWorkspaceFiles} files.`);
189
+ }
190
+ const componentPattern = new RegExp(`(?:function|class)\\s+${escapeRegExp(query.componentName)}\\b|(?:const|let|var)\\s+${escapeRegExp(query.componentName)}\\s*=|export\\s+default\\s+function\\s+${escapeRegExp(query.componentName)}\\b`);
191
+ for (const file of files) {
192
+ let text;
193
+ try {
194
+ text = await readFile(file, "utf8");
195
+ }
196
+ catch {
197
+ continue;
198
+ }
199
+ const match = componentPattern.exec(text);
200
+ if (!match)
201
+ continue;
202
+ const line = text.slice(0, match.index).split("\n").length;
203
+ addSourceLookupCandidate(candidates, {
204
+ componentName: query.componentName,
205
+ confidence: "low",
206
+ evidence: [`local workspace contains a matching ${query.componentName} declaration`],
207
+ file,
208
+ line,
209
+ source: "workspace-search",
210
+ });
211
+ if (candidates.filter((candidate) => candidate.source === "workspace-search").length >= 10)
212
+ break;
213
+ }
214
+ }
215
+ export async function analyzeSourceLookupResults(data, compiled, cwd, context) {
216
+ const items = getBatchResultItems(data);
217
+ const candidates = [];
218
+ const limitations = [
219
+ "Experimental lookup only reports candidates with evidence; it cannot guarantee a DOM node maps to one source file.",
220
+ "React source hints require the page to be opened with --enable react-devtools and source information from the app build.",
221
+ ];
222
+ let unsupported = false;
223
+ for (const item of items) {
224
+ const command = Array.isArray(item.command) ? item.command : [];
225
+ const result = isRecord(item.result) && "data" in item.result ? item.result.data : item.result;
226
+ if (item.success === false && command[0] === "react")
227
+ unsupported = true;
228
+ if (command[0] === "react" && command[1] === "inspect") {
229
+ collectSourceCandidatesFromValue(result, "react-inspect", candidates, ["react inspect returned source-like metadata"]);
230
+ }
231
+ if (command[0] === "get" && command[1] === "html") {
232
+ collectDomSourceCandidates(result, candidates);
233
+ }
234
+ }
235
+ await collectWorkspaceComponentCandidates(compiled.query, cwd, candidates, limitations);
236
+ const status = candidates.length > 0 ? "candidates-found" : unsupported ? "unsupported" : "no-candidates";
237
+ const electronContext = status === "no-candidates" ? context?.electronContext : undefined;
238
+ const workspaceRoot = context?.workspaceRoot ?? cwd;
239
+ if (electronContext) {
240
+ limitations.push(`Workspace source scan is limited to the Pi tool session cwd: ${workspaceRoot}.`, "Packaged Electron app code may live inside installed app resources or app.asar outside the workspace; the wrapper does not unpack asar files or scan app bundle resources.");
241
+ }
242
+ return {
243
+ candidates,
244
+ electronContext,
245
+ limitations,
246
+ status,
247
+ summary: candidates.length > 0
248
+ ? `Source lookup found ${candidates.length} candidate location(s).`
249
+ : unsupported
250
+ ? "Source lookup could not inspect React metadata in this session."
251
+ : electronContext
252
+ ? `Source lookup found no candidate locations. The workspace scan was limited to ${workspaceRoot}; packaged Electron app code may live outside that cwd in app resources or app.asar.`
253
+ : "Source lookup found no candidate locations.",
254
+ workspaceRoot: electronContext ? workspaceRoot : undefined,
255
+ };
256
+ }
257
+ export function compileAgentBrowserNetworkSourceLookup(input) {
258
+ if (!isRecord(input))
259
+ return { error: "networkSourceLookup must be an object." };
260
+ const filter = input.filter;
261
+ const requestId = input.requestId;
262
+ const session = input.session;
263
+ const url = input.url;
264
+ if (filter !== undefined && (typeof filter !== "string" || filter.trim().length === 0))
265
+ return { error: "networkSourceLookup.filter must be a non-empty string when provided." };
266
+ if (requestId !== undefined && (typeof requestId !== "string" || requestId.trim().length === 0))
267
+ return { error: "networkSourceLookup.requestId must be a non-empty string when provided." };
268
+ if (session !== undefined && (typeof session !== "string" || session.trim().length === 0))
269
+ return { error: "networkSourceLookup.session must be a non-empty string when provided." };
270
+ if (url !== undefined && (typeof url !== "string" || url.trim().length === 0))
271
+ return { error: "networkSourceLookup.url must be a non-empty string when provided." };
272
+ if (filter === undefined && requestId === undefined && url === undefined)
273
+ return { error: "networkSourceLookup requires requestId, filter, or url." };
274
+ const maxWorkspaceFiles = validateLookupMaxWorkspaceFiles(input.maxWorkspaceFiles, "networkSourceLookup.maxWorkspaceFiles");
275
+ if (maxWorkspaceFiles.error)
276
+ return { error: maxWorkspaceFiles.error };
277
+ const steps = [];
278
+ if (typeof requestId === "string") {
279
+ steps.push({ action: "network", args: ["network", "request", requestId] });
280
+ }
281
+ const effectiveFilter = typeof filter === "string" ? filter : typeof url === "string" ? url : undefined;
282
+ if (effectiveFilter) {
283
+ steps.push({ action: "network", args: ["network", "requests", "--filter", effectiveFilter] });
284
+ }
285
+ const args = typeof session === "string" ? ["--session", session, "batch"] : ["batch"];
286
+ return { compiled: { args, query: { filter, maxWorkspaceFiles: maxWorkspaceFiles.value, requestId, session, url }, stdin: JSON.stringify(steps.map((step) => step.args)), steps } };
287
+ }
288
+ function getResultPayload(item) {
289
+ return isRecord(item.result) && "data" in item.result ? item.result.data : item.result;
290
+ }
291
+ function networkRequestMatchesQuery(url, queryText) {
292
+ return queryText === undefined || url === undefined || url.includes(queryText) || queryText.includes(url);
293
+ }
294
+ function isFailedNetworkRecord(request) {
295
+ const status = typeof request.status === "number" ? request.status : undefined;
296
+ const error = typeof request.error === "string" ? request.error : undefined;
297
+ return request.failed === true || error !== undefined || (status !== undefined && status >= 400);
298
+ }
299
+ function getFailedNetworkRequests(data, queryText) {
300
+ const failed = [];
301
+ for (const item of getBatchResultItems(data)) {
302
+ const payload = getResultPayload(item);
303
+ const requests = isRecord(payload) && Array.isArray(payload.requests) ? payload.requests : Array.isArray(payload) ? payload : isRecord(payload) ? [payload] : [];
304
+ for (const request of requests) {
305
+ if (!isRecord(request))
306
+ continue;
307
+ const url = typeof request.url === "string" ? request.url : undefined;
308
+ if (!networkRequestMatchesQuery(url, queryText) || !isFailedNetworkRecord(request))
309
+ continue;
310
+ failed.push({
311
+ error: typeof request.error === "string" ? request.error : undefined,
312
+ method: typeof request.method === "string" ? request.method : undefined,
313
+ requestId: typeof request.id === "string" ? request.id : typeof request.requestId === "string" ? request.requestId : undefined,
314
+ status: typeof request.status === "number" ? request.status : undefined,
315
+ url,
316
+ });
317
+ }
318
+ }
319
+ return failed;
320
+ }
321
+ function addNetworkCandidate(candidates, candidate) {
322
+ const key = [candidate.source, candidate.file ?? "", candidate.line ?? "", candidate.requestUrl ?? ""].join(":");
323
+ if (!candidates.some((existing) => [existing.source, existing.file ?? "", existing.line ?? "", existing.requestUrl ?? ""].join(":") === key))
324
+ candidates.push(candidate);
325
+ }
326
+ function collectInitiatorCandidates(data, failedRequests, candidates) {
327
+ const failedRequestIds = new Set(failedRequests.map((request) => request.requestId).filter((value) => value !== undefined));
328
+ const failedRequestUrls = new Set(failedRequests.map((request) => request.url).filter((value) => value !== undefined));
329
+ for (const item of getBatchResultItems(data)) {
330
+ const payload = getResultPayload(item);
331
+ const requestValues = isRecord(payload) && Array.isArray(payload.requests) ? payload.requests : [payload];
332
+ for (const value of requestValues) {
333
+ if (!isRecord(value))
334
+ continue;
335
+ const requestUrl = typeof value.url === "string" ? value.url : undefined;
336
+ const requestId = typeof value.id === "string" ? value.id : typeof value.requestId === "string" ? value.requestId : undefined;
337
+ const correlatesWithFailedRequest = (requestId !== undefined && failedRequestIds.has(requestId)) || (requestUrl !== undefined && failedRequestUrls.has(requestUrl));
338
+ if (!correlatesWithFailedRequest && !isFailedNetworkRecord(value))
339
+ continue;
340
+ for (const field of [value.initiator, value.stack, value.source, value.trace]) {
341
+ const localCandidates = [];
342
+ collectSourceCandidatesFromValue(field, "dom-attribute", localCandidates, ["failed network request included source-like initiator metadata"]);
343
+ for (const candidate of localCandidates) {
344
+ addNetworkCandidate(candidates, { confidence: "medium", evidence: candidate.evidence, file: candidate.file, line: candidate.line, requestUrl, source: "initiator" });
345
+ }
346
+ }
347
+ }
348
+ }
349
+ }
350
+ async function collectWorkspaceRequestCandidates(query, failedRequests, cwd, candidates, limitations) {
351
+ const needles = [...new Set([query.url, query.filter, ...failedRequests.map((request) => request.url)].filter((value) => typeof value === "string" && value.length > 0).flatMap((value) => {
352
+ try {
353
+ const parsed = new URL(value);
354
+ return [value, parsed.pathname].filter((item) => item && item !== "/");
355
+ }
356
+ catch {
357
+ return [value];
358
+ }
359
+ }))].slice(0, 8);
360
+ if (needles.length === 0)
361
+ return;
362
+ const files = await walkWorkspaceSourceFiles(cwd, query.maxWorkspaceFiles);
363
+ if (files.length >= query.maxWorkspaceFiles)
364
+ limitations.push(`Workspace source scan stopped at ${query.maxWorkspaceFiles} files.`);
365
+ for (const file of files) {
366
+ let text;
367
+ try {
368
+ text = await readFile(file, "utf8");
369
+ }
370
+ catch {
371
+ continue;
372
+ }
373
+ for (const needle of needles) {
374
+ const index = text.indexOf(needle);
375
+ if (index === -1)
376
+ continue;
377
+ addNetworkCandidate(candidates, { confidence: "low", evidence: [`local workspace contains request URL literal ${needle}`], file, line: text.slice(0, index).split("\n").length, requestUrl: needle, source: "workspace-search" });
378
+ if (candidates.filter((candidate) => candidate.source === "workspace-search").length >= 10)
379
+ return;
380
+ }
381
+ }
382
+ }
383
+ export function redactNetworkSourceLookupUrl(value) {
384
+ if (!value)
385
+ return value;
386
+ try {
387
+ const isRelative = value.startsWith("/");
388
+ const url = new URL(value, isRelative ? "https://redacted.invalid" : undefined);
389
+ url.username = url.username ? "[REDACTED]" : "";
390
+ url.password = url.password ? "[REDACTED]" : "";
391
+ for (const key of [...url.searchParams.keys()]) {
392
+ url.searchParams.set(key, "[REDACTED]");
393
+ }
394
+ if (/(?:token|secret|password|passwd|pwd|key|auth|session|jwt|credential)/i.test(url.hash)) {
395
+ url.hash = "#[REDACTED]";
396
+ }
397
+ return isRelative ? `${url.pathname}${url.search}${url.hash}` : url.toString();
398
+ }
399
+ catch {
400
+ return redactSensitiveText(value
401
+ .replace(/([a-z][a-z0-9+.-]*:\/\/)\S+:\S+@/gi, "$1[REDACTED]@")
402
+ .replace(/([?&][^=]+)=([^&#\s"'\]]+)/g, "$1=[REDACTED]"));
403
+ }
404
+ }
405
+ export function redactNetworkSourceLookupArgs(args) {
406
+ return redactInvocationArgs(args).map((arg) => redactNetworkSourceLookupUrl(arg) ?? arg);
407
+ }
408
+ export function redactNetworkSourceLookupSurface(value) {
409
+ if (typeof value === "string")
410
+ return redactNetworkSourceLookupUrl(value) ?? value;
411
+ if (Array.isArray(value))
412
+ return value.map((item) => redactNetworkSourceLookupSurface(item));
413
+ if (!isRecord(value))
414
+ return value;
415
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactNetworkSourceLookupSurface(item)]));
416
+ }
417
+ export function redactNetworkSourceLookupAnalysis(analysis) {
418
+ return {
419
+ ...analysis,
420
+ candidates: analysis.candidates.map((candidate) => ({
421
+ ...candidate,
422
+ evidence: candidate.evidence.map((item) => redactNetworkSourceLookupUrl(item) ?? redactSensitiveText(item)),
423
+ file: redactNetworkSourceLookupUrl(candidate.file),
424
+ requestUrl: redactNetworkSourceLookupUrl(candidate.requestUrl),
425
+ })),
426
+ failedRequests: analysis.failedRequests.map((request) => ({ ...request, error: redactNetworkSourceLookupUrl(request.error), url: redactNetworkSourceLookupUrl(request.url) })),
427
+ };
428
+ }
429
+ export async function analyzeNetworkSourceLookupResults(data, compiled, cwd) {
430
+ const limitations = [
431
+ "Experimental network source hints report candidates only; failed requests can be triggered indirectly by frameworks, caches, service workers, or third-party scripts.",
432
+ "Initiator/source-map metadata is upstream/browser-build dependent and may be absent.",
433
+ ];
434
+ const failedRequests = getFailedNetworkRequests(data, compiled.query.url ?? compiled.query.filter);
435
+ const candidates = [];
436
+ collectInitiatorCandidates(data, failedRequests, candidates);
437
+ await collectWorkspaceRequestCandidates(compiled.query, failedRequests, cwd, candidates, limitations);
438
+ const status = failedRequests.length === 0 ? "no-failed-requests" : candidates.length > 0 ? "failed-requests-found" : "no-candidates";
439
+ return { candidates, failedRequests, limitations, status, summary: failedRequests.length === 0 ? "Network source lookup found no failed requests." : candidates.length > 0 ? `Network source lookup found ${failedRequests.length} failed request(s) and ${candidates.length} candidate source hint(s).` : `Network source lookup found ${failedRequests.length} failed request(s) but no source candidates.` };
440
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Purpose: Define the Pi tool input schema for the native agent_browser wrapper.
3
+ * Responsibilities: Keep TypeBox schema construction separate from runtime execution and input-mode compilers.
4
+ * Scope: Schema-only; behavioral validation lives in the mode compilers.
5
+ */
6
+ import { JsonSchema } from "../json-schema.js";
7
+ import { StringEnum as localStringEnum } from "../string-enum-schema.js";
8
+ import { ELECTRON_DISCOVERY_DEFAULT_MAX_RESULTS, ELECTRON_DISCOVERY_MAX_RESULTS, } from "../electron/discovery.js";
9
+ import { AGENT_BROWSER_ELECTRON_HANDOFFS, AGENT_BROWSER_ELECTRON_TARGET_TYPES, AGENT_BROWSER_JOB_STEP_ACTIONS, AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS, AGENT_BROWSER_QA_LOAD_STATES, AGENT_BROWSER_SEMANTIC_ACTIONS, AGENT_BROWSER_SEMANTIC_LOCATORS, DEFAULT_SESSION_MODE, SOURCE_LOOKUP_MAX_WORKSPACE_FILES, } from "./types.js";
10
+ export function createAgentBrowserParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
11
+ return Type.Object({
12
+ args: Type.Optional(Type.Array(Type.String({ description: "Exact agent-browser CLI arguments, excluding the binary name. Do not pass --json; the wrapper injects it. First-call recipe: open → snapshot -i → click/fill @eN → snapshot -i." }), {
13
+ description: "Exact agent-browser CLI arguments, excluding the binary name and any shell operators. Required unless semanticAction, job, qa, sourceLookup, networkSourceLookup, or electron is provided. Do not include --json (wrapper injects it). Typical first calls: open, snapshot -i, click/fill current @refs, then snapshot -i again after navigation or DOM changes.",
14
+ minItems: 1,
15
+ })),
16
+ semanticAction: Type.Optional(Type.Object({
17
+ action: StringEnum(AGENT_BROWSER_SEMANTIC_ACTIONS, {
18
+ description: "Intent action to compile to an existing agent-browser find command, direct selector/ref command, or upstream select when action=select.",
19
+ }),
20
+ locator: Type.Optional(StringEnum(AGENT_BROWSER_SEMANTIC_LOCATORS, {
21
+ description: "Upstream find locator family to use for check/click/fill actions.",
22
+ })),
23
+ value: Type.Optional(Type.String({ description: "Locator value for find actions, or a single option value for select actions. For locator=role, role may be supplied instead." })),
24
+ values: Type.Optional(Type.Array(Type.String({ description: "Option value for select actions." }), { description: "One or more option values for select actions.", minItems: 1 })),
25
+ selector: Type.Optional(Type.String({ description: "Selector or @ref for direct click/check/fill actions, or for select actions compiled to select <selector> <value...>." })),
26
+ text: Type.Optional(Type.String({ description: "Text/value argument for fill actions." })),
27
+ role: Type.Optional(Type.String({ description: "Role locator value for locator=role. May be used instead of value; when both are set they must match." })),
28
+ name: Type.Optional(Type.String({ description: "Accessible name filter for locator=role; compiles to --name <name>." })),
29
+ session: Type.Optional(Type.String({ description: "Optional upstream session name; prepends --session <name> before the compiled command." })),
30
+ }, { additionalProperties: false })),
31
+ qa: Type.Optional(Type.Union([
32
+ Type.Object({
33
+ attached: Type.Literal(true, { description: "Run the QA preset against the currently attached session instead of opening qa.url." }),
34
+ expectedText: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())], { description: "Text that must appear on the page." })),
35
+ expectedSelector: Type.Optional(Type.String({ description: "Selector or @ref that must appear on the page." })),
36
+ screenshotPath: Type.Optional(Type.String({ description: "Optional evidence screenshot path captured at the end of the QA preset." })),
37
+ checkConsole: Type.Optional(Type.Boolean({ description: "Whether to inspect console messages and fail on console errors. Defaults to false for qa.attached because upstream buffers may predate the check." })),
38
+ checkErrors: Type.Optional(Type.Boolean({ description: "Whether to inspect page errors and fail when errors are present. Defaults to false for qa.attached because upstream buffers may predate the check." })),
39
+ checkNetwork: Type.Optional(Type.Boolean({ description: "Whether to inspect network requests and fail on actionable request failures; benign icon misses warn. Defaults to false for qa.attached because upstream buffers may predate the check." })),
40
+ loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES, { description: "Page readiness state for the QA preset before assertions and diagnostics. Defaults to domcontentloaded; use networkidle only for pages without long-lived background requests." })),
41
+ }, { additionalProperties: false }),
42
+ Type.Object({
43
+ url: Type.String({ description: "URL to open for a lightweight QA preset." }),
44
+ attached: Type.Optional(Type.Literal(false, { description: "When omitted or false, qa.url is required and opened before checks." })),
45
+ expectedText: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())], { description: "Text that must appear on the page." })),
46
+ expectedSelector: Type.Optional(Type.String({ description: "Selector or @ref that must appear on the page." })),
47
+ screenshotPath: Type.Optional(Type.String({ description: "Optional evidence screenshot path captured at the end of the QA preset." })),
48
+ checkConsole: Type.Optional(Type.Boolean({ description: "Whether to fail on console error messages. Defaults to true." })),
49
+ checkErrors: Type.Optional(Type.Boolean({ description: "Whether to fail on page errors. Defaults to true." })),
50
+ checkNetwork: Type.Optional(Type.Boolean({ description: "Whether to inspect network requests and fail on actionable request failures; benign icon misses warn. Defaults to true." })),
51
+ loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES, { description: "Page readiness state for the QA preset before assertions and diagnostics. Defaults to domcontentloaded; use networkidle only for pages without long-lived background requests." })),
52
+ }, { additionalProperties: false }),
53
+ ], { description: "Lightweight QA preset. Use qa.url to open a URL, or qa.attached=true to check the current attached session without opening a URL." })),
54
+ sourceLookup: Type.Optional(Type.Object({
55
+ selector: Type.Optional(Type.String({ description: "Visible selector or @ref whose DOM metadata should be inspected for source hints." })),
56
+ reactFiberId: Type.Optional(Type.String({ description: "React fiber id to inspect with upstream react inspect. Requires a session opened with --enable react-devtools." })),
57
+ componentName: Type.Optional(Type.String({ description: "Component name to correlate with react tree output and bounded local workspace search." })),
58
+ includeDomHints: Type.Optional(Type.Boolean({ description: "Whether selector lookups should inspect DOM HTML attributes for source-like metadata. Defaults to true." })),
59
+ maxWorkspaceFiles: Type.Optional(Type.Number({ description: "Maximum local source files to scan when componentName is provided. Defaults to 2000 and cannot exceed 5000.", minimum: 1, maximum: SOURCE_LOOKUP_MAX_WORKSPACE_FILES })),
60
+ }, { additionalProperties: false, description: "EXPERIMENTAL: local UI-to-source candidates only (confidence/evidence, not guaranteed mappings). Compiles to batch; mutually exclusive with other input modes." })),
61
+ networkSourceLookup: Type.Optional(Type.Object({
62
+ filter: Type.Optional(Type.String({ description: "Optional upstream network requests filter pattern." })),
63
+ requestId: Type.Optional(Type.String({ description: "Optional network request id to inspect with network request <id>." })),
64
+ session: Type.Optional(Type.String({ description: "Optional upstream session name; prepends --session <name> before the generated batch." })),
65
+ url: Type.Optional(Type.String({ description: "Optional failed request URL or URL fragment to correlate with local source." })),
66
+ maxWorkspaceFiles: Type.Optional(Type.Number({ description: "Maximum local source files to scan for URL literals. Defaults to 2000 and cannot exceed 5000.", minimum: 1, maximum: SOURCE_LOOKUP_MAX_WORKSPACE_FILES })),
67
+ }, { additionalProperties: false, description: "EXPERIMENTAL: failed-request-to-source candidates only (initiator metadata and bounded workspace URL literals; not definitive blame). Compiles to batch; mutually exclusive with other input modes." })),
68
+ electron: Type.Optional(Type.Union([
69
+ Type.Object({
70
+ action: StringEnum(["list"], { description: "List discovered Electron apps." }),
71
+ query: Type.Optional(Type.String({ description: "Optional case-insensitive substring filter for electron.list across app name, bundle id, desktop id, and paths.", minLength: 1 })),
72
+ maxResults: Type.Optional(Type.Integer({ description: `Maximum electron.list apps to return. Defaults to ${ELECTRON_DISCOVERY_DEFAULT_MAX_RESULTS}; values above ${ELECTRON_DISCOVERY_MAX_RESULTS} are clamped.`, minimum: 1 })),
73
+ }, { additionalProperties: false }),
74
+ Type.Object({
75
+ action: StringEnum(["launch"], { description: "Launch an Electron app with an isolated wrapper-owned profile." }),
76
+ appPath: Type.String({ description: "Electron launch target: macOS .app bundle path. Exactly one launch target is required for electron.launch.", minLength: 1 }),
77
+ appArgs: Type.Optional(Type.Array(Type.String({ description: "Argument passed to the Electron application.", minLength: 1 }), { description: "Optional Electron app argv. Wrapper-owned lifecycle/debug flags are rejected." })),
78
+ handoff: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_HANDOFFS, { description: "Post-launch handoff depth. Defaults to snapshot." })),
79
+ targetType: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_TARGET_TYPES, { description: "Preferred CDP target type. Defaults to page." })),
80
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded launch timeout in milliseconds.", minimum: 1 })),
81
+ allow: Type.Optional(Type.Array(Type.String({ description: "App identifier allowed by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned allow list for electron.launch policy checks." })),
82
+ deny: Type.Optional(Type.Array(Type.String({ description: "App identifier denied by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned deny list for electron.launch policy checks; deny wins over allow." })),
83
+ }, { additionalProperties: false }),
84
+ Type.Object({
85
+ action: StringEnum(["launch"], { description: "Launch an Electron app with an isolated wrapper-owned profile." }),
86
+ appName: Type.String({ description: "Electron launch target: app display name discovered by electron.list. Exactly one launch target is required for electron.launch.", minLength: 1 }),
87
+ appArgs: Type.Optional(Type.Array(Type.String({ description: "Argument passed to the Electron application.", minLength: 1 }), { description: "Optional Electron app argv. Wrapper-owned lifecycle/debug flags are rejected." })),
88
+ handoff: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_HANDOFFS, { description: "Post-launch handoff depth. Defaults to snapshot." })),
89
+ targetType: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_TARGET_TYPES, { description: "Preferred CDP target type. Defaults to page." })),
90
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded launch timeout in milliseconds.", minimum: 1 })),
91
+ allow: Type.Optional(Type.Array(Type.String({ description: "App identifier allowed by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned allow list for electron.launch policy checks." })),
92
+ deny: Type.Optional(Type.Array(Type.String({ description: "App identifier denied by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned deny list for electron.launch policy checks; deny wins over allow." })),
93
+ }, { additionalProperties: false }),
94
+ Type.Object({
95
+ action: StringEnum(["launch"], { description: "Launch an Electron app with an isolated wrapper-owned profile." }),
96
+ bundleId: Type.String({ description: "Electron launch target: macOS bundle identifier discovered by electron.list. Exactly one launch target is required for electron.launch.", minLength: 1 }),
97
+ appArgs: Type.Optional(Type.Array(Type.String({ description: "Argument passed to the Electron application.", minLength: 1 }), { description: "Optional Electron app argv. Wrapper-owned lifecycle/debug flags are rejected." })),
98
+ handoff: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_HANDOFFS, { description: "Post-launch handoff depth. Defaults to snapshot." })),
99
+ targetType: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_TARGET_TYPES, { description: "Preferred CDP target type. Defaults to page." })),
100
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded launch timeout in milliseconds.", minimum: 1 })),
101
+ allow: Type.Optional(Type.Array(Type.String({ description: "App identifier allowed by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned allow list for electron.launch policy checks." })),
102
+ deny: Type.Optional(Type.Array(Type.String({ description: "App identifier denied by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned deny list for electron.launch policy checks; deny wins over allow." })),
103
+ }, { additionalProperties: false }),
104
+ Type.Object({
105
+ action: StringEnum(["launch"], { description: "Launch an Electron app with an isolated wrapper-owned profile." }),
106
+ executablePath: Type.String({ description: "Electron launch target: executable path. Discovery is not required when this is provided. Exactly one launch target is required for electron.launch.", minLength: 1 }),
107
+ appArgs: Type.Optional(Type.Array(Type.String({ description: "Argument passed to the Electron application.", minLength: 1 }), { description: "Optional Electron app argv. Wrapper-owned lifecycle/debug flags are rejected." })),
108
+ handoff: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_HANDOFFS, { description: "Post-launch handoff depth. Defaults to snapshot." })),
109
+ targetType: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_TARGET_TYPES, { description: "Preferred CDP target type. Defaults to page." })),
110
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded launch timeout in milliseconds.", minimum: 1 })),
111
+ allow: Type.Optional(Type.Array(Type.String({ description: "App identifier allowed by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned allow list for electron.launch policy checks." })),
112
+ deny: Type.Optional(Type.Array(Type.String({ description: "App identifier denied by the caller for electron.launch.", minLength: 1 }), { description: "Optional caller-owned deny list for electron.launch policy checks; deny wins over allow." })),
113
+ }, { additionalProperties: false }),
114
+ Type.Object({
115
+ action: StringEnum(["status", "cleanup"], { description: "Inspect or cleanup one wrapper-tracked Electron launch by launchId." }),
116
+ launchId: Type.String({ description: "Wrapper launch id for electron.status and electron.cleanup.", minLength: 1 }),
117
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded status/cleanup timeout in milliseconds.", minimum: 1 })),
118
+ }, { additionalProperties: false }),
119
+ Type.Object({
120
+ action: StringEnum(["status", "cleanup"], { description: "Inspect or cleanup all wrapper-tracked Electron launches." }),
121
+ all: Type.Literal(true, { description: "Apply electron.status or electron.cleanup to all wrapper-owned launches." }),
122
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded status/cleanup timeout in milliseconds.", minimum: 1 })),
123
+ }, { additionalProperties: false }),
124
+ Type.Object({
125
+ action: StringEnum(["status", "cleanup"], { description: "Inspect or cleanup the only active wrapper-tracked Electron launch." }),
126
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded status/cleanup timeout in milliseconds.", minimum: 1 })),
127
+ }, { additionalProperties: false }),
128
+ Type.Object({
129
+ action: StringEnum(["probe"], { description: "Probe the current attached Electron managed session; launchId is accepted for launch-scoped follow-up actions." }),
130
+ launchId: Type.Optional(Type.String({ description: "Wrapper launch id for electron.probe follow-up targeting.", minLength: 1 })),
131
+ timeoutMs: Type.Optional(Type.Integer({ description: "Bounded probe timeout in milliseconds.", minimum: 1 })),
132
+ }, { additionalProperties: false }),
133
+ ], { description: "Electron wrapper action. Fields are action-specific and unsupported fields are rejected." })),
134
+ job: Type.Optional(Type.Object({
135
+ failFast: Type.Optional(Type.Boolean({ description: "Stop the compiled batch on the first failed job step. Defaults to true so later mutating steps do not run after setup/assertion failures." })),
136
+ steps: Type.Array(Type.Object({
137
+ action: StringEnum(AGENT_BROWSER_JOB_STEP_ACTIONS, {
138
+ description: "Constrained one-call job step compiled to existing upstream batch commands.",
139
+ }),
140
+ url: Type.Optional(Type.String({ description: "URL for open steps; exact URL or * / ** glob-style URL pattern for assertUrl steps." })),
141
+ loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES, { description: "Optional readiness wait to insert immediately after an open step; use domcontentloaded/load/networkidle when the next job step needs page hydration evidence before clicking or reading." })),
142
+ selector: Type.Optional(Type.String({ description: "Selector or @ref for click/fill/type/select-like steps; omit when using semantic locator fields on click/fill steps." })),
143
+ locator: Type.Optional(StringEnum(AGENT_BROWSER_SEMANTIC_LOCATORS, { description: "Semantic locator for click/fill steps when selector is omitted." })),
144
+ role: Type.Optional(Type.String({ description: "Role locator value for click/fill steps when locator is role." })),
145
+ name: Type.Optional(Type.String({ description: "Accessible name filter for role locator click/fill steps." })),
146
+ text: Type.Optional(Type.String({ description: "Text for fill steps or visible text for assertText steps." })),
147
+ value: Type.Optional(Type.String({ description: "Single option value for select steps, or locator value for semantic click/fill steps." })),
148
+ values: Type.Optional(Type.Array(Type.String({ description: "Option value for select steps." }), { description: "One or more option values for select steps.", minItems: 1 })),
149
+ path: Type.Optional(Type.String({ description: "Artifact/download path for waitForDownload or screenshot steps." })),
150
+ delayMs: Type.Optional(Type.Integer({ description: `Optional per-character delay for type steps; when set, the job compiles to focus/keyboard type/wait steps instead of instant fill-like typing, capped at ${AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS} characters.`, minimum: 1 })),
151
+ press: Type.Optional(Type.String({ description: "Optional key to press after a type step, for example Enter." })),
152
+ milliseconds: Type.Optional(Type.Number({ description: "Milliseconds for wait steps." })),
153
+ }, { additionalProperties: false }), { minItems: 1 }),
154
+ }, { additionalProperties: false })),
155
+ stdin: Type.Optional(Type.String({ description: "Optional raw stdin content; only supported for batch, eval --stdin, auth save --password-stdin, and is generated internally by job, qa, sourceLookup, or networkSourceLookup mode. Do not use with electron mode." })),
156
+ outputPath: Type.Optional(Type.String({ description: "Optional workspace-relative or absolute file path that receives the model-facing command data/result after the browser command completes. Useful for eval/get/snapshot captures that should become durable local artifacts.", minLength: 1 })),
157
+ timeoutMs: Type.Optional(Type.Integer({ description: "Optional per-call wrapper subprocess watchdog in milliseconds for browser CLI args/job/qa/source lookup calls. Use for long opens or large output captures; explicit long wait steps are forwarded, so set timeoutMs above the wait duration plus a small grace window when overriding the derived watchdog. Electron actions use electron.timeoutMs instead.", minimum: 1 })),
158
+ sessionMode: Type.Optional(StringEnum(["auto", "fresh"], {
159
+ description: "Session handling mode. `auto` reuses the extension-managed pi-scoped session when possible. `fresh` switches that managed session to a fresh upstream launch so launch-scoped flags like --profile, --executable-path, --session-name, --cdp, --state, --auto-connect, --init-script, --enable, -p/--provider, or iOS --device apply and later auto calls follow the new browser.",
160
+ default: DEFAULT_SESSION_MODE,
161
+ })),
162
+ }, { additionalProperties: false });
163
+ }
164
+ export const AGENT_BROWSER_PARAMS = createAgentBrowserParamsSchema();