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.
Files changed (64) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/README.md +75 -42
  3. package/dist/extensions/agent-browser/index.js +13 -83
  4. package/dist/extensions/agent-browser/lib/argv-grammar.js +8 -2
  5. package/dist/extensions/agent-browser/lib/batch-lifecycle.js +1 -1
  6. package/dist/extensions/agent-browser/lib/command-policy.js +4 -7
  7. package/dist/extensions/agent-browser/lib/command-taxonomy.js +19 -11
  8. package/dist/extensions/agent-browser/lib/config-policy.js +25 -1
  9. package/dist/extensions/agent-browser/lib/config.js +1 -1
  10. package/dist/extensions/agent-browser/lib/input-modes/job.js +0 -9
  11. package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -1
  12. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +18 -4
  13. package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +3 -138
  14. package/dist/extensions/agent-browser/lib/managed-session-restore.js +1 -81
  15. package/dist/extensions/agent-browser/lib/managed-session-storage.js +4 -1
  16. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +31 -26
  17. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +60 -8
  18. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +1 -4
  19. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +119 -2
  20. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +7 -6
  21. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +31 -40
  22. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +76 -70
  23. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +6 -8
  24. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +5 -10
  25. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +41 -21
  26. package/dist/extensions/agent-browser/lib/page-target-validation.js +270 -0
  27. package/dist/extensions/agent-browser/lib/playbook.js +13 -12
  28. package/dist/extensions/agent-browser/lib/process-identity.js +11 -10
  29. package/dist/extensions/agent-browser/lib/process.js +18 -82
  30. package/dist/extensions/agent-browser/lib/recording-reservations.js +11 -78
  31. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +1 -1
  32. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +27 -14
  33. package/dist/extensions/agent-browser/lib/results/presentation/common.js +20 -2
  34. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +11 -16
  35. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +12 -9
  36. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +2 -2
  37. package/dist/extensions/agent-browser/lib/results/presentation.js +31 -4
  38. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +1 -1
  39. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +9 -0
  40. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +3 -3
  41. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +2 -1
  42. package/dist/extensions/agent-browser/lib/results/snapshot.js +4 -4
  43. package/dist/extensions/agent-browser/lib/runtime.js +73 -72
  44. package/dist/extensions/agent-browser/lib/session-page-state.js +12 -3
  45. package/dist/extensions/agent-browser/lib/temp.js +1 -2
  46. package/dist/extensions/agent-browser/lib/upstream-version.js +5 -5
  47. package/dist/extensions/agent-browser/lib/web-search.js +108 -24
  48. package/dist/scripts/agent-browser-target.mjs +19 -1
  49. package/docs/ARCHITECTURE.md +24 -20
  50. package/docs/COMMAND_REFERENCE.md +181 -49
  51. package/docs/ELECTRON.md +2 -2
  52. package/docs/RELEASE.md +10 -8
  53. package/docs/REQUIREMENTS.md +8 -7
  54. package/docs/SUPPORT_MATRIX.md +31 -26
  55. package/docs/TOOL_CONTRACT.md +89 -56
  56. package/package.json +1 -1
  57. package/scripts/agent-browser-capability-baseline.mjs +65 -5
  58. package/scripts/agent-browser-target.mjs +19 -1
  59. package/scripts/config.mjs +1 -0
  60. package/scripts/doctor.mjs +15 -9
  61. package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -20
  62. package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +0 -601
  63. package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -78
  64. package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -37
