pi-agent-browser-native 0.2.47 → 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.
- package/CHANGELOG.md +63 -19
- package/README.md +52 -19
- package/dist/extensions/agent-browser/index.js +785 -0
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
- package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
- package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
- package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
- package/dist/extensions/agent-browser/lib/config.js +122 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
- package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
- package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
- package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
- package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
- package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
- package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
- package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
- package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
- package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
- package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
- package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
- package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
- package/dist/extensions/agent-browser/lib/playbook.js +121 -0
- package/dist/extensions/agent-browser/lib/process.js +448 -0
- package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
- package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
- package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
- package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
- package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
- package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
- package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
- package/dist/extensions/agent-browser/lib/results/network.js +73 -0
- package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
- package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
- package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
- package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
- package/dist/extensions/agent-browser/lib/results/text.js +40 -0
- package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
- package/dist/extensions/agent-browser/lib/runtime.js +816 -0
- package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
- package/dist/extensions/agent-browser/lib/temp.js +498 -0
- package/dist/extensions/agent-browser/lib/web-search.js +562 -0
- package/docs/ARCHITECTURE.md +10 -10
- package/docs/COMMAND_REFERENCE.md +35 -21
- package/docs/ELECTRON.md +3 -3
- package/docs/RELEASE.md +46 -26
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +35 -106
- package/docs/TOOL_CONTRACT.md +23 -21
- package/package.json +12 -8
- package/scripts/agent-browser-capability-baseline.mjs +6 -3
- package/scripts/config.mjs +8 -2
- package/scripts/doctor.mjs +19 -17
- package/scripts/platform-smoke.mjs +1 -1
- package/extensions/agent-browser/index.ts +0 -952
- package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
- package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
- package/extensions/agent-browser/lib/bash-guard.ts +0 -205
- package/extensions/agent-browser/lib/command-policy.ts +0 -71
- package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
- package/extensions/agent-browser/lib/config-policy.js +0 -690
- package/extensions/agent-browser/lib/config.ts +0 -209
- package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
- package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
- package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
- package/extensions/agent-browser/lib/electron/launch.ts +0 -499
- package/extensions/agent-browser/lib/executable-path.ts +0 -19
- package/extensions/agent-browser/lib/fs-utils.ts +0 -18
- package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
- package/extensions/agent-browser/lib/input-modes/job.ts +0 -451
- package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
- package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
- package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
- package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
- package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
- package/extensions/agent-browser/lib/input-modes.ts +0 -45
- package/extensions/agent-browser/lib/json-schema.ts +0 -73
- package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
- package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
- package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
- package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -257
- package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -912
- package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -512
- package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -1481
- package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
- package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
- package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
- package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
- package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
- package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -252
- package/extensions/agent-browser/lib/playbook.ts +0 -142
- package/extensions/agent-browser/lib/process.ts +0 -516
- package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
- package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
- package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
- package/extensions/agent-browser/lib/results/categories.ts +0 -106
- package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
- package/extensions/agent-browser/lib/results/contracts.ts +0 -241
- package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
- package/extensions/agent-browser/lib/results/envelope.ts +0 -195
- package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
- package/extensions/agent-browser/lib/results/network.ts +0 -78
- package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
- package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
- package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
- package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
- package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
- package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
- package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
- package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
- package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
- package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
- package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
- package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
- package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
- package/extensions/agent-browser/lib/results/presentation.ts +0 -257
- package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
- package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
- package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
- package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
- package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
- package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
- package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
- package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
- package/extensions/agent-browser/lib/results/text.ts +0 -40
- package/extensions/agent-browser/lib/runtime.ts +0 -988
- package/extensions/agent-browser/lib/session-page-state.ts +0 -512
- package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
- package/extensions/agent-browser/lib/temp.ts +0 -577
- package/extensions/agent-browser/lib/web-search.ts +0 -721
- /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
|
-
}
|