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,14 +1,16 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
-
import { findCommandStartIndex, parseArgvDescriptor, parseCommandInfo, } from "./argv-descriptor.js";
|
|
4
|
-
import {
|
|
3
|
+
import { extractUpstreamCommandTokens, findCommandStartIndex, parseArgvDescriptor, parseCommandInfo, } from "./argv-descriptor.js";
|
|
4
|
+
import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "./batch-lifecycle.js";
|
|
5
|
+
import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, getBooleanFlagValue, GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, GLOBAL_VALUE_FLAGS_ALLOWING_DASH_VALUE, isAgentBrowserSessionIdentityKeyInNamespace, isUpstreamEnvFlagEnabled, PREVALIDATED_VALUE_FLAGS, resolveAgentBrowserNamespace, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
|
|
5
6
|
import { needsManagedSession } from "./command-policy.js";
|
|
6
|
-
import {
|
|
7
|
-
import { isCloseCommand, isOpenNavigationCommand } from "./command-taxonomy.js";
|
|
7
|
+
import { isCloseAllCommand, isCloseCommand, isOpenNavigationCommand } from "./command-taxonomy.js";
|
|
8
8
|
import { hasLaunchScopedFlagToken, LAUNCH_SCOPED_FLAG_DEFINITIONS, LAUNCH_SCOPED_FLAG_LABEL, } from "./launch-scoped-flags.js";
|
|
9
9
|
import { MANAGED_SESSION_NAME_PREFIX, } from "./managed-session-restore.js";
|
|
10
|
-
export { extractCommandTokens, findCommandStartIndex, parseArgvDescriptor, parseCommandInfo } from "./argv-descriptor.js";
|
|
10
|
+
export { extractCommandTokens, extractUpstreamCommandTokens, findCommandStartIndex, parseArgvDescriptor, parseCommandInfo, parseWaitCommandTokens } from "./argv-descriptor.js";
|
|
11
11
|
import { isRecord } from "./parsing.js";
|
|
12
|
+
import { getAgentBrowserProcessEnvironment } from "./process-environment.js";
|
|
13
|
+
import { TARGET_AGENT_BROWSER_VERSION } from "./upstream-version.js";
|
|
12
14
|
const OPENAI_HEADLESS_COMPAT_HOSTS = new Set(["chat.com", "chat.openai.com", "chatgpt.com"]);
|
|
13
15
|
const CLOUDFLARE_HEADLESS_COMPAT_HOST = "dash.cloudflare.com";
|
|
14
16
|
const AGENT_BROWSER_IDLE_TIMEOUT_ENV = "AGENT_BROWSER_IDLE_TIMEOUT_MS";
|
|
@@ -18,7 +20,9 @@ const DEFAULT_IMPLICIT_SESSION_IDLE_TIMEOUT_MS = 15 * 60 * 1000;
|
|
|
18
20
|
const DEFAULT_IMPLICIT_SESSION_CLOSE_TIMEOUT_MS = 5_000;
|
|
19
21
|
const INSPECTION_FLAGS = new Set(["--help", "-h", "--version", "-V"]);
|
|
20
22
|
const SENSITIVE_VALUE_FLAGS = new Set(["--body", "--headers", "--password", "--proxy"]);
|
|
21
|
-
const SENSITIVE_QUERY_PARAM_PATTERN = /^(?:access(?:_|-)?token|api(?:_|-)?key|auth|authorization|bearer|client(?:_|-)?secret|code|cookie|id(?:_|-)?token|key|pass(?:word)?|refresh(?:_|-)?token|secret|sentry(?:_|-)?key|session(?:_|-)?id|sig(?:nature)?|token|write(?:_|-)?key)$/i;
|
|
23
|
+
const SENSITIVE_QUERY_PARAM_PATTERN = /^(?:access(?:_|-)?token|api(?:_|-)?key|auth|authorization|bearer|client(?:_|-)?secret|code|cookie|id(?:_|-)?token|key|pass(?:word)?|refresh(?:_|-)?token|relay(?:_|-)?state|saml(?:_|-)?request|saml(?:_|-)?response|secret|sentry(?:_|-)?key|session(?:_|-)?id|sig(?:nature)?|token|write(?:_|-)?key)$/i;
|
|
24
|
+
const AUTH_STATE_QUERY_PARAM_PATTERN = /^(?:nonce|state)$/i;
|
|
25
|
+
const AUTH_URL_CONTEXT_PATTERN = /(?:^|[./_-])(?:auth|authorize|callback|login|oauth2?|oidc|saml|sso)(?:[./?#_-]|$)/i;
|
|
22
26
|
const SENSITIVE_FIELD_NAME_PATTERN = /^(?:[A-Za-z0-9_-]*(?:api[_-]?key|access[_-]?key|private[_-]?key|secret(?:[_-]?(?:key|access[_-]?key))?|token|password|passwd|credentials?|database[_-]?url|db[_-]?url|connection[_-]?string|mongo(?:db)?[_-]?uri|redis[_-]?url)|[A-Za-z0-9]*(?:apiKey|ApiKey|apikey|privateKey|PrivateKey|databaseUrl|DatabaseUrl|dbUrl|DbUrl|connectionString|ConnectionString|mongoUri|MongoUri|mongodbUri|MongodbUri|mongoDbUri|MongoDbUri|redisUrl|RedisUrl|Token|Secret|Password|Credential|Credentials)|auth(?:orization)?|bearer|client(?:_|-)?secret|cookie|id(?:_|-)?token|pass(?:word)?|proxy(?:_|-)?authorization|refresh(?:_|-)?token|sentry(?:_|-)?key|session(?:_|-)?id|set(?:_|-)?cookie|sig(?:nature)?|write(?:_|-)?key|x(?:_|-)?api(?:_|-)?key)$/i;
|
|
23
27
|
const ENV_SECRET_ASSIGNMENT_PATTERN = /\b((?:export\s+)?([A-Za-z_][A-Za-z0-9_-]*)(\s*[:=]\s*))(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,;]+)/g;
|
|
24
28
|
const DEFAULT_HEADLESS_COMPAT_USER_AGENT_BY_PLATFORM = {
|
|
@@ -28,6 +32,7 @@ const DEFAULT_HEADLESS_COMPAT_USER_AGENT_BY_PLATFORM = {
|
|
|
28
32
|
};
|
|
29
33
|
const FALLBACK_HEADLESS_COMPAT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
30
34
|
const SHELL_OPERATOR_TOKENS = new Set(["&&", "||", "|", ";", ">", ">>", "<"]);
|
|
35
|
+
const ANDROID_SESSION_NAME_IDENTITY_LENGTH = 20;
|
|
31
36
|
const MAX_PROJECT_SLUG_LENGTH = 24;
|
|
32
37
|
const SESSION_NAME_CWD_HASH_LENGTH = 8;
|
|
33
38
|
const SESSION_NAME_SESSION_ID_LENGTH = 12;
|
|
@@ -45,48 +50,57 @@ function redactUrlToken(token) {
|
|
|
45
50
|
catch {
|
|
46
51
|
return token;
|
|
47
52
|
}
|
|
48
|
-
|
|
49
|
-
if (parsed.username.length > 0) {
|
|
53
|
+
if (parsed.username.length > 0)
|
|
50
54
|
parsed.username = "[REDACTED]";
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
if (parsed.password.length > 0) {
|
|
55
|
+
if (parsed.password.length > 0)
|
|
54
56
|
parsed.password = "[REDACTED]";
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
const hashText = parsed.hash.startsWith("#") ? parsed.hash.slice(1) : parsed.hash;
|
|
58
|
+
const hashParams = hashText.includes("=") ? new URLSearchParams(hashText) : undefined;
|
|
59
|
+
const authContext = AUTH_URL_CONTEXT_PATTERN.test(`${parsed.hostname}${parsed.pathname}`)
|
|
60
|
+
|| [...parsed.searchParams.keys(), ...(hashParams?.keys() ?? [])].some(shouldRedactQueryParam);
|
|
57
61
|
for (const [name] of parsed.searchParams) {
|
|
58
|
-
if (shouldRedactQueryParam(name)) {
|
|
62
|
+
if (shouldRedactQueryParam(name) || (authContext && AUTH_STATE_QUERY_PARAM_PATTERN.test(name))) {
|
|
59
63
|
parsed.searchParams.set(name, "[REDACTED]");
|
|
60
|
-
mutated = true;
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const hashParams = new URLSearchParams(hashText);
|
|
66
|
+
if (hashParams) {
|
|
67
|
+
let hashMutated = false;
|
|
66
68
|
for (const [name] of hashParams) {
|
|
67
|
-
if (shouldRedactQueryParam(name)) {
|
|
69
|
+
if (shouldRedactQueryParam(name) || (authContext && AUTH_STATE_QUERY_PARAM_PATTERN.test(name))) {
|
|
68
70
|
hashParams.set(name, "[REDACTED]");
|
|
69
|
-
|
|
71
|
+
hashMutated = true;
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
if (
|
|
74
|
+
if (hashMutated)
|
|
73
75
|
parsed.hash = `#${hashParams.toString()}`;
|
|
74
|
-
}
|
|
75
76
|
}
|
|
76
77
|
return parsed.toString();
|
|
77
78
|
}
|
|
78
79
|
function redactLooseUrlParameterText(text) {
|
|
79
|
-
return text.replace(/([
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return
|
|
80
|
+
return text.replace(/(?<![^\s"'`<>\])}])[^\s"'`<>\])}]*[?#&][^\s"'`<>\])}]*/g, (token) => {
|
|
81
|
+
const queryNames = [...token.matchAll(/[?#&]([^=&#\s"'`<>\])}]+)=/g)].map((match) => {
|
|
82
|
+
try {
|
|
83
|
+
return decodeURIComponent((match[1] ?? "").replace(/\+/g, " "));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return match[1] ?? "";
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const authContext = AUTH_URL_CONTEXT_PATTERN.test(token) || queryNames.some(shouldRedactQueryParam);
|
|
90
|
+
return token.replace(/([?#&])([^=&#\s"'`<>\])}]+)=([^&#\s"'`<>\])}]*)/g, (match, separator, rawName, rawValue) => {
|
|
91
|
+
if (rawValue === "[REDACTED" || rawValue === "[REDACTED]" || /%5Bredacted%5D/i.test(rawValue))
|
|
92
|
+
return match;
|
|
93
|
+
let name = rawName;
|
|
94
|
+
try {
|
|
95
|
+
name = decodeURIComponent(rawName.replace(/\+/g, " "));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Keep the raw name when percent decoding fails.
|
|
99
|
+
}
|
|
100
|
+
if (!shouldRedactQueryParam(name) && !(authContext && AUTH_STATE_QUERY_PARAM_PATTERN.test(name)))
|
|
101
|
+
return match;
|
|
102
|
+
return `${separator}${rawName}=[REDACTED]`;
|
|
103
|
+
});
|
|
90
104
|
});
|
|
91
105
|
}
|
|
92
106
|
function redactLooseUrlUserinfo(text) {
|
|
@@ -222,7 +236,7 @@ function redactEnvSecretAssignments(text) {
|
|
|
222
236
|
});
|
|
223
237
|
}
|
|
224
238
|
export function redactSensitiveText(text) {
|
|
225
|
-
return redactEmbeddedStructuredText(redactEnvSecretAssignments(redactStandaloneBasicCredential(redactBearerCredentials(redactLooseUrlUserinfo(redactLooseUrlMatches(
|
|
239
|
+
return redactEmbeddedStructuredText(redactEnvSecretAssignments(redactStandaloneBasicCredential(redactBearerCredentials(redactLooseUrlParameterText(redactLooseUrlUserinfo(redactLooseUrlMatches(text))))
|
|
226
240
|
.replace(/\b(Authorization\s*:\s*Basic)\s+[^\s",]+/gi, "$1 [REDACTED]")
|
|
227
241
|
.replace(/\b(Cookie|Set-Cookie)\s*:\s*[^\n\r"]+/gi, "$1: [REDACTED]"))));
|
|
228
242
|
}
|
|
@@ -311,7 +325,7 @@ function parseTimeoutMs(rawValue, minimumValue) {
|
|
|
311
325
|
}
|
|
312
326
|
return parsedValue;
|
|
313
327
|
}
|
|
314
|
-
export function getImplicitSessionIdleTimeoutMs(env =
|
|
328
|
+
export function getImplicitSessionIdleTimeoutMs(env = getAgentBrowserProcessEnvironment()) {
|
|
315
329
|
return parseTimeoutMs(env[IMPLICIT_SESSION_IDLE_TIMEOUT_ENV], 0) ??
|
|
316
330
|
parseTimeoutMs(env[AGENT_BROWSER_IDLE_TIMEOUT_ENV], 0) ??
|
|
317
331
|
DEFAULT_IMPLICIT_SESSION_IDLE_TIMEOUT_MS;
|
|
@@ -319,21 +333,7 @@ export function getImplicitSessionIdleTimeoutMs(env = process.env) {
|
|
|
319
333
|
function countExplicitGlobalFlags(args, targetFlag) {
|
|
320
334
|
return scanUpstreamGlobalFlagOccurrences(args, targetFlag).length;
|
|
321
335
|
}
|
|
322
|
-
function
|
|
323
|
-
const commandStartIndex = findCommandStartIndex(args) ?? args.length;
|
|
324
|
-
for (let index = 0; index < commandStartIndex; index += 1) {
|
|
325
|
-
const token = args[index];
|
|
326
|
-
if (token.startsWith("--session="))
|
|
327
|
-
return "--session";
|
|
328
|
-
if (token.startsWith("--namespace="))
|
|
329
|
-
return "--namespace";
|
|
330
|
-
const flag = token.split("=", 1)[0] ?? token;
|
|
331
|
-
if (!token.includes("=") && PREVALIDATED_VALUE_FLAGS.has(flag))
|
|
332
|
-
index += 1;
|
|
333
|
-
}
|
|
334
|
-
return undefined;
|
|
335
|
-
}
|
|
336
|
-
export function getImplicitSessionCloseTimeoutMs(env = process.env) {
|
|
336
|
+
export function getImplicitSessionCloseTimeoutMs(env = getAgentBrowserProcessEnvironment()) {
|
|
337
337
|
return parseTimeoutMs(env[IMPLICIT_SESSION_CLOSE_TIMEOUT_ENV], 0) ?? DEFAULT_IMPLICIT_SESSION_CLOSE_TIMEOUT_MS;
|
|
338
338
|
}
|
|
339
339
|
export function resolveManagedSessionState(options) {
|
|
@@ -381,15 +381,18 @@ function getManagedSessionRestoreRank(options) {
|
|
|
381
381
|
function getRestorableManagedSessionName(value, fallbackSessionName) {
|
|
382
382
|
return typeof value === "string" && isRestorableManagedSessionName(value, fallbackSessionName) ? value : undefined;
|
|
383
383
|
}
|
|
384
|
-
function
|
|
384
|
+
function getElectronCleanupClosedManagedSessions(details, fallbackSessionName) {
|
|
385
385
|
const electron = isRecord(details.electron) ? details.electron : undefined;
|
|
386
386
|
const cleanup = isRecord(electron?.cleanup) ? electron.cleanup : undefined;
|
|
387
387
|
const results = Array.isArray(cleanup?.results) ? cleanup.results : [];
|
|
388
|
-
const
|
|
388
|
+
const closedSessions = [];
|
|
389
389
|
for (const result of results) {
|
|
390
390
|
if (!isRecord(result) || !Array.isArray(result.steps))
|
|
391
391
|
continue;
|
|
392
392
|
const record = isRecord(result.record) ? result.record : undefined;
|
|
393
|
+
const fallbackNamespace = typeof record?.namespace === "string"
|
|
394
|
+
? record.namespace
|
|
395
|
+
: typeof details.namespace === "string" ? details.namespace : undefined;
|
|
393
396
|
for (const step of result.steps) {
|
|
394
397
|
if (!isRecord(step) || step.resource !== "managed-session")
|
|
395
398
|
continue;
|
|
@@ -397,11 +400,12 @@ function getElectronCleanupClosedManagedSessionNames(details, fallbackSessionNam
|
|
|
397
400
|
continue;
|
|
398
401
|
const sessionName = getRestorableManagedSessionName(step.sessionName, fallbackSessionName)
|
|
399
402
|
?? getRestorableManagedSessionName(record?.sessionName, fallbackSessionName);
|
|
403
|
+
const namespace = typeof step.namespace === "string" ? step.namespace : fallbackNamespace;
|
|
400
404
|
if (sessionName)
|
|
401
|
-
|
|
405
|
+
closedSessions.push({ namespace, sessionName });
|
|
402
406
|
}
|
|
403
407
|
}
|
|
404
|
-
return
|
|
408
|
+
return closedSessions;
|
|
405
409
|
}
|
|
406
410
|
export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName) {
|
|
407
411
|
const restoreDisabledIdentities = new Map();
|
|
@@ -441,16 +445,23 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
|
|
|
441
445
|
if (isPlainTextInspectionArgs(args)) {
|
|
442
446
|
continue;
|
|
443
447
|
}
|
|
444
|
-
for (const
|
|
445
|
-
applyManagedClose(sessionName);
|
|
448
|
+
for (const cleanupSession of getElectronCleanupClosedManagedSessions(details, fallbackSessionName)) {
|
|
449
|
+
applyManagedClose(cleanupSession.sessionName, cleanupSession.namespace);
|
|
446
450
|
}
|
|
447
451
|
const explicitSessionName = extractExplicitSessionName(args);
|
|
448
452
|
const sessionName = typeof details.sessionName === "string" ? details.sessionName : undefined;
|
|
449
453
|
const namespace = canonicalizeAgentBrowserNamespace(typeof details.namespace === "string" ? details.namespace : undefined);
|
|
450
454
|
const sessionMode = details.sessionMode === "fresh" || details.sessionMode === "auto" ? details.sessionMode : undefined;
|
|
451
455
|
const usedImplicitSession = details.usedImplicitSession === true;
|
|
456
|
+
const commandTokens = extractUpstreamCommandTokens(args);
|
|
452
457
|
const command = typeof details.command === "string" ? details.command : parseCommandInfo(args).command;
|
|
453
|
-
const
|
|
458
|
+
const batchCloseLifecycle = getSuccessfulBatchCloseLifecycle(details.batchSteps);
|
|
459
|
+
const nestedBatchEndsClosed = batchCloseLifecycle?.endsClosed === true;
|
|
460
|
+
const nestedBatchRemainsActive = batchCloseLifecycle?.endsClosed === false;
|
|
461
|
+
const closeAllApplied = details.closeAllApplied === true
|
|
462
|
+
|| (message.isError !== true && isCloseAllCommand(commandTokens))
|
|
463
|
+
|| batchHasSuccessfulCloseAll(details.batchSteps);
|
|
464
|
+
const commandClosesSession = isCloseCommand(command) || nestedBatchEndsClosed;
|
|
454
465
|
const outcome = typeof details.managedSessionOutcome === "object" && details.managedSessionOutcome !== null ? details.managedSessionOutcome : undefined;
|
|
455
466
|
const outcomeStatus = typeof outcome?.status === "string" ? outcome.status : undefined;
|
|
456
467
|
const outcomeCurrentSessionName = typeof outcome?.currentSessionName === "string" ? outcome.currentSessionName : undefined;
|
|
@@ -459,6 +470,14 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
|
|
|
459
470
|
? outcomeAttemptedSessionName ?? getRestorableManagedSessionName(outcomeCurrentSessionName, fallbackSessionName) ?? getRestorableManagedSessionName(sessionName, fallbackSessionName)
|
|
460
471
|
: undefined;
|
|
461
472
|
const restorableDetailSessionName = getRestorableManagedSessionName(sessionName, fallbackSessionName);
|
|
473
|
+
if (closeAllApplied && restoredState.active) {
|
|
474
|
+
const restoredKey = getAgentBrowserSessionIdentityKey(restoredState.sessionName, restoredState.namespace);
|
|
475
|
+
const resultKey = restorableDetailSessionName ? getAgentBrowserSessionIdentityKey(restorableDetailSessionName, namespace) : undefined;
|
|
476
|
+
const retainsCurrentSession = nestedBatchRemainsActive && resultKey === restoredKey;
|
|
477
|
+
if (isAgentBrowserSessionIdentityKeyInNamespace(restoredKey, namespace) && !retainsCurrentSession) {
|
|
478
|
+
applyManagedClose(restoredState.sessionName, restoredState.namespace);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
462
481
|
const explicitCloseSessionName = commandClosesSession && explicitSessionName && restorableDetailSessionName === explicitSessionName
|
|
463
482
|
? restorableDetailSessionName
|
|
464
483
|
: undefined;
|
|
@@ -467,13 +486,13 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
|
|
|
467
486
|
if (details.managedSessionRestoreDisabled === true && typeof sessionName === "string") {
|
|
468
487
|
restoreDisabledIdentities.set(getAgentBrowserSessionIdentityKey(sessionName, namespace), { namespace, sessionName });
|
|
469
488
|
}
|
|
470
|
-
const managedSessionName = !explicitSessionName &&
|
|
489
|
+
const managedSessionName = outcomeClosedSessionName ?? (!explicitSessionName &&
|
|
471
490
|
restorableDetailSessionName &&
|
|
472
491
|
(usedImplicitSession || sessionMode === "fresh")
|
|
473
492
|
? restorableDetailSessionName
|
|
474
493
|
: commandClosesSession
|
|
475
|
-
?
|
|
476
|
-
: undefined;
|
|
494
|
+
? explicitCloseSessionName
|
|
495
|
+
: undefined);
|
|
477
496
|
if (!managedSessionName) {
|
|
478
497
|
continue;
|
|
479
498
|
}
|
|
@@ -490,9 +509,11 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
|
|
|
490
509
|
const exitCode = typeof details.exitCode === "number" ? details.exitCode : undefined;
|
|
491
510
|
const outcomeActiveAfter = outcome?.activeAfter === true;
|
|
492
511
|
const outcomeRepresentsActiveCurrentSession = outcomeActiveAfter && outcomeCurrentSessionName === managedSessionName && (outcomeStatus === "created" || outcomeStatus === "replaced" || outcomeStatus === "unchanged");
|
|
493
|
-
const succeeded = outcomeRepresentsActiveCurrentSession
|
|
494
|
-
|
|
495
|
-
|
|
512
|
+
const succeeded = outcomeRepresentsActiveCurrentSession || nestedBatchRemainsActive
|
|
513
|
+
? true
|
|
514
|
+
: messageIsError === undefined ? exitCode === undefined || exitCode === 0 : !messageIsError;
|
|
515
|
+
if (commandClosesSession || outcomeClosedSessionName) {
|
|
516
|
+
if (nestedBatchEndsClosed || outcomeClosedSessionName || succeeded) {
|
|
496
517
|
restoreDisabledIdentities.delete(getAgentBrowserSessionIdentityKey(managedSessionName, namespace));
|
|
497
518
|
applyManagedClose(managedSessionName, namespace);
|
|
498
519
|
}
|
|
@@ -529,15 +550,24 @@ export function createEphemeralSessionSeed() {
|
|
|
529
550
|
function createCwdHash(cwd) {
|
|
530
551
|
return createHash("sha256").update(`cwd:${cwd}`).digest("hex").slice(0, SESSION_NAME_CWD_HASH_LENGTH);
|
|
531
552
|
}
|
|
532
|
-
export function createImplicitSessionName(sessionId, cwd, ephemeralSeed) {
|
|
553
|
+
export function createImplicitSessionName(sessionId, cwd, ephemeralSeed, platform = process.platform) {
|
|
554
|
+
const normalizedSessionId = sessionId?.replaceAll("-", "").toLowerCase();
|
|
555
|
+
if (platform === "android") {
|
|
556
|
+
const identity = normalizedSessionId ? `session:${normalizedSessionId}:cwd:${cwd}` : `ephemeral:${cwd}:${ephemeralSeed}`;
|
|
557
|
+
const digest = createHash("sha256").update(identity).digest("hex").slice(0, ANDROID_SESSION_NAME_IDENTITY_LENGTH);
|
|
558
|
+
return `${MANAGED_SESSION_NAME_PREFIX}${digest}`;
|
|
559
|
+
}
|
|
533
560
|
const slug = basename(cwd)
|
|
534
561
|
.toLowerCase()
|
|
535
562
|
.replace(/[^a-z0-9]+/g, "-")
|
|
536
563
|
.replace(/^-+|-+$/g, "")
|
|
537
564
|
.slice(0, MAX_PROJECT_SLUG_LENGTH) || "project";
|
|
538
565
|
const cwdHash = createCwdHash(cwd);
|
|
539
|
-
|
|
540
|
-
|
|
566
|
+
if (normalizedSessionId) {
|
|
567
|
+
const stableSessionId = createHash("sha256")
|
|
568
|
+
.update(`session:${normalizedSessionId}`)
|
|
569
|
+
.digest("hex")
|
|
570
|
+
.slice(0, SESSION_NAME_SESSION_ID_LENGTH);
|
|
541
571
|
return `${MANAGED_SESSION_NAME_PREFIX}${slug}-${stableSessionId}-${cwdHash}`;
|
|
542
572
|
}
|
|
543
573
|
const digest = createHash("sha256")
|
|
@@ -554,7 +584,7 @@ export function createFreshSessionName(baseSessionName, ephemeralSeed, ordinal)
|
|
|
554
584
|
return `${baseSessionName}-fresh-${suffix}`;
|
|
555
585
|
}
|
|
556
586
|
function getSingleKeyCommandValidationError(args) {
|
|
557
|
-
const { commandInfo, commandTokens } = parseArgvDescriptor(args);
|
|
587
|
+
const { commandInfo, upstreamCommandTokens: commandTokens } = parseArgvDescriptor(args);
|
|
558
588
|
const command = commandInfo.command;
|
|
559
589
|
if (command !== "press" && command !== "key" && command !== "keydown" && command !== "keyup")
|
|
560
590
|
return undefined;
|
|
@@ -564,14 +594,20 @@ function getSingleKeyCommandValidationError(args) {
|
|
|
564
594
|
return `agent-browser ${label} accepts exactly one key argument. Do not pass a selector or ref to ${label}; focus or click the target first, then run ${command} <key> (for example: focus @e1, then press Enter).`;
|
|
565
595
|
}
|
|
566
596
|
function getBareMcpValidationError(args) {
|
|
567
|
-
const { commandInfo, commandTokens } = parseArgvDescriptor(args);
|
|
597
|
+
const { commandInfo, upstreamCommandTokens: commandTokens } = parseArgvDescriptor(args);
|
|
568
598
|
if (commandInfo.command !== "mcp")
|
|
569
599
|
return undefined;
|
|
570
600
|
if (commandTokens.includes("--help") || commandTokens.includes("-h"))
|
|
571
601
|
return undefined;
|
|
572
602
|
return "agent-browser mcp starts a stdio MCP server for external MCP clients, not a one-shot native agent_browser tool workflow. Use the native agent_browser tool modes directly, or configure an MCP client to launch `agent-browser mcp`. Use `mcp --help` for help.";
|
|
573
603
|
}
|
|
574
|
-
|
|
604
|
+
function getUnsupportedInlineWaitDownloadError(args) {
|
|
605
|
+
const descriptor = parseArgvDescriptor(args);
|
|
606
|
+
if (descriptor.commandInfo.command !== "wait" || !descriptor.upstreamCommandTokens.some((token) => token.startsWith("--download=")))
|
|
607
|
+
return undefined;
|
|
608
|
+
return `agent-browser ${TARGET_AGENT_BROWSER_VERSION} does not support \`wait --download=<path>\`. Pass the optional path as a separate argument: \`wait --download <path>\` (or \`wait -d <path>\`).`;
|
|
609
|
+
}
|
|
610
|
+
export function validateToolArgs(args, options = {}) {
|
|
575
611
|
if (args.length === 0) {
|
|
576
612
|
return "`args` must contain at least one agent-browser command token.";
|
|
577
613
|
}
|
|
@@ -583,31 +619,34 @@ export function validateToolArgs(args) {
|
|
|
583
619
|
if (sessionModeArg) {
|
|
584
620
|
return "Do not pass `--session-mode` in args. Use the top-level agent_browser `sessionMode` field instead, for example { args: [\"--profile\", \"Default\", \"open\", \"https://example.com\"], sessionMode: \"fresh\" }.";
|
|
585
621
|
}
|
|
586
|
-
|
|
622
|
+
const inspection = !options.batchStep && isPlainTextInspectionArgs(args);
|
|
623
|
+
const invalidValueFlag = inspection ? undefined : getInvalidValueFlagDetails(args, !options.batchStep);
|
|
624
|
+
if (invalidValueFlag?.reason === "unsupported-assignment")
|
|
625
|
+
return formatInvalidValueFlagError(invalidValueFlag, options.batchStep);
|
|
626
|
+
return getBareMcpValidationError(args) ?? getSingleKeyCommandValidationError(args) ?? getUnsupportedInlineWaitDownloadError(args);
|
|
587
627
|
}
|
|
588
|
-
function getInvalidValueFlagDetails(args) {
|
|
628
|
+
function getInvalidValueFlagDetails(args, allowRestoreAssignment = true) {
|
|
589
629
|
for (let index = 0; index < args.length; index += 1) {
|
|
590
630
|
const token = args[index];
|
|
591
631
|
if (!token.startsWith("-")) {
|
|
592
632
|
continue;
|
|
593
633
|
}
|
|
594
634
|
const normalizedToken = token.split("=", 1)[0] ?? token;
|
|
595
|
-
if (
|
|
596
|
-
|
|
635
|
+
if (token.includes("=")
|
|
636
|
+
&& (PREVALIDATED_VALUE_FLAGS.has(normalizedToken)
|
|
637
|
+
|| GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES.has(normalizedToken)
|
|
638
|
+
|| (!allowRestoreAssignment && normalizedToken === "--restore"))) {
|
|
639
|
+
return {
|
|
640
|
+
flag: normalizedToken,
|
|
641
|
+
index,
|
|
642
|
+
reason: "unsupported-assignment",
|
|
643
|
+
};
|
|
597
644
|
}
|
|
598
|
-
if (
|
|
599
|
-
const value = token.slice(token.indexOf("=") + 1).trim();
|
|
600
|
-
if (value.length === 0) {
|
|
601
|
-
return {
|
|
602
|
-
flag: normalizedToken,
|
|
603
|
-
index,
|
|
604
|
-
reason: "missing-value",
|
|
605
|
-
};
|
|
606
|
-
}
|
|
645
|
+
if (!PREVALIDATED_VALUE_FLAGS.has(normalizedToken)) {
|
|
607
646
|
continue;
|
|
608
647
|
}
|
|
609
648
|
const receivedToken = args[index + 1];
|
|
610
|
-
if (receivedToken === undefined) {
|
|
649
|
+
if (receivedToken === undefined || (normalizedToken === "--args" && receivedToken.length === 0)) {
|
|
611
650
|
return {
|
|
612
651
|
flag: normalizedToken,
|
|
613
652
|
index,
|
|
@@ -626,7 +665,15 @@ function getInvalidValueFlagDetails(args) {
|
|
|
626
665
|
}
|
|
627
666
|
return undefined;
|
|
628
667
|
}
|
|
629
|
-
function formatInvalidValueFlagError(details) {
|
|
668
|
+
function formatInvalidValueFlagError(details, batchStep = false) {
|
|
669
|
+
if (details.reason === "unsupported-assignment") {
|
|
670
|
+
if (batchStep) {
|
|
671
|
+
return details.flag === "--restore"
|
|
672
|
+
? "Global `--restore=<key>` belongs before `batch` in top-level args, not inside a batch step."
|
|
673
|
+
: `Global \`${details.flag}=<value>\` is not supported inside a batch step. Move \`${details.flag}\` and its value before \`batch\` as separate top-level args.`;
|
|
674
|
+
}
|
|
675
|
+
return `agent-browser ${TARGET_AGENT_BROWSER_VERSION} does not support \`${details.flag}=<value>\`. Pass \`${details.flag}\` and its value as separate arguments.`;
|
|
676
|
+
}
|
|
630
677
|
if (details.reason === "unexpected-flag" && details.receivedToken) {
|
|
631
678
|
return `Flag \`${details.flag}\` requires a value, but received \`${details.receivedToken}\` instead. Pass a non-flag value immediately after \`${details.flag}\`.`;
|
|
632
679
|
}
|
|
@@ -683,7 +730,7 @@ function parseComparableNavigationUrl(url) {
|
|
|
683
730
|
export function getDefaultHeadlessCompatUserAgent(platform = process.platform) {
|
|
684
731
|
return DEFAULT_HEADLESS_COMPAT_USER_AGENT_BY_PLATFORM[platform] ?? FALLBACK_HEADLESS_COMPAT_USER_AGENT;
|
|
685
732
|
}
|
|
686
|
-
export function canUseHeadlessCompatibilityUserAgent(args, env =
|
|
733
|
+
export function canUseHeadlessCompatibilityUserAgent(args, env = getAgentBrowserProcessEnvironment()) {
|
|
687
734
|
if (hasFlagToken(args, "--user-agent") || hasFlagToken(args, "--args"))
|
|
688
735
|
return false;
|
|
689
736
|
if (hasFlagToken(args, "--cdp") || hasFlagToken(args, "--provider") || hasFlagToken(args, "-p"))
|
|
@@ -716,6 +763,14 @@ function getCompatibilityWorkaround(args, commandInfo) {
|
|
|
716
763
|
reason: "OpenAI web properties currently challenge the default headless Chrome user agent; inject a normal Chrome user agent to preserve the default headless workflow without requiring headed mode or auto-connect.",
|
|
717
764
|
};
|
|
718
765
|
}
|
|
766
|
+
function stripExplicitSessionArgs(args) {
|
|
767
|
+
const sessionTokenIndexes = new Set();
|
|
768
|
+
for (const occurrence of scanUpstreamGlobalFlagOccurrences(args, "--session")) {
|
|
769
|
+
sessionTokenIndexes.add(occurrence.index);
|
|
770
|
+
sessionTokenIndexes.add(occurrence.index + 1);
|
|
771
|
+
}
|
|
772
|
+
return args.filter((_token, index) => !sessionTokenIndexes.has(index));
|
|
773
|
+
}
|
|
719
774
|
function stripExplicitNamespaceArgs(args) {
|
|
720
775
|
const namespaceTokenIndexes = new Set();
|
|
721
776
|
for (const occurrence of scanUpstreamGlobalFlagOccurrences(args, "--namespace")) {
|
|
@@ -731,7 +786,6 @@ export function getStartupScopedFlags(args) {
|
|
|
731
786
|
}
|
|
732
787
|
export function buildExecutionPlan(args, options) {
|
|
733
788
|
const invalidValueFlag = getInvalidValueFlagDetails(args);
|
|
734
|
-
const unsupportedIdentityAssignment = getUnsupportedLeadingIdentityAssignment(args);
|
|
735
789
|
const explicitNamespacePresent = scanUpstreamGlobalFlagOccurrences(args, "--namespace").length > 0;
|
|
736
790
|
const explicitNamespace = extractExplicitNamespace(args);
|
|
737
791
|
const managedSessionNamespace = canonicalizeAgentBrowserNamespace(options.managedSessionNamespace);
|
|
@@ -741,7 +795,7 @@ export function buildExecutionPlan(args, options) {
|
|
|
741
795
|
const commandInfo = argvDescriptor.commandInfo;
|
|
742
796
|
const commandNeedsManagedSession = !plainTextInspection && needsManagedSession(argvDescriptor);
|
|
743
797
|
const effectiveArgs = plainTextInspection ? [...args] : args.includes("--json") ? [] : ["--json"];
|
|
744
|
-
let namespace = explicitNamespace;
|
|
798
|
+
let namespace = explicitNamespacePresent ? explicitNamespace ?? "" : undefined;
|
|
745
799
|
if (plainTextInspection) {
|
|
746
800
|
return {
|
|
747
801
|
commandInfo,
|
|
@@ -763,16 +817,6 @@ export function buildExecutionPlan(args, options) {
|
|
|
763
817
|
validationError: formatInvalidValueFlagError(invalidValueFlag),
|
|
764
818
|
};
|
|
765
819
|
}
|
|
766
|
-
if (unsupportedIdentityAssignment) {
|
|
767
|
-
return {
|
|
768
|
-
commandInfo: {},
|
|
769
|
-
effectiveArgs,
|
|
770
|
-
plainTextInspection: false,
|
|
771
|
-
startupScopedFlags: [],
|
|
772
|
-
usedImplicitSession: false,
|
|
773
|
-
validationError: `${unsupportedIdentityAssignment}=... is not supported by agent-browser 0.33.2. Pass ${unsupportedIdentityAssignment} and its value as separate arguments.`,
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
820
|
for (const flag of ["--session", "--namespace"]) {
|
|
777
821
|
if (countExplicitGlobalFlags(args, flag) <= 1)
|
|
778
822
|
continue;
|
|
@@ -786,8 +830,13 @@ export function buildExecutionPlan(args, options) {
|
|
|
786
830
|
};
|
|
787
831
|
}
|
|
788
832
|
const explicitSessionName = extractExplicitSessionName(args);
|
|
789
|
-
if (explicitSessionName && !
|
|
790
|
-
|
|
833
|
+
if (explicitSessionName && !explicitNamespacePresent) {
|
|
834
|
+
const targetsCurrentManagedSession = options.managedSessionActive
|
|
835
|
+
&& getAgentBrowserSessionIdentityKey(explicitSessionName, managedSessionNamespace)
|
|
836
|
+
=== getAgentBrowserSessionIdentityKey(options.managedSessionName, managedSessionNamespace);
|
|
837
|
+
namespace = targetsCurrentManagedSession
|
|
838
|
+
? managedSessionNamespace
|
|
839
|
+
: resolveAgentBrowserNamespace(args, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE);
|
|
791
840
|
}
|
|
792
841
|
const shouldCreateFreshManagedSession = !explicitSessionName && options.sessionMode === "fresh" && commandInfo.command !== undefined && !isCloseCommand(commandInfo.command);
|
|
793
842
|
let argsToAppend = args;
|
|
@@ -816,8 +865,8 @@ export function buildExecutionPlan(args, options) {
|
|
|
816
865
|
].join(" ");
|
|
817
866
|
}
|
|
818
867
|
else {
|
|
819
|
-
namespace = explicitNamespacePresent ? explicitNamespace : managedSessionNamespace;
|
|
820
|
-
if (namespace)
|
|
868
|
+
namespace = explicitNamespacePresent ? explicitNamespace ?? "" : managedSessionNamespace;
|
|
869
|
+
if (namespace !== undefined)
|
|
821
870
|
effectiveArgs.push("--namespace", namespace);
|
|
822
871
|
effectiveArgs.push("--session", options.managedSessionName);
|
|
823
872
|
if (explicitNamespacePresent)
|
|
@@ -828,7 +877,7 @@ export function buildExecutionPlan(args, options) {
|
|
|
828
877
|
}
|
|
829
878
|
}
|
|
830
879
|
else if (shouldCreateFreshManagedSession && commandNeedsManagedSession) {
|
|
831
|
-
if (namespace)
|
|
880
|
+
if (namespace !== undefined)
|
|
832
881
|
effectiveArgs.push("--namespace", namespace);
|
|
833
882
|
effectiveArgs.push("--session", options.freshSessionName);
|
|
834
883
|
if (explicitNamespacePresent)
|
|
@@ -836,14 +885,43 @@ export function buildExecutionPlan(args, options) {
|
|
|
836
885
|
managedSessionName = options.freshSessionName;
|
|
837
886
|
sessionName = options.freshSessionName;
|
|
838
887
|
}
|
|
839
|
-
|
|
840
|
-
&& canUseHeadlessCompatibilityUserAgent(args)
|
|
841
|
-
&& options.managedSessionActive
|
|
888
|
+
const targetsActiveManagedSession = options.managedSessionActive
|
|
842
889
|
&& sessionName
|
|
843
|
-
&& getAgentBrowserSessionIdentityKey(sessionName, namespace) === getAgentBrowserSessionIdentityKey(options.managedSessionName, options.managedSessionNamespace)
|
|
844
|
-
|
|
890
|
+
&& getAgentBrowserSessionIdentityKey(sessionName, namespace) === getAgentBrowserSessionIdentityKey(options.managedSessionName, options.managedSessionNamespace);
|
|
891
|
+
if (targetsActiveManagedSession && startupScopedFlags.length > 0 && !isCloseCommand(commandInfo.command) && !validationError) {
|
|
892
|
+
const recoveryArgs = explicitSessionName ? stripExplicitSessionArgs(args) : args;
|
|
893
|
+
recoveryHint = {
|
|
894
|
+
exampleArgs: recoveryArgs,
|
|
895
|
+
exampleParams: { args: recoveryArgs, sessionMode: "fresh" },
|
|
896
|
+
reason: `Launch-scoped flags (${LAUNCH_SCOPED_FLAG_LABEL}) need a fresh upstream launch once the extension-managed session is already active.`,
|
|
897
|
+
recommendedSessionMode: "fresh",
|
|
898
|
+
};
|
|
899
|
+
validationError = [
|
|
900
|
+
`The current extension-managed agent-browser session is already running, so launch-scoped flags ${startupScopedFlags.join(", ")} would replace or be ignored by upstream agent-browser.`,
|
|
901
|
+
explicitSessionName
|
|
902
|
+
? "Remove the explicit `--session` and retry with `sessionMode: \"fresh\"` to force a fresh upstream launch."
|
|
903
|
+
: "Retry this call with `sessionMode: \"fresh\"` to force a fresh upstream launch.",
|
|
904
|
+
].join(" ");
|
|
905
|
+
}
|
|
906
|
+
if (targetsActiveManagedSession && canUseHeadlessCompatibilityUserAgent(args)) {
|
|
907
|
+
if (requestedCompatibilityWorkaround && !options.managedSessionCompatibilityWorkaround && !validationError) {
|
|
908
|
+
compatibilityWorkaround = undefined;
|
|
909
|
+
const recoveryArgs = explicitSessionName ? stripExplicitSessionArgs(args) : args;
|
|
910
|
+
recoveryHint = {
|
|
911
|
+
exampleArgs: recoveryArgs,
|
|
912
|
+
exampleParams: { args: recoveryArgs, sessionMode: "fresh" },
|
|
913
|
+
reason: "The requested site compatibility user agent is launch-scoped and needs a fresh browser session.",
|
|
914
|
+
recommendedSessionMode: "fresh",
|
|
915
|
+
};
|
|
916
|
+
validationError = explicitSessionName
|
|
917
|
+
? "The current extension-managed agent-browser session is already running without the user agent required by this site. Remove the explicit `--session` and retry with `sessionMode: \"fresh\"` so the compatibility user agent is applied at launch."
|
|
918
|
+
: "The current extension-managed agent-browser session is already running without the user agent required by this site. Retry this call with `sessionMode: \"fresh\"` so the compatibility user agent is applied at launch.";
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
compatibilityWorkaround = requestedCompatibilityWorkaround ?? options.managedSessionCompatibilityWorkaround;
|
|
922
|
+
}
|
|
845
923
|
}
|
|
846
|
-
if (compatibilityWorkaround) {
|
|
924
|
+
if (compatibilityWorkaround && !targetsActiveManagedSession) {
|
|
847
925
|
effectiveArgs.push("--user-agent", getDefaultHeadlessCompatUserAgent());
|
|
848
926
|
}
|
|
849
927
|
effectiveArgs.push(...argsToAppend);
|