pi-agent-browser-native 0.2.72 → 0.2.75

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 (52) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +14 -12
  3. package/dist/extensions/agent-browser/index.js +148 -16
  4. package/dist/extensions/agent-browser/lib/argv-grammar.js +122 -0
  5. package/dist/extensions/agent-browser/lib/command-taxonomy.js +11 -0
  6. package/dist/extensions/agent-browser/lib/electron/cdp.js +2 -2
  7. package/dist/extensions/agent-browser/lib/electron/launch.js +48 -12
  8. package/dist/extensions/agent-browser/lib/input-modes/params.js +96 -98
  9. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +88 -2
  10. package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +22 -0
  11. package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +432 -0
  12. package/dist/extensions/agent-browser/lib/managed-session-restore.js +374 -0
  13. package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +367 -0
  14. package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +589 -0
  15. package/dist/extensions/agent-browser/lib/managed-session-storage.js +299 -0
  16. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +35 -0
  17. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +9 -2
  18. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +40 -22
  19. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +15 -6
  20. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +54 -33
  21. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +182 -0
  22. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
  23. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
  24. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
  25. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
  26. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +629 -430
  27. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +148 -57
  28. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +30 -40
  29. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +102 -19
  30. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +13 -1
  31. package/dist/extensions/agent-browser/lib/playbook.js +9 -8
  32. package/dist/extensions/agent-browser/lib/process-identity.js +82 -0
  33. package/dist/extensions/agent-browser/lib/process.js +271 -34
  34. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +5 -3
  35. package/dist/extensions/agent-browser/lib/results/presentation/common.js +2 -1
  36. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +35 -12
  37. package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +42 -0
  38. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +7 -0
  39. package/dist/extensions/agent-browser/lib/runtime.js +114 -107
  40. package/dist/extensions/agent-browser/lib/session-page-state.js +48 -17
  41. package/dist/extensions/agent-browser/lib/temp.js +13 -25
  42. package/docs/ARCHITECTURE.md +10 -9
  43. package/docs/COMMAND_REFERENCE.md +43 -23
  44. package/docs/ELECTRON.md +10 -10
  45. package/docs/RELEASE.md +3 -2
  46. package/docs/REQUIREMENTS.md +1 -1
  47. package/docs/SUPPORT_MATRIX.md +19 -18
  48. package/docs/TOOL_CONTRACT.md +29 -26
  49. package/docs/platform-smoke.md +2 -2
  50. package/package.json +1 -1
  51. package/platform-smoke.config.mjs +1 -1
  52. package/scripts/agent-browser-capability-baseline.mjs +11 -3
@@ -1,5 +1,7 @@
1
1
  import { copyFile, mkdir } from "node:fs/promises";
2
2
  import { dirname, resolve } from "node:path";
3
+ import { isCloseCommand } from "../../command-taxonomy.js";
4
+ import { cleanupElectronLaunchResources } from "../../electron/cleanup.js";
3
5
  import { launchElectronApp } from "../../electron/launch.js";
4
6
  import { pathExists } from "../../fs-utils.js";
5
7
  import { getCompiledSemanticActionSessionPrefix } from "../../input-modes.js";
@@ -14,7 +16,10 @@ import { applyNamespaceToNextActions } from "../../results/next-actions.js";
14
16
  import { buildSessionAwareStaleRefNextActions, buildSessionTabRecoveryNextActions } from "../../results/recovery-next-actions.js";
15
17
  import { resolveVisibleRefActionFromSnapshot } from "../../results/selector-recovery.js";
16
18
  import { extractRefSnapshotFromData } from "../../session-page-state.js";
17
- import { buildExecutionPlan, createFreshSessionName, extractCommandTokens, redactInvocationArgs, } from "../../runtime.js";
19
+ import { buildExecutionPlan, createFreshSessionName, extractCommandTokens, getDefaultHeadlessCompatUserAgent, redactInvocationArgs, } from "../../runtime.js";
20
+ import { buildOwnedManagedSessionRestoreContext, canonicalizeOwnedManagedSessionCloseArgs, withOwnedManagedSessionContext, } from "../../managed-session-restore.js";
21
+ import { getCallerOwnedSessionLivePageVerificationRequirement, getManagedSessionStateAccessValidationError, getManagedSessionTargetAccessValidationError, } from "../../managed-session-state-policy.js";
22
+ import { acquireOwnedManagedSessionDaemonPolicy } from "./managed-session-daemon-policy.js";
18
23
  import { applyOpenResultTabCorrection, buildManagedSessionOutcome, buildPinnedBatchPlan, buildSessionDetailFields, buildStaleRefPreflight, getSessionContextKey, extractStringResultField, collectAnySessionTabSelection, collectSessionTabSelection, getGuardedRefUsage, getTraceOwnerGuardMessage, runSessionCommandData, shouldPinSessionTabForCommand, } from "./session-state.js";
19
24
  import { parseBatchStdinJsonArray } from "../batch-stdin.js";
20
25
  import { buildElectronHostFailureResult, getElectronLaunchFailureCategory, redactRecoveryHint } from "./final-result.js";
@@ -317,8 +322,28 @@ export async function prepareBrowserRun(options) {
317
322
  let electronLaunch;
318
323
  const sessionMode = compiledElectron?.action === "launch" ? "fresh" : params.sessionMode ?? "auto";
319
324
  const freshSessionName = createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal + 1);
