pi-agent-browser-native 0.5.0 → 0.6.6
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 +144 -0
- package/README.md +75 -42
- package/dist/extensions/agent-browser/index.js +13 -83
- package/dist/extensions/agent-browser/lib/argv-grammar.js +8 -2
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +1 -1
- package/dist/extensions/agent-browser/lib/command-policy.js +4 -7
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +19 -11
- package/dist/extensions/agent-browser/lib/config-policy.js +25 -1
- package/dist/extensions/agent-browser/lib/config.js +1 -1
- package/dist/extensions/agent-browser/lib/input-modes/job.js +0 -9
- package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -1
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +18 -4
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +3 -138
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +1 -81
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +4 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +31 -26
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +60 -8
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +1 -4
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +119 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +7 -6
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +31 -40
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +76 -70
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +6 -8
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +5 -10
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +41 -21
- package/dist/extensions/agent-browser/lib/page-target-validation.js +270 -0
- package/dist/extensions/agent-browser/lib/playbook.js +13 -12
- package/dist/extensions/agent-browser/lib/process-identity.js +11 -10
- package/dist/extensions/agent-browser/lib/process.js +18 -82
- package/dist/extensions/agent-browser/lib/recording-reservations.js +11 -78
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +1 -1
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +27 -14
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +20 -2
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +11 -16
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +12 -9
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +2 -2
- package/dist/extensions/agent-browser/lib/results/presentation.js +31 -4
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +1 -1
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +9 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +3 -3
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +2 -1
- package/dist/extensions/agent-browser/lib/results/snapshot.js +4 -4
- package/dist/extensions/agent-browser/lib/runtime.js +73 -72
- package/dist/extensions/agent-browser/lib/session-page-state.js +12 -3
- package/dist/extensions/agent-browser/lib/temp.js +1 -2
- package/dist/extensions/agent-browser/lib/upstream-version.js +5 -5
- package/dist/extensions/agent-browser/lib/web-search.js +108 -24
- package/dist/scripts/agent-browser-target.mjs +19 -1
- package/docs/ARCHITECTURE.md +24 -20
- package/docs/COMMAND_REFERENCE.md +181 -49
- package/docs/ELECTRON.md +2 -2
- package/docs/RELEASE.md +10 -8
- package/docs/REQUIREMENTS.md +8 -7
- package/docs/SUPPORT_MATRIX.md +31 -26
- package/docs/TOOL_CONTRACT.md +89 -56
- package/package.json +1 -1
- package/scripts/agent-browser-capability-baseline.mjs +65 -5
- package/scripts/agent-browser-target.mjs +19 -1
- package/scripts/config.mjs +1 -0
- package/scripts/doctor.mjs +15 -9
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -20
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +0 -601
- package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -78
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -37
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { parseArgvDescriptor } from "./argv-descriptor.js";
|
|
2
|
+
import { GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, VALUE_FLAGS, } from "./argv-grammar.js";
|
|
3
|
+
import { needsManagedSession } from "./command-policy.js";
|
|
4
|
+
import { isUnverifiedPageTransitionCommand } from "./command-taxonomy.js";
|
|
5
|
+
import { parseBatchCommandArgument, parseUserBatchStdin } from "./orchestration/batch-stdin.js";
|
|
6
|
+
const UNVERIFIED_PAGE_MESSAGE = "The active page became unverified after a tab, attachment, history, script, or state-load transition. Run get url or navigate explicitly before page-content inspection.";
|
|
7
|
+
const BATCH_UNVERIFIED_PAGE_MESSAGE = `${UNVERIFIED_PAGE_MESSAGE} In a batch, put get url after the transition before later content steps, or split the batch at that boundary.`;
|
|
8
|
+
const UNSAFE_BATCH_ARGUMENT_MESSAGE = "Batch command arguments could not be inspected. Use batch stdin JSON command arrays instead.";
|
|
9
|
+
const NESTED_BATCH_ARGUMENT_MESSAGE = "Nested batch commands are not supported. Flatten the batch steps instead.";
|
|
10
|
+
const NON_BAIL_BATCH_NAVIGATION_MESSAGE = "Batches that change or re-verify the page target before page-content access must use exact batch --bail so a failed step cannot act on an unverified or prior page.";
|
|
11
|
+
const MAX_NON_BAIL_BATCH_PAGE_STATES = 64;
|
|
12
|
+
const EXPLICIT_NAVIGATION_COMMANDS = new Set(["a11y", "goto", "navigate", "open", "pushstate", "visit", "vitals", "web-vitals"]);
|
|
13
|
+
const POSITIONAL_VALUE_FLAGS = new Set([...VALUE_FLAGS, "--llms"]);
|
|
14
|
+
function getPositionalOperands(commandTokens) {
|
|
15
|
+
const values = [];
|
|
16
|
+
for (let index = 1; index < commandTokens.length; index += 1) {
|
|
17
|
+
const token = commandTokens[index];
|
|
18
|
+
if (!token || (token.includes("=") && token.startsWith("-")))
|
|
19
|
+
continue;
|
|
20
|
+
if (POSITIONAL_VALUE_FLAGS.has(token)) {
|
|
21
|
+
index += 1;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES.has(token)) {
|
|
25
|
+
if (["true", "false"].includes(commandTokens[index + 1] ?? ""))
|
|
26
|
+
index += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!token.startsWith("-") || token.includes("/") || token.includes("\\"))
|
|
30
|
+
values.push(token);
|
|
31
|
+
}
|
|
32
|
+
return values;
|
|
33
|
+
}
|
|
34
|
+
function getExplicitNavigationTarget(args) {
|
|
35
|
+
const descriptor = parseArgvDescriptor(args);
|
|
36
|
+
const positionals = getPositionalOperands(descriptor.upstreamCommandTokens);
|
|
37
|
+
if (EXPLICIT_NAVIGATION_COMMANDS.has(descriptor.commandInfo.command ?? ""))
|
|
38
|
+
return positionals[0];
|
|
39
|
+
if (descriptor.commandInfo.command === "tab" && descriptor.commandInfo.subcommand === "new")
|
|
40
|
+
return positionals[1];
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
function getResultingExplicitNavigationTarget(args, currentPageUrl) {
|
|
44
|
+
const target = getExplicitNavigationTarget(args);
|
|
45
|
+
if (target === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
if (parseArgvDescriptor(args).commandInfo.command !== "pushstate")
|
|
48
|
+
return target;
|
|
49
|
+
try {
|
|
50
|
+
return new URL(target).href;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
if (!currentPageUrl)
|
|
54
|
+
return undefined;
|
|
55
|
+
try {
|
|
56
|
+
return new URL(target, currentPageUrl).href;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function getBatchCommandSteps(args, stdin) {
|
|
64
|
+
const descriptor = parseArgvDescriptor(args);
|
|
65
|
+
if (descriptor.commandInfo.command !== "batch")
|
|
66
|
+
return { steps: [] };
|
|
67
|
+
const argumentCommands = descriptor.upstreamCommandTokens.slice(1).filter((command) => command !== "--bail");
|
|
68
|
+
const steps = [];
|
|
69
|
+
if (argumentCommands.length > 0) {
|
|
70
|
+
for (const command of argumentCommands) {
|
|
71
|
+
const parsed = parseBatchCommandArgument(command);
|
|
72
|
+
if (parsed.error || parsed.step === undefined)
|
|
73
|
+
return { error: parsed.error ?? UNSAFE_BATCH_ARGUMENT_MESSAGE, steps: [] };
|
|
74
|
+
if (parseArgvDescriptor(parsed.step).commandInfo.command === "batch")
|
|
75
|
+
return { error: NESTED_BATCH_ARGUMENT_MESSAGE, steps: [] };
|
|
76
|
+
steps.push(parsed.step);
|
|
77
|
+
}
|
|
78
|
+
return { steps };
|
|
79
|
+
}
|
|
80
|
+
const parsedStdin = parseUserBatchStdin(stdin);
|
|
81
|
+
if (parsedStdin.error)
|
|
82
|
+
return { error: parsedStdin.error, steps: [] };
|
|
83
|
+
for (const step of parsedStdin.steps ?? []) {
|
|
84
|
+
if (parseArgvDescriptor(step).commandInfo.command === "batch")
|
|
85
|
+
return { error: NESTED_BATCH_ARGUMENT_MESSAGE, steps: [] };
|
|
86
|
+
steps.push(step);
|
|
87
|
+
}
|
|
88
|
+
return { steps };
|
|
89
|
+
}
|
|
90
|
+
function batchBailsOnFirstError(args) {
|
|
91
|
+
const descriptor = parseArgvDescriptor(args);
|
|
92
|
+
return descriptor.commandInfo.command === "batch" && descriptor.upstreamCommandTokens.slice(1).includes("--bail");
|
|
93
|
+
}
|
|
94
|
+
function commandMayChangePageTarget(args, trustedBatchTabSelection) {
|
|
95
|
+
const descriptor = parseArgvDescriptor(args);
|
|
96
|
+
return getExplicitNavigationTarget(args) !== undefined
|
|
97
|
+
|| (isUnverifiedPageTransitionCommand(descriptor.commandInfo.command, descriptor.commandInfo.subcommand)
|
|
98
|
+
&& !(trustedBatchTabSelection && descriptor.commandInfo.command === "tab"));
|
|
99
|
+
}
|
|
100
|
+
function commandVerifiesPageTarget(args) {
|
|
101
|
+
const { command, subcommand } = parseArgvDescriptor(args).commandInfo;
|
|
102
|
+
return command === "get" && subcommand === "url";
|
|
103
|
+
}
|
|
104
|
+
function deduplicatePossibleBatchPageStates(states) {
|
|
105
|
+
const deduplicated = new Map();
|
|
106
|
+
for (const state of states) {
|
|
107
|
+
const key = `${state.pageUrlUnknown ? "unknown" : "known"}\0${state.currentPageUrl ?? ""}`;
|
|
108
|
+
const existing = deduplicated.get(key);
|
|
109
|
+
if (!existing)
|
|
110
|
+
deduplicated.set(key, state);
|
|
111
|
+
else
|
|
112
|
+
existing.retainedAfterFailedNavigation ||= state.retainedAfterFailedNavigation;
|
|
113
|
+
}
|
|
114
|
+
return [...deduplicated.values()];
|
|
115
|
+
}
|
|
116
|
+
export function commandRequiresLivePageVerification(args, stdin) {
|
|
117
|
+
const descriptor = parseArgvDescriptor(args);
|
|
118
|
+
if (descriptor.commandInfo.command === "eval")
|
|
119
|
+
return true;
|
|
120
|
+
if (descriptor.commandInfo.command !== "batch")
|
|
121
|
+
return false;
|
|
122
|
+
const batch = getBatchCommandSteps(args, stdin);
|
|
123
|
+
return batch.error === undefined && batch.steps.some((step) => commandRequiresLivePageVerification(step));
|
|
124
|
+
}
|
|
125
|
+
function getResultingPageState(options) {
|
|
126
|
+
const descriptor = parseArgvDescriptor(options.args);
|
|
127
|
+
if (descriptor.commandInfo.command === "get" && descriptor.commandInfo.subcommand === "url") {
|
|
128
|
+
return { currentPageUrl: options.currentPageUrl, pageUrlUnknown: false };
|
|
129
|
+
}
|
|
130
|
+
const rawExplicitTarget = getExplicitNavigationTarget(options.args);
|
|
131
|
+
const explicitTarget = getResultingExplicitNavigationTarget(options.args, options.currentPageUrl);
|
|
132
|
+
if (explicitTarget !== undefined)
|
|
133
|
+
return { currentPageUrl: explicitTarget, pageUrlUnknown: false };
|
|
134
|
+
if (rawExplicitTarget !== undefined)
|
|
135
|
+
return { pageUrlUnknown: true };
|
|
136
|
+
if (isUnverifiedPageTransitionCommand(descriptor.commandInfo.command, descriptor.commandInfo.subcommand)) {
|
|
137
|
+
return options.trustedBatchTabSelection && descriptor.commandInfo.command === "tab"
|
|
138
|
+
? { currentPageUrl: options.currentPageUrl, pageUrlUnknown: options.pageUrlUnknown }
|
|
139
|
+
: { pageUrlUnknown: true };
|
|
140
|
+
}
|
|
141
|
+
return { currentPageUrl: options.currentPageUrl, pageUrlUnknown: options.pageUrlUnknown };
|
|
142
|
+
}
|
|
143
|
+
export function getResultingPageTargetState(options) {
|
|
144
|
+
const descriptor = parseArgvDescriptor(options.args);
|
|
145
|
+
let state = { currentPageUrl: options.currentPageUrl, pageUrlUnknown: options.pageUrlUnknown ?? false };
|
|
146
|
+
let pageTargetMayHaveChanged = false;
|
|
147
|
+
if (descriptor.commandInfo.command !== "batch") {
|
|
148
|
+
pageTargetMayHaveChanged = getExplicitNavigationTarget(options.args) !== undefined
|
|
149
|
+
|| isUnverifiedPageTransitionCommand(descriptor.commandInfo.command, descriptor.commandInfo.subcommand);
|
|
150
|
+
return { ...getResultingPageState({ ...state, args: options.args, trustedBatchTabSelection: false }), pageTargetMayHaveChanged };
|
|
151
|
+
}
|
|
152
|
+
const batch = getBatchCommandSteps(options.args, options.stdin);
|
|
153
|
+
if (batch.error)
|
|
154
|
+
return { pageTargetMayHaveChanged: true, pageUrlUnknown: true };
|
|
155
|
+
for (let index = 0; index < batch.steps.length; index += 1) {
|
|
156
|
+
const step = batch.steps[index];
|
|
157
|
+
const trustedBatchTabSelection = options.trustedFirstBatchTabSelection === true && index === 0;
|
|
158
|
+
const stepDescriptor = parseArgvDescriptor(step);
|
|
159
|
+
pageTargetMayHaveChanged ||= getExplicitNavigationTarget(step) !== undefined
|
|
160
|
+
|| (isUnverifiedPageTransitionCommand(stepDescriptor.commandInfo.command, stepDescriptor.commandInfo.subcommand)
|
|
161
|
+
&& !(trustedBatchTabSelection && stepDescriptor.commandInfo.command === "tab"));
|
|
162
|
+
state = getResultingPageState({ ...state, args: step, trustedBatchTabSelection });
|
|
163
|
+
}
|
|
164
|
+
return { ...state, pageTargetMayHaveChanged };
|
|
165
|
+
}
|
|
166
|
+
function isRecoveringPageTransitionCommand(command, subcommand) {
|
|
167
|
+
return command !== "eval"
|
|
168
|
+
&& !(command === "webmcp" && subcommand === "invoke")
|
|
169
|
+
&& isUnverifiedPageTransitionCommand(command, subcommand);
|
|
170
|
+
}
|
|
171
|
+
function getUnverifiedPageError(options) {
|
|
172
|
+
if (!options.pageUrlUnknown)
|
|
173
|
+
return undefined;
|
|
174
|
+
const { command, subcommand } = parseArgvDescriptor(options.args).commandInfo;
|
|
175
|
+
const closesPage = ["close", "exit", "quit"].includes(command ?? "") || (command === "tab" && subcommand === "close");
|
|
176
|
+
const inspectsTarget = (command === "tab" && subcommand === "list") || (command === "get" && subcommand === "url");
|
|
177
|
+
const selectsTab = command === "tab" && subcommand !== undefined && !["close", "list", "new"].includes(subcommand);
|
|
178
|
+
const settlesPendingWebMcp = command === "webmcp" && ["result", "cancel"].includes(subcommand ?? "");
|
|
179
|
+
const handlesBlockingDialog = command === "dialog" && ["status", "accept", "dismiss"].includes(subcommand ?? "");
|
|
180
|
+
const transitionsPage = options.allowUnverifiedPageTransitions === true && isRecoveringPageTransitionCommand(command, subcommand);
|
|
181
|
+
const navigatesExplicitly = getExplicitNavigationTarget(options.args) !== undefined;
|
|
182
|
+
return closesPage || handlesBlockingDialog || inspectsTarget || selectsTab || settlesPendingWebMcp || transitionsPage || navigatesExplicitly || (options.trustedBatchTabSelection && command === "tab")
|
|
183
|
+
? undefined
|
|
184
|
+
: UNVERIFIED_PAGE_MESSAGE;
|
|
185
|
+
}
|
|
186
|
+
export function getPageTargetValidationError(options) {
|
|
187
|
+
const descriptor = parseArgvDescriptor(options.args);
|
|
188
|
+
const command = descriptor.commandInfo.command;
|
|
189
|
+
if (["close", "exit", "quit"].includes(command ?? ""))
|
|
190
|
+
return undefined;
|
|
191
|
+
if (command === "batch") {
|
|
192
|
+
const batch = getBatchCommandSteps(options.args, options.stdin);
|
|
193
|
+
if (batch.error)
|
|
194
|
+
return batch.error.startsWith("agent_browser batch stdin") || batch.error === NESTED_BATCH_ARGUMENT_MESSAGE
|
|
195
|
+
? batch.error
|
|
196
|
+
: UNSAFE_BATCH_ARGUMENT_MESSAGE;
|
|
197
|
+
const bailOnFirstError = batchBailsOnFirstError(options.args);
|
|
198
|
+
let possibleStates = [{
|
|
199
|
+
currentPageUrl: options.currentPageUrl,
|
|
200
|
+
pageUrlUnknown: options.pageUrlUnknown ?? false,
|
|
201
|
+
retainedAfterFailedNavigation: false,
|
|
202
|
+
}];
|
|
203
|
+
for (let index = 0; index < batch.steps.length; index += 1) {
|
|
204
|
+
const step = batch.steps[index];
|
|
205
|
+
const trustedBatchTabSelection = options.trustedFirstBatchTabSelection === true && index === 0;
|
|
206
|
+
let directError;
|
|
207
|
+
let failedNavigationHazard = false;
|
|
208
|
+
for (const state of possibleStates) {
|
|
209
|
+
const error = getUnverifiedPageError({
|
|
210
|
+
allowUnverifiedPageTransitions: options.allowUnverifiedPageTransitions,
|
|
211
|
+
args: step,
|
|
212
|
+
pageUrlUnknown: state.pageUrlUnknown,
|
|
213
|
+
trustedBatchTabSelection,
|
|
214
|
+
});
|
|
215
|
+
if (!error)
|
|
216
|
+
continue;
|
|
217
|
+
if (state.retainedAfterFailedNavigation)
|
|
218
|
+
failedNavigationHazard = true;
|
|
219
|
+
else
|
|
220
|
+
directError ??= error;
|
|
221
|
+
}
|
|
222
|
+
if (directError)
|
|
223
|
+
return BATCH_UNVERIFIED_PAGE_MESSAGE;
|
|
224
|
+
if (failedNavigationHazard)
|
|
225
|
+
return NON_BAIL_BATCH_NAVIGATION_MESSAGE;
|
|
226
|
+
const mayChangePageTarget = commandMayChangePageTarget(step, trustedBatchTabSelection);
|
|
227
|
+
const verifiesPageTarget = commandVerifiesPageTarget(step);
|
|
228
|
+
const nextStates = [];
|
|
229
|
+
for (const state of possibleStates) {
|
|
230
|
+
const successState = getResultingPageState({
|
|
231
|
+
args: step,
|
|
232
|
+
currentPageUrl: state.currentPageUrl,
|
|
233
|
+
pageUrlUnknown: state.pageUrlUnknown,
|
|
234
|
+
trustedBatchTabSelection,
|
|
235
|
+
});
|
|
236
|
+
if (nextStates.length >= MAX_NON_BAIL_BATCH_PAGE_STATES)
|
|
237
|
+
return NON_BAIL_BATCH_NAVIGATION_MESSAGE;
|
|
238
|
+
nextStates.push({ ...successState, retainedAfterFailedNavigation: mayChangePageTarget || verifiesPageTarget ? false : state.retainedAfterFailedNavigation });
|
|
239
|
+
if (!bailOnFirstError && (mayChangePageTarget || (verifiesPageTarget && state.pageUrlUnknown))) {
|
|
240
|
+
nextStates.push({ ...state, retainedAfterFailedNavigation: true });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
possibleStates = deduplicatePossibleBatchPageStates(nextStates);
|
|
244
|
+
}
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
return getUnverifiedPageError({
|
|
248
|
+
allowUnverifiedPageTransitions: options.allowUnverifiedPageTransitions,
|
|
249
|
+
args: options.args,
|
|
250
|
+
pageUrlUnknown: options.pageUrlUnknown,
|
|
251
|
+
trustedBatchTabSelection: options.trustedFirstBatchTabSelection,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
export function getExplicitSessionPageVerificationRequirement(options) {
|
|
255
|
+
const descriptor = parseArgvDescriptor(options.args);
|
|
256
|
+
if (!needsManagedSession(descriptor))
|
|
257
|
+
return undefined;
|
|
258
|
+
if (isRecoveringPageTransitionCommand(descriptor.commandInfo.command, descriptor.commandInfo.subcommand))
|
|
259
|
+
return undefined;
|
|
260
|
+
const validationError = getPageTargetValidationError({
|
|
261
|
+
args: options.args,
|
|
262
|
+
pageUrlUnknown: true,
|
|
263
|
+
stdin: options.stdin,
|
|
264
|
+
allowUnverifiedPageTransitions: true,
|
|
265
|
+
trustedFirstBatchTabSelection: options.trustedFirstBatchTabSelection,
|
|
266
|
+
});
|
|
267
|
+
return validationError === UNVERIFIED_PAGE_MESSAGE || validationError === BATCH_UNVERIFIED_PAGE_MESSAGE || validationError === NON_BAIL_BATCH_NAVIGATION_MESSAGE
|
|
268
|
+
? UNVERIFIED_PAGE_MESSAGE
|
|
269
|
+
: undefined;
|
|
270
|
+
}
|
|
@@ -13,32 +13,33 @@ export const QUICK_START_GUIDELINES = [
|
|
|
13
13
|
"Common first calls (first-call recipe): { args: [\"open\", \"<url>\"] } → { args: [\"snapshot\", \"-i\"] } → { args: [\"click\", \"@eN\"] } or { args: [\"fill\", \"@eN\", \"<text>\"] } using @refs and visible labels from that snapshot, then { args: [\"snapshot\", \"-i\"] } after navigation or DOM changes. On https://example.com/ the main link label is Learn more (use exact snapshot text, not guessed link copy).",
|
|
14
14
|
"Locator-first clicks/fills and native select changes without hand-building argv: { semanticAction: { action: \"click\", locator: \"text\", value: \"Close\" } }, { semanticAction: { action: \"fill\", locator: \"label\", value: \"Email\", text: \"user@example.com\" } }, direct current targets such as { semanticAction: { action: \"fill\", selector: \"@e1\", text: \"prompt\" } }, or { semanticAction: { action: \"select\", selector: \"#flavor\", value: \"chocolate\" } }; add semanticAction.session when targeting a named upstream browser session; details.compiledSemanticAction shows the semantic target, while details.effectiveArgs may show a resolved current @ref for active-session role/name click/check/fill actions to avoid hidden duplicate matches; semanticAction does not expose uncheck while upstream find ... uncheck is not runtime-supported, so use raw uncheck with a stable selector or current ref; selector-not-found failures may append bounded click try-*-candidate next actions or, for fill misses with current editable refs, details.richInputRecovery with focus/click actions that do not copy fill text; stale-ref failures can return retry-semantic-action-after-stale-ref for compiled find actions when retry safety is provable.",
|
|
15
15
|
`Common advanced calls: { args: ["batch", "--bail"], stdin: "[[\"open\",\"https://example.com\"],[\"snapshot\",\"-i\"]]" }, { job: { steps: [{ action: "open", url: "https://example.com" }, { action: "assertText", text: "Example Domain" }, { action: "screenshot", path: ".dogfood/example.png" }] } }, { qa: { url: "https://example.com", expectedText: "Example Domain", screenshotPath: ".dogfood/qa-example.png" } } (example.com smoke only; elsewhere match exact visible text from snapshot -i), { electron: { action: "list", query: "code" } }, { electron: { action: "launch", appName: "Visual Studio Code", handoff: "snapshot" } }, { electron: { action: "probe" } }, { qa: { attached: true, expectedText: "Explorer" } }, { args: ["eval", "--stdin"], stdin: "document.title", outputPath: "logs/page-title.json" }, { args: ["auth", "save", "name", "--password-stdin"], stdin: "<password from user-approved secret source>" }, { args: ["--profile", "Default", "open", "https://example.com/account"], sessionMode: "fresh" }, and { args: ["open", "--enable", "react-devtools", "https://example.com"], sessionMode: "fresh" }. For app pages with a native dropdown, job steps can include { action: "select", selector: "#flavor", value: "chocolate" } before the dependent assertion; for locator-friendly pages, job click/fill steps can use semantic locator fields such as { action: "fill", locator: "role", role: "searchbox", name: "Search", text: "agent browser" }; for human-paced input, job type steps can use { action: "type", selector: "#prompt", text: "hello", delayMs: 20, press: "Enter" }; delayed typing is capped at 200 characters per step, and generated per-character rows are compacted in visible batch prose while full rows remain in details.batchSteps.`,
|
|
16
|
-
"Constrained job navigation is explicit only: click (and select/submit flows that may navigate) does not prove the next page loaded; add assertUrl and/or assertText after navigation-prone steps before screenshot or later interactions. Keep jobs short around navigation, click, and rerender boundaries on dynamic React/product apps; avoid a whole checkout in one job. If a long job times out and details.timeoutPartialProgress shows a mutating incomplete step, inspect current page state and continue with a shorter job or single action instead of blindly retrying the mutating step. Example: { job: { steps: [{ action: \"open\", url: \"https://shop.example/checkout\" }, { action: \"fill\", selector: \"#email\", text: \"user@example.com\" }, { action: \"click\", selector: \"#continue\" }, { action: \"assertUrl\", url: \"**/shipping\" }, { action: \"assertText\", text: \"Shipping address\" }, { action: \"screenshot\", path: \".dogfood/shipping.png\" }] } }. Top-level click may add pageChangeSummary hints, but job never auto-inserts post-click asserts.",
|
|
17
|
-
"High-value command reference: click <selector> --new-tab opens link-like targets in a new tab; select <selector> <value...> changes native dropdown values; wrapper-handled scroll <dir> [px|percent] and scroll to end/top target document scrolling before upstream fallback, while scroll <selector> <dir> [px|percent] targets nested scrollers; download <selector> <path> saves a file triggered by a click; read [url] returns agent-readable text (explicit URLs prefer markdown without
|
|
16
|
+
"Constrained job navigation is explicit only: click (and select/submit flows that may navigate) does not prove the next page loaded; add an assertUrl that does not already match the starting page and/or assertText for new page state after navigation-prone steps before screenshot or later interactions. Keep jobs short around navigation, click, and rerender boundaries on dynamic React/product apps; avoid a whole checkout in one job. If a long job times out and details.timeoutPartialProgress shows a mutating incomplete step, inspect current page state and continue with a shorter job or single action instead of blindly retrying the mutating step. Example: { job: { steps: [{ action: \"open\", url: \"https://shop.example/checkout\" }, { action: \"fill\", selector: \"#email\", text: \"user@example.com\" }, { action: \"click\", selector: \"#continue\" }, { action: \"assertUrl\", url: \"**/shipping\" }, { action: \"assertText\", text: \"Shipping address\" }, { action: \"screenshot\", path: \".dogfood/shipping.png\" }] } }. Top-level click may add pageChangeSummary hints, but job never auto-inserts post-click asserts.",
|
|
17
|
+
"High-value command reference: click <selector> --new-tab opens link-like targets in a new tab; select <selector> <value...> changes native dropdown values; wrapper-handled scroll <dir> [px|percent] and scroll to end/top target document scrolling before upstream fallback, while scroll <selector> <dir> [px|percent] targets nested scrollers; download <selector> <path> saves a file triggered by a click; read [url] returns agent-readable text (explicit URLs prefer markdown without requiring a Chrome page; omit the URL for rendered active-tab DOM); get title/url need no selector; get text/html/value/count <selector> and get attr <selector> <name> read elements/page state (use body for whole-page text/html); screenshot [selector] [path] captures a page or element image; pdf <path> saves a PDF; tab list and tab <tab-id-or-label> inspect or recover the active tab; react tree, react inspect <fiberId>, react renders start/stop, and react suspense introspect React after --enable react-devtools; vitals [url] measures Core Web Vitals; pushstate <url> performs SPA navigation; tap <selector> and swipe <direction> [distance] support iOS/provider touch flows.",
|
|
18
18
|
"For artifact-producing commands, read the visible artifact block and details.artifactVerification before using files: check requested path, absolute path, existence, size bytes, artifact kind, optional mediaType, status, optional limitation, and verified/missing/pending/unverified counts. details.artifacts contains per-file metadata; record start rows are pending/openRecording until record stop writes the target. Upstream record start uses a fresh active page for video capture, so prior in-page DOM and JavaScript state does not carry over; the wrapper blocks prior @e… refs as stale-ref even when the start fails as already-active, and record restart with a URL navigates and invalidates the same way (plain record restart keeps the page), so take a fresh snapshot before continuing. The wrapper creates parent directories for direct artifact paths and can save simple loopback HTTP(S) anchor downloads directly to the requested path before upstream download fallback. Browser close does not delete explicit saved files; if close reports details.artifactCleanup, use host file tools to remove paths listed in explicitArtifactPaths (when non-empty) after inspection. If close fails with details.promptGuard.reason=requested-artifacts-missing-before-close, save the exact required artifact path before closing. A bare inbound image/video path is not a requested output artifact and does not block close. For annotated screenshots inside batch, put --annotate in top-level args (for example { args: [\"--annotate\", \"batch\"], stdin: \"[[\\\"screenshot\\\",\\\"/tmp/page.png\\\"]]\" }) rather than inside the screenshot step; if annotation labels crowd a dense page, use a scoped or non-annotated screenshot plus snapshot refs instead.",
|
|
19
|
-
"When
|
|
19
|
+
"When failure output shows Next actions, prefer those exact native agent_browser follow-up payloads over guessed commands. The same actions are available in details.nextActions to callers that expose structured details; short stdin is shown inline, while long stdin stays details-only.",
|
|
20
20
|
];
|
|
21
|
-
export const WEB_SEARCH_PROMPT_GUIDELINE = "
|
|
21
|
+
export const WEB_SEARCH_PROMPT_GUIDELINE = "Prefer agent_browser_web_search for current or external web facts and URL discovery over public search-engine forms that can hit anti-bot/CAPTCHA-gated pages. For research before implementation, pass searchType: deep-lite unless webSearch.defaultSearchType already does; omit it for everyday lookups so config/auto wins. Provider rank is not proof of authority: when correctness or version matters, prefer the vendor or project's primary current docs, inspect page-date and version clues, and constrain one follow-up after discovering the official domain (Exa includeDomains; Brave site: in query). Do not count URL aliases as independent sources. Use agent_browser after you have a target URL that needs interaction, screenshots, or DOM inspection.";
|
|
22
22
|
export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
23
23
|
"Use top-level script only for one-shot loops, conditional page branches, or multi-page aggregation that would otherwise require several calls: await browser({ args, stdin?, timeoutMs? }), branch on its ok field, and emit one bounded JSON value. Script gets an isolated non-profile browser session that is always closed, cannot use caller session/namespace/lifecycle/attachment controls, inherited agent-browser launch/proxy settings, or host APIs, and is not a reusable named recipe. One top-level approval can authorize up to 25 inner calls, so inspect the full source before approval. Use args/job/qa for ordinary linear flows.",
|
|
24
24
|
"Standard workflow: open the page, snapshot -i, interact using current @refs from that snapshot, and re-snapshot after navigation, scrolling, rerendering, or other major DOM changes because refs are page-scoped; the wrapper fails mutation-prone stale/recycled refs before upstream can silently target a different current-page element. On dense pages, use wrapper-side snapshot -i --search <text> or snapshot -i --filter role=<role> to render matching refs while preserving the full ref map in details.refSnapshot, add snapshot --viewport when scroll position or above/below-fold context matters, and add snapshot --diff when a quick before/after ref-map delta would prevent reading a full spill file.",
|
|
25
25
|
"For ordinary forms from one snapshot, batch multiple fill @refs before the submit/click step to avoid serial tool calls; if a fill may autosubmit, navigate, or rerender later fields, split the flow and refresh refs first.",
|
|
26
|
-
"Do not use browser automation to drive public search-engine forms such as Google for discovery; headless jobs that type a query and press Enter can be redirected to anti-bot or CAPTCHA pages.
|
|
26
|
+
"Do not use browser automation to drive public search-engine forms such as Google for discovery; headless jobs that type a query and press Enter can be redirected to anti-bot or CAPTCHA pages. Prefer agent_browser_web_search for live discovery, then agent_browser on a target URL. Do not attempt CAPTCHA bypass.",
|
|
27
27
|
"Snapshot choice: prefer snapshot -i for routine clicks/fills (interactive @refs, main-content-first). Use snapshot --compact when you need a denser same-page tree without full spill; use full snapshot (no -i) only when you need the complete accessibility tree. Re-snapshot after navigation or major DOM changes. When snapshot -i compacts because the tree is oversized, scan visible output for Omitted high-value controls and optional details.data.highValueControlRefIds before opening the spill file: those list bounded searchboxes, textboxes, comboboxes, buttons, named action links, tabs, checkboxes, radios, options, and menuitems that did not fit the key/other ref previews.",
|
|
28
28
|
"When a visible text or accessible-name target should survive ref churn, prefer find locators such as role, text, label, placeholder, alt, title, or testid with the intended action instead of guessing a CSS selector.",
|
|
29
|
-
"For desktop or host-controlled rich inputs, if semanticAction fill misses, refresh refs and prefer a current editable @ref from details.richInputRecovery or the latest snapshot; focus or click that ref, then use keyboard
|
|
29
|
+
"For desktop or host-controlled rich inputs, if semanticAction fill misses, refresh refs and prefer a current editable @ref from details.richInputRecovery or the latest snapshot; focus or click that ref, then use keyboard type for framework-controlled editors that require real key events. keyboard inserttext is paste-like and can change a DOM value without updating application state, so use it only when later application-state evidence proves the edit was accepted. Do not auto-submit with Enter or a submit button unless the user flow explicitly calls for it.",
|
|
30
30
|
"Do not assume Playwright selector dialects such as text=Close or button:has-text('Close') are supported wrapper syntax unless current upstream agent-browser behavior has been verified.",
|
|
31
31
|
"For authenticated or user-specific content explicitly requested by the user, such as feeds, inboxes, account pages, or private dashboards, use a real profile only when the user/config asks for it or profiles have been inspected; do not assume --profile Default exists on every machine. Do not use a real profile for public pages just because they are dashboards. Treat visible page content from real profiles as model-visible transcript data. On macOS, copied Chrome profiles may omit encrypted cookies, so profile selection alone is not proof of authentication; verify the target page and use a user-approved headed login once when needed. Use --auto-connect only if profile-based reuse is unavailable or the task is specifically about attaching to a running debug-enabled browser. If profile/user-data-dir resolution fails, stop retrying opens, run profiles and/or doctor through agent_browser, then report what the user needs to configure.",
|
|
32
32
|
"Do not invent fixed explicit session names for routine tasks. Use the implicit session unless you truly need multiple isolated browser sessions in the same conversation.",
|
|
33
33
|
`When using launch-scoped flags (${LAUNCH_SCOPED_FLAG_LABEL}), put them on the first command for that session. If you intentionally use an explicit --session, keep using that same explicit session for follow-ups.`,
|
|
34
34
|
"Caller-owned explicit sessions are serialized per effective canonical namespace/session inside this extension while live URL checks, semantic-action snapshots, and the requested command run. For raw batches whose later content step depends on navigation, use exact batch --bail or split the calls; unsafe continue-after-navigation-failure shapes are rejected before the batch runs.",
|
|
35
|
-
"After a successful `connect`, `--cdp`, or enabled `--auto-connect` call, verify with get url and keep using the resulting session without repeating the attach flag. The wrapper remembers that attachment across active-branch reload/resume
|
|
35
|
+
"After a successful `connect`, `--cdp`, or enabled `--auto-connect` call, verify with get url and keep using the resulting session without repeating the attach flag. The wrapper remembers that attachment across active-branch reload/resume and live-checks the URL before later page reads/interactions because an attached browser can drift externally; caller config, file access, launch arguments, and environment pass through unchanged. A successful close clears the marker. When several named sessions share one Chrome, pass --pin-tab once (AGENT_BROWSER_PIN_TAB) so a closed bound tab fails as tab_gone instead of acting on a neighbor; recover with tab new or tab list. --no-pin-tab turns the sticky pin off. tab list includes each tab's CDP targetId, accepted as a tab ref.",
|
|
36
36
|
`If you already used the implicit session and now need launch-scoped flags (${LAUNCH_SCOPED_FLAG_LABEL}), retry with top-level sessionMode set to fresh or pass an explicit --session for the new launch; never pass --session-mode inside args. After a successful unnamed fresh launch, later auto calls follow that new session.`,
|
|
37
37
|
"For WebGPU pages, use args [\"--webgpu\", \"open\", \"<url>\"] on a fresh local browser launch; use doctor --webgpu (or --headed on Linux/Windows capture paths) to prove rendering before trusting a non-black screenshot. WebGPU cannot be combined with --cdp, --auto-connect, or provider launches unless --webgpu false overrides an enabled config/environment default.",
|
|
38
|
+
"For experimental WebMCP page tools, use webmcp list, then webmcp invoke <tool> with --params and optional --frame/--detach/--timeout; use webmcp result or cancel for detached calls. Locally managed Chrome enables WebMCP by default. --no-webmcp is launch-scoped and requires a fresh session; invoke/result/cancel can mutate or navigate, so refresh snapshot refs afterward.",
|
|
38
39
|
"For --allowed-domains, use a fresh local Chrome context. Upstream rejects CDP/auto-connect, profiles, restore/state replay, direct-page providers, iOS/Safari, and startup/profile Chrome args because they cannot guarantee containment; Chromium also disables RTCPeerConnection while the allowlist is active.",
|
|
39
40
|
"For React introspection, launch the page with --enable react-devtools before first navigation, then use react tree, react inspect <fiberId>, sourceLookup candidates for local UI source hints, react renders start/stop, or react suspense; sourceLookup is experimental and reports confidence/evidence instead of guaranteed DOM-to-file mappings. For failed fetches and APIs, networkSourceLookup (experimental) correlates failed network requests with initiator metadata and bounded workspace URL literals—candidates only, not definitive blame. Use vitals [url] for Core Web Vitals and hydration timing, and pushstate <url> for client-side SPA navigation.",
|
|
40
41
|
"For first-navigation setup, use open without a URL plus network route --resource-type <csv>, cookies set --curl <file>, or --init-script/--enable before navigate/opening the target page.",
|
|
41
|
-
"For stateful browser context work,
|
|
42
|
+
"For stateful browser context work, use auth save --password-stdin with the tool stdin field for credentials, auth list/show/delete/remove for local auth-profile maintenance, auth login when you need the browser to fill a saved profile, and state save/load/list/show/rename/clear/clean for upstream saved-state lifecycle. State paths, restore identifiers, wrapper-prefixed sessions, and all upstream list rows remain available; credential values inside cookie/storage/auth payloads are still redacted from presentation.",
|
|
42
43
|
"Upstream restore sessions periodically autosave cookies and localStorage while the browser stays open, including page-driven background changes; AGENT_BROWSER_AUTOSAVE_INTERVAL_MS controls the interval (30000 by default; 0 disables periodic saves but keeps save-on-close), while the never value for --restore-save disables automatic saves for that restore session. For wrapper-owned headed launches, the wrapper defaults the interval to 0 because upstream 0.33.2 collects multi-origin storage through visible temporary tabs, then records and reapplies the effective launch-time value to helpers and follow-ups so daemon configuration remains stable. Native close still saves, but direct window close can lose newer state because upstream exempts headed browsers from idle shutdown; set AGENT_BROWSER_AUTOSAVE_INTERVAL_MS before launch when periodic preservation matters, because changing it on a running wrapper-owned headed session requires close plus a fresh launch.",
|
|
43
44
|
"For batch chains that touch cookies, storage, auth, or other secret-bearing commands, use details.batchSteps for per-step artifacts, categories, spill paths, and full structured errors; top-level details.data on batch is only a compact redacted step matrix (success, argv-redacted command, redacted result or scrubbed error text) built from the same presentation rules as standalone calls.",
|
|
44
45
|
"For non-core families, pass current upstream commands through the native tool directly: network requests, network route <url>, network har start/stop [path], diff snapshot, diff screenshot --baseline <file>, diff url <u1> <u2>, trace start, trace stop [path], profiler start, profiler stop [path], record start <path>, record stop, console/errors [--clear], highlight <selector>, inspect, clipboard read, clipboard write <text>, clipboard copy/paste, stream enable/disable/status, dashboard start/stop, device list for iOS simulator inventory, and chat <message>. For compact network requests output, prefer details.nextActions for request detail, route-mock diagnostics, actionable failed-request networkSourceLookup, filtering, clearing the aggregate buffer before repro, or HAR capture follow-ups instead of guessing request-id syntax. Artifact-producing commands report details.artifacts and verification state; long-running starts such as stream, dashboard, trace/profiler, and record should be paired with the matching stop/disable command when the task is done; stream enable already-enabled outcomes are treated as idempotent success with status/disable follow-ups.",
|
|
@@ -47,11 +48,11 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
47
48
|
"For dialogs and frames, use dialog status/accept/dismiss and frame <selector|main> through native args; dialog commands and eval snippets that look like alert/confirm/prompt/dialog triggers are shorter-bounded than normal browser calls, and timed-out dialog-like interactions may add inspect-dialog-after-timeout, dismiss-dialog-after-timeout, or recover-fresh-session-after-dialog-timeout nextActions. When --confirm-actions produces a pending confirmation, use details.nextActions or exact confirm <id> / deny <id> calls instead of inventing ids.",
|
|
48
49
|
"If a session lands on the wrong page or tab, an interaction changes origin unexpectedly, or an open call returns blocked, blank, or otherwise unexpected results, use tab list / tab <tab-id-or-label> / snapshot -i to recover state before retrying different URLs or fallback strategies. For headed demos, put --headed on the first launch with sessionMode=fresh and verify with screenshot/tab/get-url evidence because tool success cannot prove the OS window is visible to the user. For desktop readiness, prefer real conditions first: wait --text, wait --url, wait --fn, wait --load <state>, wait --download, or qa.attached; for disappearance checks, use wait --fn predicates instead of stale upstream-help examples like wait <selector> --state hidden. Use electron.probe/status for wrapper-owned launch health or target mismatch. Fixed waits are a last resort: use explicit --timeout or top-level timeoutMs for legitimately slow waits, and treat a successful payload like \"waited\":\"timeout\" as elapsed time only—verify completion with an observed condition, fresh snapshot, or screenshot.",
|
|
49
50
|
"For feed, timeline, or inbox reading tasks, focus on the main timeline/list region and read the first item there rather than unrelated composer or sidebar content.",
|
|
50
|
-
"For read-only browsing tasks, use read <url> for documentation or other unstructured text without
|
|
51
|
+
"For read-only browsing tasks, use read <url> for documentation or other unstructured text without requiring a Chrome page, or read with no URL for rendered active-tab DOM. Prefer the current snapshot, structured ref labels, getters, or scoped eval --stdin when you need interactive structure or targeted page state. Only click into media viewers, detail routes, or new pages when the current view does not contain the needed information.",
|
|
51
52
|
"For downloads, prefer download <selector> <path> when an element click should save a file; simple loopback anchor downloads are saved to the requested path when the wrapper can resolve an HTTP(S) href. Do not rely on click alone when you need the downloaded file on disk.",
|
|
52
53
|
"On dashboards with nested scroll containers, verify scroll with a screenshot or fresh snapshot -i; if the viewport did not move, details.data.scrolled may be false/noMovement true and you should prefer scrollintoview <@ref> or target the actual scrollable region with scroll <selector> <dir> [px|percent]. For native selects, use select <selector> <value...> (or semanticAction/job select) instead of clicking option refs; for custom comboboxes, a click/semanticAction may only focus the field, so re-snapshot and fall back to type, press Enter/arrow keys, or visible option refs.",
|
|
53
54
|
"When using eval --stdin, scope checks and actions to the target element or route whenever possible instead of relying on broad page-wide text heuristics.",
|
|
54
|
-
"When using eval --stdin for extraction, pass the JavaScript through the native tool stdin field, not as an extra args token after --stdin, and return the value you want instead of relying on console.log as the primary result channel. Prefer plain expressions like ({ title: document.title }) or explicitly invoked functions like (() => ({ title: document.title }))(); use outputPath when the eval/get/snapshot data should be saved as a durable local file, but never reuse a screenshot, download, recording, or other browser artifact destination as outputPath. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called.
|
|
55
|
+
"When using eval --stdin for extraction, pass the JavaScript through the native tool stdin field, not as an extra args token after --stdin, and return the value you want instead of relying on console.log as the primary result channel. Prefer plain expressions like ({ title: document.title }) or explicitly invoked functions like (() => ({ title: document.title }))(); use outputPath when the eval/get/snapshot data should be saved as a durable local file, but never reuse a screenshot, download, recording, or other browser artifact destination as outputPath. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called. Local file pages and caller-selected output paths are supported when upstream allows them. If get text on a broad CSS selector surfaces details.selectorTextVisibility or selectorTextVisibilityAll, prefer a visible @ref, a more specific selector, or the inspect-visible-text-candidates nextAction over hidden tab content.",
|
|
55
56
|
"When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.",
|
|
56
57
|
"When commands save or spill files (screenshots, downloads, PDFs, traces, recordings, HAR, large snapshot spills), use the user's exact requested paths when given and treat paths as provisional until details.artifactVerification shows every row verified: branch on missingCount, pendingCount, unverifiedCount, per-entry state, and optional limitation before downstream file use or PASS/FAIL reporting.",
|
|
57
58
|
"For evidence-only screenshots, QA captures, or other audit artifacts, save to an explicit path and branch on details.artifactVerification plus details.artifacts before reporting PASS/FAIL; do not require vision review of inline image attachments unless the user asked for visual inspection.",
|
|
@@ -83,7 +84,7 @@ export const RUNTIME_PROMPT_GUIDELINES = [
|
|
|
83
84
|
"For agent_browser, use open → snapshot -i → @refs; re-snapshot after changes. In authenticated unattended/auto-approved employee flows, ordinary requested non-destructive submissions may proceed. Honor explicit stops; require explicit authorization for purchases, production-control, destructive/irreversible, or account/security/privacy changes.",
|
|
84
85
|
"Use agent_browser sessionMode=fresh for launch flags. Use requested/configured profiles only; run profiles/doctor on failure. --allowed-domains cannot restore; macOS profile copies may omit encrypted cookies. Verify auth; use a user-approved headed login if needed. Profile content is model-visible.",
|
|
85
86
|
"agent_browser: exact user paths; verify artifactVerification/artifacts before success claims. Save promptGuard-required files before close; record stop needs ffmpeg; close keeps files; waited:timeout proves nothing.",
|
|
86
|
-
"When agent_browser details.nextActions exists, use
|
|
87
|
+
"When agent_browser details.nextActions exists, use them. Check Omitted high-value controls in dense snapshots. Dashboards: verify scroll via screenshot/snapshot.",
|
|
87
88
|
"agent_browser: read <url> for docs/text or active DOM; get title/url; get text/html/value/count <selector>; get attr <selector> <name>. Batch 3+ getters; heed visibility warnings.",
|
|
88
89
|
];
|
|
89
90
|
export function buildBrowserExecutablePathGuideline(executablePath) {
|
|
@@ -103,7 +104,7 @@ export function buildToolPromptGuidelines(options) {
|
|
|
103
104
|
const browserDefaultProfileGuideline = buildBrowserDefaultProfileGuideline(options.browserDefaultProfile);
|
|
104
105
|
const browserExecutablePathGuideline = buildBrowserExecutablePathGuideline(options.browserExecutablePath);
|
|
105
106
|
return [
|
|
106
|
-
...TOOL_PROMPT_GUIDELINES_PREFIX,
|
|
107
|
+
...(options.includeWebSearch ? ["Prefer agent_browser_web_search for facts; agent_browser for pages."] : TOOL_PROMPT_GUIDELINES_PREFIX),
|
|
107
108
|
...(options.docs ? [buildInstalledDocsGuideline(options.docs)] : []),
|
|
108
109
|
...RUNTIME_PROMPT_GUIDELINES,
|
|
109
110
|
...(browserExecutablePathGuideline ? [browserExecutablePathGuideline] : []),
|
|
@@ -31,10 +31,18 @@ export function buildProcessStartIdentityCommands(pid, platform = process.platfo
|
|
|
31
31
|
return [];
|
|
32
32
|
return platform === "win32"
|
|
33
33
|
? [primary]
|
|
34
|
-
: [
|
|
34
|
+
: [
|
|
35
|
+
primary,
|
|
36
|
+
...(platform === "android"
|
|
37
|
+
? [{ ...primary, file: "/bin/ps" }, { ...primary, file: "/usr/bin/ps" }]
|
|
38
|
+
: [{ ...primary, file: "/usr/bin/ps" }, { ...primary, file: "ps" }]),
|
|
39
|
+
];
|
|
35
40
|
}
|
|
36
41
|
export function normalizeProcessStartIdentity(stdout) {
|
|
37
|
-
|
|
42
|
+
const trimmed = stdout.trim();
|
|
43
|
+
if (!trimmed || trimmed.includes("\0") || /[\r\n]/.test(trimmed))
|
|
44
|
+
return undefined;
|
|
45
|
+
return trimmed.replace(/\s+/g, " ");
|
|
38
46
|
}
|
|
39
47
|
let currentProcessStartIdentityPromise;
|
|
40
48
|
async function executeProcessStartIdentityCommand(command) {
|
|
@@ -65,13 +73,6 @@ export async function readProcessStartIdentity(pid, platform = process.platform)
|
|
|
65
73
|
});
|
|
66
74
|
return await currentProcessStartIdentityPromise;
|
|
67
75
|
}
|
|
68
|
-
|
|
69
|
-
export function processStartIdentitiesMatch(recorded, current, platform = process.platform) {
|
|
70
|
-
if (platform === "win32") {
|
|
71
|
-
const recordedIsCurrentFormat = recorded.startsWith(WINDOWS_PROCESS_START_IDENTITY_PREFIX);
|
|
72
|
-
const currentIsCurrentFormat = current.startsWith(WINDOWS_PROCESS_START_IDENTITY_PREFIX);
|
|
73
|
-
if (recordedIsCurrentFormat !== currentIsCurrentFormat)
|
|
74
|
-
return undefined;
|
|
75
|
-
}
|
|
76
|
+
export function processStartIdentitiesMatch(recorded, current) {
|
|
76
77
|
return recorded === current;
|
|
77
78
|
}
|