@@ -9,25 +9,24 @@ import { canUseHeadlessCompatibilityUserAgent, createEphemeralSessionSeed, creat
9
9
  import { extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace, isUpstreamEnvFlagEnabled, resolveAgentBrowserNamespace } from "./lib/argv-grammar.js";
10
10
  import { parseArgvDescriptor } from "./lib/argv-descriptor.js";
11
11
  import { needsManagedSession } from "./lib/command-policy.js";
12
- import { cleanupManagedSessionRestoreConfig, ManagedSessionRestoreState } from "./lib/managed-session-restore.js";
12
+ import { ManagedSessionRestoreState } from "./lib/managed-session-restore.js";
13
13
  import { isRecord } from "./lib/parsing.js";
14
14
  import { runAgentBrowserProcess } from "./lib/process.js";
15
15
  import { getAgentBrowserProcessEnvironment, withIsolatedAgentBrowserEnvironment } from "./lib/process-environment.js";
16
- import { TARGET_AGENT_BROWSER_VERSION, TARGET_AGENT_BROWSER_VERSION_LABEL, getAgentBrowserVersionValidationError, parseAgentBrowserVersionOutput, } from "./lib/upstream-version.js";
16
+ import { MINIMUM_AGENT_BROWSER_VERSION, SUPPORTED_AGENT_BROWSER_VERSION_LABEL, TARGET_AGENT_BROWSER_VERSION, getAgentBrowserVersionValidationError, parseAgentBrowserVersionOutput, } from "./lib/upstream-version.js";
17
17
  import { buildPromptPolicy, getLatestUserPrompt, shouldAppendBrowserSystemPrompt } from "./lib/prompt-policy.js";
18
18
  import { isCloseAllCommand, isCloseCommand } from "./lib/command-taxonomy.js";
19
19
  import { hasLaunchScopedFlagToken } from "./lib/launch-scoped-flags.js";
20
20
  import { cleanupSecureTempArtifacts } from "./lib/temp.js";
21
21
  import { AGENT_BROWSER_PARAMS } from "./lib/input-modes/params.js";
22
22
  import { AGENT_BROWSER_SCRIPT_DEFAULT_TIMEOUT_MS, AGENT_BROWSER_SCRIPT_NAMESPACE, createAgentBrowserScriptSessionName, isAgentBrowserScriptSessionName, runAgentBrowserScript, } from "./lib/input-modes/script.js";
23
- import { parseAllowedDomainsPolicyFromArgs } from "./lib/navigation-policy.js";
24
23
  import { closeManagedSession, getSessionContextKey, runAgentBrowserTool } from "./lib/orchestration/browser-run/index.js";
25
24
  import { canonicalizeExplicitArtifactDestination, getExplicitArtifactDestination } from "./lib/orchestration/browser-run/artifact-paths.js";
26
25
  import { findElectronLaunchRecordForSession, getActiveElectronRecords } from "./lib/orchestration/browser-run/session-state.js";
27
26
  import { parseBatchCommandArgument, parseUserBatchStdin } from "./lib/orchestration/batch-stdin.js";
28
27
  import { ELECTRON_POST_COMMAND_STATUS_SETTLE_MS, ELECTRON_PROFILE_ISOLATION_DETAILS, cleanupActiveElectronHostLaunches, handleElectronHostInput, restoreElectronLaunchRecordsFromBranch, } from "./lib/orchestration/electron-host/index.js";
29
28
  import { buildValidationFailureResult, resolveAgentBrowserInput } from "./lib/orchestration/input-plan.js";
30
- import { applyAgentBrowserOutputPath, getAgentBrowserOutputPathValidationError, normalizeRequestedOutputPath } from "./lib/orchestration/output-file.js";
29
+ import { applyAgentBrowserOutputPath, normalizeRequestedOutputPath } from "./lib/orchestration/output-file.js";
31
30
  import { appendScriptSessionLease, buildScriptBrowserEnvelope, buildScriptToolResult, getScriptSessionLeasesFromBranch } from "./lib/orchestration/script-mode.js";
32
31
  import { formatSessionArtifactRetentionSummary, getSessionArtifactManifestEntryKey, isPendingRecordingCommand, isSessionArtifactManifest, mergeSessionArtifactManifest, retirePendingRecordingManifestEntries } from "./lib/results/artifact-manifest.js";
33
32
  import { appendUniqueAgentBrowserNextActions, applyNamespaceToNextActions, applySessionToNextActions, buildNextToolAction } from "./lib/results/next-actions.js";
@@ -97,7 +96,7 @@ function getArtifactPreflightValidationError(options) {
97
96
  for (const [index, step] of steps.entries()) {
98
97
  const commandStep = extractUpstreamCommandTokens(step);
99
98
  if (batch) {
100
- const stepValidationError = validateToolArgs(step);
99
+ const stepValidationError = validateToolArgs(step, { batchStep: true });
101
100
  if (stepValidationError)
102
101
  return `Unsupported batch step ${index + 1}: ${stepValidationError}`;
103
102
  if (sawBatchClose && commandStep[0] === "record" && (commandStep[1] === "start" || commandStep[1] === "restart")) {
@@ -359,65 +358,6 @@ function restoreAttachedSessionKeysFromBranch(branch) {
359
358
  }
360
359
  return attachedSessionKeys;
361
360
  }
362
- function restoreAllowedDomainsBySessionFromBranch(branch) {
363
- const restoredPolicies = new Map();
364
- for (const entry of branch) {
365
- if (!isRecord(entry) || entry.type !== "message")
366
- continue;
367
- const message = isRecord(entry.message) ? entry.message : undefined;
368
- if (!message || message.toolName !== "agent_browser")
369
- continue;
370
- const details = isRecord(message.details) ? message.details : undefined;
371
- if (!details)
372
- continue;
373
- const succeeded = getSuccessfulToolResult(details, message);
374
- const batchCloseLifecycle = getSuccessfulBatchCloseLifecycle(details.batchSteps);
375
- const args = getToolResultArgs(details);
376
- const command = typeof details.command === "string" ? details.command : extractUpstreamCommandTokens(args)[0];
377
- const sessionName = typeof details.sessionName === "string" ? details.sessionName : undefined;
378
- const namespace = typeof details.namespace === "string" ? details.namespace : undefined;
379
- const sessionKey = getSessionContextKey(sessionName, namespace);
380
- const explicitSessionName = extractExplicitSessionName(args);
381
- if (detailsReportCloseAllApplied(details, succeeded))
382
- deleteIdentityKeysInNamespace(restoredPolicies, namespace);
383
- const outcome = getManagedSessionOutcome(details);
384
- const outcomeSucceeded = outcome?.succeeded === true;
385
- const outcomeStatus = typeof outcome?.status === "string" ? outcome.status : undefined;
386
- const outcomeCurrentSessionName = typeof outcome?.currentSessionName === "string" ? outcome.currentSessionName : undefined;
387
- const outcomeAttemptedSessionName = typeof outcome?.attemptedSessionName === "string" ? outcome.attemptedSessionName : undefined;
388
- if (outcomeSucceeded && outcomeStatus === "closed") {
389
- const closedSessionName = outcomeAttemptedSessionName ?? outcomeCurrentSessionName ?? sessionName;
390
- if (closedSessionName)
391
- restoredPolicies.delete(getSessionContextKey(closedSessionName, namespace) ?? closedSessionName);
392
- }
393
- if (outcomeSucceeded && outcomeStatus === "replaced") {
394
- const replacedSessionName = typeof outcome.replacedSessionName === "string" ? outcome.replacedSessionName : undefined;
395
- const replacedSessionNamespace = typeof outcome.replacedSessionNamespace === "string" ? outcome.replacedSessionNamespace : namespace;
396
- if (replacedSessionName)
397
- restoredPolicies.delete(getSessionContextKey(replacedSessionName, replacedSessionNamespace) ?? replacedSessionName);
398
- }
399
- if ((succeeded && isCloseCommand(command)) || batchCloseLifecycle) {
400
- const closedSessionName = explicitSessionName ?? sessionName ?? outcomeAttemptedSessionName ?? outcomeCurrentSessionName;
401
- if (closedSessionName)
402
- restoredPolicies.delete(getSessionContextKey(closedSessionName, namespace) ?? closedSessionName);
403
- }
404
- const electron = isRecord(details.electron) ? details.electron : undefined;
405
- const cleanup = isRecord(electron?.cleanup) ? electron.cleanup : undefined;
406
- const cleanupResults = Array.isArray(cleanup?.results) ? cleanup.results : [];
407
- for (const cleanupResult of cleanupResults) {
408
- for (const identity of getCleanupResultClosedManagedSessionIdentities(cleanupResult, namespace)) {
409
- restoredPolicies.delete(getSessionContextKey(identity.sessionName, identity.namespace) ?? identity.sessionName);
410
- }
411
- }
412
- const outcomeKeepsSessionCurrent = outcome?.activeAfter === true
413
- && (outcomeStatus === "created" || outcomeStatus === "replaced" || outcomeStatus === "unchanged")
414
- && outcomeCurrentSessionName === sessionName;
415
- const policy = (succeeded || outcomeKeepsSessionCurrent) && sessionKey && !isCloseCommand(command) && batchCloseLifecycle?.endsClosed !== true ? parseAllowedDomainsPolicyFromArgs(args) : undefined;
416
- if (policy && sessionKey)
417
- restoredPolicies.set(sessionKey, policy);
418
- }
419
- return restoredPolicies;
420
- }
421
361
  function trackOwnedManagedSession(sessions, sessionName, cwd, options = {}) {
422
362
  if (!sessionName)
423
363
  return;
@@ -905,7 +845,6 @@ export default function agentBrowserExtension(pi) {
905
845
  let activeRecordingReservations = new Map();
906
846
  let recordingSessionTombstones = new Map();
907
847
  let recordingSessionTombstonesToPersist = new Map();
908
- let allowedDomainsBySession = new Map();
909
848
  let attachedSessionKeys = new Set();
910
849
  let networkRoutesBySession = new Map();
911
850
  let electronLaunchRecords = new Map();
@@ -1064,15 +1003,14 @@ export default function agentBrowserExtension(pi) {
1064
1003
  failureCategory: "validation-error",
1065
1004
  observedVersion,
1066
1005
  resultCategory: "failure",
1067
- versionValidation: { expected: TARGET_AGENT_BROWSER_VERSION_LABEL, observed: observedVersion },
1006
+ minimumSupportedVersion: MINIMUM_AGENT_BROWSER_VERSION,
1007
+ versionValidation: { expected: SUPPORTED_AGENT_BROWSER_VERSION_LABEL, observed: observedVersion },
1068
1008
  },
1069
1009
  isError: true,
1070
1010
  };
1071
1011
  };
1072
1012
  const clearSessionScopedBrowserState = (sessionName, namespace) => {
1073
1013
  const key = getSessionContextKey(sessionName, namespace) ?? sessionName;
1074
- allowedDomainsBySession = new Map(allowedDomainsBySession);
1075
- allowedDomainsBySession.delete(key);
1076
1014
  attachedSessionKeys.delete(key);
1077
1015
  networkRoutesBySession = new Map(networkRoutesBySession);
1078
1016
  networkRoutesBySession.delete(key);
@@ -1184,7 +1122,6 @@ export default function agentBrowserExtension(pi) {
1184
1122
  for (const [key, reservation] of activeRecordingReservations)
1185
1123
  restoredRecordingState.active.set(key, reservation);
1186
1124
  activeRecordingReservations = restoredRecordingState.active;
1187
- allowedDomainsBySession = restoreAllowedDomainsBySessionFromBranch(branch);
1188
1125
  attachedSessionKeys = restoreAttachedSessionKeysFromBranch(branch);
1189
1126
  networkRoutesBySession = new Map();
1190
1127
  electronLaunchRecords = restoreElectronLaunchRecordsFromBranch(branch);
@@ -1350,7 +1287,6 @@ export default function agentBrowserExtension(pi) {
1350
1287
  activeRecordingReservations = new Map();
1351
1288
  recordingSessionTombstones = new Map();
1352
1289
  recordingSessionTombstonesToPersist = new Map();
1353
- allowedDomainsBySession = new Map();
1354
1290
  attachedSessionKeys = new Set();
1355
1291
  networkRoutesBySession = new Map();
1356
1292
  electronLaunchRecords = new Map();
@@ -1358,7 +1294,6 @@ export default function agentBrowserExtension(pi) {
1358
1294
  branchOwnedElectronLaunchIds = new Set();
1359
1295
  electronChildProcesses = new Map();
1360
1296
  ownedManagedSessions.clear();
1361
- cleanupManagedSessionRestoreConfig();
1362
1297
  await cleanupSecureTempArtifacts({ preservePaths: preservedElectronProfileDirs });
1363
1298
  });
1364
1299
  pi.on("before_agent_start", async (event, ctx) => {
@@ -1401,7 +1336,7 @@ export default function agentBrowserExtension(pi) {
1401
1336
  const agentBrowserTool = {
1402
1337
  name: "agent_browser",
1403
1338
  label: "Agent Browser",
1404
- description: "Browse and interact with websites using agent-browser. Use this for web research, reading live docs, opening pages, taking snapshots or screenshots, clicking links, filling forms, extracting page content, and authenticated/profile-based browser work. Input choice: `script` for one-shot JavaScript orchestration; default `args` for open → snapshot -i → click/fill @refs; `semanticAction` for stable role/text/label targets; `job` or `qa` for multi-step checks; `electron` only for desktop apps; experimental `sourceLookup` / `networkSourceLookup` for candidates only.",
1339
+ description: "Browse and interact with websites using agent-browser. Use this for reading live pages, opening known URLs, taking snapshots or screenshots, clicking links, filling forms, extracting page content, and authenticated/profile-based browser work. Input choice: `script` for one-shot JavaScript orchestration; default `args` for open → snapshot -i → click/fill @refs; `semanticAction` for stable role/text/label targets; `job` or `qa` for multi-step checks; `electron` only for desktop apps; experimental `sourceLookup` / `networkSourceLookup` for candidates only.",
1405
1340
  promptSnippet: "Browse websites, read live docs, click and fill pages, extract browser content, take screenshots, and automate real web workflows.",
1406
1341
  promptGuidelines: toolPromptGuidelines,
1407
1342
  parameters: AGENT_BROWSER_PARAMS,
@@ -1428,10 +1363,6 @@ export default function agentBrowserExtension(pi) {
1428
1363
  if (resolvedInput.status === "invalid") {
1429
1364
  return buildValidationFailureResult(resolvedInput);
1430
1365
  }
1431
- const outputPathValidationError = getAgentBrowserOutputPathValidationError(outputPath, ctx.cwd);
1432
- if (outputPathValidationError) {
1433
- return buildValidationFailureResult({ attemptedKind: resolvedInput.kind, kind: "invalid", redactedArgs: resolvedInput.redactedArgs, status: "invalid", toolArgs: resolvedInput.toolArgs, toolStdin: resolvedInput.toolStdin, validationError: outputPathValidationError });
1434
- }
1435
1366
  const applyUnserializedOutputPath = async (result, preserveTextContent = false) => {
1436
1367
  if (!outputPath || result.isError === true || (isRecord(result.details) && result.details.resultCategory === "failure"))
1437
1368
  return result;
@@ -1451,10 +1382,8 @@ export default function agentBrowserExtension(pi) {
1451
1382
  const versionCheckCommand = extractUpstreamCommandTokens(resolvedInput.toolArgs)[0];
1452
1383
  const electronHostOnlyAction = resolvedInput.kind === "electron" && ["cleanup", "list", "status"].includes(resolvedInput.compiledElectron.action);
1453
1384
  const browserBackedVersionCheck = needsManagedSession(parseArgvDescriptor(resolvedInput.toolArgs));
1454
- if (!electronHostOnlyAction && browserBackedVersionCheck && !isPlainTextInspectionArgs(resolvedInput.toolArgs) && !isCloseCommand(versionCheckCommand) && signal?.aborted !== true) {
1455
- const versionFailure = resolvedInput.kind === "script"
1456
- ? await withIsolatedAgentBrowserEnvironment(() => validateUpstreamVersion(ctx.cwd, signal))
1457
- : await validateUpstreamVersion(ctx.cwd, signal);
1385
+ if (resolvedInput.kind !== "script" && !electronHostOnlyAction && browserBackedVersionCheck && !isPlainTextInspectionArgs(resolvedInput.toolArgs) && !isCloseCommand(versionCheckCommand) && signal?.aborted !== true) {
1386
+ const versionFailure = await validateUpstreamVersion(ctx.cwd, signal);
1458
1387
  if (versionFailure)
1459
1388
  return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, result: versionFailure });
1460
1389
  }
@@ -1496,6 +1425,10 @@ export default function agentBrowserExtension(pi) {
1496
1425
  });
1497
1426
  activeScriptExecutions.add(scriptExecution);
1498
1427
  try {
1428
+ // Keep preflight inside shutdown tracking so quit cannot race into starting the sandbox afterward.
1429
+ const versionFailure = await withIsolatedAgentBrowserEnvironment(() => validateUpstreamVersion(ctx.cwd, scriptController.signal));
1430
+ if (versionFailure)
1431
+ return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, result: versionFailure });
1499
1432
  const pendingRun = runAgentBrowserScript({
1500
1433
  beforeFirstCall() {
1501
1434
  appendScriptSessionLease(pi, sessionName, "active");
@@ -1666,7 +1599,6 @@ export default function agentBrowserExtension(pi) {
1666
1599
  const generationAtStart = branchStateGeneration;
1667
1600
  const sessionPageStateUpdate = sessionPageState.beginUpdate();
1668
1601
  const browserRunState = {
1669
- allowedDomainsBySession,
1670
1602
  artifactManifest,
1671
1603
  attachedSessionKeys,
1672
1604
  closedManagedSessionNames: new Set(),
@@ -1688,7 +1620,6 @@ export default function agentBrowserExtension(pi) {
1688
1620
  sessionPageState,
1689
1621
  traceOwners,
1690
1622
  };
1691
- const initialAllowedDomainsBySession = browserRunState.allowedDomainsBySession;
1692
1623
  const initialArtifactManifest = browserRunState.artifactManifest;
1693
1624
  const initialNetworkRoutesBySession = browserRunState.networkRoutesBySession;
1694
1625
  const attachedSessionRequested = isAttachedBrowserInvocation(toolArgs)
@@ -1748,7 +1679,6 @@ export default function agentBrowserExtension(pi) {
1748
1679
  }
1749
1680
  }
1750
1681
  if (branchRestoreStillCurrent) {
1751
- allowedDomainsBySession = mergeBrowserRunMap(allowedDomainsBySession, initialAllowedDomainsBySession, browserRunState.allowedDomainsBySession);
1752
1682
  networkRoutesBySession = mergeBrowserRunMap(networkRoutesBySession, initialNetworkRoutesBySession, browserRunState.networkRoutesBySession);
1753
1683
  artifactManifest = mergeBrowserRunArtifactManifest(artifactManifest, initialArtifactManifest, browserRunState.artifactManifest);
1754
1684
  const handledBatchCloseKeys = syncRecordingReservationsFromResult(result);
@@ -12,6 +12,7 @@ export const GLOBAL_VALUE_FLAGS = [
12
12
  "--restore-check-fn",
13
13
  "--proxy",
14
14
  "--proxy-bypass",
15
+ "--ca-cert",
15
16
  "--headers",
16
17
  "--executable-path",
17
18
  "--extension",
@@ -37,6 +38,7 @@ export const GLOBAL_VALUE_FLAGS = [
37
38
  "--idle-timeout",
38
39
  ];
39
40
  export const COMMAND_VALUE_FLAGS = [
41
+ "--allowed-origins",
40
42
  "--baseline",
41
43
  "--body",
42
44
  "--categories",
@@ -47,6 +49,7 @@ export const COMMAND_VALUE_FLAGS = [
47
49
  "--domain",
48
50
  "--expires",
49
51
  "--filter",
52
+ "--frame",
50
53
  "--fn",
51
54
  "--label",
52
55
  "--load",
@@ -57,6 +60,7 @@ export const COMMAND_VALUE_FLAGS = [
57
60
  "--prefix",
58
61
  "--path",
59
62
  "--port",
63
+ "--params",
60
64
  "--resource-type",
61
65
  "--resource-types",
62
66
  "--sameSite",
@@ -90,7 +94,9 @@ export const GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES = new Set([
90
94
  "--ignore-https-errors",
91
95
  "--json",
92
96
  "--no-auto-dialog",
97
+ "--no-ca-cert",
93
98
  "--no-pin-tab",
99
+ "--no-webmcp",
94
100
  "--offline",
95
101
  "--pin-tab",
96
102
  "--quick",
@@ -169,7 +175,7 @@ export function isAgentBrowserSessionIdentityKeyInNamespace(identityKey, namespa
169
175
  const prefix = getAgentBrowserSessionIdentityKey("", namespace);
170
176
  return prefix ? identityKey.startsWith(prefix) : !identityKey.includes("\0");
171
177
  }
172
- /** Mirror upstream 0.34.0 global parsing: full argv, no `--` sentinel, and only global value payloads are skipped. */
178
+ /** Mirror upstream global parsing: full argv, no `--` sentinel, and only global value payloads are skipped. */
173
179
  export function scanUpstreamGlobalFlagOccurrences(args, targetFlag) {
174
180
  const occurrences = [];
175
181
  for (let index = 0; index < args.length; index += 1) {
@@ -295,7 +301,7 @@ export function projectUpstreamGlobalFlags(args) {
295
301
  }
296
302
  return { indices, tokens };
297
303
  }
298
- /** Mirror upstream 0.34.0 clean_args: remove global flags wherever they appear before command parsing. */
304
+ /** Mirror upstream clean_args: remove global flags wherever they appear before command parsing. */
299
305
  export function stripUpstreamGlobalFlags(args) {
300
306
  return projectUpstreamGlobalFlags(args).tokens;
301
307
  }
@@ -53,7 +53,7 @@ export function getSuccessfulBatchCloseLifecycle(rows, fallbackCommands = []) {
53
53
  statePath = typeof result?.statePath === "string" ? result.statePath : undefined;
54
54
  }
55
55
  else if (sawClose && command === "record") {
56
- if (browserLaunched === true || (subcommand === "stop" && browserLaunched === undefined)) {
56
+ if (browserLaunched !== false) {
57
57
  endsClosed = false;
58
58
  browserActiveAfterClose = true;
59
59
  }
@@ -5,8 +5,7 @@ const EMPTY_BOOLEAN_FLAGS = new Set();
5
5
  const JSON_BOOLEAN_FLAGS = new Set(["--json"]);
6
6
  const AUTH_SAVE_BOOLEAN_FLAGS = new Set(["--json", "--password-stdin"]);
7
7
  const AUTH_SAVE_VALUE_FLAGS = new Set(["--password", "--password-selector", "--submit-selector", "--url", "--username", "--username-selector"]);
8
- const DASHBOARD_SUBCOMMANDS = new Set(["start", "stop"]);
9
- const DASHBOARD_START_VALUE_FLAGS = new Set(["--port"]);
8
+ const DASHBOARD_VALUE_FLAGS = new Set(["--allowed-origins", "--port"]);
10
9
  const DOCTOR_BOOLEAN_FLAGS = new Set(["--fix", "--headed", "--json", "--offline", "--quick", "--webgpu"]);
11
10
  const INSTALL_BOOLEAN_FLAGS = new Set(["--with-deps", "-d"]);
12
11
  const STATE_SESSIONLESS_SUBCOMMANDS = new Set(["list", "show", "clear", "clean", "rename"]);
@@ -26,11 +25,9 @@ function isSessionlessAuthCommand(commandTokens) {
26
25
  }
27
26
  function isSessionlessDashboardCommand(commandTokens) {
28
27
  const [, subcommand, ...rest] = commandTokens;
29
- if (subcommand === undefined)
30
- return true;
31
- if (!DASHBOARD_SUBCOMMANDS.has(subcommand))
32
- return false;
33
- return subcommand === "start" ? hasOnlyOptionFlags(rest, JSON_BOOLEAN_FLAGS, DASHBOARD_START_VALUE_FLAGS) : rest.length === 0;
28
+ if (subcommand === "stop")
29
+ return rest.length === 0;
30
+ return hasOnlyOptionFlags(subcommand === "start" ? rest : commandTokens.slice(1), JSON_BOOLEAN_FLAGS, DASHBOARD_VALUE_FLAGS);
34
31
  }
35
32
  function isSessionlessStateCommand(commandTokens) {
36
33
  const [, subcommand, firstArg, secondArg, ...rest] = commandTokens;
@@ -1,6 +1,7 @@
1
1
  const ADDITIONAL_COMMAND_TOKENS = [
2
- "a11y", "auth", "chat", "clipboard", "confirm", "connect", "dashboard", "deny", "device", "dialog", "diff", "doctor", "errors", "eval", "find", "frame", "get", "highlight", "inspect", "install", "is", "mcp", "plugin", "plugins", "profiles", "profiler", "react", "record", "removeinitscript", "session", "set", "skills", "snapshot", "state", "stream", "trace", "upgrade", "vitals", "wait", "web-vitals", "window",
2
+ "a11y", "auth", "chat", "clipboard", "confirm", "connect", "dashboard", "deny", "device", "dialog", "diff", "doctor", "errors", "eval", "find", "frame", "get", "highlight", "inspect", "install", "is", "mcp", "plugin", "plugins", "profiles", "profiler", "react", "record", "removeinitscript", "session", "set", "skills", "snapshot", "state", "stream", "trace", "upgrade", "vitals", "wait", "web-vitals", "webmcp", "window",
3
3
  ];
4
+ const WEBMCP_PAGE_MUTATION_SUBCOMMANDS = new Set(["invoke", "result", "cancel"]);
4
5
  const COMMAND_CAPABILITIES = [
5
6
  {
6
7
  command: "back",
@@ -309,8 +310,8 @@ export function isCloseAllCommand(commandTokens) {
309
310
  export function isOpenNavigationCommand(command) {
310
311
  return hasCommandCapability(command, "openNavigation");
311
312
  }
312
- export function isReadOnlyDiagnosticSessionTargetCommand(command, _subcommand) {
313
- return hasCommandCapability(command, "readOnlyDiagnosticSessionTarget");
313
+ export function isReadOnlyDiagnosticSessionTargetCommand(command, subcommand) {
314
+ return hasCommandCapability(command, "readOnlyDiagnosticSessionTarget") || (command === "webmcp" && subcommand === "list");
314
315
  }
315
316
  export function isSessionTabPinningExcludedCommand(command) {
316
317
  return hasCommandCapability(command, "excludedFromPinning");
@@ -326,8 +327,11 @@ export function isRecordPageTransitionCommand(tokens) {
326
327
  return true;
327
328
  return tokens[1] === "restart" && tokens.length >= 4;
328
329
  }
330
+ export function isWebMcpPageMutationCommand(tokens) {
331
+ return isWebMcpPageMutation(tokens[0], tokens[1]);
332
+ }
329
333
  export function isRefInvalidatingBatchCommand(step) {
330
- return hasCommandCapability(step[0], "invalidatesBatchRefs") || isRecordPageTransitionCommand(step);
334
+ return hasCommandCapability(step[0], "invalidatesBatchRefs") || isRecordPageTransitionCommand(step) || isWebMcpPageMutationCommand(step);
331
335
  }
332
336
  export function isRefGuardedCommand(command) {
333
337
  return hasCommandCapability(command, "guardsPageRefs");
@@ -335,17 +339,21 @@ export function isRefGuardedCommand(command) {
335
339
  export function isElectronPostCommandHealthCommand(command) {
336
340
  return hasCommandCapability(command, "eligibleForElectronHealthProbe");
337
341
  }
338
- export function isNavigationObservableCommandName(command) {
339
- return hasCommandCapability(command, "navigationObservable");
342
+ function isWebMcpPageMutation(command, subcommand) {
343
+ return command === "webmcp" && WEBMCP_PAGE_MUTATION_SUBCOMMANDS.has(subcommand ?? "");
344
+ }
345
+ export function isNavigationObservableCommandName(command, subcommand) {
346
+ return hasCommandCapability(command, "navigationObservable") || isWebMcpPageMutation(command, subcommand);
340
347
  }
341
348
  export function isUnverifiedPageTransitionCommand(command, subcommand) {
342
349
  return ["back", "connect", "eval", "forward", "reload"].includes(command ?? "")
343
350
  || (command === "state" && subcommand === "load")
344
- || (command === "tab" && subcommand !== undefined && !["list", "new"].includes(subcommand));
351
+ || (command === "tab" && subcommand !== undefined && !["list", "new"].includes(subcommand))
352
+ || isWebMcpPageMutation(command, subcommand);
345
353
  }
346
- export function isPageMutationCommand(command) {
347
- return hasCommandCapability(command, "triggersPostMutationSnapshot");
354
+ export function isPageMutationCommand(command, subcommand) {
355
+ return hasCommandCapability(command, "triggersPostMutationSnapshot") || isWebMcpPageMutation(command, subcommand);
348
356
  }
349
- export function isPageChangeSummaryCommand(command) {
350
- return hasCommandCapability(command, "eligibleForPageChangeSummary");
357
+ export function isPageChangeSummaryCommand(command, subcommand) {
358
+ return hasCommandCapability(command, "eligibleForPageChangeSummary") || isWebMcpPageMutation(command, subcommand);
351
359
  }
@@ -7,10 +7,11 @@ import { join, resolve } from "node:path";
7
7
  /** @typedef {"global" | "project" | "override"} ConfigLayerScope */
8
8
  /** @typedef {"literal" | "env" | "command"} CredentialSourceKind */
9
9
  /** @typedef {"exa" | "brave"} WebSearchProvider */
10
+ /** @typedef {"auto" | "fast" | "instant" | "deep-lite" | "deep" | "deep-reasoning"} ExaSearchType */
10
11
  /** @typedef {"exaApiKey" | "braveApiKey"} WebSearchProviderConfigKey */
11
12
  /** @typedef {{ provider: WebSearchProvider; apiKeyEnv: string; configKey: WebSearchProviderConfigKey; label: string }} WebSearchProviderDescriptor */
12
13
  /** @typedef {{ name: string; policy?: BrowserDefaultProfilePolicy }} BrowserDefaultProfileConfig */
13
- /** @typedef {{ enabled?: boolean; preferredProvider?: WebSearchProvider; braveApiKey?: string; exaApiKey?: string }} WebSearchConfig */
14
+ /** @typedef {{ enabled?: boolean; preferredProvider?: WebSearchProvider; defaultSearchType?: ExaSearchType; braveApiKey?: string; exaApiKey?: string }} WebSearchConfig */
14
15
  /** @typedef {{ defaultProfile?: BrowserDefaultProfileConfig; executablePath?: string }} BrowserConfig */
15
16
  /** @typedef {{ version?: 1; webSearch?: WebSearchConfig; browser?: BrowserConfig }} AgentBrowserConfig */
16
17
  /** @typedef {{ config: AgentBrowserConfig; path: string; scope: ConfigLayerScope }} ConfigLayer */
@@ -45,6 +46,8 @@ export const WEB_SEARCH_PROVIDER_DESCRIPTORS = Object.freeze({
45
46
  export const WEB_SEARCH_PROVIDERS = Object.freeze(["exa", "brave"]);
46
47
  /** @type {WebSearchProvider} */
47
48
  export const DEFAULT_WEB_SEARCH_PROVIDER = "exa";
49
+ /** @type {readonly ExaSearchType[]} */
50
+ export const EXA_SEARCH_TYPES = Object.freeze(["auto", "fast", "instant", "deep-lite", "deep", "deep-reasoning"]);
48
51
  /** @type {Readonly<Record<WebSearchProvider, WebSearchProviderConfigKey>>} */
49
52
  export const WEB_SEARCH_PROVIDER_CONFIG_KEYS = Object.freeze({
50
53
  exa: WEB_SEARCH_PROVIDER_DESCRIPTORS.exa.configKey,
@@ -182,6 +185,24 @@ export function validateWebSearchProvider(value, path, errors) {
182
185
  }
183
186
  return provider;
184
187
  }
188
+ /**
189
+ * @param {unknown} value
190
+ * @param {string} path
191
+ * @param {string[]} errors
192
+ * @returns {ExaSearchType | undefined}
193
+ */
194
+ function validateExaSearchType(value, path, errors) {
195
+ if (value === undefined)
196
+ return undefined;
197
+ const searchType = validateString(value, path, errors)?.trim();
198
+ if (searchType === undefined)
199
+ return undefined;
200
+ if (!EXA_SEARCH_TYPES.includes(/** @type {ExaSearchType} */ (searchType))) {
201
+ errors.push(`${path} must be one of ${EXA_SEARCH_TYPES.join(", ")}.`);
202
+ return undefined;
203
+ }
204
+ return /** @type {ExaSearchType} */ (searchType);
205
+ }
185
206
  /**
186
207
  * @param {unknown} value
187
208
  * @param {string} path
@@ -251,6 +272,9 @@ export function validateAgentBrowserConfig(value, path, errors, warnings) {
251
272
  const preferredProvider = validateWebSearchProvider(value.webSearch.preferredProvider, `${path}.webSearch.preferredProvider`, errors);
252
273
  if (preferredProvider)
253
274
  webSearch.preferredProvider = preferredProvider;
275
+ const defaultSearchType = validateExaSearchType(value.webSearch.defaultSearchType, `${path}.webSearch.defaultSearchType`, errors);
276
+ if (defaultSearchType)
277
+ webSearch.defaultSearchType = defaultSearchType;
254
278
  for (const provider of WEB_SEARCH_PROVIDERS) {
255
279
  const descriptor = getWebSearchProviderDescriptor(provider);
256
280
  const apiKey = validateString(value.webSearch[descriptor.configKey], `${path}.webSearch.${descriptor.configKey}`, errors);
@@ -2,7 +2,7 @@ import { exec as execCallback } from "node:child_process";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { promisify } from "node:util";
4
4
  import { SECRET_COMMAND_TIMEOUT_MS, buildAgentBrowserConfigState, getAgentBrowserConfigPaths, getWebSearchCredentialSource, getWebSearchProviderOrder, loadAgentBrowserConfigStateSync, mergeAgentBrowserConfig, parseAgentBrowserConfigLayer, resolveEnvInterpolations, } from "./config-policy.js";
5
- export { AGENT_BROWSER_CONFIG_ENV, BRAVE_API_KEY_ENV, CONFIG_RELATIVE_PATH, DEFAULT_WEB_SEARCH_PROVIDER, EXA_API_KEY_ENV, GLOBAL_CONFIG_RELATIVE_PATH, SECRET_COMMAND_TIMEOUT_MS, WEB_SEARCH_PROVIDER_CONFIG_KEYS, WEB_SEARCH_PROVIDER_DESCRIPTORS, WEB_SEARCH_PROVIDER_ENV_VARS, WEB_SEARCH_PROVIDERS, buildAgentBrowserConfigState, buildWebSearchCredentialSources, canRegisterWebSearchTool, classifyCredentialSource, formatBrowserExecutableStatus, formatBrowserProfileStatus, getAgentBrowserConfigPaths, getCredentialSourceSummary, getGlobalAgentBrowserConfigPath, getProjectAgentBrowserConfigPath, getWebSearchCredentialSource, getWebSearchProviderConfigKey, getWebSearchProviderDescriptor, getWebSearchProviderEnvVar, getWebSearchProviderLabel, getWebSearchProviderOrder, hasPotentialCredentialSource, isPlaintextCredentialValue, isProjectSafeCredentialValueForProvider, isWebSearchProvider, loadAgentBrowserConfigStateSync, mergeAgentBrowserConfig, parseAgentBrowserConfigLayer, resolveEnvInterpolations, summarizeConfigFiles, validateAgentBrowserConfig, validateWebSearchProvider, } from "./config-policy.js";
5
+ export { AGENT_BROWSER_CONFIG_ENV, BRAVE_API_KEY_ENV, CONFIG_RELATIVE_PATH, DEFAULT_WEB_SEARCH_PROVIDER, EXA_API_KEY_ENV, EXA_SEARCH_TYPES, GLOBAL_CONFIG_RELATIVE_PATH, SECRET_COMMAND_TIMEOUT_MS, WEB_SEARCH_PROVIDER_CONFIG_KEYS, WEB_SEARCH_PROVIDER_DESCRIPTORS, WEB_SEARCH_PROVIDER_ENV_VARS, WEB_SEARCH_PROVIDERS, buildAgentBrowserConfigState, buildWebSearchCredentialSources, canRegisterWebSearchTool, classifyCredentialSource, formatBrowserExecutableStatus, formatBrowserProfileStatus, getAgentBrowserConfigPaths, getCredentialSourceSummary, getGlobalAgentBrowserConfigPath, getProjectAgentBrowserConfigPath, getWebSearchCredentialSource, getWebSearchProviderConfigKey, getWebSearchProviderDescriptor, getWebSearchProviderEnvVar, getWebSearchProviderLabel, getWebSearchProviderOrder, hasPotentialCredentialSource, isPlaintextCredentialValue, isProjectSafeCredentialValueForProvider, isWebSearchProvider, loadAgentBrowserConfigStateSync, mergeAgentBrowserConfig, parseAgentBrowserConfigLayer, resolveEnvInterpolations, summarizeConfigFiles, validateAgentBrowserConfig, validateWebSearchProvider, } from "./config-policy.js";
6
6
  const exec = promisify(execCallback);
7
7
  async function readConfigLayer(path, scope, errors, warnings) {
8
8
  let raw;
@@ -213,15 +213,6 @@ export function compileAgentBrowserJob(input) {
213
213
  }
214
214
  return { compiled: { args: failFast ? ["batch", "--bail"] : ["batch"], failFast, stdin: JSON.stringify(steps.map((step) => step.args)), steps } };
215
215
  }
216
- export function isHttpOrHttpsUrl(url) {
217
- try {
218
- const protocol = new URL(url).protocol;
219
- return protocol === "http:" || protocol === "https:";
220
- }
221
- catch {
222
- return false;
223
- }
224
- }
225
216
  function describeQaChecksRun(checks) {
226
217
  const parts = [`load:${checks.loadState}`];
227
218
  if (checks.expectedText.length > 0)
@@ -106,7 +106,7 @@ export function createAgentBrowserParamsSchema(Type = JsonSchema, StringEnum = l
106
106
  failFast: Type.Optional(Type.Boolean({ description: "Stop on first failure; default true." })),
107
107
  steps: Type.Array(Type.Object({
108
108
  action: StringEnum(AGENT_BROWSER_JOB_STEP_ACTIONS),
109
- url: Type.Optional(Type.String({ description: "Open URL or assertUrl glob." })),
109
+ url: Type.Optional(Type.String({ description: "Open URL, or assertUrl exact URL/glob that distinguishes the expected state from the starting page." })),
110
110
  loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES, { description: "Readiness wait after open." })),
111
111
  selector: Type.Optional(Type.String({ description: "Selector or @ref." })),
112
112
  locator: Type.Optional(StringEnum(AGENT_BROWSER_SEMANTIC_LOCATORS, { description: "Locator when selector is omitted." })),
@@ -1,5 +1,5 @@
1
1
  import { findCommandStartIndex } from "./argv-descriptor.js";
2
- import { isBooleanFlagEnabled, optionalGlobalValueFlagConsumesNext } from "./argv-grammar.js";
2
+ import { isBooleanFlagEnabled } from "./argv-grammar.js";
3
3
  export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
4
4
  {
5
5
  flag: "--auto-connect",
@@ -17,6 +17,14 @@ export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
17
17
  flag: "--cdp",
18
18
  reason: "selects the browser/CDP endpoint used when an upstream session is launched",
19
19
  },
20
+ {
21
+ flag: "--ca-cert",
22
+ reason: "selects isolated Chromium CA trust and may relaunch the browser",
23
+ },
24
+ {
25
+ flag: "--no-ca-cert",
26
+ reason: "clears retained isolated Chromium CA trust and may relaunch the browser",
27
+ },
20
28
  {
21
29
  flag: "--enable",
22
30
  reason: "selects built-in page init scripts before the upstream browser session is launched",
@@ -29,6 +37,10 @@ export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
29
37
  flag: "--webgpu",
30
38
  reason: "selects the platform-specific WebGPU browser launch preset",
31
39
  },
40
+ {
41
+ flag: "--no-webmcp",
42
+ reason: "selects whether locally launched Chrome enables experimental WebMCP support",
43
+ },
32
44
  {
33
45
  flag: "--init-script",
34
46
  reason: "registers page init scripts before the upstream browser session is launched",
@@ -120,6 +132,7 @@ export const MANAGED_RESTORE_INCOMPATIBLE_FLAGS = [
120
132
  "--user-agent",
121
133
  "--proxy",
122
134
  "--proxy-bypass",
135
+ "--ca-cert",
123
136
  "--ignore-https-errors",
124
137
  "--allow-file-access",
125
138
  "--webgpu",
@@ -148,6 +161,7 @@ export const MANAGED_RESTORE_INCOMPATIBLE_ENVS = [
148
161
  "AGENT_BROWSER_USER_AGENT",
149
162
  "AGENT_BROWSER_PROXY",
150
163
  "AGENT_BROWSER_PROXY_BYPASS",
164
+ "AGENT_BROWSER_CA_CERT",
151
165
  "AGENT_BROWSER_PLUGINS",
152
166
  "AGENT_BROWSER_IOS_DEVICE",
153
167
  "AGENT_BROWSER_IOS_UDID",
@@ -170,12 +184,12 @@ export function hasLaunchScopedFlagToken(args, flag) {
170
184
  const commandStartIndex = findCommandStartIndex(args);
171
185
  const command = commandStartIndex === undefined ? undefined : args[commandStartIndex];
172
186
  return args.some((token, index) => {
173
- if (token !== flag && !token.startsWith(`${flag}=`))
187
+ if (token.startsWith(`${flag}=`))
188
+ return flag === "--restore";
189
+ if (token !== flag)
174
190
  return false;
175
191
  if (flag === "--auto-connect")
176
192
  return isBooleanFlagEnabled(args, flag);
177
- if (flag === "--restore" && token === "--restore" && optionalGlobalValueFlagConsumesNext(flag, args[index + 1]))
178
- return true;
179
193
  if (flag === "--state" && command === "wait" && commandStartIndex !== undefined && index > commandStartIndex)
180
194
  return false;
181
195
  return true;