325
+ const rawManagedStateAccessError = getManagedSessionStateAccessValidationError({
326
+ args: runtimeToolArgs,
327
+ cwd,
328
+ stdin: runtimeToolStdin,
329
+ trustedFirstBatchTabSelection: true,
330
+ });
331
+ if (rawManagedStateAccessError) {
332
+ return {
333
+ kind: "early-result",
334
+ result: {
335
+ content: [{ type: "text", text: rawManagedStateAccessError }],
336
+ details: {
337
+ args: redactedArgs,
338
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, errorText: rawManagedStateAccessError, succeeded: false, validationError: rawManagedStateAccessError }),
339
+ validationError: rawManagedStateAccessError,
340
+ },
341
+ isError: true,
342
+ },
343
+ };
344
+ }
320
345
  if (compiledElectron?.action === "launch") {
321
- const launchResult = await launchElectronApp(compiledElectron);
346
+ const launchResult = await launchElectronApp({ ...compiledElectron, signal });
322
347
  if (!launchResult.ok) {
323
348
  const managedSessionOutcome = buildManagedSessionOutcome({
324
349
  activeAfter: state.managedSessionActive,
@@ -343,470 +368,644 @@ export async function prepareBrowserRun(options) {
343
368
  runtimeToolArgs = ["connect", electronLaunch.connectArg];
344
369
  runtimeToolStdin = undefined;
345
370
  }
346
- const preparedArgs = await prepareAgentBrowserArgs(runtimeToolArgs, runtimeToolStdin, cwd);
347
- const userRequestedJson = runtimeToolArgs.includes("--json");
348
- let executionPlan = buildExecutionPlan(preparedArgs.args, {
349
- freshSessionName,
350
- managedSessionActive: state.managedSessionActive,
351
- managedSessionName: state.managedSessionName,
352
- managedSessionNamespace: state.managedSessionNamespace,
353
- sessionMode,
354
- });
355
- const idleTimeoutMismatch = getIdleTimeoutMismatch(preparedArgs.args, options.implicitSessionIdleTimeoutMs);
356
- if (idleTimeoutMismatch)
357
- executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: idleTimeoutMismatch };
358
- const sessionStateKey = getSessionContextKey(executionPlan.sessionName, executionPlan.namespace);
359
- const priorSessionPageState = sessionPageState.get(sessionStateKey);
360
- const priorSessionTabTarget = priorSessionPageState.tabTarget;
361
- const sessionTabPinningReason = priorSessionPageState.pinningReason;
362
- const priorRefSnapshotState = priorSessionPageState.refSnapshot;
363
- const priorRefSnapshotInvalidation = priorSessionPageState.refSnapshotInvalidation;
364
- let semanticActionVisibleRefResolution;
365
- if (!executionPlan.validationError && executionPlan.managedSessionName !== freshSessionName && canResolveSemanticVisibleRef(compiledSemanticAction)) {
366
- semanticActionVisibleRefResolution = await resolveSemanticActionVisibleRefArgs({
367
- compiled: compiledSemanticAction,
368
- cwd,
369
- namespace: executionPlan.namespace,
370
- sessionName: executionPlan.sessionName,
371
- signal,
372
- });
373
- }
374
- if (semanticActionVisibleRefResolution) {
375
- executionPlan = buildExecutionPlan(semanticActionVisibleRefResolution.args, {
371
+ let managedSessionPolicyLock;
372
+ let managedSessionPolicyLockTransferred = false;
373
+ let electronLaunchTransferred = false;
374
+ try {
375
+ const preparedArgs = await prepareAgentBrowserArgs(runtimeToolArgs, runtimeToolStdin, cwd);
376
+ const userRequestedJson = runtimeToolArgs.includes("--json");
377
+ let executionPlan = buildExecutionPlan(preparedArgs.args, {
376
378
  freshSessionName,
377
379
  managedSessionActive: state.managedSessionActive,
380
+ managedSessionCompatibilityWorkaround: state.managedSessionCompatibilityWorkaround,
378
381
  managedSessionName: state.managedSessionName,
379
382
  managedSessionNamespace: state.managedSessionNamespace,
380
383
  sessionMode,
381
384
  });
382
- }
383
- const redactedEffectiveArgs = redactInvocationArgs(executionPlan.effectiveArgs);
384
- const redactedRecoveryHint = redactRecoveryHint(executionPlan.recoveryHint);
385
- const compatibilityWorkaround = executionPlan.compatibilityWorkaround;
386
- const statePatch = executionPlan.managedSessionName === freshSessionName
387
- ? { freshSessionOrdinal: freshSessionOrdinal + 1 }
388
- : {};
389
- if (executionPlan.managedSessionName === freshSessionName) {
390
- freshSessionOrdinal += 1;
391
- }
392
- if (executionPlan.validationError) {
393
- return { kind: "early-result", statePatch, result: {
394
- content: [{ type: "text", text: executionPlan.validationError }],
395
- details: {
396
- args: redactedArgs,
397
- compiledElectron: redactedCompiledElectron,
398
- compiledJob: redactedCompiledJob,
399
- compiledQaPreset: redactedCompiledQaPreset,
400
- compiledSourceLookup: redactedCompiledSourceLookup,
401
- compiledNetworkSourceLookup: redactedCompiledNetworkSourceLookup,
402
- invalidValueFlag: executionPlan.invalidValueFlag,
403
- sessionMode,
404
- sessionRecoveryHint: redactedRecoveryHint,
405
- startupScopedFlags: executionPlan.startupScopedFlags,
406
- ...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, command: executionPlan.commandInfo.command, errorText: executionPlan.validationError, succeeded: false, validationError: executionPlan.validationError }),
407
- validationError: executionPlan.validationError,
408
- },
409
- isError: true,
410
- } };
411
- }
412
- const commandTokens = semanticActionVisibleRefResolution ? extractCommandTokens(semanticActionVisibleRefResolution.args) : extractCommandTokens(preparedArgs.args);
413
- const exactSensitiveValues = getExactSensitiveStdinValues({
414
- command: executionPlan.commandInfo.command,
415
- commandTokens,
416
- stdin: runtimeToolStdin,
417
- });
418
- const traceOwnerGuardMessage = getTraceOwnerGuardMessage({
419
- command: executionPlan.commandInfo.command,
420
- sessionName: sessionStateKey,
421
- subcommand: executionPlan.commandInfo.subcommand,
422
- traceOwners,
423
- });
424
- if (traceOwnerGuardMessage) {
425
- return { kind: "early-result", statePatch, result: {
426
- content: [{ type: "text", text: traceOwnerGuardMessage }],
427
- details: {
428
- args: redactedArgs,
429
- command: executionPlan.commandInfo.command,
430
- compatibilityWorkaround,
431
- effectiveArgs: redactedEffectiveArgs,
432
- sessionMode,
433
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: traceOwnerGuardMessage, succeeded: false, validationError: traceOwnerGuardMessage }),
434
- validationError: traceOwnerGuardMessage,
435
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
436
- },
437
- isError: true,
438
- } };
439
- }
440
- const stdinValidationError = validateStdinCommandContract({
441
- command: executionPlan.commandInfo.command,
442
- commandTokens,
443
- stdin: runtimeToolStdin,
444
- });
445
- if (stdinValidationError) {
446
- return { kind: "early-result", statePatch, result: {
447
- content: [{ type: "text", text: stdinValidationError }],
448
- details: {
449
- args: redactedArgs,
450
- command: executionPlan.commandInfo.command,
451
- compatibilityWorkaround,
452
- effectiveArgs: redactedEffectiveArgs,
453
- sessionMode,
454
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: stdinValidationError, succeeded: false, validationError: stdinValidationError }),
455
- validationError: stdinValidationError,
456
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
457
- },
458
- isError: true,
459
- } };
460
- }
461
- const resolvedSemanticActionRefSnapshot = semanticActionVisibleRefResolution?.snapshot
462
- ? { ...semanticActionVisibleRefResolution.snapshot, target: semanticActionVisibleRefResolution.snapshot.target ?? priorSessionTabTarget }
463
- : undefined;
464
- const promptRefSnapshot = resolvedSemanticActionRefSnapshot ?? priorRefSnapshotState;
465
- const requestedArtifactCloseViolation = await findRequestedArtifactCloseViolation({ artifactManifest: state.artifactManifest, command: executionPlan.commandInfo.command, cwd, promptPolicy: options.promptPolicy });
466
- if (requestedArtifactCloseViolation) {
467
- return { kind: "early-result", statePatch, result: {
468
- content: [{ type: "text", text: requestedArtifactCloseViolation.message }],
469
- details: {
470
- args: redactedArgs,
471
- command: executionPlan.commandInfo.command,
472
- compatibilityWorkaround,
473
- effectiveArgs: redactedEffectiveArgs,
474
- promptGuard: requestedArtifactCloseViolation,
475
- sessionMode,
476
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: requestedArtifactCloseViolation.message, failureCategory: "policy-blocked", succeeded: false, validationError: requestedArtifactCloseViolation.message }),
477
- validationError: requestedArtifactCloseViolation.message,
478
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
479
- },
480
- isError: true,
481
- } };
482
- }
483
- const staleRefPreflight = buildStaleRefPreflight({
484
- commandTokens,
485
- currentTarget: priorSessionTabTarget,
486
- refSnapshot: resolvedSemanticActionRefSnapshot ?? priorRefSnapshotState,
487
- refSnapshotInvalidation: resolvedSemanticActionRefSnapshot ? undefined : priorRefSnapshotInvalidation,
488
- stdin: runtimeToolStdin,
489
- });
490
- if (staleRefPreflight) {
491
- return { kind: "early-result", statePatch, result: {
492
- content: [{ type: "text", text: staleRefPreflight.message }],
493
- details: {
494
- args: redactedArgs,
495
- command: executionPlan.commandInfo.command,
496
- compatibilityWorkaround,
497
- effectiveArgs: redactedEffectiveArgs,
498
- nextActions: applyNamespaceToNextActions(buildSessionAwareStaleRefNextActions(executionPlan.sessionName), executionPlan.namespace),
499
- refIds: staleRefPreflight.refIds,
500
- refSnapshot: staleRefPreflight.snapshot,
501
- refSnapshotInvalidation: staleRefPreflight.snapshotInvalidation,
502
- sessionMode,
503
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: staleRefPreflight.message, failureCategory: "stale-ref", succeeded: false }),
504
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
505
- },
506
- isError: true,
507
- } };
508
- }
509
- const samePageRefFreshnessPreflight = await collectSamePageRefFreshnessPreflight({
510
- commandTokens,
511
- cwd,
512
- currentTarget: priorSessionTabTarget,
513
- previousSnapshot: resolvedSemanticActionRefSnapshot ? undefined : priorRefSnapshotState,
514
- namespace: executionPlan.namespace,
515
- sessionName: executionPlan.sessionName,
516
- signal,
517
- });
518
- if (samePageRefFreshnessPreflight) {
519
- if (samePageRefFreshnessPreflight.snapshot && sessionStateKey) {
520
- sessionPageState.applyRefSnapshot({ fallbackTarget: priorSessionTabTarget, sessionName: sessionStateKey, snapshot: samePageRefFreshnessPreflight.snapshot, update: options.sessionPageStateUpdate });
521
- }
522
- return { kind: "early-result", statePatch, result: {
523
- content: [{ type: "text", text: samePageRefFreshnessPreflight.message }],
524
- details: {
525
- args: redactedArgs,
526
- command: executionPlan.commandInfo.command,
527
- compatibilityWorkaround,
528
- effectiveArgs: redactedEffectiveArgs,
529
- nextActions: applyNamespaceToNextActions(buildSessionAwareStaleRefNextActions(executionPlan.sessionName), executionPlan.namespace),
530
- refIds: samePageRefFreshnessPreflight.refIds,
531
- refSnapshot: samePageRefFreshnessPreflight.snapshot,
532
- sessionMode,
533
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: samePageRefFreshnessPreflight.message, failureCategory: "stale-ref", succeeded: false }),
534
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
535
- },
536
- isError: true,
537
- } };
538
- }
539
- if (compiledQaPreset?.checks.attached) {
540
- const qaAttachedPrecondition = await validateQaAttachedPrecondition({
541
- cwd,
542
- namespace: executionPlan.namespace,
543
- sessionName: executionPlan.sessionName,
544
- signal,
545
- });
546
- if (qaAttachedPrecondition) {
547
- return { kind: "early-result", statePatch, result: {
548
- content: [{ type: "text", text: qaAttachedPrecondition.error }],
549
- details: {
550
- args: redactedArgs,
551
- compiledQaPreset: redactedCompiledQaPreset,
552
- compatibilityWorkaround,
553
- effectiveArgs: redactedEffectiveArgs,
554
- nextActions: applyNamespaceToNextActions(qaAttachedPrecondition.nextActions, executionPlan.namespace),
555
- sessionMode,
556
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: qaAttachedPrecondition.error, succeeded: false, validationError: qaAttachedPrecondition.error }),
557
- validationError: qaAttachedPrecondition.error,
558
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
559
- },
560
- isError: true,
561
- } };
562
- }
563
- }
564
- const persistentArtifactStore = getPersistentSessionArtifactStore(options.ctx);
565
- const snapshotFilter = await trySnapshotFilter({
566
- artifactManifest: state.artifactManifest,
567
- commandTokens,
568
- compatibilityWorkaround,
569
- cwd,
570
- effectiveArgs: redactedEffectiveArgs,
571
- persistentArtifactStore,
572
- previousRefSnapshot: priorRefSnapshotState,
573
- redactedArgs,
574
- namespace: executionPlan.namespace,
575
- sessionMode,
576
- sessionName: executionPlan.sessionName,
577
- sessionStateKey,
578
- sessionPageState,
579
- sessionPageStateUpdate: options.sessionPageStateUpdate,
580
- signal,
581
- usedImplicitSession: executionPlan.usedImplicitSession,
582
- });
583
- if (snapshotFilter)
584
- return { kind: "early-result", statePatch: { ...statePatch, artifactManifest: snapshotFilter.artifactManifest ?? statePatch.artifactManifest }, result: snapshotFilter.result };
585
- const networkRequestsPageFilter = await tryNetworkRequestsPageFilter({
586
- commandTokens,
587
- compatibilityWorkaround,
588
- cwd,
589
- effectiveArgs: redactedEffectiveArgs,
590
- redactedArgs,
591
- sessionMode,
592
- namespace: executionPlan.namespace,
593
- sessionName: executionPlan.sessionName,
594
- signal,
595
- usedImplicitSession: executionPlan.usedImplicitSession,
596
- });
597
- if (networkRequestsPageFilter)
598
- return { kind: "early-result", statePatch, result: networkRequestsPageFilter };
599
- if (executionPlan.startupScopedFlags.length === 0) {
600
- const containerScroll = await tryContainerScroll({
601
- commandTokens,
602
- compatibilityWorkaround,
385
+ const idleTimeoutMismatch = getIdleTimeoutMismatch(preparedArgs.args, options.implicitSessionIdleTimeoutMs);
386
+ if (idleTimeoutMismatch)
387
+ executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: idleTimeoutMismatch };
388
+ const ownedSessionKey = getSessionContextKey(executionPlan.sessionName, executionPlan.namespace);
389
+ const plannedSessionPageState = sessionPageState.get(ownedSessionKey);
390
+ const managedStateAccessError = getManagedSessionStateAccessValidationError({
391
+ args: executionPlan.effectiveArgs,
392
+ currentPageUrl: plannedSessionPageState.tabTarget?.url,
393
+ pageUrlUnknown: plannedSessionPageState.tabTargetUnknown === true,
603
394
  cwd,
604
- effectiveArgs: redactedEffectiveArgs,
605
- redactedArgs,
606
- sessionMode,
607
- namespace: executionPlan.namespace,
608
- sessionName: executionPlan.sessionName,
609
- signal,
610
- usedImplicitSession: executionPlan.usedImplicitSession,
395
+ stdin: runtimeToolStdin,
611
396
  });
612
- if (containerScroll)
613
- return { kind: "early-result", statePatch, result: containerScroll };
614
- const pageScrollTo = await tryPageScrollTo({
615
- commandTokens,
616
- compatibilityWorkaround,
617
- cwd,
618
- effectiveArgs: redactedEffectiveArgs,
619
- redactedArgs,
620
- sessionMode,
397
+ if (!executionPlan.validationError && managedStateAccessError)
398
+ executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: managedStateAccessError };
399
+ const recordedOwnedSession = ownedSessionKey ? state.ownedManagedSessions.get(ownedSessionKey) : undefined;
400
+ const ownedManagedSession = buildOwnedManagedSessionRestoreContext({
401
+ args: executionPlan.effectiveArgs,
402
+ cwd: recordedOwnedSession?.cwd ?? cwd,
403
+ currentManagedSessionName: state.managedSessionName,
404
+ currentManagedSessionNamespace: state.managedSessionNamespace,
405
+ managedSessionName: executionPlan.managedSessionName,
621
406
  namespace: executionPlan.namespace,
622
- sessionName: executionPlan.sessionName,
623
- signal,
624
- usedImplicitSession: executionPlan.usedImplicitSession,
625
- });
626
- if (pageScrollTo)
627
- return { kind: "early-result", statePatch, result: pageScrollTo };
628
- }
629
- const directAnchorDownload = await tryDirectAnchorDownload({
630
- artifactManifest: state.artifactManifest,
631
- commandTokens,
632
- compatibilityWorkaround,
633
- cwd,
634
- effectiveArgs: redactedEffectiveArgs,
635
- redactedArgs,
636
- sessionMode,
637
- namespace: executionPlan.namespace,
638
- sessionName: executionPlan.sessionName,
639
- signal,
640
- usedImplicitSession: executionPlan.usedImplicitSession,
641
- });
642
- if (directAnchorDownload)
643
- return { kind: "early-result", statePatch: { ...statePatch, artifactManifest: directAnchorDownload.artifactManifest ?? statePatch.artifactManifest }, result: directAnchorDownload.result };
644
- let pinnedBatchUnwrapMode;
645
- let includePinnedNavigationSummary = false;
646
- let sessionTabCorrection;
647
- let processArgs = executionPlan.effectiveArgs;
648
- let processStdin = preparedArgs.stdin ?? runtimeToolStdin;
649
- if (priorSessionTabTarget &&
650
- shouldPinSessionTabForCommand({
651
- command: executionPlan.commandInfo.command,
652
- commandTokens,
653
- pinningRequired: sessionTabPinningReason !== undefined,
407
+ recordedOwnedSession,
408
+ restoreState: state.managedSessionRestoreState,
654
409
  sessionName: executionPlan.sessionName,
655
410
  stdin: runtimeToolStdin,
656
- })) {
657
- const collectTabSelection = promptRefSnapshot && getGuardedRefUsage(commandTokens, runtimeToolStdin).length > 0 ? collectAnySessionTabSelection : collectSessionTabSelection;
658
- const plannedSessionTabSelection = await collectTabSelection({
659
- cwd,
660
- namespace: executionPlan.namespace,
661
- sessionName: executionPlan.sessionName,
662
- signal,
663
- target: priorSessionTabTarget,
411
+ compatibilityUserAgent: executionPlan.compatibilityWorkaround ? getDefaultHeadlessCompatUserAgent() : undefined,
412
+ wrapperInjectedUserAgent: executionPlan.compatibilityWorkaround !== undefined,
664
413
  });
665
- if (plannedSessionTabSelection && executionPlan.sessionName) {
666
- if (executionPlan.commandInfo.command === "eval" && runtimeToolStdin !== undefined) {
667
- const appliedSessionTabSelection = await applyOpenResultTabCorrection({
668
- correction: plannedSessionTabSelection,
414
+ const managedSessionTargetError = getManagedSessionTargetAccessValidationError(executionPlan.effectiveArgs, ownedManagedSession !== undefined);
415
+ if (!executionPlan.validationError && managedSessionTargetError)
416
+ executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: managedSessionTargetError };
417
+ if (!executionPlan.validationError && ownedManagedSession) {
418
+ const closeCommand = isCloseCommand(executionPlan.commandInfo.command);
419
+ const policy = await acquireOwnedManagedSessionDaemonPolicy({
420
+ context: ownedManagedSession,
421
+ mode: closeCommand ? "close" : "reuse",
422
+ signal,
423
+ });
424
+ managedSessionPolicyLock = policy.lock;
425
+ if (policy.error) {
426
+ executionPlan = {
427
+ ...executionPlan,
428
+ recoveryHint: undefined,
429
+ validationError: policy.error,
430
+ };
431
+ }
432
+ else if (closeCommand && managedSessionPolicyLock) {
433
+ executionPlan = {
434
+ ...executionPlan,
435
+ effectiveArgs: canonicalizeOwnedManagedSessionCloseArgs({
436
+ args: executionPlan.effectiveArgs,
437
+ cwd,
438
+ ownedManagedSession: true,
439
+ restoreState: state.managedSessionRestoreState,
440
+ }, true),
441
+ };
442
+ }
443
+ }
444
+ return await withOwnedManagedSessionContext(ownedManagedSession, async () => {
445
+ const managedSessionRestoreDisabled = () => state.managedSessionRestoreState.isDisabled(executionPlan.sessionName, executionPlan.namespace);
446
+ const sessionStateKey = getSessionContextKey(executionPlan.sessionName, executionPlan.namespace);
447
+ const priorSessionPageState = sessionPageState.get(sessionStateKey);
448
+ let priorSessionTabTarget = priorSessionPageState.tabTarget;
449
+ let priorSessionTabTargetUnknown = priorSessionPageState.tabTargetUnknown;
450
+ const sessionTabPinningReason = priorSessionPageState.pinningReason;
451
+ const priorRefSnapshotState = priorSessionPageState.refSnapshot;
452
+ const priorRefSnapshotInvalidation = priorSessionPageState.refSnapshotInvalidation;
453
+ let semanticActionVisibleRefResolution;
454
+ let callerOwnedLivePageVerified = false;
455
+ const isCallerOwnedExplicitSession = () => executionPlan.sessionName !== undefined
456
+ && executionPlan.usedImplicitSession === false
457
+ && ownedManagedSession === undefined;
458
+ const verifyCallerOwnedLivePage = async (options) => {
459
+ if (!options.requirement || !executionPlan.sessionName)
460
+ return;
461
+ let liveUrl;
462
+ try {
463
+ const liveUrlData = await runSessionCommandData({
464
+ args: ["get", "url"],
465
+ cwd,
466
+ namespace: executionPlan.namespace,
467
+ sessionName: executionPlan.sessionName,
468
+ signal,
469
+ throwOnFailure: true,
470
+ });
471
+ liveUrl = extractStringResultField(liveUrlData, "result") ?? extractStringResultField(liveUrlData, "url");
472
+ }
473
+ catch (error) {
474
+ if (signal?.aborted)
475
+ throw signal.reason ?? error;
476
+ }
477
+ if (liveUrl === undefined) {
478
+ executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: options.requirement };
479
+ return;
480
+ }
481
+ const livePageValidationError = getManagedSessionStateAccessValidationError({
482
+ args: options.args,
483
+ currentPageUrl: liveUrl,
484
+ cwd,
485
+ pageUrlUnknown: false,
486
+ stdin: options.stdin,
487
+ });
488
+ if (livePageValidationError) {
489
+ executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: livePageValidationError };
490
+ return;
491
+ }
492
+ callerOwnedLivePageVerified = true;
493
+ priorSessionTabTarget ??= { url: liveUrl };
494
+ priorSessionTabTargetUnknown = undefined;
495
+ };
496
+ const mayResolveSemanticVisibleRef = executionPlan.managedSessionName !== freshSessionName && canResolveSemanticVisibleRef(compiledSemanticAction);
497
+ if (!executionPlan.validationError && mayResolveSemanticVisibleRef && isCallerOwnedExplicitSession()) {
498
+ await verifyCallerOwnedLivePage({
499
+ args: ["snapshot", "-i"],
500
+ requirement: getCallerOwnedSessionLivePageVerificationRequirement({ args: ["snapshot", "-i"], cwd }),
501
+ });
502
+ }
503
+ if (!executionPlan.validationError && mayResolveSemanticVisibleRef) {
504
+ semanticActionVisibleRefResolution = await resolveSemanticActionVisibleRefArgs({
505
+ compiled: compiledSemanticAction,
506
+ cwd,
507
+ namespace: executionPlan.namespace,
508
+ sessionName: executionPlan.sessionName,
509
+ signal,
510
+ });
511
+ }
512
+ if (semanticActionVisibleRefResolution) {
513
+ executionPlan = buildExecutionPlan(semanticActionVisibleRefResolution.args, {
514
+ freshSessionName,
515
+ managedSessionActive: state.managedSessionActive,
516
+ managedSessionCompatibilityWorkaround: state.managedSessionCompatibilityWorkaround,
517
+ managedSessionName: state.managedSessionName,
518
+ managedSessionNamespace: state.managedSessionNamespace,
519
+ sessionMode,
520
+ });
521
+ }
522
+ const commandTokens = semanticActionVisibleRefResolution ? extractCommandTokens(semanticActionVisibleRefResolution.args) : extractCommandTokens(preparedArgs.args);
523
+ const resolvedSemanticActionRefSnapshot = semanticActionVisibleRefResolution?.snapshot
524
+ ? { ...semanticActionVisibleRefResolution.snapshot, target: semanticActionVisibleRefResolution.snapshot.target ?? priorSessionTabTarget }
525
+ : undefined;
526
+ const preLiveStaleRefPreflight = buildStaleRefPreflight({
527
+ commandTokens,
528
+ currentTarget: priorSessionTabTarget,
529
+ refSnapshot: resolvedSemanticActionRefSnapshot ?? priorRefSnapshotState,
530
+ refSnapshotInvalidation: resolvedSemanticActionRefSnapshot ? undefined : priorRefSnapshotInvalidation,
531
+ stdin: runtimeToolStdin,
532
+ });
533
+ const callerOwnedPageAccessEligible = !executionPlan.validationError
534
+ && preLiveStaleRefPreflight === undefined
535
+ && validateStdinCommandContract({ command: executionPlan.commandInfo.command, commandTokens, stdin: runtimeToolStdin }) === undefined
536
+ && isCallerOwnedExplicitSession();
537
+ const callerOwnedLivePageRequirement = callerOwnedPageAccessEligible
538
+ && !callerOwnedLivePageVerified
539
+ ? getCallerOwnedSessionLivePageVerificationRequirement({
540
+ args: executionPlan.effectiveArgs,
541
+ cwd,
542
+ stdin: runtimeToolStdin,
543
+ })
544
+ : undefined;
545
+ await verifyCallerOwnedLivePage({
546
+ args: executionPlan.effectiveArgs,
547
+ requirement: callerOwnedLivePageRequirement,
548
+ stdin: runtimeToolStdin,
549
+ });
550
+ const redactedEffectiveArgs = redactInvocationArgs(executionPlan.effectiveArgs);
551
+ const redactedRecoveryHint = redactRecoveryHint(executionPlan.recoveryHint);
552
+ const compatibilityWorkaround = executionPlan.compatibilityWorkaround;
553
+ const statePatch = executionPlan.managedSessionName === freshSessionName
554
+ ? { freshSessionOrdinal: freshSessionOrdinal + 1 }
555
+ : {};
556
+ if (executionPlan.managedSessionName === freshSessionName) {
557
+ freshSessionOrdinal += 1;
558
+ }
559
+ if (executionPlan.validationError) {
560
+ return { kind: "early-result", statePatch, result: {
561
+ content: [{ type: "text", text: executionPlan.validationError }],
562
+ details: {
563
+ args: redactedArgs,
564
+ compiledElectron: redactedCompiledElectron,
565
+ compiledJob: redactedCompiledJob,
566
+ compiledQaPreset: redactedCompiledQaPreset,
567
+ compiledSourceLookup: redactedCompiledSourceLookup,
568
+ compiledNetworkSourceLookup: redactedCompiledNetworkSourceLookup,
569
+ invalidValueFlag: executionPlan.invalidValueFlag,
570
+ sessionMode,
571
+ sessionRecoveryHint: redactedRecoveryHint,
572
+ startupScopedFlags: executionPlan.startupScopedFlags,
573
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, command: executionPlan.commandInfo.command, errorText: executionPlan.validationError, succeeded: false, validationError: executionPlan.validationError }),
574
+ validationError: executionPlan.validationError,
575
+ },
576
+ isError: true,
577
+ } };
578
+ }
579
+ const exactSensitiveValues = getExactSensitiveStdinValues({
580
+ command: executionPlan.commandInfo.command,
581
+ commandTokens,
582
+ stdin: runtimeToolStdin,
583
+ });
584
+ const traceOwnerGuardMessage = getTraceOwnerGuardMessage({
585
+ command: executionPlan.commandInfo.command,
586
+ sessionName: sessionStateKey,
587
+ subcommand: executionPlan.commandInfo.subcommand,
588
+ traceOwners,
589
+ });
590
+ if (traceOwnerGuardMessage) {
591
+ return { kind: "early-result", statePatch, result: {
592
+ content: [{ type: "text", text: traceOwnerGuardMessage }],
593
+ details: {
594
+ args: redactedArgs,
595
+ command: executionPlan.commandInfo.command,
596
+ compatibilityWorkaround,
597
+ effectiveArgs: redactedEffectiveArgs,
598
+ sessionMode,
599
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: traceOwnerGuardMessage, succeeded: false, validationError: traceOwnerGuardMessage }),
600
+ validationError: traceOwnerGuardMessage,
601
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
602
+ },
603
+ isError: true,
604
+ } };
605
+ }
606
+ const stdinValidationError = validateStdinCommandContract({
607
+ command: executionPlan.commandInfo.command,
608
+ commandTokens,
609
+ stdin: runtimeToolStdin,
610
+ });
611
+ if (stdinValidationError) {
612
+ return { kind: "early-result", statePatch, result: {
613
+ content: [{ type: "text", text: stdinValidationError }],
614
+ details: {
615
+ args: redactedArgs,
616
+ command: executionPlan.commandInfo.command,
617
+ compatibilityWorkaround,
618
+ effectiveArgs: redactedEffectiveArgs,
619
+ sessionMode,
620
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: stdinValidationError, succeeded: false, validationError: stdinValidationError }),
621
+ validationError: stdinValidationError,
622
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
623
+ },
624
+ isError: true,
625
+ } };
626
+ }
627
+ const promptRefSnapshot = resolvedSemanticActionRefSnapshot ?? priorRefSnapshotState;
628
+ const requestedArtifactCloseViolation = await findRequestedArtifactCloseViolation({ artifactManifest: state.artifactManifest, command: executionPlan.commandInfo.command, cwd, promptPolicy: options.promptPolicy });
629
+ if (requestedArtifactCloseViolation) {
630
+ return { kind: "early-result", statePatch, result: {
631
+ content: [{ type: "text", text: requestedArtifactCloseViolation.message }],
632
+ details: {
633
+ args: redactedArgs,
634
+ command: executionPlan.commandInfo.command,
635
+ compatibilityWorkaround,
636
+ effectiveArgs: redactedEffectiveArgs,
637
+ promptGuard: requestedArtifactCloseViolation,
638
+ sessionMode,
639
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: requestedArtifactCloseViolation.message, failureCategory: "policy-blocked", succeeded: false, validationError: requestedArtifactCloseViolation.message }),
640
+ validationError: requestedArtifactCloseViolation.message,
641
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
642
+ },
643
+ isError: true,
644
+ } };
645
+ }
646
+ const staleRefPreflight = buildStaleRefPreflight({
647
+ commandTokens,
648
+ currentTarget: priorSessionTabTarget,
649
+ refSnapshot: resolvedSemanticActionRefSnapshot ?? priorRefSnapshotState,
650
+ refSnapshotInvalidation: resolvedSemanticActionRefSnapshot ? undefined : priorRefSnapshotInvalidation,
651
+ stdin: runtimeToolStdin,
652
+ });
653
+ if (staleRefPreflight) {
654
+ return { kind: "early-result", statePatch, result: {
655
+ content: [{ type: "text", text: staleRefPreflight.message }],
656
+ details: {
657
+ args: redactedArgs,
658
+ command: executionPlan.commandInfo.command,
659
+ compatibilityWorkaround,
660
+ effectiveArgs: redactedEffectiveArgs,
661
+ nextActions: applyNamespaceToNextActions(buildSessionAwareStaleRefNextActions(executionPlan.sessionName), executionPlan.namespace),
662
+ refIds: staleRefPreflight.refIds,
663
+ refSnapshot: staleRefPreflight.snapshot,
664
+ refSnapshotInvalidation: staleRefPreflight.snapshotInvalidation,
665
+ sessionMode,
666
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: staleRefPreflight.message, failureCategory: "stale-ref", succeeded: false }),
667
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
668
+ },
669
+ isError: true,
670
+ } };
671
+ }
672
+ const samePageRefFreshnessPreflight = await collectSamePageRefFreshnessPreflight({
673
+ commandTokens,
674
+ cwd,
675
+ currentTarget: priorSessionTabTarget,
676
+ previousSnapshot: resolvedSemanticActionRefSnapshot ? undefined : priorRefSnapshotState,
677
+ namespace: executionPlan.namespace,
678
+ sessionName: executionPlan.sessionName,
679
+ signal,
680
+ });
681
+ if (samePageRefFreshnessPreflight) {
682
+ if (samePageRefFreshnessPreflight.snapshot && sessionStateKey) {
683
+ sessionPageState.applyRefSnapshot({ fallbackTarget: priorSessionTabTarget, sessionName: sessionStateKey, snapshot: samePageRefFreshnessPreflight.snapshot, update: options.sessionPageStateUpdate });
684
+ }
685
+ return { kind: "early-result", statePatch, result: {
686
+ content: [{ type: "text", text: samePageRefFreshnessPreflight.message }],
687
+ details: {
688
+ args: redactedArgs,
689
+ command: executionPlan.commandInfo.command,
690
+ compatibilityWorkaround,
691
+ effectiveArgs: redactedEffectiveArgs,
692
+ nextActions: applyNamespaceToNextActions(buildSessionAwareStaleRefNextActions(executionPlan.sessionName), executionPlan.namespace),
693
+ refIds: samePageRefFreshnessPreflight.refIds,
694
+ refSnapshot: samePageRefFreshnessPreflight.snapshot,
695
+ sessionMode,
696
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: samePageRefFreshnessPreflight.message, failureCategory: "stale-ref", succeeded: false }),
697
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
698
+ },
699
+ isError: true,
700
+ } };
701
+ }
702
+ if (compiledQaPreset?.checks.attached) {
703
+ const qaAttachedPrecondition = await validateQaAttachedPrecondition({
669
704
  cwd,
670
705
  namespace: executionPlan.namespace,
671
706
  sessionName: executionPlan.sessionName,
672
707
  signal,
673
708
  });
674
- if (!appliedSessionTabSelection) {
675
- const error = "agent-browser could not re-select the intended tab before running the command.";
709
+ if (qaAttachedPrecondition) {
676
710
  return { kind: "early-result", statePatch, result: {
677
- content: [{ type: "text", text: error }],
711
+ content: [{ type: "text", text: qaAttachedPrecondition.error }],
678
712
  details: {
679
713
  args: redactedArgs,
680
- command: executionPlan.commandInfo.command,
714
+ compiledQaPreset: redactedCompiledQaPreset,
681
715
  compatibilityWorkaround,
682
716
  effectiveArgs: redactedEffectiveArgs,
717
+ nextActions: applyNamespaceToNextActions(qaAttachedPrecondition.nextActions, executionPlan.namespace),
683
718
  sessionMode,
684
- sessionTabCorrection: plannedSessionTabSelection,
685
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: error, failureCategory: "tab-drift", succeeded: false, tabDrift: true, validationError: error }),
686
- nextActions: applyNamespaceToNextActions(buildSessionTabRecoveryNextActions({
687
- kind: "tab-drift",
688
- resultCategory: "failure",
689
- sessionName: executionPlan.sessionName,
690
- tabCorrection: plannedSessionTabSelection,
691
- target: priorSessionTabTarget,
692
- }), executionPlan.namespace),
693
- validationError: error,
694
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
719
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: qaAttachedPrecondition.error, succeeded: false, validationError: qaAttachedPrecondition.error }),
720
+ validationError: qaAttachedPrecondition.error,
721
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
695
722
  },
696
723
  isError: true,
697
724
  } };
