pi-agent-browser-native 0.2.61 → 0.2.62

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 (33) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +10 -10
  3. package/dist/extensions/agent-browser/index.js +51 -30
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +6 -3
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +29 -1
  6. package/dist/extensions/agent-browser/lib/command-policy.js +10 -1
  7. package/dist/extensions/agent-browser/lib/command-taxonomy.js +7 -0
  8. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +5 -2
  9. package/dist/extensions/agent-browser/lib/input-modes/params.js +2 -1
  10. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +25 -1
  11. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +4 -3
  12. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +25 -17
  13. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +14 -11
  14. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +3 -2
  15. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +2 -1
  16. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +3 -3
  17. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +5 -4
  18. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +4 -4
  19. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +43 -27
  20. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +66 -54
  21. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +24 -15
  22. package/dist/extensions/agent-browser/lib/playbook.js +4 -4
  23. package/dist/extensions/agent-browser/lib/results/next-actions.js +19 -1
  24. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +8 -7
  25. package/dist/extensions/agent-browser/lib/results/presentation.js +2 -1
  26. package/dist/extensions/agent-browser/lib/runtime.js +62 -10
  27. package/dist/extensions/agent-browser/lib/session-page-state.js +14 -7
  28. package/docs/ARCHITECTURE.md +5 -5
  29. package/docs/COMMAND_REFERENCE.md +61 -23
  30. package/docs/SUPPORT_MATRIX.md +9 -8
  31. package/docs/TOOL_CONTRACT.md +20 -20
  32. package/package.json +1 -1
  33. package/scripts/agent-browser-capability-baseline.mjs +36 -4
@@ -5,7 +5,7 @@ import { formatSessionArtifactRetentionSummary } from "../artifact-manifest.js";
5
5
  import { classifyAgentBrowserFailureCategory } from "../categories.js";
6
6
  import { detectConfirmationRequired } from "../confirmation.js";
7
7
  import { applyNetworkRouteRecords, buildNetworkRouteDiagnostics } from "../network-routes.js";
8
- import { withOptionalSessionArgs } from "../next-actions.js";
8
+ import { applyNamespaceToNextActions, withOptionalSessionArgs } from "../next-actions.js";
9
9
  import { stringifyModelFacing } from "./common.js";
10
10
  import { buildArtifactVerificationSummary, classifyPresentationSuccessCategory, manifestHasNewNoticeWorthyEntries } from "./artifacts.js";
11
11
  import { formatBatchStepCommand, getPresentationImages, getPresentationPaths, getPresentationText, isStringArray } from "./content.js";
@@ -181,7 +181,7 @@ function formatBatchStepsText(steps) {
181
181
  return lines.join("\n\n");
182
182
  }
183
183
  async function buildBatchStepPresentation(options) {
184
- const { artifactManifest, artifactRequest, buildNestedToolPresentation, cwd, index, item, networkRoutes, persistentArtifactStore, sessionName } = options;
184
+ const { artifactManifest, artifactRequest, buildNestedToolPresentation, cwd, index, item, namespace, networkRoutes, persistentArtifactStore, sessionName } = options;
185
185
  const command = isStringArray(item.command) ? item.command : undefined;
186
186
  const redactedCommand = command ? redactInvocationArgs(command) : undefined;
187
187
  const commandText = formatBatchStepCommand(hasModelFacingArgRedaction(redactedCommand) ? redactedCommand : command, index);
@@ -196,13 +196,13 @@ async function buildBatchStepPresentation(options) {
196
196
  errorText,
197
197
  });
198
198
  const confirmationRequired = detectConfirmationRequired(item.error);
199
- const nextActions = mergePresentationNextActions(buildAgentBrowserNextActions({
199
+ const nextActions = applyNamespaceToNextActions(mergePresentationNextActions(buildAgentBrowserNextActions({
200
200
  args: command,
201
201
  command: command?.[0],
202
202
  confirmationId: confirmationRequired?.id,
203
203
  failureCategory,
204
204
  resultCategory: "failure",
205
- }), isWaitTextAssertionCommand(command) ? [buildWaitTextAssertionFailureNextAction(sessionName)] : undefined);
205
+ }), isWaitTextAssertionCommand(command) ? [buildWaitTextAssertionFailureNextAction(sessionName)] : undefined), namespace);
206
206
  const presentation = {
207
207
  content: [{ type: "text", text: errorText }],
208
208
  failureCategory,
@@ -254,7 +254,7 @@ async function buildBatchStepPresentation(options) {
254
254
  });
