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
@@ -1,516 +0,0 @@
1
- /**
2
- * Purpose: Execute the upstream agent-browser binary for the pi-agent-browser extension.
3
- * Responsibilities: Spawn the agent-browser subprocess, forward a curated environment surface, stream optional stdin, bound in-memory output buffering, spill oversized stdout safely to a private temp file under a disk budget, and honor abort signals.
4
- * Scope: Process execution only; argument planning, output formatting, and pi tool registration live elsewhere.
5
- * Usage: Called by the extension tool after argument validation and session planning are complete.
6
- * Invariants/Assumptions: The binary name is always `agent-browser`; Windows routes through PowerShell to invoke npm launchers with escaped argv; callers handle semantic success/error interpretation.
7
- */
8
-
9
- import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
10
- import { chmod, mkdir } from "node:fs/promises";
11
- import { env as processEnv, platform as processPlatform } from "node:process";
12
-
13
- import { GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, GLOBAL_VALUE_FLAGS, getFlagName } from "./argv-grammar.js";
14
- import { openSecureTempFile, writeSecureTempChunk } from "./temp.js";
15
-
16
- const MAX_BUFFERED_STDOUT_BYTES = 512 * 1_024;
17
- const MAX_BUFFERED_STDERR_CHARS = 32_000;
18
- const MAX_BUFFERED_STDOUT_TAIL_CHARS = 32_000;
19
- const PROCESS_STDOUT_SPILL_FILE_PREFIX = "process-stdout";
20
- const AGENT_BROWSER_SOCKET_DIR_ENV = "AGENT_BROWSER_SOCKET_DIR";
21
- const AGENT_BROWSER_DEFAULT_TIMEOUT_ENV = "AGENT_BROWSER_DEFAULT_TIMEOUT";
22
- const PI_AGENT_BROWSER_PROCESS_TIMEOUT_ENV = "PI_AGENT_BROWSER_PROCESS_TIMEOUT_MS";
23
- const DEFAULT_AGENT_BROWSER_SOCKET_DIR_PREFIX = "/tmp/piab";
24
- export const SAFE_AGENT_BROWSER_OPERATION_TIMEOUT_MS = 25_000;
25
- const DEFAULT_AGENT_BROWSER_PROCESS_TIMEOUT_MS = 35_000;
26
- /** Grace period after `exit` before resolving when `close` is delayed by inherited stdio handles. */
27
- const EXIT_STDIO_GRACE_MS = 100;
28
- const httpProxyEnvName = "http_proxy";
29
- const httpsProxyEnvName = "https_proxy";
30
- const allProxyEnvName = "all_proxy";
31
- const noProxyEnvName = "no_proxy";
32
- const INHERITED_ENV_NAMES = new Set([
33
- "ALL_PROXY",
34
- "APPDATA",
35
- "CI",
36
- "COLORTERM",
37
- "COMSPEC",
38
- "DBUS_SESSION_BUS_ADDRESS",
39
- "DISPLAY",
40
- "FORCE_COLOR",
41
- "HOME",
42
- "HOMEDRIVE",
43
- "HOMEPATH",
44
- "HTTPS_PROXY",
45
- "HTTP_PROXY",
46
- "LANG",
47
- "LC_ALL",
48
- "LC_CTYPE",
49
- "LOCALAPPDATA",
50
- "LOGNAME",
51
- "NO_COLOR",
52
- "NO_PROXY",
53
- "NODE_EXTRA_CA_CERTS",
54
- "NODE_TLS_REJECT_UNAUTHORIZED",
55
- "OS",
56
- "PATH",
57
- "PATHEXT",
58
- "PWD",
59
- "SHELL",
60
- "SSL_CERT_DIR",
61
- "SSL_CERT_FILE",
62
- "SYSTEMROOT",
63
- "TEMP",
64
- "TERM",
65
- "TMP",
66
- "TMPDIR",
67
- "TZ",
68
- "USER",
69
- "USERNAME",
70
- "USERPROFILE",
71
- "WAYLAND_DISPLAY",
72
- "XAUTHORITY",
73
- "AWS_ACCESS_KEY_ID",
74
- "AWS_SECRET_ACCESS_KEY",
75
- "AWS_SESSION_TOKEN",
76
- "AWS_PROFILE",
77
- "AWS_REGION",
78
- "AWS_DEFAULT_REGION",
79
- httpProxyEnvName,
80
- httpsProxyEnvName,
81
- allProxyEnvName,
82
- noProxyEnvName,
83
- ]);
84
- const INHERITED_ENV_PREFIXES = [
85
- "AGENT_BROWSER_",
86
- "AGENTCORE_",
87
- "AI_GATEWAY_",
88
- "BROWSERBASE_",
89
- "BROWSERLESS_",
90
- "BROWSER_USE_",
91
- "KERNEL_",
92
- "XDG_",
93
- ] as const;
94
-
95
- export interface ProcessRunResult {
96
- aborted: boolean;
97
- exitCode: number;
98
- spawnError?: Error;
99
- stderr: string;
100
- stdout: string;
101
- stdoutSpillPath?: string;
102
- timedOut: boolean;
103
- timeoutMs?: number;
104
- }
105
-
106
- function appendTail(text: string, addition: string, maxChars: number): string {
107
- const combined = text + addition;
108
- return combined.length <= maxChars ? combined : combined.slice(combined.length - maxChars);
109
- }
110
-
111
- function quoteWindowsPowerShellArg(value: string): string {
112
- return `'${value.replace(/'/g, "''")}'`;
113
- }
114
-
115
- const WINDOWS_LEADING_GLOBAL_VALUE_FLAGS = new Set<string>(GLOBAL_VALUE_FLAGS);
116
-
117
- /** Exported for unit tests that lock Windows launcher argv ordering. */
118
- export function reorderWindowsLeadingGlobalArgs(args: string[]): string[] {
119
- const leadingGlobals: string[] = [];
120
- let index = 0;
121
- while (index < args.length && args[index]?.startsWith("-")) {
122
- const token = args[index];
123
- const flagName = getFlagName(token);
124
- leadingGlobals.push(token);
125
- index += 1;
126
- if (WINDOWS_LEADING_GLOBAL_VALUE_FLAGS.has(flagName) && !token.includes("=") && index < args.length) {
127
- leadingGlobals.push(args[index]);
128
- index += 1;
129
- continue;
130
- }
131
- if (GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES.has(flagName) && ["true", "false"].includes(args[index] ?? "")) {
132
- leadingGlobals.push(args[index]);
133
- index += 1;
134
- }
135
- }
136
- if (leadingGlobals.length === 0 || index >= args.length) return args;
137
- return [args[index], ...leadingGlobals, ...args.slice(index + 1)];
138
- }
139
-
140
- function buildAgentBrowserSpawnCommand(args: string[]): { command: string; args: string[] } {
141
- if (processPlatform !== "win32") {
142
- return { command: "agent-browser", args };
143
- }
144
- const commandLine = ["&", "agent-browser", ...reorderWindowsLeadingGlobalArgs(args).map(quoteWindowsPowerShellArg)].join(" ");
145
- return { command: "powershell.exe", args: ["-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", commandLine] };
146
- }
147
-
148
- function terminateSpawnedChild(child: ChildProcessWithoutNullStreams, signal: NodeJS.Signals): void {
149
- if (processPlatform === "win32" && child.pid) {
150
- const killer = spawn("taskkill.exe", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" });
151
- killer.on("error", () => undefined);
152
- killer.unref();
153
- }
154
- child.kill(signal);
155
- }
156
-
157
- /** Exported for unit tests that lock subprocess exit-code precedence. */
158
- export function resolveSpawnedChildExitCode(input: {
159
- closeCode?: number | null;
160
- exitCode?: number | null;
161
- useExitFallback: boolean;
162
- timedOut: boolean;
163
- spawnError?: Error;
164
- }): number {
165
- // Precedence: observed `close` code when present, then wrapper timeout (124), then
166
- // post-`exit` fallback when inherited stdio delays `close`, then spawn failure (127).
167
- if (input.closeCode !== null && input.closeCode !== undefined) {
168
- return input.closeCode;
169
- }
170
- if (input.timedOut) {
171
- return 124;
172
- }
173
- if (input.useExitFallback && input.exitCode !== null && input.exitCode !== undefined) {
174
- return input.exitCode;
175
- }
176
- return input.spawnError ? 127 : 0;
177
- }
178
-
179
- interface SpawnedChildCompletionWatcher {
180
- clear: () => void;
181
- }
182
-
183
- function watchSpawnedChildCompletion(
184
- child: ChildProcessWithoutNullStreams,
185
- options: {
186
- graceMs: number;
187
- onComplete: (exitCode: number) => void;
188
- getContext: () => { timedOut: boolean; spawnError?: Error };
189
- },
190
- ): SpawnedChildCompletionWatcher {
191
- let exited = false;
192
- let exitCode: number | null = null;
193
- let postExitTimer: NodeJS.Timeout | undefined;
194
- // `completed` suppresses duplicate exit/close callbacks; `settled` in `finish` guards async spill cleanup.
195
- let completed = false;
196
-
197
- const complete = (closeCode?: number | null) => {
198
- if (completed) return;
199
- completed = true;
200
- if (postExitTimer) {
201
- clearTimeout(postExitTimer);
202
- postExitTimer = undefined;
203
- }
204
- const context = options.getContext();
205
- options.onComplete(
206
- resolveSpawnedChildExitCode({
207
- closeCode,
208
- exitCode,
209
- useExitFallback: exited,
210
- timedOut: context.timedOut,
211
- spawnError: context.spawnError,
212
- }),
213
- );
214
- };
215
-
216
- child.once("exit", (code) => {
217
- exited = true;
218
- exitCode = code;
219
- postExitTimer = setTimeout(() => {
220
- destroySpawnedChildStreams(child);
221
- complete(undefined);
222
- }, options.graceMs);
223
- postExitTimer.unref?.();
224
- });
225
- child.once("close", (code) => {
226
- complete(code);
227
- });
228
-
229
- return {
230
- clear: () => {
231
- if (postExitTimer) {
232
- clearTimeout(postExitTimer);
233
- postExitTimer = undefined;
234
- }
235
- },
236
- };
237
- }
238
-
239
- function destroySpawnedChildStreams(child: ChildProcessWithoutNullStreams): void {
240
- child.stdin?.destroy();
241
- child.stdout?.destroy();
242
- child.stderr?.destroy();
243
- }
244
-
245
- function parsePositiveIntegerEnv(value: string | undefined): number | undefined {
246
- if (value === undefined || !/^\d+$/.test(value.trim())) {
247
- return undefined;
248
- }
249
- const parsed = Number(value.trim());
250
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
251
- }
252
-
253
- function clampUpstreamDefaultTimeout(childEnv: NodeJS.ProcessEnv): void {
254
- const requestedTimeout = parsePositiveIntegerEnv(childEnv[AGENT_BROWSER_DEFAULT_TIMEOUT_ENV]);
255
- if (requestedTimeout === undefined || requestedTimeout > SAFE_AGENT_BROWSER_OPERATION_TIMEOUT_MS) {
256
- childEnv[AGENT_BROWSER_DEFAULT_TIMEOUT_ENV] = String(SAFE_AGENT_BROWSER_OPERATION_TIMEOUT_MS);
257
- }
258
- }
259
-
260
- export function getAgentBrowserProcessTimeoutMs(env: NodeJS.ProcessEnv = processEnv): number {
261
- return parsePositiveIntegerEnv(env[PI_AGENT_BROWSER_PROCESS_TIMEOUT_ENV]) ?? DEFAULT_AGENT_BROWSER_PROCESS_TIMEOUT_MS;
262
- }
263
-
264
- export function getAgentBrowserSocketDir(
265
- platform: NodeJS.Platform = processPlatform,
266
- uid: number | undefined = typeof process.getuid === "function" ? process.getuid() : undefined,
267
- ): string | undefined {
268
- if (platform === "win32") {
269
- return undefined;
270
- }
271
- return `${DEFAULT_AGENT_BROWSER_SOCKET_DIR_PREFIX}${typeof uid === "number" ? `-${uid}` : ""}`;
272
- }
273
-
274
- async function ensureAgentBrowserSocketDir(socketDir: string): Promise<boolean> {
275
- try {
276
- await mkdir(socketDir, { recursive: true, mode: 0o700 });
277
- await chmod(socketDir, 0o700).catch(() => undefined);
278
- return true;
279
- } catch {
280
- return false;
281
- }
282
- }
283
-
284
- function getChildEnvName(name: string): string | undefined {
285
- if (processPlatform === "win32") {
286
- const upperName = name.toUpperCase();
287
- if (INHERITED_ENV_NAMES.has(upperName)) return upperName;
288
- return INHERITED_ENV_PREFIXES.some((prefix) => upperName.startsWith(prefix)) ? upperName : undefined;
289
- }
290
- if (INHERITED_ENV_NAMES.has(name) || INHERITED_ENV_PREFIXES.some((prefix) => name.startsWith(prefix))) {
291
- return name;
292
- }
293
- return undefined;
294
- }
295
-
296
- export function buildAgentBrowserProcessEnv(
297
- baseEnv: NodeJS.ProcessEnv = processEnv,
298
- overrides: NodeJS.ProcessEnv | undefined = undefined,
299
- ): NodeJS.ProcessEnv {
300
- const childEnv: NodeJS.ProcessEnv = {};
301
- for (const [name, value] of Object.entries(baseEnv)) {
302
- const childName = getChildEnvName(name);
303
- if (value !== undefined && childName) {
304
- childEnv[childName] = value;
305
- }
306
- }
307
-
308
- if (!overrides) {
309
- clampUpstreamDefaultTimeout(childEnv);
310
- return childEnv;
311
- }
312
-
313
- for (const [name, value] of Object.entries(overrides)) {
314
- const childName = getChildEnvName(name) ?? name;
315
- if (value === undefined) {
316
- delete childEnv[childName];
317
- } else {
318
- childEnv[childName] = value;
319
- }
320
- }
321
- clampUpstreamDefaultTimeout(childEnv);
322
- return childEnv;
323
- }
324
-
325
- export async function runAgentBrowserProcess(options: {
326
- args: string[];
327
- cwd: string;
328
- env?: NodeJS.ProcessEnv;
329
- signal?: AbortSignal;
330
- stdin?: string;
331
- timeoutMs?: number;
332
- }): Promise<ProcessRunResult> {
333
- const { args, cwd, env, signal, stdin } = options;
334
- const timeoutMs = options.timeoutMs ?? getAgentBrowserProcessTimeoutMs();
335
- const explicitSocketDir = env?.[AGENT_BROWSER_SOCKET_DIR_ENV];
336
- let effectiveEnv = explicitSocketDir === undefined ? { ...env, [AGENT_BROWSER_SOCKET_DIR_ENV]: undefined } : env;
337
- const requestedSocketDir = explicitSocketDir ?? getAgentBrowserSocketDir();
338
- if (requestedSocketDir && (await ensureAgentBrowserSocketDir(requestedSocketDir))) {
339
- effectiveEnv = { ...env, [AGENT_BROWSER_SOCKET_DIR_ENV]: requestedSocketDir };
340
- }
341
-
342
- return await new Promise<ProcessRunResult>((resolve) => {
343
- let aborted = false;
344
- let settled = false;
345
- let spawnError: Error | undefined;
346
- let stderr = "";
347
- let stdoutBuffers: Buffer[] = [];
348
- let stdoutBufferedBytes = 0;
349
- let stdoutTail = "";
350
- let stdoutSpillHandle: Awaited<ReturnType<typeof openSecureTempFile>>["fileHandle"] | undefined;
351
- let stdoutSpillPath: string | undefined;
352
- let pendingStdoutWrite = Promise.resolve();
353
- let stdoutSpillError: Error | undefined;
354
- let killTimer: NodeJS.Timeout | undefined;
355
- let timeoutTimer: NodeJS.Timeout | undefined;
356
- let abortListener: (() => void) | undefined;
357
- let timedOut = false;
358
- let completionWatcher: SpawnedChildCompletionWatcher | undefined;
359
-
360
- const queueStdoutChunk = (buffer: Buffer) => {
361
- stdoutTail = appendTail(stdoutTail, buffer.toString("utf8"), MAX_BUFFERED_STDOUT_TAIL_CHARS);
362
- if (stdoutSpillError) return;
363
- if (!stdoutSpillPath && stdoutBufferedBytes + buffer.length <= MAX_BUFFERED_STDOUT_BYTES) {
364
- stdoutBuffers.push(buffer);
365
- stdoutBufferedBytes += buffer.length;
366
- return;
367
- }
368
-
369
- pendingStdoutWrite = pendingStdoutWrite
370
- .then(async () => {
371
- if (stdoutSpillError) return;
372
- if (!stdoutSpillHandle || !stdoutSpillPath) {
373
- const tempFile = await openSecureTempFile(PROCESS_STDOUT_SPILL_FILE_PREFIX, ".json");
374
- stdoutSpillHandle = tempFile.fileHandle;
375
- stdoutSpillPath = tempFile.path;
376
- if (stdoutBuffers.length > 0) {
377
- await writeSecureTempChunk({
378
- content: Buffer.concat(stdoutBuffers),
379
- fileHandle: stdoutSpillHandle,
380
- path: stdoutSpillPath,
381
- });
382
- stdoutBuffers = [];
383
- stdoutBufferedBytes = 0;
384
- }
385
- }
386
- await writeSecureTempChunk({ content: buffer, fileHandle: stdoutSpillHandle, path: stdoutSpillPath });
387
- })
388
- .catch((error) => {
389
- stdoutSpillError = error instanceof Error ? error : new Error(String(error));
390
- });
391
- };
392
-
393
- const removeAbortListener = () => {
394
- if (!signal || !abortListener) return;
395
- signal.removeEventListener("abort", abortListener);
396
- abortListener = undefined;
397
- };
398
-
399
- const finish = (exitCode: number) => {
400
- if (settled) return;
401
- settled = true;
402
- void pendingStdoutWrite.finally(async () => {
403
- removeAbortListener();
404
- if (killTimer) {
405
- clearTimeout(killTimer);
406
- }
407
- if (timeoutTimer) {
408
- clearTimeout(timeoutTimer);
409
- }
410
- completionWatcher?.clear();
411
- if (stdoutSpillHandle) {
412
- await stdoutSpillHandle.close().catch(() => undefined);
413
- }
414
- if (!spawnError && stdoutSpillError) {
415
- spawnError = stdoutSpillError;
416
- }
417
- // Idempotent teardown: streams may already be destroyed by the post-`exit` fallback.
418
- destroySpawnedChildStreams(child);
419
- resolve({
420
- aborted,
421
- exitCode,
422
- spawnError,
423
- stderr,
424
- stdout: stdoutSpillPath ? stdoutTail : Buffer.concat(stdoutBuffers).toString("utf8"),
425
- stdoutSpillPath,
426
- timedOut,
427
- timeoutMs: timedOut ? timeoutMs : undefined,
428
- });
429
- });
430
- };
431
-
432
- const spawnCommand = buildAgentBrowserSpawnCommand(args);
433
- const child = spawn(spawnCommand.command, spawnCommand.args, {
434
- cwd,
435
- env: buildAgentBrowserProcessEnv(processEnv, effectiveEnv),
436
- stdio: ["pipe", "pipe", "pipe"],
437
- });
438
-
439
- const terminateChild = (reason: "abort" | "timeout") => {
440
- if (settled) return;
441
- if (reason === "abort") {
442
- aborted = true;
443
- } else {
444
- timedOut = true;
445
- }
446
- terminateSpawnedChild(child, "SIGTERM");
447
- killTimer = setTimeout(() => {
448
- terminateSpawnedChild(child, "SIGKILL");
449
- }, 2_000);
450
- };
451
- const recordStdinError = (error: unknown) => {
452
- const stdinError = error instanceof Error ? error : new Error(String(error));
453
- const errorCode = (stdinError as NodeJS.ErrnoException).code;
454
- if (errorCode === "EPIPE" || errorCode === "EOF" || errorCode === "ERR_STREAM_DESTROYED") {
455
- return;
456
- }
457
- if (!spawnError) {
458
- spawnError = stdinError;
459
- }
460
- };
461
- const writeChildStdin = () => {
462
- if (aborted || signal?.aborted) {
463
- child.stdin.destroy();
464
- return;
465
- }
466
- try {
467
- if (stdin) {
468
- child.stdin.write(stdin);
469
- }
470
- child.stdin.end();
471
- } catch (error) {
472
- recordStdinError(error);
473
- child.stdin.destroy();
474
- }
475
- };
476
-
477
- child.stdin.on("error", recordStdinError);
478
- child.once("error", (error) => {
479
- spawnError = error instanceof Error ? error : new Error(String(error));
480
- finish(
481
- resolveSpawnedChildExitCode({
482
- useExitFallback: false,
483
- timedOut,
484
- spawnError,
485
- }),
486
- );
487
- });
488
- completionWatcher = watchSpawnedChildCompletion(child, {
489
- graceMs: EXIT_STDIO_GRACE_MS,
490
- onComplete: finish,
491
- getContext: () => ({ timedOut, spawnError }),
492
- });
493
- child.stdout.on("data", (chunk: Buffer | string) => {
494
- queueStdoutChunk(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
495
- });
496
- child.stderr.on("data", (chunk: Buffer | string) => {
497
- stderr = appendTail(stderr, chunk.toString(), MAX_BUFFERED_STDERR_CHARS);
498
- });
499
-
500
- if (timeoutMs > 0) {
501
- timeoutTimer = setTimeout(() => terminateChild("timeout"), timeoutMs);
502
- timeoutTimer.unref?.();
503
- }
504
-
505
- if (signal) {
506
- if (signal.aborted) {
507
- terminateChild("abort");
508
- } else {
509
- abortListener = () => terminateChild("abort");
510
- signal.addEventListener("abort", abortListener, { once: true });
511
- }
512
- }
513
-
514
- writeChildStdin();
515
- });
516
- }
@@ -1,105 +0,0 @@
1
- /**
2
- * Purpose: Derive operator prompt constraints for browser-run preflight guards and legacy bash policy.
3
- * Responsibilities: Parse the latest user message into requested artifact paths and legacy bash allowance.
4
- * Scope: Pure prompt-text policy; enforcement lives in orchestration prompt-guards and the extension entrypoint.
5
- */
6
-
7
- export interface PromptRequestedArtifact {
8
- kind: "recording" | "screenshot";
9
- path: string;
10
- required: boolean;
11
- }
12
-
13
- export interface PromptPolicy {
14
- allowLegacyAgentBrowserBash: boolean;
15
- requestedArtifacts: PromptRequestedArtifact[];
16
- }
17
-
18
- const BROWSER_PROMPT_PATTERNS = [
19
- /\b(?:agent[_ -]?browser|browser automation|eval\s+--stdin|screenshot|snapshot|tab\s+list)\b/i,
20
- /\b(?:react\s+(?:tree|inspect|renders|suspense)|web\s+vitals|core\s+web\s+vitals|pushstate)\b/i,
21
- /\b(?:live\s+docs?|online\s+research|research\s+(?:online|the\s+web)|search\s+(?:online|the\s+web)|web\s+research)\b/i,
22
- /\bbrowser\b.*\b(?:automation|click|fill|navigate|open|page|screenshot|site|snapshot|tab|url|visit|web(?:site| page)?)\b/i,
23
- /\b(?:browse|click|fill|login|navigate|open|visit)\b.*\b(?:https?:\/\/\S+|page|site|tab|url|web(?:site| page)?)\b/i,
24
- ];
25
-
26
- const LEGACY_BASH_ALLOW_PATTERNS = [
27
- /\b(?:bash-oriented workflow|bash workflow)\b/i,
28
- /\b(?:use|via|through|with)\s+bash\b/i,
29
- /\bnpx\s+agent-browser\b/i,
30
- /\bagent-browser\s+--(?:help|version)\b/i,
31
- /\bdebug(?:ging)?\b.*\b(?:agent[_ -]?browser|agent_browser|browser integration)\b/i,
32
- ];
33
-
34
- const PROMPT_ARTIFACT_PATH_PATTERN = /(?:^|[\s"'`(:])((?:\/[^\s"'`),;]+|[A-Za-z]:[\\/][^\s"'`),;]+|\.{1,2}[\\/][^\s"'`),;]+|[^\s"'`),;:\\/]+(?:[\\/][^\s"'`),;]+)+|[^\s"'`),;:\\/]+)\.(?:png|jpe?g|webp|gif|webm|mp4|har|pdf|trace|json))(?:[\s"'`),;.]|$)/gi;
35
-
36
- function extractPromptRequestedArtifacts(prompt: string): PromptRequestedArtifact[] {
37
- const artifacts: PromptRequestedArtifact[] = [];
38
- const seen = new Set<string>();
39
- for (const line of prompt.split(/\r?\n/)) {
40
- const lowerLine = line.toLowerCase();
41
- const kind = lowerLine.includes("screenshot")
42
- ? "screenshot"
43
- : /\b(?:screen\s+recording|recording|webm|video)\b/.test(lowerLine)
44
- ? "recording"
45
- : undefined;
46
- if (!kind) continue;
47
- PROMPT_ARTIFACT_PATH_PATTERN.lastIndex = 0;
48
- for (const match of line.matchAll(PROMPT_ARTIFACT_PATH_PATTERN)) {
49
- const path = match[1]?.trim();
50
- if (!path) continue;
51
- const key = `${kind}:${path}`;
52
- if (seen.has(key)) continue;
53
- seen.add(key);
54
- artifacts.push({
55
- kind,
56
- path,
57
- required: kind === "screenshot" || !/\b(?:if|when)\s+(?:recording\s+)?(?:is\s+)?available\b/i.test(line),
58
- });
59
- }
60
- }
61
- return artifacts;
62
- }
63
-
64
- export function buildPromptPolicy(prompt: string): PromptPolicy {
65
- return {
66
- allowLegacyAgentBrowserBash: LEGACY_BASH_ALLOW_PATTERNS.some((pattern) => pattern.test(prompt)),
67
- requestedArtifacts: extractPromptRequestedArtifacts(prompt),
68
- };
69
- }
70
-
71
- function getMessageText(content: unknown): string {
72
- if (typeof content === "string") return content;
73
- if (!Array.isArray(content)) return "";
74
-
75
- return content
76
- .map((item) => {
77
- if (typeof item !== "object" || item === null) return "";
78
- return item.type === "text" && typeof item.text === "string" ? item.text : "";
79
- })
80
- .filter((text) => text.length > 0)
81
- .join("\n");
82
- }
83
-
84
- export function shouldAppendBrowserSystemPrompt(prompt: string): boolean {
85
- const normalizedPrompt = prompt.trim();
86
- if (normalizedPrompt.length === 0) {
87
- return false;
88
- }
89
- return BROWSER_PROMPT_PATTERNS.some((pattern) => pattern.test(normalizedPrompt));
90
- }
91
-
92
- export function getLatestUserPrompt(branch: unknown[]): string {
93
- for (let index = branch.length - 1; index >= 0; index -= 1) {
94
- const entry = branch[index];
95
- if (typeof entry !== "object" || entry === null || !("type" in entry) || entry.type !== "message") {
96
- continue;
97
- }
98
- const message = "message" in entry ? entry.message : undefined;
99
- if (typeof message !== "object" || message === null || !("role" in message) || message.role !== "user") {
100
- continue;
101
- }
102
- return getMessageText("content" in message ? message.content : undefined);
103
- }
104
- return "";
105
- }