698
725
  }
699
- sessionTabCorrection = appliedSessionTabSelection;
700
726
  }
701
- else {
702
- const pinnedBatchPlan = buildPinnedBatchPlan({
727
+ const persistentArtifactStore = getPersistentSessionArtifactStore(options.ctx);
728
+ const snapshotFilter = await trySnapshotFilter({
729
+ artifactManifest: state.artifactManifest,
730
+ commandTokens,
731
+ compatibilityWorkaround,
732
+ cwd,
733
+ effectiveArgs: redactedEffectiveArgs,
734
+ managedSessionRestoreDisabled,
735
+ persistentArtifactStore,
736
+ previousRefSnapshot: priorRefSnapshotState,
737
+ redactedArgs,
738
+ namespace: executionPlan.namespace,
739
+ sessionMode,
740
+ sessionName: executionPlan.sessionName,
741
+ sessionStateKey,
742
+ sessionPageState,
743
+ sessionPageStateUpdate: options.sessionPageStateUpdate,
744
+ signal,
745
+ usedImplicitSession: executionPlan.usedImplicitSession,
746
+ });
747
+ if (snapshotFilter)
748
+ return { kind: "early-result", statePatch: { ...statePatch, artifactManifest: snapshotFilter.artifactManifest ?? statePatch.artifactManifest }, result: snapshotFilter.result };
749
+ const networkRequestsPageFilter = await tryNetworkRequestsPageFilter({
750
+ commandTokens,
751
+ compatibilityWorkaround,
752
+ cwd,
753
+ effectiveArgs: redactedEffectiveArgs,
754
+ managedSessionRestoreDisabled,
755
+ redactedArgs,
756
+ sessionMode,
757
+ namespace: executionPlan.namespace,
758
+ sessionName: executionPlan.sessionName,
759
+ signal,
760
+ usedImplicitSession: executionPlan.usedImplicitSession,
761
+ });
762
+ if (networkRequestsPageFilter)
763
+ return { kind: "early-result", statePatch, result: networkRequestsPageFilter };
764
+ if (executionPlan.startupScopedFlags.length === 0) {
765
+ const containerScroll = await tryContainerScroll({
766
+ commandTokens,
767
+ compatibilityWorkaround,
768
+ cwd,
769
+ effectiveArgs: redactedEffectiveArgs,
770
+ managedSessionRestoreDisabled,
771
+ redactedArgs,
772
+ sessionMode,
773
+ namespace: executionPlan.namespace,
774
+ sessionName: executionPlan.sessionName,
775
+ signal,
776
+ usedImplicitSession: executionPlan.usedImplicitSession,
777
+ });
778
+ if (containerScroll)
779
+ return { kind: "early-result", statePatch, result: containerScroll };
780
+ const pageScrollTo = await tryPageScrollTo({
781
+ commandTokens,
782
+ compatibilityWorkaround,
783
+ cwd,
784
+ effectiveArgs: redactedEffectiveArgs,
785
+ managedSessionRestoreDisabled,
786
+ redactedArgs,
787
+ sessionMode,
788
+ namespace: executionPlan.namespace,
789
+ sessionName: executionPlan.sessionName,
790
+ signal,
791
+ usedImplicitSession: executionPlan.usedImplicitSession,
792
+ });
793
+ if (pageScrollTo)
794
+ return { kind: "early-result", statePatch, result: pageScrollTo };
795
+ }
796
+ const directAnchorDownload = await tryDirectAnchorDownload({
797
+ artifactManifest: state.artifactManifest,
798
+ commandTokens,
799
+ compatibilityWorkaround,
800
+ cwd,
801
+ effectiveArgs: redactedEffectiveArgs,
802
+ managedSessionRestoreDisabled,
803
+ redactedArgs,
804
+ sessionMode,
805
+ namespace: executionPlan.namespace,
806
+ sessionName: executionPlan.sessionName,
807
+ signal,
808
+ usedImplicitSession: executionPlan.usedImplicitSession,
809
+ });
810
+ if (directAnchorDownload)
811
+ return { kind: "early-result", statePatch: { ...statePatch, artifactManifest: directAnchorDownload.artifactManifest ?? statePatch.artifactManifest }, result: directAnchorDownload.result };
812
+ let pinnedBatchUnwrapMode;
813
+ let includePinnedNavigationSummary = false;
814
+ let sessionTabCorrection;
815
+ let processArgs = executionPlan.effectiveArgs;
816
+ let processStdin = preparedArgs.stdin ?? runtimeToolStdin;
817
+ if (priorSessionTabTarget &&
818
+ shouldPinSessionTabForCommand({
703
819
  command: executionPlan.commandInfo.command,
704
820
  commandTokens,
705
- selectedTab: plannedSessionTabSelection.selectedTab,
821
+ pinningRequired: sessionTabPinningReason !== undefined,
822
+ sessionName: executionPlan.sessionName,
706
823
  stdin: runtimeToolStdin,
824
+ })) {
825
+ const collectTabSelection = promptRefSnapshot && getGuardedRefUsage(commandTokens, runtimeToolStdin).length > 0 ? collectAnySessionTabSelection : collectSessionTabSelection;
826
+ const plannedSessionTabSelection = await collectTabSelection({
827
+ cwd,
828
+ namespace: executionPlan.namespace,
829
+ sessionName: executionPlan.sessionName,
830
+ signal,
831
+ target: priorSessionTabTarget,
707
832
  });
708
- if (pinnedBatchPlan && "error" in pinnedBatchPlan) {
709
- return { kind: "early-result", statePatch, result: {
710
- content: [{ type: "text", text: pinnedBatchPlan.error }],
711
- details: {
712
- args: redactedArgs,
713
- command: executionPlan.commandInfo.command,
714
- compatibilityWorkaround,
715
- effectiveArgs: redactedEffectiveArgs,
716
- sessionMode,
717
- sessionTabCorrection: plannedSessionTabSelection,
718
- ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: pinnedBatchPlan.error, failureCategory: "tab-drift", succeeded: false, tabDrift: true, validationError: pinnedBatchPlan.error }),
719
- nextActions: applyNamespaceToNextActions(buildSessionTabRecoveryNextActions({
720
- kind: "tab-drift",
721
- resultCategory: "failure",
722
- sessionName: executionPlan.sessionName,
723
- tabCorrection: plannedSessionTabSelection,
724
- target: priorSessionTabTarget,
725
- }), executionPlan.namespace),
726
- validationError: pinnedBatchPlan.error,
727
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
728
- },
729
- isError: true,
730
- } };
833
+ if (plannedSessionTabSelection && executionPlan.sessionName) {
834
+ if (executionPlan.commandInfo.command === "eval" && runtimeToolStdin !== undefined) {
835
+ const appliedSessionTabSelection = await applyOpenResultTabCorrection({
836
+ correction: plannedSessionTabSelection,
837
+ cwd,
838
+ namespace: executionPlan.namespace,
839
+ sessionName: executionPlan.sessionName,
840
+ signal,
841
+ });
842
+ if (!appliedSessionTabSelection) {
843
+ const error = "agent-browser could not re-select the intended tab before running the command.";
844
+ return { kind: "early-result", statePatch, result: {
845
+ content: [{ type: "text", text: error }],
846
+ details: {
847
+ args: redactedArgs,
848
+ command: executionPlan.commandInfo.command,
849
+ compatibilityWorkaround,
850
+ effectiveArgs: redactedEffectiveArgs,
851
+ sessionMode,
852
+ sessionTabCorrection: plannedSessionTabSelection,
853
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: error, failureCategory: "tab-drift", succeeded: false, tabDrift: true, validationError: error }),
854
+ nextActions: applyNamespaceToNextActions(buildSessionTabRecoveryNextActions({
855
+ kind: "tab-drift",
856
+ resultCategory: "failure",
857
+ sessionName: executionPlan.sessionName,
858
+ tabCorrection: plannedSessionTabSelection,
859
+ target: priorSessionTabTarget,
860
+ }), executionPlan.namespace),
861
+ validationError: error,
862
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
863
+ },
864
+ isError: true,
865
+ } };
866
+ }
867
+ sessionTabCorrection = appliedSessionTabSelection;
868
+ }
869
+ else {
870
+ const pinnedBatchPlan = buildPinnedBatchPlan({
871
+ command: executionPlan.commandInfo.command,
872
+ commandTokens,
873
+ selectedTab: plannedSessionTabSelection.selectedTab,
874
+ stdin: runtimeToolStdin,
875
+ });
876
+ if (pinnedBatchPlan && "error" in pinnedBatchPlan) {
877
+ return { kind: "early-result", statePatch, result: {
878
+ content: [{ type: "text", text: pinnedBatchPlan.error }],
879
+ details: {
880
+ args: redactedArgs,
881
+ command: executionPlan.commandInfo.command,
882
+ compatibilityWorkaround,
883
+ effectiveArgs: redactedEffectiveArgs,
884
+ sessionMode,
885
+ sessionTabCorrection: plannedSessionTabSelection,
886
+ ...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: pinnedBatchPlan.error, failureCategory: "tab-drift", succeeded: false, tabDrift: true, validationError: pinnedBatchPlan.error }),
887
+ nextActions: applyNamespaceToNextActions(buildSessionTabRecoveryNextActions({
888
+ kind: "tab-drift",
889
+ resultCategory: "failure",
890
+ sessionName: executionPlan.sessionName,
891
+ tabCorrection: plannedSessionTabSelection,
892
+ target: priorSessionTabTarget,
893
+ }), executionPlan.namespace),
894
+ validationError: pinnedBatchPlan.error,
895
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
896
+ },
897
+ isError: true,
898
+ } };
899
+ }
900
+ if (pinnedBatchPlan) {
901
+ sessionTabCorrection = plannedSessionTabSelection;
902
+ processArgs = ["--json", ...(executionPlan.namespace ? ["--namespace", executionPlan.namespace] : []), "--session", executionPlan.sessionName, "batch"];
903
+ processStdin = JSON.stringify(pinnedBatchPlan.steps);
904
+ includePinnedNavigationSummary = pinnedBatchPlan.includeNavigationSummary;
905
+ pinnedBatchUnwrapMode = pinnedBatchPlan.unwrapMode;
906
+ }
907
+ }
731
908
  }
