pi-agent-browser-native 0.2.72 → 0.2.74

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