255
255
  const text = getPresentationText(presentation) || presentation.summary;
256
256
  const stepSucceeded = presentation.resultCategory !== "failure";
257
- const nextActions = presentation.nextActions ?? buildAgentBrowserNextActions({
257
+ const nextActions = applyNamespaceToNextActions(presentation.nextActions ?? buildAgentBrowserNextActions({
258
258
  artifacts: presentation.artifacts,
259
259
  args: command,
260
260
  command: command?.[0],
@@ -262,7 +262,7 @@ async function buildBatchStepPresentation(options) {
262
262
  resultCategory: stepSucceeded ? "success" : "failure",
263
263
  savedFilePath: presentation.savedFilePath,
264
264
  successCategory: presentation.successCategory,
265
- });
265
+ }), namespace);
266
266
  const pageChangeSummary = buildPageChangeSummary({
267
267
  artifacts: presentation.artifacts,
268
268
  commandInfo: commandInfoWithTokens,
@@ -299,7 +299,7 @@ async function buildBatchStepPresentation(options) {
299
299
  };
300
300
  }
301
301
  export async function buildBatchPresentation(options) {
302
- const { artifactRequests, buildNestedToolPresentation, cwd, data, networkRoutes, persistentArtifactStore, sessionName, summary } = options;
302
+ const { artifactRequests, buildNestedToolPresentation, cwd, data, namespace, networkRoutes, persistentArtifactStore, sessionName, summary } = options;
303
303
  const steps = [];
304
304
  const protectedPersistentPaths = [];
305
305
  let currentArtifactManifest = options.artifactManifest;
@@ -312,6 +312,7 @@ export async function buildBatchPresentation(options) {
312
312
  cwd,
313
313
  index,
314
314
  item,
315
+ namespace,
315
316
  networkRoutes: currentNetworkRoutes,
316
317
  persistentArtifactStore: persistentArtifactStore ? { ...persistentArtifactStore, protectedPaths: protectedPersistentPaths } : undefined,
317
318
  sessionName,
@@ -37,7 +37,7 @@ function shouldAddAnnotatedScreenshotGuidance(commandInfo, args) {
37
37
  return commandInfo.command === "screenshot" && (args?.includes("--annotate") ?? false);
38
38
  }
39
39
  export async function buildToolPresentation(options) {
40
- const { args, artifactManifest, artifactRequest, commandInfo, compiledSemanticAction, cwd, envelope, errorText, networkRouteDiagnostics, networkRoutes, persistentArtifactStore, sessionName, } = options;
40
+ const { args, artifactManifest, artifactRequest, commandInfo, compiledSemanticAction, cwd, envelope, errorText, namespace, networkRouteDiagnostics, networkRoutes, persistentArtifactStore, sessionName, } = options;
41
41
  const commandInfoWithTokens = commandInfo.commandTokens || !args ? commandInfo : { ...commandInfo, commandTokens: extractCommandTokens(args) };
42
42
  const presentationCommandInfo = resolvePresentationCommandInfo(commandInfoWithTokens, compiledSemanticAction);
43
43
  if (errorText) {
@@ -58,6 +58,7 @@ export async function buildToolPresentation(options) {
58
58
  buildNestedToolPresentation: buildToolPresentation,
59
59
  cwd,
60
60
  data,
61
+ namespace,
61
62
  networkRoutes,
62
63
  persistentArtifactStore,
63
64
  sessionName,
@@ -10,7 +10,7 @@
10
10
  import { createHash, randomUUID } from "node:crypto";
11
11
  import { basename } from "node:path";
12
12
  import { findCommandStartIndex, parseArgvDescriptor, parseCommandInfo, } from "./argv-descriptor.js";
13
- import { GLOBAL_VALUE_FLAGS_ALLOWING_DASH_VALUE, PREVALIDATED_VALUE_FLAGS, } from "./argv-grammar.js";
13
+ import { GLOBAL_VALUE_FLAGS_ALLOWING_DASH_VALUE, PREVALIDATED_VALUE_FLAGS, optionalGlobalValueFlagConsumesNext, } from "./argv-grammar.js";
14
14
  import { needsManagedSession } from "./command-policy.js";
15
15
  import { isCloseCommand, isOpenNavigationCommand } from "./command-taxonomy.js";
16
16
  import { LAUNCH_SCOPED_FLAG_DEFINITIONS, LAUNCH_SCOPED_FLAG_LABEL, LAUNCH_SCOPED_TAB_CORRECTION_FLAGS } from "./launch-scoped-flags.js";
@@ -326,18 +326,22 @@ export function getImplicitSessionCloseTimeoutMs(env = process.env) {
326
326
  return parseTimeoutMs(env[IMPLICIT_SESSION_CLOSE_TIMEOUT_ENV], 0) ?? DEFAULT_IMPLICIT_SESSION_CLOSE_TIMEOUT_MS;
327
327
  }
328
328
  export function resolveManagedSessionState(options) {
329
- const { command, managedSessionName, priorActive, priorSessionName, succeeded } = options;
329
+ const { command, managedSessionName, managedSessionNamespace, priorActive, priorNamespace, priorSessionName, succeeded } = options;
330
330
  if (!managedSessionName) {
331
- return { active: priorActive, sessionName: priorSessionName };
331
+ return { active: priorActive, ...(priorNamespace ? { namespace: priorNamespace } : {}), sessionName: priorSessionName };
332
332
  }
333
333
  if (isCloseCommand(command) && managedSessionName === priorSessionName) {
334
- return { active: succeeded ? false : priorActive, sessionName: priorSessionName };
334
+ if (managedSessionNamespace !== priorNamespace)
335
+ return { active: priorActive, ...(priorNamespace ? { namespace: priorNamespace } : {}), sessionName: priorSessionName };
336
+ const namespace = succeeded ? undefined : priorNamespace;
337
+ return { active: succeeded ? false : priorActive, ...(namespace ? { namespace } : {}), sessionName: priorSessionName };
335
338
  }
336
339
  if (!succeeded) {
337
- return { active: priorActive, sessionName: priorSessionName };
340
+ return { active: priorActive, ...(priorNamespace ? { namespace: priorNamespace } : {}), sessionName: priorSessionName };
338
341
  }
339
342
  return {
340
343
  active: true,
344
+ ...(managedSessionNamespace ? { namespace: managedSessionNamespace } : {}),
341
345
  replacedSessionName: priorActive && priorSessionName !== managedSessionName ? priorSessionName : undefined,
342
346
  sessionName: managedSessionName,
343
347
  };
@@ -395,13 +399,13 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
395
399
  let closedSessionName;
396
400
  let freshSessionOrdinal = 0;
397
401
  const freshSessionRanks = new Map();
398
- const applyManagedClose = (sessionName) => {
402
+ const applyManagedClose = (sessionName, namespace) => {
399
403
  const restoreRank = getManagedSessionRestoreRank({
400
404
  fallbackSessionName,
401
405
  freshSessionRanks,
402
406
  sessionName,
403
407
  });
404
- if (restoreRank === undefined || sessionName !== restoredState.sessionName)
408
+ if (restoreRank === undefined || sessionName !== restoredState.sessionName || namespace !== restoredState.namespace)
405
409
  return;
406
410
  restoredState = { active: false, sessionName: restoredState.sessionName };
407
411
  closedSessionName = sessionName;
@@ -427,6 +431,7 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
427
431
  }
428
432
  const explicitSessionName = extractExplicitSessionName(args);
429
433
  const sessionName = typeof details.sessionName === "string" ? details.sessionName : undefined;
434
+ const namespace = typeof details.namespace === "string" ? details.namespace : undefined;
430
435
  const sessionMode = details.sessionMode === "fresh" || details.sessionMode === "auto" ? details.sessionMode : undefined;
431
436
  const usedImplicitSession = details.usedImplicitSession === true;
432
437
  const command = typeof details.command === "string" ? details.command : parseCommandInfo(args).command;
@@ -468,7 +473,7 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
468
473
  const succeeded = outcomeRepresentsActiveCurrentSession ? true : messageIsError === undefined ? exitCode === undefined || exitCode === 0 : !messageIsError;
469
474
  if (commandClosesSession) {
470
475
  if (succeeded)
471
- applyManagedClose(managedSessionName);
476
+ applyManagedClose(managedSessionName, namespace);
472
477
  continue;
473
478
  }
474
479
  const staleCompletion = succeeded && restoreRank < activeRestoreRank;
@@ -478,7 +483,9 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
478
483
  restoredState = resolveManagedSessionState({
479
484
  command,
480
485
  managedSessionName,
486
+ managedSessionNamespace: namespace,
481
487
  priorActive: restoredState.active,
488
+ priorNamespace: restoredState.namespace,
482
489
  priorSessionName: restoredState.sessionName,
483
490
  succeeded,
484
491
  });
@@ -720,6 +727,31 @@ export function extractExplicitSessionName(args) {
720
727
  }
721
728
  return undefined;
722
729
  }
730
+ export function extractExplicitNamespace(args) {
731
+ for (const [index, token] of args.entries()) {
732
+ if (token === "--namespace") {
733
+ return args[index + 1];
734
+ }
735
+ if (token.startsWith("--namespace=")) {
736
+ return token.slice("--namespace=".length);
737
+ }
738
+ }
739
+ return undefined;
740
+ }
741
+ function stripExplicitNamespaceArgs(args) {
742
+ const stripped = [];
743
+ for (let index = 0; index < args.length; index += 1) {
744
+ const token = args[index];
745
+ if (token === "--namespace") {
746
+ index += 1;
747
+ continue;
748
+ }
749
+ if (token.startsWith("--namespace="))
750
+ continue;
751
+ stripped.push(token);
752
+ }
753
+ return stripped;
754
+ }
723
755
  function hasLaunchScopedFlagToken(args, flag) {
724
756
  const commandStartIndex = findCommandStartIndex(args);
725
757
  const command = commandStartIndex === undefined ? undefined : args[commandStartIndex];
@@ -728,6 +760,8 @@ function hasLaunchScopedFlagToken(args, flag) {
728
760
  return false;
729
761
  if (flag === "--auto-connect")
730
762
  return isBooleanFlagEnabled(args, flag);
763
+ if (flag === "--restore" && token === "--restore" && optionalGlobalValueFlagConsumesNext(flag, args[index + 1]))
764
+ return true;
731
765
  if (flag === "--state" && command === "wait" && commandStartIndex !== undefined && index > commandStartIndex) {
732
766
  return false;
733
767
  }
@@ -750,13 +784,15 @@ export function hasLaunchScopedTabCorrectionFlag(args) {
750
784
  }
751
785
  export function buildExecutionPlan(args, options) {
752
786
  const invalidValueFlag = getInvalidValueFlagDetails(args);
753
- const startupScopedFlags = getStartupScopedFlags(args);
787
+ const explicitNamespace = extractExplicitNamespace(args);
788
+ const startupScopedFlags = getStartupScopedFlags(args).filter((flag) => !(flag === "--namespace" && explicitNamespace === options.managedSessionNamespace));
754
789
  const plainTextInspection = isPlainTextInspectionArgs(args);
755
790
  const argvDescriptor = parseArgvDescriptor(args);
756
791
  const commandTokens = argvDescriptor.commandTokens;
757
792
  const commandInfo = argvDescriptor.commandInfo;
758
793
  const commandNeedsManagedSession = !plainTextInspection && needsManagedSession(argvDescriptor);
759
794
  const effectiveArgs = plainTextInspection ? [...args] : args.includes("--json") ? [] : ["--json"];
795
+ let namespace = explicitNamespace;
760
796
  if (invalidValueFlag) {
761
797
  return {
762
798
  commandInfo: {},
@@ -772,6 +808,7 @@ export function buildExecutionPlan(args, options) {
772
808
  return {
773
809
  commandInfo,
774
810
  effectiveArgs,
811
+ namespace,
775
812
  plainTextInspection,
776
813
  startupScopedFlags,
777
814
  usedImplicitSession: false,
@@ -779,7 +816,12 @@ export function buildExecutionPlan(args, options) {
779
816
  }
780
817
  const explicitSessionName = extractExplicitSessionName(args);
781
818
  const shouldCreateFreshManagedSession = !explicitSessionName && options.sessionMode === "fresh" && commandInfo.command !== undefined && !isCloseCommand(commandInfo.command);
819
+ let argsToAppend = args;
782
820
  const compatibilityWorkaround = getCompatibilityWorkaround(args, commandInfo);
821
+ if (explicitSessionName && explicitNamespace) {
822
+ effectiveArgs.push("--namespace", explicitNamespace);
823
+ argsToAppend = stripExplicitNamespaceArgs(args);
824
+ }
783
825
  let managedSessionName;
784
826
  let recoveryHint;
785
827
  let sessionName = explicitSessionName;
@@ -799,26 +841,36 @@ export function buildExecutionPlan(args, options) {
799
841
  ].join(" ");
800
842
  }
801
843
  else {
844
+ namespace = explicitNamespace ?? options.managedSessionNamespace;
845
+ if (namespace)
846
+ effectiveArgs.push("--namespace", namespace);
802
847
  effectiveArgs.push("--session", options.managedSessionName);
848
+ if (explicitNamespace)
849
+ argsToAppend = stripExplicitNamespaceArgs(args);
803
850
  managedSessionName = options.managedSessionName;
804
851
  sessionName = options.managedSessionName;
805
852
  usedImplicitSession = true;
806
853
  }
807
854
  }
808
855
  else if (shouldCreateFreshManagedSession && commandNeedsManagedSession) {
856
+ if (namespace)
857
+ effectiveArgs.push("--namespace", namespace);
809
858
  effectiveArgs.push("--session", options.freshSessionName);
859
+ if (explicitNamespace)
860
+ argsToAppend = stripExplicitNamespaceArgs(args);
810
861
  managedSessionName = options.freshSessionName;
811
862
  sessionName = options.freshSessionName;
812
863
  }
813
864
  if (compatibilityWorkaround) {
814
865
  effectiveArgs.push("--user-agent", getDefaultHeadlessCompatUserAgent());
815
866
  }
816
- effectiveArgs.push(...args);
867
+ effectiveArgs.push(...argsToAppend);
817
868
  return {
818
869
  commandInfo,
819
870
  compatibilityWorkaround,
820
871
  effectiveArgs,
821
872
  managedSessionName,
873
+ namespace,
822
874
  plainTextInspection,
823
875
  recoveryHint,
824
876
  sessionName,
@@ -291,6 +291,11 @@ function stripRefSnapshotOrder(snapshot) {
291
291
  function stripRefSnapshotInvalidationOrder(invalidation) {
292
292
  return invalidation ? { reason: invalidation.reason, summary: invalidation.summary } : undefined;
293
293
  }
294
+ export function getSessionPageStateKey(sessionName, namespace) {
295
+ if (!sessionName)
296
+ return undefined;
297
+ return namespace ? `${namespace}\u0000${sessionName}` : sessionName;
298
+ }
294
299
  export class SessionPageState {
295
300
  refSnapshotInvalidations = new Map();
296
301
  refSnapshots = new Map();
@@ -310,13 +315,15 @@ export class SessionPageState {
310
315
  if (!details)
311
316
  continue;
312
317
  const sessionName = typeof details.sessionName === "string" ? details.sessionName : undefined;
313
- if (!sessionName)
318
+ const namespace = typeof details.namespace === "string" ? details.namespace : undefined;
319
+ const sessionKey = getSessionPageStateKey(sessionName, namespace);
320
+ if (!sessionKey)
314
321
  continue;
315
322
  const command = typeof details.command === "string" ? details.command : undefined;
316
323
  const subcommand = typeof details.subcommand === "string" ? details.subcommand : undefined;
317
324
  if (isCloseCommand(command) && message.isError !== true) {
318
325
  restoredOrder += 1;
319
- state.clearSession(sessionName);
326
+ state.clearSession(sessionKey);
320
327
  continue;
321
328
  }
322
329
  const tabTarget = getRestoredSessionTabTarget(details, command, subcommand);
@@ -326,14 +333,14 @@ export class SessionPageState {
326
333
  continue;
327
334
  restoredOrder += 1;
328
335
  if (tabTarget)
329
- state.tabTargets.set(sessionName, { order: restoredOrder, target: tabTarget });
336
+ state.tabTargets.set(sessionKey, { order: restoredOrder, target: tabTarget });
330
337
  if (refSnapshotInvalidation) {
331
- state.refSnapshots.delete(sessionName);
332
- state.refSnapshotInvalidations.set(sessionName, { ...refSnapshotInvalidation, order: restoredOrder });
338
+ state.refSnapshots.delete(sessionKey);
339
+ state.refSnapshotInvalidations.set(sessionKey, { ...refSnapshotInvalidation, order: restoredOrder });
333
340
  }
334
341
  else if (refSnapshot) {
335
- state.refSnapshotInvalidations.delete(sessionName);
336
- state.refSnapshots.set(sessionName, { ...refSnapshot, order: restoredOrder });
342
+ state.refSnapshotInvalidations.delete(sessionKey);
343
+ state.refSnapshots.set(sessionKey, { ...refSnapshot, order: restoredOrder });
337
344
  }
338
345
  }
339
346
  state.updateOrder = Math.max(restoredOrder, getLatestTabTargetOrder(state.tabTargets), getLatestRefStateOrder(state.refSnapshots, state.refSnapshotInvalidations));
@@ -153,9 +153,9 @@ Practical policy:
153
153
  - once the wrapper observes tab-drift risk for a session (profile restore correction, overlapping stale opens, or restored session state), later active-tab commands may synthesize a tiny upstream `batch` that re-selects that tab and then runs the requested command in the same upstream invocation; routine same-session commands avoid `tab list` preflights to reduce probes that can perturb upstream click behavior
154
154
  - for sessions with observed tab-drift risk, after a successful command on a known tab target, the wrapper may best-effort restore that same target again if restored/background tabs steal focus after the command returns; routine same-session commands skip this post-command `tab list` probe
155
155
  - keep a per-session `refSnapshot` aligned with the last successful `snapshot` (including refs merged from a successful `batch` by taking the last successful `snapshot` step in batch result order): restore it from persisted tool `details` when reloading, resuming, or moving to a different Pi session-tree branch, store bounded ref role/name metadata from the same snapshot for wrapper-side current-ref diagnostics, drop it on successful close commands (`close`, `quit`, or `exit`), and refuse mutation-prone `@e…` argv before spawn when the active tab URL no longer matches the snapshot URL, when a ref id was never in that snapshot, or when `batch` stdin would reuse `@e…` on a guarded step after an earlier invalidating step without a later `snapshot` step in the same stdin array. Same-snapshot `fill @e…` rows are guarded but do not themselves set that invalidation latch, so ordinary form fills can precede a click/submit row in one batch—see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) for the agent-visible contract and failure text; typed per-session tab/ref/pinning state lives in `extensions/agent-browser/lib/session-page-state.ts` and is updated from `extensions/agent-browser/index.ts` after each tool result
156
- - for top-level non-Electron direct `click` commands, install a bounded in-page target-specific event probe before upstream runs; if upstream reports success but no trusted pointer/mouse/click event reached the resolved target, fail the tool and report `details.clickDispatch` with explicit retry/inspect next actions (the wrapper does not replay clicks in-page). The probe is intentionally skipped for unresolved `find click` locators and for `batch`/`job`/`qa` click steps. For `@e…` targets it only covers current refs whose latest stored `refSnapshot.refs` role is `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`; it uses that role/name metadata, including snapshot-order `duplicateIndex` for duplicate-name refs, instead of taking a fresh pre-click snapshot that could recycle upstream refs
156
+ - for top-level non-Electron direct `click` commands with an eligible target, install a bounded in-page target-specific event probe before upstream runs; if upstream reports success but no trusted pointer/mouse/click event reached the resolved target, fail the tool and report `details.clickDispatch` with explicit retry/inspect next actions (the wrapper does not replay clicks in-page). The probe covers `xpath=` targets and current `@e…` / `ref=` refs whose latest stored `refSnapshot.refs` role is `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`; it uses that role/name metadata, including snapshot-order `duplicateIndex` for duplicate-name refs, instead of taking a fresh pre-click snapshot that could recycle upstream refs. The probe is intentionally skipped for CSS selector clicks, unresolved `find … click` locators, and `batch`/`job`/`qa` click steps
157
157
  - derive narrow prompt guards only for concrete evidence invariants: exact required screenshot paths block browser close until the artifact manifest verifies those paths. The wrapper intentionally does not infer broad business/user intent from prompt text such as order/payment/post boundaries; agents must follow those instructions themselves. The artifact guard is bounded preflight policy (`details.promptGuard`, `failureCategory: "policy-blocked"`), not a reusable browser recipe layer
158
- - after successful `get text` on a non-ref CSS selector, optionally issue one read-only `eval --stdin` probe per qualifying selector when multiple DOM matches or a hidden first match with visible peers could misread tabbed or off-screen content; merge `details.selectorTextVisibility` / `selectorTextVisibilityAll`, visible warning lines, and `inspect-visible-text-candidates*` next actions as documented in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) and `RQ-0074` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md)
158
+ - after successful `get text` on a qualifying non-ref CSS selector, optionally issue one read-only `eval --stdin` probe per selector when multiple DOM matches or a hidden first match with visible peers could misread tabbed or off-screen content; simple id selectors and sensitive-looking literals skip this probe. Merge `details.selectorTextVisibility` / `selectorTextVisibilityAll`, visible warning lines, and `inspect-visible-text-candidates*` next actions as documented in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) and `RQ-0074` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md)
159
159
  - for local Unix launches, set a short private socket directory so extension-generated session names do not fail on the upstream Unix socket-path length limit
160
160
  - keep wrapper-spawned upstream CLI calls bounded by clamping `AGENT_BROWSER_DEFAULT_TIMEOUT` to the upstream documented 25-second default while deriving a longer subprocess watchdog for explicit long `wait <ms>` / `wait --timeout <ms>` calls; dialog commands, likely dialog-trigger clicks/taps/finds, and `eval --stdin` snippets that look like alert/confirm/prompt/dialog triggers use shorter wrapper subprocess budgets so blocking JavaScript prompts surface recovery actions before the full default watchdog
161
161
 
@@ -166,7 +166,7 @@ This is primarily about ownership clarity and avoiding surprise, not adding a he
166
166
  `agent-browser` startup flags are sticky once a session is already running.
167
167
  The extension should surface that clearly and avoid hidden restart behavior in v1.
168
168
 
169
- That means explicit startup-scoping flags like `--auto-connect`, `--cdp`, `--enable`, `--executable-path`, `--init-script`, `--device`, `--profile`, `--provider`, `-p`, `--session-name`, and `--state` should remain explicit upstream choices instead of being wrapped in extra hidden restart or cloning logic.
169
+ That means explicit startup-scoping flags like `--auto-connect`, `--cdp`, `--enable`, `--executable-path`, `--init-script`, `--device`, `--namespace`, `--profile`, `--provider`, `-p`, `--restore`, `--restore-save`, restore check flags, `--session-name`, and `--state` should remain explicit upstream choices instead of being wrapped in extra hidden restart or cloning logic.
170
170
 
171
171
  The wrapper may still apply narrow compatibility normalizations when observed behavior justifies them and the result remains thin, local, and opt-out. For example, if a specific site starts rejecting the default local headless Chrome user agent while the same flow works with a normal Chrome UA, the extension may inject a domain-specific fallback UA only when the caller did not already choose `--user-agent`, `--headed`, `--cdp`, `--auto-connect`, or a provider-backed launch.
172
172
 
@@ -177,10 +177,10 @@ That failure should include a structured recovery hint pointing to `sessionMode:
177
177
  Implementation detail lives in `extensions/agent-browser/lib/launch-scoped-flags.ts` (canonical flag metadata shared with playbook/docs assertions), `extensions/agent-browser/lib/argv-descriptor.ts` and `extensions/agent-browser/lib/argv-grammar.ts` (command discovery, `VALUE_FLAGS`, `parseArgvDescriptor`) plus `extensions/agent-browser/lib/runtime.ts` (`getStartupScopedFlags`, `buildExecutionPlan`):
178
178
 
179
179
  - **Command discovery:** Leading argv is scanned with a value-taking allowlist so known global flags and documented command flags consume their values before the upstream command word is identified. Missing-value prevalidation is intentionally limited to upstream global value flags; command-scoped flags and literal text are left to upstream parsing so values like `fill #field --password` are not rejected by wrapper heuristics before the CLI sees them. When upstream adds new global flags that take values ahead of the command, extend both the command-discovery and prevalidation allowlists; when it adds command-specific flags, extend only command discovery/redaction as needed. A smaller set of global boolean flags may be followed by an optional `true`/`false` literal; when present, that literal is consumed as the flag value before command discovery continues.
180
- - **`--state` disambiguation:** Persisted browser `--state` before the command participates in launch-scoped validation and tab-correction hints. The same flag spelling after a `wait` command is excluded from startup-scoped detection so upstream help examples such as `wait @ref --state hidden` do not spuriously require `sessionMode: "fresh"` while an implicit session is active. As of upstream `agent-browser 0.30.1`, the parser still does not implement those `wait --state` examples as distinct wait modes, so agent-facing docs recommend `wait --fn` predicates for disappearance checks instead.
180
+ - **`--state` disambiguation:** Persisted browser `--state` before the command participates in launch-scoped validation and tab-correction hints. The same flag spelling after a `wait` command is excluded from startup-scoped detection so upstream help examples such as `wait @ref --state hidden` do not spuriously require `sessionMode: "fresh"` while an implicit session is active. As of the current upstream baseline, the parser still does not implement those `wait --state` examples as distinct wait modes, so agent-facing docs recommend `wait --fn` predicates for disappearance checks instead.
181
181
  - **`--auto-connect`:** Treated as launch-scoped only when enabled (`--auto-connect` bare or `true`). `--auto-connect false` is ignored for startup-scoped blocking so disabled attach hints do not force a fresh launch.
182
182
 
183
- **Sessionless inspection and local commands:** Plain-text global help and version probes (`--help`, `-h`, `--version`, `-V`) must never allocate or bind the extension-managed session. The same session-ownership rule applies to read-only upstream `skills list`, `skills get …`, and `skills path …`, local auth profile management (`auth save/list/show/delete/remove`), plus local/setup surfaces such as `profiles`, `dashboard start/stop`, `device list`, `doctor`, `install`, `upgrade`, `session list`, and targeted/all local saved-state maintenance (`state list/show`, `state clear --all`, `state clear -a`, `state clear <session-name>`, `state clean --older-than <days>`, `state rename`). Non-plain-text sessionless commands still run with `--json` for machine-readable output, but the planner does not prepend the implicit managed `--session`, so an agent can inspect local capabilities or start/stop the standalone dashboard without consuming the implicit session slot before a real `open`. Browser-backed, context-dependent, or incomplete commands such as root `session`, untargeted `state clear`, bare `state clean`, `auth login`, `state save`, and `state load` keep normal managed-session injection. Command-shape allowlisting lives in `extensions/agent-browser/lib/command-policy.ts` (`needsManagedSession`), while `extensions/agent-browser/lib/runtime.ts` (`isPlainTextInspectionArgs`, `buildExecutionPlan`) applies that decision to execution planning.
183
+ **Sessionless inspection and local commands:** Plain-text global help and version probes (`--help`, `-h`, `--version`, `-V`) must never allocate or bind the extension-managed session. The same session-ownership rule applies to read-only upstream `skills list`, `skills get …`, and `skills path …`, local auth profile management (`auth save/list/show/delete/remove`), plus local/setup surfaces such as `profiles`, `dashboard start/stop`, `device list`, `doctor`, `install`, `upgrade`, `session id`, `session info`, `session list`, and targeted/all local saved-state maintenance (`state list/show`, `state clear --all`, `state clear -a`, `state clear <session-name>`, `state clean --older-than <days>`, `state rename`). Non-plain-text sessionless commands still run with `--json` for machine-readable output, but the planner does not prepend the implicit managed `--session`, so an agent can inspect local capabilities or start/stop the standalone dashboard without consuming the implicit session slot before a real `open`. Browser-backed, context-dependent, or incomplete commands such as root `session`, untargeted `state clear`, bare `state clean`, `auth login`, `state save`, and `state load` keep normal managed-session injection. Command-shape allowlisting lives in `extensions/agent-browser/lib/command-policy.ts` (`needsManagedSession`), while `extensions/agent-browser/lib/runtime.ts` (`isPlainTextInspectionArgs`, `buildExecutionPlan`) applies that decision to execution planning.
184
184
 
185
185
  A successful unnamed `sessionMode: "fresh"` launch should become the new extension-managed session so later default calls follow that browser instead of silently snapping back to the older managed session.
186
186