732
- if (pinnedBatchPlan) {
733
- sessionTabCorrection = plannedSessionTabSelection;
734
- processArgs = ["--json", ...(executionPlan.namespace ? ["--namespace", executionPlan.namespace] : []), "--session", executionPlan.sessionName, "batch"];
735
- processStdin = JSON.stringify(pinnedBatchPlan.steps);
736
- includePinnedNavigationSummary = pinnedBatchPlan.includeNavigationSummary;
737
- pinnedBatchUnwrapMode = pinnedBatchPlan.unwrapMode;
909
+ }
910
+ const clickDispatchProbe = pinnedBatchUnwrapMode === undefined && compiledElectron === undefined
911
+ ? await prepareClickDispatchProbe({ commandTokens, cwd, namespace: executionPlan.namespace, refSnapshot: promptRefSnapshot, sessionName: executionPlan.sessionName, signal })
912
+ : undefined;
913
+ let readTimeoutPageUrl = priorSessionTabTarget?.url;
914
+ if (options.params.timeoutMs === undefined && readTimeoutPageUrl === undefined && executionPlan.sessionName && commandTimeoutNeedsActivePageUrl(commandTokens, processStdin)) {
915
+ try {
916
+ const data = await runSessionCommandData({ args: ["get", "url"], cwd, namespace: executionPlan.namespace, sessionName: executionPlan.sessionName, signal });
917
+ readTimeoutPageUrl = extractStringResultField(data, "result") ?? extractStringResultField(data, "url");
738
918
  }
919
+ catch { }
739
920
  }
