pi-agent-browser-native 0.3.0 → 0.6.5
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 +265 -0
- package/README.md +130 -54
- package/dist/extensions/agent-browser/index.js +781 -169
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +35 -3
- package/dist/extensions/agent-browser/lib/argv-grammar.js +50 -2
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +71 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +5 -8
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +53 -12
- 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 +61 -13
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +2 -2
- package/dist/extensions/agent-browser/lib/input-modes/params.js +23 -24
- package/dist/extensions/agent-browser/lib/input-modes/script.js +462 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +51 -12
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +26 -4
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +6 -139
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +26 -116
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +2 -4
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +54 -25
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +26 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +110 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +54 -48
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +71 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +6 -7
- 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 +152 -64
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +244 -102
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +63 -37
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +20 -21
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +36 -18
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +41 -21
- package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +299 -0
- package/dist/extensions/agent-browser/lib/page-target-validation.js +270 -0
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +32 -10
- package/dist/extensions/agent-browser/lib/playbook.js +29 -25
- package/dist/extensions/agent-browser/lib/process-environment.js +14 -0
- package/dist/extensions/agent-browser/lib/process-identity.js +5 -12
- package/dist/extensions/agent-browser/lib/process.js +130 -104
- package/dist/extensions/agent-browser/lib/recording-reservations.js +116 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +63 -6
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +62 -4
- package/dist/extensions/agent-browser/lib/results/categories.js +6 -1
- package/dist/extensions/agent-browser/lib/results/next-actions.js +19 -5
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +85 -38
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +86 -18
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +38 -2
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +18 -17
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +38 -20
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +60 -15
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +1 -10
- package/dist/extensions/agent-browser/lib/results/presentation.js +36 -6
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +3 -1
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +9 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +54 -11
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +13 -7
- 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 +186 -108
- package/dist/extensions/agent-browser/lib/session-page-state.js +71 -10
- package/dist/extensions/agent-browser/lib/temp.js +1 -2
- package/dist/extensions/agent-browser/lib/upstream-version.js +14 -0
- package/dist/extensions/agent-browser/lib/web-search.js +108 -24
- package/dist/extensions/agent-browser/script-worker.js +169 -0
- package/dist/scripts/agent-browser-target.mjs +21 -0
- package/docs/ARCHITECTURE.md +57 -34
- package/docs/COMMAND_REFERENCE.md +255 -68
- package/docs/ELECTRON.md +2 -2
- package/docs/RELEASE.md +12 -10
- package/docs/REQUIREMENTS.md +11 -8
- package/docs/SUPPORT_MATRIX.md +36 -24
- package/docs/TOOL_CONTRACT.md +169 -95
- package/package.json +3 -1
- package/platform-smoke.config.mjs +2 -2
- package/scripts/agent-browser-capability-baseline.mjs +87 -9
- package/scripts/agent-browser-target.mjs +21 -0
- package/scripts/build.mjs +41 -0
- package/scripts/config.mjs +1 -0
- package/scripts/doctor.mjs +16 -9
- package/scripts/platform-smoke/browser-dogfood-windows.ps1 +9 -3
- package/scripts/platform-smoke/targets.mjs +12 -6
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -20
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +0 -583
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { copyFile, mkdir } from "node:fs/promises";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
|
-
import { getBooleanFlagValue, isUpstreamEnvFlagEnabled } from "../../argv-grammar.js";
|
|
3
|
+
import { getBooleanFlagValue, isUpstreamEnvFlagEnabled, projectUpstreamGlobalFlags } from "../../argv-grammar.js";
|
|
4
4
|
import { isCloseCommand } from "../../command-taxonomy.js";
|
|
5
5
|
import { cleanupElectronLaunchResources } from "../../electron/cleanup.js";
|
|
6
6
|
import { launchElectronApp } from "../../electron/launch.js";
|
|
@@ -17,15 +17,16 @@ import { applyNamespaceToNextActions } from "../../results/next-actions.js";
|
|
|
17
17
|
import { buildSessionAwareStaleRefNextActions, buildSessionTabRecoveryNextActions } from "../../results/recovery-next-actions.js";
|
|
18
18
|
import { resolveVisibleRefActionFromSnapshot } from "../../results/selector-recovery.js";
|
|
19
19
|
import { extractRefSnapshotFromData } from "../../session-page-state.js";
|
|
20
|
-
import { buildExecutionPlan, createFreshSessionName, extractCommandTokens, getDefaultHeadlessCompatUserAgent, redactInvocationArgs, } from "../../runtime.js";
|
|
21
|
-
import { buildOwnedManagedSessionRestoreContext,
|
|
22
|
-
import {
|
|
20
|
+
import { buildExecutionPlan, canUseHeadlessCompatibilityUserAgent, createFreshSessionName, extractCommandTokens, extractUpstreamCommandTokens, getDefaultHeadlessCompatUserAgent, parseWaitCommandTokens, redactInvocationArgs, } from "../../runtime.js";
|
|
21
|
+
import { buildOwnedManagedSessionRestoreContext, resolveExplicitAutosaveInterval, withOwnedManagedSessionContext, } from "../../managed-session-restore.js";
|
|
22
|
+
import { getAgentBrowserProcessEnvironment } from "../../process-environment.js";
|
|
23
|
+
import { getExplicitSessionPageVerificationRequirement, getPageTargetValidationError, } from "../../page-target-validation.js";
|
|
23
24
|
import { acquireOwnedManagedSessionDaemonPolicy, getRunningHeadedAutosavePolicyChangeError } from "./managed-session-daemon-policy.js";
|
|
24
25
|
import { applyOpenResultTabCorrection, buildManagedSessionOutcome, buildPinnedBatchPlan, buildSessionDetailFields, buildStaleRefPreflight, getSessionContextKey, extractStringResultField, collectAnySessionTabSelection, collectSessionTabSelection, getGuardedRefUsage, getTraceOwnerGuardMessage, runSessionCommandData, shouldPinSessionTabForCommand, } from "./session-state.js";
|
|
25
|
-
import { parseBatchStdinJsonArray } from "../batch-stdin.js";
|
|
26
|
-
import { buildElectronHostFailureResult, getElectronLaunchFailureCategory, redactRecoveryHint } from "./final-result.js";
|
|
26
|
+
import { getUpstreamEffectiveBatchSteps, parseBatchStdinJsonArray } from "../batch-stdin.js";
|
|
27
|
+
import { buildElectronHostFailureResult, formatAgentBrowserNextActionsText, getElectronLaunchFailureCategory, redactRecoveryHint } from "./final-result.js";
|
|
27
28
|
import { prepareClickDispatchProbe } from "./click-dispatch.js";
|
|
28
|
-
import { collectScrollPositionSnapshot, validateQaAttachedPrecondition } from "./diagnostics.js";
|
|
29
|
+
import { buildUnsupportedScrollIntoViewRecovery, collectScrollPositionSnapshot, validateQaAttachedPrecondition } from "./diagnostics.js";
|
|
29
30
|
import { getScreenshotPathTokenIndex } from "./artifact-paths.js";
|
|
30
31
|
import { findRequestedArtifactCloseViolation } from "./prompt-guards.js";
|
|
31
32
|
export function normalizeRunInput(input) {
|
|
@@ -43,6 +44,7 @@ export function normalizeRunInput(input) {
|
|
|
43
44
|
return { ...base, compiledSemanticAction: input.compiledSemanticAction, redactedCompiledSemanticAction: input.redactedCompiledSemanticAction };
|
|
44
45
|
case "sourceLookup":
|
|
45
46
|
return { ...base, compiledSourceLookup: input.compiledSourceLookup, redactedCompiledSourceLookup: input.redactedCompiledSourceLookup };
|
|
47
|
+
case "script":
|
|
46
48
|
case "args":
|
|
47
49
|
return base;
|
|
48
50
|
}
|
|
@@ -58,12 +60,8 @@ function getArtifactParentPathTokenIndex(commandTokens) {
|
|
|
58
60
|
return 1;
|
|
59
61
|
if (commandTokens[0] === "state" && commandTokens[1] === "save" && commandTokens.length >= 3)
|
|
60
62
|
return 2;
|
|
61
|
-
if (commandTokens[0] === "wait")
|
|
62
|
-
|
|
63
|
-
const pathIndex = downloadIndex >= 0 ? downloadIndex + 1 : -1;
|
|
64
|
-
if (pathIndex > 0 && typeof commandTokens[pathIndex] === "string" && !commandTokens[pathIndex].startsWith("-"))
|
|
65
|
-
return pathIndex;
|
|
66
|
-
}
|
|
63
|
+
if (commandTokens[0] === "wait")
|
|
64
|
+
return parseWaitCommandTokens(commandTokens).downloadPathIndex;
|
|
67
65
|
return undefined;
|
|
68
66
|
}
|
|
69
67
|
async function ensureArtifactParentDirectory(commandTokens, cwd) {
|
|
@@ -76,10 +74,14 @@ async function ensureArtifactParentDirectory(commandTokens, cwd) {
|
|
|
76
74
|
await mkdir(dirname(resolve(cwd, requestedPath)), { recursive: true });
|
|
77
75
|
}
|
|
78
76
|
async function normalizeScreenshotPathInTokens(commandTokens, cwd) {
|
|
79
|
-
const
|
|
80
|
-
|
|
77
|
+
const scopedCommandTokens = extractCommandTokens(commandTokens);
|
|
78
|
+
const projection = projectUpstreamGlobalFlags(scopedCommandTokens);
|
|
79
|
+
const projectedPathTokenIndex = getScreenshotPathTokenIndex(projection.tokens);
|
|
80
|
+
const scopedPathTokenIndex = projectedPathTokenIndex === undefined ? undefined : projection.indices[projectedPathTokenIndex];
|
|
81
|
+
if (scopedPathTokenIndex === undefined) {
|
|
81
82
|
return { tokens: commandTokens };
|
|
82
83
|
}
|
|
84
|
+
const screenshotPathTokenIndex = commandTokens.length - scopedCommandTokens.length + scopedPathTokenIndex;
|
|
83
85
|
const requestedPath = commandTokens[screenshotPathTokenIndex];
|
|
84
86
|
const absolutePath = resolve(cwd, requestedPath);
|
|
85
87
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
@@ -98,8 +100,28 @@ async function normalizeScreenshotPathInTokens(commandTokens, cwd) {
|
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
102
|
async function prepareBatchScreenshotPaths(args, stdin, cwd) {
|
|
101
|
-
const commandTokens =
|
|
102
|
-
if (commandTokens[0] !== "batch"
|
|
103
|
+
const commandTokens = extractUpstreamCommandTokens(args);
|
|
104
|
+
if (commandTokens[0] !== "batch") {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const argumentSteps = getUpstreamEffectiveBatchSteps(commandTokens, undefined);
|
|
108
|
+
if (argumentSteps.length > 0) {
|
|
109
|
+
// Upstream executes raw argument steps exclusively and ignores stdin, so
|
|
110
|
+
// prepare parent directories for the rows that will run and skip stdin
|
|
111
|
+
// preparation (no directories for never-executed rows).
|
|
112
|
+
for (const step of argumentSteps) {
|
|
113
|
+
const stepTokens = extractUpstreamCommandTokens(step);
|
|
114
|
+
await ensureArtifactParentDirectory(stepTokens, cwd);
|
|
115
|
+
if (stepTokens[0] === "screenshot") {
|
|
116
|
+
// Reuse the screenshot path resolution for its parent-directory side
|
|
117
|
+
// effect only: raw strings are never rewritten, so the normalized
|
|
118
|
+
// tokens and path request are deliberately discarded.
|
|
119
|
+
await normalizeScreenshotPathInTokens(step, cwd);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
if (stdin === undefined) {
|
|
103
125
|
return undefined;
|
|
104
126
|
}
|
|
105
127
|
const parsed = parseBatchStdinJsonArray(stdin);
|
|
@@ -112,8 +134,9 @@ async function prepareBatchScreenshotPaths(args, stdin, cwd) {
|
|
|
112
134
|
if (!Array.isArray(step) || !step.every((item) => typeof item === "string")) {
|
|
113
135
|
return step;
|
|
114
136
|
}
|
|
115
|
-
|
|
116
|
-
|
|
137
|
+
const upstreamStep = extractUpstreamCommandTokens(step);
|
|
138
|
+
await ensureArtifactParentDirectory(upstreamStep, cwd);
|
|
139
|
+
if (upstreamStep[0] !== "screenshot") {
|
|
117
140
|
return step;
|
|
118
141
|
}
|
|
119
142
|
const normalized = await normalizeScreenshotPathInTokens(step, cwd);
|
|
@@ -137,7 +160,7 @@ export async function prepareAgentBrowserArgs(args, stdin, cwd) {
|
|
|
137
160
|
return preparedBatch;
|
|
138
161
|
}
|
|
139
162
|
const commandTokens = extractCommandTokens(args);
|
|
140
|
-
await ensureArtifactParentDirectory(
|
|
163
|
+
await ensureArtifactParentDirectory(extractUpstreamCommandTokens(args), cwd);
|
|
141
164
|
const normalized = await normalizeScreenshotPathInTokens(commandTokens, cwd);
|
|
142
165
|
if (!normalized.request) {
|
|
143
166
|
return { args };
|
|
@@ -181,7 +204,7 @@ const LIKELY_DIALOG_TRIGGER_PROCESS_TIMEOUT_MS = 8_000;
|
|
|
181
204
|
const LIKELY_DIALOG_TRIGGER_PROCESS_TIMEOUT_ENV = "PI_AGENT_BROWSER_DIALOG_TRIGGER_PROCESS_TIMEOUT_MS";
|
|
182
205
|
const DIALOG_TRIGGER_TEXT_PATTERN = /\b(?:alert|confirm|dialog|prompt)\b/i;
|
|
183
206
|
function getPositiveIntegerEnv(name) {
|
|
184
|
-
const value =
|
|
207
|
+
const value = getAgentBrowserProcessEnvironment()[name];
|
|
185
208
|
if (!value || !/^\d+$/.test(value.trim()))
|
|
186
209
|
return undefined;
|
|
187
210
|
const parsed = Number(value.trim());
|
|
@@ -215,9 +238,7 @@ function describeRef(refSnapshot, refId) {
|
|
|
215
238
|
return ref ? `${ref.role} ${JSON.stringify(ref.name)}` : "not present";
|
|
216
239
|
}
|
|
217
240
|
function getSamePageFreshnessPreflightFailure(options) {
|
|
218
|
-
|
|
219
|
-
return undefined;
|
|
220
|
-
const refIds = getRefIdsFromDirectCommand(options.commandTokens);
|
|
241
|
+
const { refIds } = options;
|
|
221
242
|
if (refIds.length === 0)
|
|
222
243
|
return undefined;
|
|
223
244
|
const previousUrl = options.previousSnapshot.target?.url;
|
|
@@ -243,7 +264,8 @@ function getSamePageFreshnessPreflightFailure(options) {
|
|
|
243
264
|
};
|
|
244
265
|
}
|
|
245
266
|
async function collectSamePageRefFreshnessPreflight(options) {
|
|
246
|
-
|
|
267
|
+
const refIds = [...new Set(getGuardedRefUsage(options.commandTokens, options.stdin))];
|
|
268
|
+
if (!options.previousSnapshot || !options.sessionName || refIds.length === 0)
|
|
247
269
|
return undefined;
|
|
248
270
|
const previousUrl = options.previousSnapshot.target?.url;
|
|
249
271
|
const currentTargetUrl = options.currentTarget?.url;
|
|
@@ -254,17 +276,16 @@ async function collectSamePageRefFreshnessPreflight(options) {
|
|
|
254
276
|
if (!currentSnapshot)
|
|
255
277
|
return undefined;
|
|
256
278
|
const snapshotWithTarget = { ...currentSnapshot, target: currentSnapshot.target ?? options.currentTarget };
|
|
257
|
-
const mismatch = getSamePageFreshnessPreflightFailure({
|
|
279
|
+
const mismatch = getSamePageFreshnessPreflightFailure({ currentSnapshot: snapshotWithTarget, previousSnapshot: options.previousSnapshot, refIds });
|
|
258
280
|
if (!mismatch)
|
|
259
281
|
return undefined;
|
|
260
282
|
return { message: mismatch.message, refIds: mismatch.refIds, snapshot: snapshotWithTarget };
|
|
261
283
|
}
|
|
262
284
|
function getIdleTimeoutMismatch(args, configuredValue) {
|
|
263
285
|
for (let index = 0; index < args.length; index += 1) {
|
|
264
|
-
|
|
265
|
-
if (token !== "--idle-timeout" && !token.startsWith("--idle-timeout="))
|
|
286
|
+
if (args[index] !== "--idle-timeout")
|
|
266
287
|
continue;
|
|
267
|
-
const requestedToken =
|
|
288
|
+
const requestedToken = args[++index];
|
|
268
289
|
if (!requestedToken || !/^\d+$/.test(requestedToken) || Number(requestedToken) === Number(configuredValue))
|
|
269
290
|
continue;
|
|
270
291
|
return `--idle-timeout ${requestedToken} conflicts with this Pi process's managed-session idle timeout (${configuredValue} ms). Restart Pi with PI_AGENT_BROWSER_IMPLICIT_SESSION_IDLE_TIMEOUT_MS=${requestedToken} and omit --idle-timeout; changing the launch value for one call can restart the upstream browser and discard the active tab.`;
|
|
@@ -297,7 +318,14 @@ export function validateStdinCommandContract(options) {
|
|
|
297
318
|
return `agent_browser stdin is only supported for \`batch\`, \`eval --stdin\`, and \`auth save --password-stdin\`; remove stdin from ${commandLabel} or use one of those command forms.`;
|
|
298
319
|
}
|
|
299
320
|
function canResolveSemanticVisibleRef(compiled) {
|
|
300
|
-
|
|
321
|
+
if (!compiled?.locator)
|
|
322
|
+
return false;
|
|
323
|
+
if (compiled.action === "select")
|
|
324
|
+
return true;
|
|
325
|
+
return compiled.locator === "role" && ["check", "click", "fill"].includes(compiled.action);
|
|
326
|
+
}
|
|
327
|
+
function requiresResolvedSemanticVisibleRef(compiled) {
|
|
328
|
+
return compiled?.action === "select" && compiled.locator !== undefined;
|
|
301
329
|
}
|
|
302
330
|
function resolveSemanticActionVisibleRefArgsFromSnapshot(compiled, snapshotData) {
|
|
303
331
|
if (!canResolveSemanticVisibleRef(compiled))
|
|
@@ -316,6 +344,7 @@ export async function resolveSemanticActionVisibleRefArgs(options) {
|
|
|
316
344
|
export async function prepareBrowserRun(options) {
|
|
317
345
|
const { cwd, onUpdate, params, signal, state } = options;
|
|
318
346
|
const { sessionPageState, traceOwners, managedSessionBaseName, ephemeralSessionSeed } = state;
|
|
347
|
+
const agentBrowserProcessEnv = getAgentBrowserProcessEnvironment();
|
|
319
348
|
let freshSessionOrdinal = state.freshSessionOrdinal;
|
|
320
349
|
const { compiledElectron, compiledJob, compiledNetworkSourceLookup, compiledQaPreset, compiledSemanticAction, compiledSourceLookup, redactedArgs, redactedCompiledElectron, redactedCompiledJob, redactedCompiledNetworkSourceLookup, redactedCompiledQaPreset, redactedCompiledSemanticAction, redactedCompiledSourceLookup, toolArgs, toolStdin, } = normalizeRunInput(options.input);
|
|
321
350
|
let runtimeToolArgs = toolArgs;
|
|
@@ -323,21 +352,20 @@ export async function prepareBrowserRun(options) {
|
|
|
323
352
|
let electronLaunch;
|
|
324
353
|
const sessionMode = compiledElectron?.action === "launch" ? "fresh" : params.sessionMode ?? "auto";
|
|
325
354
|
const freshSessionName = createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal + 1);
|
|
326
|
-
const
|
|
355
|
+
const rawPageTargetError = getPageTargetValidationError({
|
|
327
356
|
args: runtimeToolArgs,
|
|
328
|
-
cwd,
|
|
329
357
|
stdin: runtimeToolStdin,
|
|
330
358
|
trustedFirstBatchTabSelection: true,
|
|
331
359
|
});
|
|
332
|
-
if (
|
|
360
|
+
if (rawPageTargetError) {
|
|
333
361
|
return {
|
|
334
362
|
kind: "early-result",
|
|
335
363
|
result: {
|
|
336
|
-
content: [{ type: "text", text:
|
|
364
|
+
content: [{ type: "text", text: rawPageTargetError }],
|
|
337
365
|
details: {
|
|
338
366
|
args: redactedArgs,
|
|
339
|
-
...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, errorText:
|
|
340
|
-
validationError:
|
|
367
|
+
...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, errorText: rawPageTargetError, succeeded: false, validationError: rawPageTargetError }),
|
|
368
|
+
validationError: rawPageTargetError,
|
|
341
369
|
},
|
|
342
370
|
isError: true,
|
|
343
371
|
},
|
|
@@ -388,30 +416,53 @@ export async function prepareBrowserRun(options) {
|
|
|
388
416
|
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: idleTimeoutMismatch };
|
|
389
417
|
const ownedSessionKey = getSessionContextKey(executionPlan.sessionName, executionPlan.namespace);
|
|
390
418
|
const plannedSessionPageState = sessionPageState.get(ownedSessionKey);
|
|
391
|
-
const
|
|
419
|
+
const pageTargetError = getPageTargetValidationError({
|
|
392
420
|
args: executionPlan.effectiveArgs,
|
|
393
421
|
currentPageUrl: plannedSessionPageState.tabTarget?.url,
|
|
394
422
|
pageUrlUnknown: plannedSessionPageState.tabTargetUnknown === true,
|
|
395
|
-
cwd,
|
|
396
423
|
stdin: runtimeToolStdin,
|
|
397
424
|
});
|
|
398
|
-
if (!executionPlan.validationError &&
|
|
399
|
-
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError:
|
|
425
|
+
if (!executionPlan.validationError && pageTargetError)
|
|
426
|
+
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: pageTargetError };
|
|
400
427
|
const recordedOwnedSession = ownedSessionKey ? state.ownedManagedSessions.get(ownedSessionKey) : undefined;
|
|
401
428
|
const targetsCurrentManagedSession = state.managedSessionActive
|
|
402
429
|
&& ownedSessionKey === getSessionContextKey(state.managedSessionName, state.managedSessionNamespace);
|
|
430
|
+
const targetsOffCurrentOwnedSession = recordedOwnedSession !== undefined && !targetsCurrentManagedSession;
|
|
431
|
+
const offCurrentLaunchScopedFlags = targetsOffCurrentOwnedSession
|
|
432
|
+
? executionPlan.startupScopedFlags.filter((flag) => flag !== "--namespace")
|
|
433
|
+
: [];
|
|
434
|
+
const offCurrentCompatibilityUpgrade = targetsOffCurrentOwnedSession
|
|
435
|
+
&& executionPlan.compatibilityWorkaround !== undefined
|
|
436
|
+
&& recordedOwnedSession.compatibilityWorkaround === undefined;
|
|
437
|
+
if (targetsOffCurrentOwnedSession && canUseHeadlessCompatibilityUserAgent(preparedArgs.args, agentBrowserProcessEnv)) {
|
|
438
|
+
const compatibilityWorkaround = executionPlan.compatibilityWorkaround ?? recordedOwnedSession.compatibilityWorkaround;
|
|
439
|
+
if (compatibilityWorkaround) {
|
|
440
|
+
const userAgentIndex = executionPlan.effectiveArgs.indexOf("--user-agent");
|
|
441
|
+
executionPlan = {
|
|
442
|
+
...executionPlan,
|
|
443
|
+
compatibilityWorkaround,
|
|
444
|
+
effectiveArgs: userAgentIndex < 0
|
|
445
|
+
? executionPlan.effectiveArgs
|
|
446
|
+
: [...executionPlan.effectiveArgs.slice(0, userAgentIndex), ...executionPlan.effectiveArgs.slice(userAgentIndex + 2)],
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
}
|
|
403
450
|
const retainedHeadedAutosaveDisabled = recordedOwnedSession?.headedManagedAutosaveDisabled === true
|
|
404
451
|
|| (targetsCurrentManagedSession && state.managedSessionHeadedAutosaveDisabled === true);
|
|
405
452
|
const retainedHeadedAutosaveInterval = recordedOwnedSession?.headedManagedAutosaveInterval
|
|
406
453
|
?? (targetsCurrentManagedSession ? state.managedSessionHeadedAutosaveInterval : undefined);
|
|
407
|
-
const explicitAutosaveInterval = resolveExplicitAutosaveInterval(
|
|
454
|
+
const explicitAutosaveInterval = resolveExplicitAutosaveInterval(agentBrowserProcessEnv.AGENT_BROWSER_AUTOSAVE_INTERVAL_MS);
|
|
408
455
|
const autosavePolicyChangeError = getRunningHeadedAutosavePolicyChangeError(retainedHeadedAutosaveInterval, isCloseCommand(executionPlan.commandInfo.command));
|
|
409
456
|
if (!executionPlan.validationError && autosavePolicyChangeError) {
|
|
410
457
|
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: autosavePolicyChangeError };
|
|
411
458
|
}
|
|
412
|
-
const headedLaunch = getBooleanFlagValue(executionPlan.effectiveArgs, "--headed") ?? isUpstreamEnvFlagEnabled(
|
|
459
|
+
const headedLaunch = getBooleanFlagValue(executionPlan.effectiveArgs, "--headed") ?? isUpstreamEnvFlagEnabled(agentBrowserProcessEnv.AGENT_BROWSER_HEADED);
|
|
460
|
+
const providerLaunch = executionPlan.startupScopedFlags.some((flag) => flag === "--provider" || flag === "-p") || agentBrowserProcessEnv.AGENT_BROWSER_PROVIDER !== undefined;
|
|
413
461
|
const headedManagedAutosaveDisabled = retainedHeadedAutosaveDisabled || (explicitAutosaveInterval === undefined && headedLaunch);
|
|
414
462
|
const headedManagedAutosaveInterval = retainedHeadedAutosaveInterval ?? (headedLaunch ? explicitAutosaveInterval ?? "0" : undefined);
|
|
463
|
+
const compatibilityUserAgent = executionPlan.compatibilityWorkaround ? getDefaultHeadlessCompatUserAgent() : undefined;
|
|
464
|
+
const compatibilityUserAgentApplied = compatibilityUserAgent !== undefined
|
|
465
|
+
&& executionPlan.effectiveArgs.some((token, index) => token === "--user-agent" && executionPlan.effectiveArgs[index + 1] === compatibilityUserAgent);
|
|
415
466
|
const ownedManagedSession = buildOwnedManagedSessionRestoreContext({
|
|
416
467
|
args: executionPlan.effectiveArgs,
|
|
417
468
|
cwd: recordedOwnedSession?.cwd ?? cwd,
|
|
@@ -421,16 +472,14 @@ export async function prepareBrowserRun(options) {
|
|
|
421
472
|
headedManagedAutosaveInterval,
|
|
422
473
|
managedSessionName: executionPlan.managedSessionName,
|
|
423
474
|
namespace: executionPlan.namespace,
|
|
475
|
+
parentEnv: agentBrowserProcessEnv,
|
|
424
476
|
recordedOwnedSession,
|
|
425
477
|
restoreState: state.managedSessionRestoreState,
|
|
426
478
|
sessionName: executionPlan.sessionName,
|
|
427
479
|
stdin: runtimeToolStdin,
|
|
428
|
-
compatibilityUserAgent:
|
|
429
|
-
wrapperInjectedUserAgent:
|
|
480
|
+
compatibilityUserAgent: compatibilityUserAgentApplied ? compatibilityUserAgent : undefined,
|
|
481
|
+
wrapperInjectedUserAgent: compatibilityUserAgentApplied,
|
|
430
482
|
});
|
|
431
|
-
const managedSessionTargetError = getManagedSessionTargetAccessValidationError(executionPlan.effectiveArgs, ownedManagedSession !== undefined);
|
|
432
|
-
if (!executionPlan.validationError && managedSessionTargetError)
|
|
433
|
-
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: managedSessionTargetError };
|
|
434
483
|
if (!executionPlan.validationError && ownedManagedSession) {
|
|
435
484
|
const closeCommand = isCloseCommand(executionPlan.commandInfo.command);
|
|
436
485
|
const policy = await acquireOwnedManagedSessionDaemonPolicy({
|
|
@@ -446,15 +495,25 @@ export async function prepareBrowserRun(options) {
|
|
|
446
495
|
validationError: policy.error,
|
|
447
496
|
};
|
|
448
497
|
}
|
|
449
|
-
else if (closeCommand &&
|
|
498
|
+
else if (!closeCommand && policy.daemonStatus === "active" && offCurrentLaunchScopedFlags.length > 0) {
|
|
450
499
|
executionPlan = {
|
|
451
500
|
...executionPlan,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
501
|
+
recoveryHint: undefined,
|
|
502
|
+
validationError: `This older wrapper-owned session is already running, so launch-scoped flags ${offCurrentLaunchScopedFlags.join(", ")} would replace or be ignored by upstream agent-browser. Close it first, or remove the explicit --session and retry with sessionMode: \"fresh\".`,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
else if (!closeCommand && policy.daemonStatus === "active" && offCurrentCompatibilityUpgrade) {
|
|
506
|
+
executionPlan = {
|
|
507
|
+
...executionPlan,
|
|
508
|
+
recoveryHint: undefined,
|
|
509
|
+
validationError: "This older wrapper-owned session is already running without the user agent required by this site. Close it first, or remove the explicit --session and retry with sessionMode: \"fresh\".",
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
else if (!closeCommand && policy.daemonStatus === "inactive" && compatibilityUserAgent && !compatibilityUserAgentApplied) {
|
|
513
|
+
ownedManagedSession.compatibilityUserAgent = compatibilityUserAgent;
|
|
514
|
+
executionPlan = {
|
|
515
|
+
...executionPlan,
|
|
516
|
+
effectiveArgs: ["--user-agent", compatibilityUserAgent, ...executionPlan.effectiveArgs],
|
|
458
517
|
};
|
|
459
518
|
}
|
|
460
519
|
}
|
|
@@ -500,10 +559,9 @@ export async function prepareBrowserRun(options) {
|
|
|
500
559
|
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: request.requirement };
|
|
501
560
|
return;
|
|
502
561
|
}
|
|
503
|
-
const livePageValidationError =
|
|
562
|
+
const livePageValidationError = getPageTargetValidationError({
|
|
504
563
|
args: request.args,
|
|
505
564
|
currentPageUrl: liveUrl,
|
|
506
|
-
cwd,
|
|
507
565
|
pageUrlUnknown: false,
|
|
508
566
|
stdin: request.stdin,
|
|
509
567
|
});
|
|
@@ -515,11 +573,12 @@ export async function prepareBrowserRun(options) {
|
|
|
515
573
|
priorSessionTabTarget ??= { url: liveUrl };
|
|
516
574
|
priorSessionTabTargetUnknown = undefined;
|
|
517
575
|
};
|
|
518
|
-
const
|
|
576
|
+
const hasPotentialLiveSemanticSession = state.managedSessionActive || priorSessionTabTarget !== undefined || isCallerOwnedExplicitSession() || options.preserveAttachedBrowserSession === true;
|
|
577
|
+
const mayResolveSemanticVisibleRef = executionPlan.managedSessionName !== freshSessionName && hasPotentialLiveSemanticSession && canResolveSemanticVisibleRef(compiledSemanticAction);
|
|
519
578
|
if (!executionPlan.validationError && mayResolveSemanticVisibleRef && requiresLivePageVerification()) {
|
|
520
579
|
await verifyLivePage({
|
|
521
580
|
args: ["snapshot", "-i"],
|
|
522
|
-
requirement:
|
|
581
|
+
requirement: getExplicitSessionPageVerificationRequirement({ args: ["snapshot", "-i"] }),
|
|
523
582
|
});
|
|
524
583
|
}
|
|
525
584
|
if (!executionPlan.validationError && mayResolveSemanticVisibleRef) {
|
|
@@ -531,6 +590,17 @@ export async function prepareBrowserRun(options) {
|
|
|
531
590
|
signal,
|
|
532
591
|
});
|
|
533
592
|
}
|
|
593
|
+
if (!executionPlan.validationError && requiresResolvedSemanticVisibleRef(compiledSemanticAction) && !semanticActionVisibleRefResolution) {
|
|
594
|
+
const freshLocatorError = executionPlan.managedSessionName === freshSessionName
|
|
595
|
+
? "semanticAction select with locator cannot resolve a current @ref in sessionMode fresh. Open the page first, then reuse that session, or pass selector plus value/values."
|
|
596
|
+
: undefined;
|
|
597
|
+
executionPlan = {
|
|
598
|
+
...executionPlan,
|
|
599
|
+
validationError: freshLocatorError ?? (hasPotentialLiveSemanticSession
|
|
600
|
+
? "semanticAction select with locator could not resolve to exactly one current visible combobox/listbox ref. Run snapshot -i and retry with selector or a more specific role/name."
|
|
601
|
+
: "semanticAction select with locator requires an active browser session so the wrapper can resolve a current @ref; open a page first or pass selector plus value/values."),
|
|
602
|
+
};
|
|
603
|
+
}
|
|
534
604
|
if (semanticActionVisibleRefResolution) {
|
|
535
605
|
executionPlan = buildExecutionPlan(semanticActionVisibleRefResolution.args, {
|
|
536
606
|
freshSessionName,
|
|
@@ -541,7 +611,14 @@ export async function prepareBrowserRun(options) {
|
|
|
541
611
|
sessionMode,
|
|
542
612
|
});
|
|
543
613
|
}
|
|
544
|
-
const commandTokens = semanticActionVisibleRefResolution ?
|
|
614
|
+
const commandTokens = semanticActionVisibleRefResolution ? extractUpstreamCommandTokens(semanticActionVisibleRefResolution.args) : extractUpstreamCommandTokens(preparedArgs.args);
|
|
615
|
+
const unsupportedScrollIntoViewRecovery = executionPlan.validationError || executionPlan.plainTextInspection
|
|
616
|
+
? undefined
|
|
617
|
+
: [commandTokens, ...getUpstreamEffectiveBatchSteps(commandTokens, runtimeToolStdin)]
|
|
618
|
+
.map((tokens) => buildUnsupportedScrollIntoViewRecovery({ commandTokens: tokens, sessionName: executionPlan.sessionName }))
|
|
619
|
+
.find((recovery) => recovery !== undefined);
|
|
620
|
+
if (unsupportedScrollIntoViewRecovery)
|
|
621
|
+
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: unsupportedScrollIntoViewRecovery.error };
|
|
545
622
|
const resolvedSemanticActionRefSnapshot = semanticActionVisibleRefResolution?.snapshot
|
|
546
623
|
? { ...semanticActionVisibleRefResolution.snapshot, target: semanticActionVisibleRefResolution.snapshot.target ?? priorSessionTabTarget }
|
|
547
624
|
: undefined;
|
|
@@ -558,9 +635,8 @@ export async function prepareBrowserRun(options) {
|
|
|
558
635
|
&& requiresLivePageVerification();
|
|
559
636
|
const livePageRequirement = livePageAccessEligible
|
|
560
637
|
&& !livePageVerified
|
|
561
|
-
?
|
|
638
|
+
? getExplicitSessionPageVerificationRequirement({
|
|
562
639
|
args: executionPlan.effectiveArgs,
|
|
563
|
-
cwd,
|
|
564
640
|
stdin: runtimeToolStdin,
|
|
565
641
|
})
|
|
566
642
|
: undefined;
|
|
@@ -579,8 +655,10 @@ export async function prepareBrowserRun(options) {
|
|
|
579
655
|
freshSessionOrdinal += 1;
|
|
580
656
|
}
|
|
581
657
|
if (executionPlan.validationError) {
|
|
658
|
+
const nextActions = applyNamespaceToNextActions(unsupportedScrollIntoViewRecovery?.nextActions, executionPlan.namespace);
|
|
659
|
+
const nextActionsText = formatAgentBrowserNextActionsText(nextActions);
|
|
582
660
|
return { kind: "early-result", statePatch, result: {
|
|
583
|
-
content: [{ type: "text", text: executionPlan.validationError }],
|
|
661
|
+
content: [{ type: "text", text: [executionPlan.validationError, nextActionsText].filter((text) => text !== undefined).join("\n\n") }],
|
|
584
662
|
details: {
|
|
585
663
|
args: redactedArgs,
|
|
586
664
|
compiledElectron: redactedCompiledElectron,
|
|
@@ -589,6 +667,7 @@ export async function prepareBrowserRun(options) {
|
|
|
589
667
|
compiledSourceLookup: redactedCompiledSourceLookup,
|
|
590
668
|
compiledNetworkSourceLookup: redactedCompiledNetworkSourceLookup,
|
|
591
669
|
invalidValueFlag: executionPlan.invalidValueFlag,
|
|
670
|
+
nextActions,
|
|
592
671
|
sessionMode,
|
|
593
672
|
sessionRecoveryHint: redactedRecoveryHint,
|
|
594
673
|
startupScopedFlags: executionPlan.startupScopedFlags,
|
|
@@ -696,6 +775,7 @@ export async function prepareBrowserRun(options) {
|
|
|
696
775
|
cwd,
|
|
697
776
|
currentTarget: priorSessionTabTarget,
|
|
698
777
|
previousSnapshot: resolvedSemanticActionRefSnapshot ? undefined : priorRefSnapshotState,
|
|
778
|
+
stdin: runtimeToolStdin,
|
|
699
779
|
namespace: executionPlan.namespace,
|
|
700
780
|
sessionName: executionPlan.sessionName,
|
|
701
781
|
signal,
|
|
@@ -921,7 +1001,11 @@ export async function prepareBrowserRun(options) {
|
|
|
921
1001
|
}
|
|
922
1002
|
if (pinnedBatchPlan) {
|
|
923
1003
|
sessionTabCorrection = plannedSessionTabSelection;
|
|
924
|
-
|
|
1004
|
+
// The rewritten batch must keep the caller's fail-fast semantics:
|
|
1005
|
+
// upstream reads only the exact --bail token, so re-emit it whenever
|
|
1006
|
+
// the original batch tokens carried it (argv or stdin/job/qa mode).
|
|
1007
|
+
const pinnedBailArgs = commandTokens.includes("--bail") ? ["--bail"] : [];
|
|
1008
|
+
processArgs = ["--json", ...(executionPlan.namespace !== undefined ? ["--namespace", executionPlan.namespace] : []), "--session", executionPlan.sessionName, "batch", ...pinnedBailArgs];
|
|
925
1009
|
processStdin = JSON.stringify(pinnedBatchPlan.steps);
|
|
926
1010
|
includePinnedNavigationSummary = pinnedBatchPlan.includeNavigationSummary;
|
|
927
1011
|
pinnedBatchUnwrapMode = pinnedBatchPlan.unwrapMode;
|
|
@@ -963,6 +1047,8 @@ export async function prepareBrowserRun(options) {
|
|
|
963
1047
|
kind: "ready",
|
|
964
1048
|
prepared: {
|
|
965
1049
|
commandTokens,
|
|
1050
|
+
headedLaunch,
|
|
1051
|
+
providerLaunch,
|
|
966
1052
|
managedSessionPolicyLock,
|
|
967
1053
|
compiledElectron,
|
|
968
1054
|
compiledJob,
|
|
@@ -990,7 +1076,9 @@ export async function prepareBrowserRun(options) {
|
|
|
990
1076
|
redactedCompiledJob,
|
|
991
1077
|
redactedCompiledNetworkSourceLookup,
|
|
992
1078
|
redactedCompiledQaPreset,
|
|
993
|
-
redactedCompiledSemanticAction
|
|
1079
|
+
redactedCompiledSemanticAction: semanticActionVisibleRefResolution && redactedCompiledSemanticAction?.action === "select"
|
|
1080
|
+
? { ...redactedCompiledSemanticAction, args: redactInvocationArgs(semanticActionVisibleRefResolution.args) }
|
|
1081
|
+
: redactedCompiledSemanticAction,
|
|
994
1082
|
redactedCompiledSourceLookup,
|
|
995
1083
|
redactedEffectiveArgs,
|
|
996
1084
|
redactedProcessArgs,
|