740
- }
921
+ const processTimeoutMs = options.params.timeoutMs ?? getDialogAwareProcessTimeoutMs(commandTokens, promptRefSnapshot, processStdin) ?? getCommandAwareProcessTimeoutMs(commandTokens, processStdin, readTimeoutPageUrl);
922
+ const redactedProcessArgs = redactInvocationArgs(processArgs);
923
+ const scrollAmount = Number(commandTokens.find((token) => /^\d+(?:\.\d+)?$/.test(token)));
924
+ const shouldProbeScrollNoop = executionPlan.commandInfo.command === "scroll" && executionPlan.startupScopedFlags.length === 0 && (state.managedSessionActive || sessionMode === "fresh") && (!Number.isFinite(scrollAmount) || scrollAmount >= 500);
925
+ const scrollPositionBefore = shouldProbeScrollNoop
926
+ ? await collectScrollPositionSnapshot({ cwd, namespace: executionPlan.namespace, sessionName: executionPlan.sessionName, signal })
927
+ : undefined;
928
+ onUpdate?.({
929
+ content: [{ type: "text", text: `Running agent-browser ${buildInvocationPreview(redactedProcessArgs)}` }],
930
+ details: {
931
+ compatibilityWorkaround,
932
+ effectiveArgs: redactedProcessArgs,
933
+ sessionMode,
934
+ sessionTabCorrection,
935
+ ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
936
+ },
937
+ });
938
+ managedSessionPolicyLockTransferred = true;
939
+ electronLaunchTransferred = true;
940
+ return {
941
+ kind: "ready",
942
+ prepared: {
943
+ commandTokens,
944
+ managedSessionPolicyLock,
945
+ compiledElectron,
946
+ compiledJob,
947
+ compiledNetworkSourceLookup,
948
+ compiledQaPreset,
949
+ compiledSemanticAction,
950
+ compiledSourceLookup,
951
+ compatibilityWorkaround,
952
+ clickDispatchProbe,
953
+ electronLaunch,
954
+ exactSensitiveValues,
955
+ executionPlan,
956
+ includePinnedNavigationSummary,
957
+ ownedManagedSessionContext: ownedManagedSession,
958
+ pinnedBatchUnwrapMode,
959
+ preparedArgs,
960
+ priorRefSnapshotState,
961
+ priorSessionTabTarget,
962
+ priorSessionTabTargetUnknown,
963
+ processArgs,
964
+ processStdin,
965
+ processTimeoutMs,
966
+ redactedArgs,
967
+ redactedCompiledElectron,
968
+ redactedCompiledJob,
969
+ redactedCompiledNetworkSourceLookup,
970
+ redactedCompiledQaPreset,
971
+ redactedCompiledSemanticAction,
972
+ redactedCompiledSourceLookup,
973
+ redactedEffectiveArgs,
974
+ redactedProcessArgs,
975
+ redactedRecoveryHint,
976
+ resolvedSemanticActionRefSnapshot,
977
+ runtimeToolArgs,
978
+ runtimeToolStdin,
979
+ scrollPositionBefore,
980
+ sessionMode,
981
+ sessionTabCorrection,
982
+ sessionTabPinningReason,
983
+ shouldProbeScrollNoop,
984
+ statePatch,
985
+ userRequestedJson,
986
+ },
987
+ };
988
+ });
741
989
  }
742
- const clickDispatchProbe = pinnedBatchUnwrapMode === undefined && compiledElectron === undefined
743
- ? await prepareClickDispatchProbe({ commandTokens, cwd, namespace: executionPlan.namespace, refSnapshot: promptRefSnapshot, sessionName: executionPlan.sessionName, signal })
744
- : undefined;
745
- let readTimeoutPageUrl = priorSessionTabTarget?.url;
746
- if (options.params.timeoutMs === undefined && readTimeoutPageUrl === undefined && executionPlan.sessionName && commandTimeoutNeedsActivePageUrl(commandTokens, processStdin)) {
747
- try {
748
- const data = await runSessionCommandData({ args: ["get", "url"], cwd, namespace: executionPlan.namespace, sessionName: executionPlan.sessionName, signal });
749
- readTimeoutPageUrl = extractStringResultField(data, "result") ?? extractStringResultField(data, "url");
990
+ finally {
991
+ if (!managedSessionPolicyLockTransferred)
992
+ await managedSessionPolicyLock?.release();
993
+ if (electronLaunch && !electronLaunchTransferred) {
994
+ try {
995
+ const cleanup = await cleanupElectronLaunchResources({
996
+ child: electronLaunch.child,
997
+ record: electronLaunch.record,
998
+ timeoutMs: options.implicitSessionCloseTimeoutMs,
999
+ });
1000
+ if (cleanup.partial) {
1001
+ state.electronLaunchRecords.set(cleanup.launchId, cleanup.record);
1002
+ state.electronChildProcesses.set(cleanup.launchId, electronLaunch.child);
1003
+ }
1004
+ }
1005
+ catch {
1006
+ state.electronLaunchRecords.set(electronLaunch.record.launchId, electronLaunch.record);
1007
+ state.electronChildProcesses.set(electronLaunch.record.launchId, electronLaunch.child);
1008
+ }
750
1009
  }
751
- catch { }
752
1010
  }
753
- const processTimeoutMs = options.params.timeoutMs ?? getDialogAwareProcessTimeoutMs(commandTokens, promptRefSnapshot, processStdin) ?? getCommandAwareProcessTimeoutMs(commandTokens, processStdin, readTimeoutPageUrl);
754
- const redactedProcessArgs = redactInvocationArgs(processArgs);
755
- const scrollAmount = Number(commandTokens.find((token) => /^\d+(?:\.\d+)?$/.test(token)));
756
- const shouldProbeScrollNoop = executionPlan.commandInfo.command === "scroll" && executionPlan.startupScopedFlags.length === 0 && (state.managedSessionActive || sessionMode === "fresh") && (!Number.isFinite(scrollAmount) || scrollAmount >= 500);
757
- const scrollPositionBefore = shouldProbeScrollNoop
758
- ? await collectScrollPositionSnapshot({ cwd, namespace: executionPlan.namespace, sessionName: executionPlan.sessionName, signal })
759
- : undefined;
760
- onUpdate?.({
761
- content: [{ type: "text", text: `Running agent-browser ${buildInvocationPreview(redactedProcessArgs)}` }],
762
- details: {
763
- compatibilityWorkaround,
764
- effectiveArgs: redactedProcessArgs,
765
- sessionMode,
766
- sessionTabCorrection,
767
- ...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace),
768
- },
769
- });
770
- return { kind: "ready", prepared: {
771
- commandTokens,
772
- compiledElectron,
773
- compiledJob,
774
- compiledNetworkSourceLookup,
775
- compiledQaPreset,
776
- compiledSemanticAction,
777
- compiledSourceLookup,
778
- compatibilityWorkaround,
779
- clickDispatchProbe,
780
- electronLaunch,
781
- exactSensitiveValues,
782
- executionPlan,
783
- includePinnedNavigationSummary,
784
- pinnedBatchUnwrapMode,
785
- preparedArgs,
786
- priorRefSnapshotState,
787
- priorSessionTabTarget,
788
- processArgs,
789
- processStdin,
790
- processTimeoutMs,
791
- redactedArgs,
792
- redactedCompiledElectron,
793
- redactedCompiledJob,
794
- redactedCompiledNetworkSourceLookup,
795
- redactedCompiledQaPreset,
796
- redactedCompiledSemanticAction,
797
- redactedCompiledSourceLookup,
798
- redactedEffectiveArgs,
799
- redactedProcessArgs,
800
- redactedRecoveryHint,
801
- resolvedSemanticActionRefSnapshot,
802
- runtimeToolArgs,
803
- runtimeToolStdin,
804
- scrollPositionBefore,
805
- sessionMode,
806
- sessionTabCorrection,
807
- sessionTabPinningReason,
808
- shouldProbeScrollNoop,
809
- statePatch,
810
- userRequestedJson,
811
- } };
812
1011
  }