pi-agent-browser-native 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -1
- package/README.md +20 -5
- package/dist/extensions/agent-browser/index.js +138 -75
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +6 -5
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +10 -1
- package/dist/extensions/agent-browser/lib/input-modes/params.js +20 -7
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +0 -1
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +13 -12
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +6 -25
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +2 -3
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +10 -3
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +4 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +104 -117
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +61 -35
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +100 -135
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +3 -1
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +3 -1
- package/dist/extensions/agent-browser/lib/page-target-validation.js +10 -10
- package/dist/extensions/agent-browser/lib/parsing.js +7 -0
- package/dist/extensions/agent-browser/lib/playbook.js +5 -8
- package/dist/extensions/agent-browser/lib/process-identity.js +10 -2
- package/dist/extensions/agent-browser/lib/process.js +23 -7
- package/dist/extensions/agent-browser/lib/recording-reservations.js +3 -1
- package/dist/extensions/agent-browser/lib/results/envelope.js +4 -1
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +45 -43
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +4 -4
- package/dist/extensions/agent-browser/lib/runtime.js +18 -2
- package/dist/extensions/agent-browser/lib/session-page-state.js +29 -10
- package/docs/ARCHITECTURE.md +7 -4
- package/docs/COMMAND_REFERENCE.md +22 -15
- package/docs/ELECTRON.md +6 -6
- package/docs/RELEASE.md +14 -5
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +13 -3
- package/docs/TOOL_CONTRACT.md +33 -22
- package/package.json +1 -1
|
@@ -16,13 +16,13 @@ import { buildAgentBrowserResultCategoryDetails } from "../../results/categories
|
|
|
16
16
|
import { applyNamespaceToNextActions } from "../../results/next-actions.js";
|
|
17
17
|
import { buildSessionAwareStaleRefNextActions, buildSessionTabRecoveryNextActions } from "../../results/recovery-next-actions.js";
|
|
18
18
|
import { resolveVisibleRefActionFromSnapshot } from "../../results/selector-recovery.js";
|
|
19
|
-
import { extractRefSnapshotFromData } from "../../session-page-state.js";
|
|
20
|
-
import { buildExecutionPlan, canUseHeadlessCompatibilityUserAgent, createFreshSessionName, extractCommandTokens, extractUpstreamCommandTokens, getDefaultHeadlessCompatUserAgent, parseWaitCommandTokens, redactInvocationArgs, } from "../../runtime.js";
|
|
19
|
+
import { buildPageTransitionRefSnapshotInvalidation, extractRefSnapshotFromData, normalizeComparableUrl } from "../../session-page-state.js";
|
|
20
|
+
import { buildExecutionPlan, canUseHeadlessCompatibilityUserAgent, createFreshSessionName, extractCommandTokens, extractUpstreamCommandTokens, getDefaultHeadlessCompatUserAgent, parseWaitCommandTokens, redactInvocationArgs, redactSensitiveText, } from "../../runtime.js";
|
|
21
21
|
import { buildOwnedManagedSessionRestoreContext, resolveExplicitAutosaveInterval, withOwnedManagedSessionContext, } from "../../managed-session-restore.js";
|
|
22
22
|
import { getAgentBrowserProcessEnvironment } from "../../process-environment.js";
|
|
23
23
|
import { getExplicitSessionPageVerificationRequirement, getPageTargetValidationError, } from "../../page-target-validation.js";
|
|
24
24
|
import { acquireOwnedManagedSessionDaemonPolicy, getRunningHeadedAutosavePolicyChangeError } from "./managed-session-daemon-policy.js";
|
|
25
|
-
import {
|
|
25
|
+
import { buildManagedSessionOutcome, buildSessionDetailFields, buildStaleRefPreflight, getSessionContextKey, extractStringResultField, ensureSessionTabTarget, getGuardedRefUsage, getTraceOwnerGuardMessage, runSessionCommandData, shouldPinSessionTabForCommand, } from "./session-state.js";
|
|
26
26
|
import { getUpstreamEffectiveBatchSteps, parseBatchStdinJsonArray } from "../batch-stdin.js";
|
|
27
27
|
import { buildElectronHostFailureResult, formatAgentBrowserNextActionsText, getElectronLaunchFailureCategory, redactRecoveryHint } from "./final-result.js";
|
|
28
28
|
import { prepareClickDispatchProbe } from "./click-dispatch.js";
|
|
@@ -241,8 +241,8 @@ function getSamePageFreshnessPreflightFailure(options) {
|
|
|
241
241
|
const { refIds } = options;
|
|
242
242
|
if (refIds.length === 0)
|
|
243
243
|
return undefined;
|
|
244
|
-
const previousUrl = options.previousSnapshot.target?.url;
|
|
245
|
-
const currentUrl = options.currentSnapshot.target?.url;
|
|
244
|
+
const previousUrl = normalizeComparableUrl(options.previousSnapshot.target?.url);
|
|
245
|
+
const currentUrl = normalizeComparableUrl(options.currentSnapshot.target?.url);
|
|
246
246
|
if (!previousUrl || !currentUrl || previousUrl !== currentUrl || currentUrl === "about:blank")
|
|
247
247
|
return undefined;
|
|
248
248
|
const mismatchedRefs = refIds.filter((refId) => {
|
|
@@ -267,8 +267,8 @@ async function collectSamePageRefFreshnessPreflight(options) {
|
|
|
267
267
|
const refIds = [...new Set(getGuardedRefUsage(options.commandTokens, options.stdin))];
|
|
268
268
|
if (!options.previousSnapshot || !options.sessionName || refIds.length === 0)
|
|
269
269
|
return undefined;
|
|
270
|
-
const previousUrl = options.previousSnapshot.target?.url;
|
|
271
|
-
const currentTargetUrl = options.currentTarget?.url;
|
|
270
|
+
const previousUrl = normalizeComparableUrl(options.previousSnapshot.target?.url);
|
|
271
|
+
const currentTargetUrl = normalizeComparableUrl(options.currentTarget?.url);
|
|
272
272
|
if (currentTargetUrl === "about:blank" || (previousUrl && currentTargetUrl && previousUrl !== currentTargetUrl))
|
|
273
273
|
return undefined;
|
|
274
274
|
const snapshotData = await runSessionCommandData({ args: ["snapshot", "-i"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal });
|
|
@@ -401,7 +401,29 @@ export async function prepareBrowserRun(options) {
|
|
|
401
401
|
let managedSessionPolicyLockTransferred = false;
|
|
402
402
|
let electronLaunchTransferred = false;
|
|
403
403
|
try {
|
|
404
|
-
|
|
404
|
+
let preparedArgs;
|
|
405
|
+
try {
|
|
406
|
+
preparedArgs = await prepareAgentBrowserArgs(runtimeToolArgs, runtimeToolStdin, cwd);
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
signal?.throwIfAborted();
|
|
410
|
+
if (!(error instanceof Error) || !("syscall" in error) || error.syscall !== "mkdir" || !("path" in error) || typeof error.path !== "string")
|
|
411
|
+
throw error;
|
|
412
|
+
const guidance = "Choose a writable artifact path whose parent components are directories. Use absolute paths in raw batch artifact rows.";
|
|
413
|
+
const validationError = redactSensitiveText(`Could not prepare artifact directory ${error.path}: ${error.message}. ${guidance}`);
|
|
414
|
+
const nextActions = [{ artifactPath: redactSensitiveText(error.path), id: "verify-artifact-path", reason: guidance, safety: "The requested browser command did not run; inspect the directory with host file tools before retrying.", tool: "agent_browser" }];
|
|
415
|
+
return { kind: "early-result", result: {
|
|
416
|
+
content: [{ type: "text", text: validationError }],
|
|
417
|
+
details: {
|
|
418
|
+
agentBrowserStarted: false,
|
|
419
|
+
args: redactedArgs,
|
|
420
|
+
nextActions,
|
|
421
|
+
...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, succeeded: false, validationError }),
|
|
422
|
+
validationError,
|
|
423
|
+
},
|
|
424
|
+
isError: true,
|
|
425
|
+
} };
|
|
426
|
+
}
|
|
405
427
|
const userRequestedJson = runtimeToolArgs.includes("--json");
|
|
406
428
|
let executionPlan = buildExecutionPlan(preparedArgs.args, {
|
|
407
429
|
freshSessionName,
|
|
@@ -480,6 +502,8 @@ export async function prepareBrowserRun(options) {
|
|
|
480
502
|
compatibilityUserAgent: compatibilityUserAgentApplied ? compatibilityUserAgent : undefined,
|
|
481
503
|
wrapperInjectedUserAgent: compatibilityUserAgentApplied,
|
|
482
504
|
});
|
|
505
|
+
let managedSessionDaemonInactive = false;
|
|
506
|
+
let managedSessionCleanupOnlyReason;
|
|
483
507
|
if (!executionPlan.validationError && ownedManagedSession) {
|
|
484
508
|
const closeCommand = isCloseCommand(executionPlan.commandInfo.command);
|
|
485
509
|
const policy = await acquireOwnedManagedSessionDaemonPolicy({
|
|
@@ -488,7 +512,9 @@ export async function prepareBrowserRun(options) {
|
|
|
488
512
|
signal,
|
|
489
513
|
});
|
|
490
514
|
managedSessionPolicyLock = policy.lock;
|
|
515
|
+
managedSessionDaemonInactive = policy.daemonStatus === "inactive";
|
|
491
516
|
if (policy.error) {
|
|
517
|
+
managedSessionCleanupOnlyReason = policy.cleanupOnlyReason;
|
|
492
518
|
executionPlan = {
|
|
493
519
|
...executionPlan,
|
|
494
520
|
recoveryHint: undefined,
|
|
@@ -524,10 +550,68 @@ export async function prepareBrowserRun(options) {
|
|
|
524
550
|
let priorSessionTabTarget = priorSessionPageState.tabTarget;
|
|
525
551
|
let priorSessionTabTargetUnknown = priorSessionPageState.tabTargetUnknown;
|
|
526
552
|
const sessionTabPinningReason = priorSessionPageState.pinningReason;
|
|
527
|
-
|
|
528
|
-
|
|
553
|
+
let priorRefSnapshotState = priorSessionPageState.refSnapshot;
|
|
554
|
+
let priorRefSnapshotInvalidation = priorSessionPageState.refSnapshotInvalidation;
|
|
555
|
+
const coldManagedSession = (managedSessionDaemonInactive || priorSessionPageState.tabReopenPending === true)
|
|
556
|
+
&& recordedOwnedSession !== undefined
|
|
557
|
+
&& sessionTabPinningReason === "restore"
|
|
558
|
+
&& ownedManagedSession?.restoreDecision === "enabled"
|
|
559
|
+
&& !managedSessionRestoreDisabled()
|
|
560
|
+
&& !options.preserveAttachedBrowserSession;
|
|
561
|
+
if (coldManagedSession && sessionStateKey) {
|
|
562
|
+
sessionPageState.setTabReopenPending({ pending: true, sessionName: sessionStateKey, update: options.sessionPageStateUpdate });
|
|
563
|
+
priorRefSnapshotState = undefined;
|
|
564
|
+
priorRefSnapshotInvalidation = buildPageTransitionRefSnapshotInvalidation("The managed browser shut down. Reopening its URL reloads the page; run snapshot -i before using page-scoped refs.");
|
|
565
|
+
sessionPageState.applyRefSnapshotInvalidation({ invalidation: priorRefSnapshotInvalidation, sessionName: sessionStateKey, update: options.sessionPageStateUpdate });
|
|
566
|
+
}
|
|
529
567
|
let semanticActionVisibleRefResolution;
|
|
530
568
|
let livePageVerified = false;
|
|
569
|
+
let sessionTabCorrection;
|
|
570
|
+
let sessionTabSelectionError;
|
|
571
|
+
const plannedCommandTokens = extractUpstreamCommandTokens(preparedArgs.args);
|
|
572
|
+
const knownStaleRef = buildStaleRefPreflight({ commandTokens: plannedCommandTokens, currentTarget: priorSessionTabTarget, refSnapshot: priorRefSnapshotState, refSnapshotInvalidation: priorRefSnapshotInvalidation, stdin: runtimeToolStdin });
|
|
573
|
+
const invalidStdin = validateStdinCommandContract({ command: executionPlan.commandInfo.command, commandTokens: plannedCommandTokens, stdin: runtimeToolStdin });
|
|
574
|
+
const pinSessionTab = shouldPinSessionTabForCommand({
|
|
575
|
+
command: executionPlan.commandInfo.command,
|
|
576
|
+
commandTokens: plannedCommandTokens,
|
|
577
|
+
// URL QA clears diagnostics before its explicit open; those clears do not need the old tab.
|
|
578
|
+
pinningRequired: sessionTabPinningReason !== undefined && compiledQaPreset?.checks.url === undefined,
|
|
579
|
+
reopenPending: coldManagedSession,
|
|
580
|
+
sessionName: executionPlan.sessionName,
|
|
581
|
+
stdin: runtimeToolStdin,
|
|
582
|
+
});
|
|
583
|
+
if (!executionPlan.validationError && !executionPlan.plainTextInspection && !knownStaleRef && !invalidStdin && priorSessionTabTarget && pinSessionTab) {
|
|
584
|
+
signal?.throwIfAborted();
|
|
585
|
+
const reopened = !coldManagedSession || await runSessionCommandData({
|
|
586
|
+
args: ["open", priorSessionTabTarget.url], cwd, namespace: executionPlan.namespace, sessionName: executionPlan.sessionName, signal, timeoutMs: params.timeoutMs,
|
|
587
|
+
onProcessResult: ({ agentBrowserStarted }) => {
|
|
588
|
+
// A started open may have navigated even if its CLI was aborted before replying.
|
|
589
|
+
if (agentBrowserStarted && sessionStateKey)
|
|
590
|
+
sessionPageState.setTabReopenPending({ pending: false, sessionName: sessionStateKey, update: options.sessionPageStateUpdate });
|
|
591
|
+
},
|
|
592
|
+
}) !== undefined;
|
|
593
|
+
const selection = signal?.aborted ? undefined : reopened
|
|
594
|
+
? await ensureSessionTabTarget({ cwd, namespace: executionPlan.namespace, sessionName: executionPlan.sessionName, signal, target: priorSessionTabTarget })
|
|
595
|
+
: { error: "agent-browser could not reopen the remembered URL after the managed browser shut down. Navigate explicitly, then run snapshot -i before retrying." };
|
|
596
|
+
if (coldManagedSession && signal?.aborted) {
|
|
597
|
+
const errorText = "agent_browser was aborted while reopening the remembered page. The requested command did not run.";
|
|
598
|
+
return { kind: "early-result", result: {
|
|
599
|
+
content: [{ type: "text", text: errorText }],
|
|
600
|
+
details: {
|
|
601
|
+
aborted: true, args: redactedArgs, command: executionPlan.commandInfo.command,
|
|
602
|
+
effectiveArgs: redactInvocationArgs(executionPlan.effectiveArgs), sessionMode,
|
|
603
|
+
...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
|
|
604
|
+
...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, command: executionPlan.commandInfo.command, errorText, failureCategory: "aborted", succeeded: false }),
|
|
605
|
+
},
|
|
606
|
+
isError: true,
|
|
607
|
+
} };
|
|
608
|
+
}
|
|
609
|
+
signal?.throwIfAborted();
|
|
610
|
+
sessionTabCorrection = selection?.correction;
|
|
611
|
+
sessionTabSelectionError = selection?.error;
|
|
612
|
+
if (selection?.error)
|
|
613
|
+
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: selection.error };
|
|
614
|
+
}
|
|
531
615
|
const isCallerOwnedExplicitSession = () => executionPlan.sessionName !== undefined
|
|
532
616
|
&& executionPlan.usedImplicitSession === false
|
|
533
617
|
&& ownedManagedSession === undefined;
|
|
@@ -655,7 +739,7 @@ export async function prepareBrowserRun(options) {
|
|
|
655
739
|
freshSessionOrdinal += 1;
|
|
656
740
|
}
|
|
657
741
|
if (executionPlan.validationError) {
|
|
658
|
-
const nextActions = applyNamespaceToNextActions(unsupportedScrollIntoViewRecovery?.nextActions, executionPlan.namespace);
|
|
742
|
+
const nextActions = applyNamespaceToNextActions(sessionTabSelectionError ? buildSessionTabRecoveryNextActions({ kind: "tab-drift", resultCategory: "failure", sessionName: executionPlan.sessionName, tabCorrection: sessionTabCorrection, target: priorSessionTabTarget }) : unsupportedScrollIntoViewRecovery?.nextActions, executionPlan.namespace);
|
|
659
743
|
const nextActionsText = formatAgentBrowserNextActionsText(nextActions);
|
|
660
744
|
return { kind: "early-result", statePatch, result: {
|
|
661
745
|
content: [{ type: "text", text: [executionPlan.validationError, nextActionsText].filter((text) => text !== undefined).join("\n\n") }],
|
|
@@ -667,11 +751,16 @@ export async function prepareBrowserRun(options) {
|
|
|
667
751
|
compiledSourceLookup: redactedCompiledSourceLookup,
|
|
668
752
|
compiledNetworkSourceLookup: redactedCompiledNetworkSourceLookup,
|
|
669
753
|
invalidValueFlag: executionPlan.invalidValueFlag,
|
|
754
|
+
managedSessionCleanupOnlyReason,
|
|
755
|
+
...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
|
|
756
|
+
...(managedSessionCleanupOnlyReason ? { namespace: ownedManagedSession?.namespace ?? "" } : {}),
|
|
670
757
|
nextActions,
|
|
671
758
|
sessionMode,
|
|
672
759
|
sessionRecoveryHint: redactedRecoveryHint,
|
|
673
760
|
startupScopedFlags: executionPlan.startupScopedFlags,
|
|
674
|
-
...
|
|
761
|
+
...(sessionTabSelectionError ? { effectiveArgs: redactedEffectiveArgs, sessionTabCorrection } : {}),
|
|
762
|
+
...(coldManagedSession ? { refSnapshotInvalidation: priorRefSnapshotInvalidation } : {}),
|
|
763
|
+
...buildAgentBrowserResultCategoryDetails({ args: redactedArgs, command: executionPlan.commandInfo.command, errorText: executionPlan.validationError, failureCategory: sessionTabSelectionError ? "tab-drift" : undefined, succeeded: false, validationError: executionPlan.validationError }),
|
|
675
764
|
validationError: executionPlan.validationError,
|
|
676
765
|
},
|
|
677
766
|
isError: true,
|
|
@@ -911,109 +1000,9 @@ export async function prepareBrowserRun(options) {
|
|
|
911
1000
|
});
|
|
912
1001
|
if (directAnchorDownload)
|
|
913
1002
|
return { kind: "early-result", statePatch: { ...statePatch, artifactManifest: directAnchorDownload.artifactManifest ?? statePatch.artifactManifest }, result: directAnchorDownload.result };
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
let processArgs = executionPlan.effectiveArgs;
|
|
918
|
-
let processStdin = preparedArgs.stdin ?? runtimeToolStdin;
|
|
919
|
-
if (priorSessionTabTarget &&
|
|
920
|
-
shouldPinSessionTabForCommand({
|
|
921
|
-
command: executionPlan.commandInfo.command,
|
|
922
|
-
commandTokens,
|
|
923
|
-
pinningRequired: sessionTabPinningReason !== undefined,
|
|
924
|
-
sessionName: executionPlan.sessionName,
|
|
925
|
-
stdin: runtimeToolStdin,
|
|
926
|
-
})) {
|
|
927
|
-
const collectTabSelection = promptRefSnapshot && getGuardedRefUsage(commandTokens, runtimeToolStdin).length > 0 ? collectAnySessionTabSelection : collectSessionTabSelection;
|
|
928
|
-
const plannedSessionTabSelection = await collectTabSelection({
|
|
929
|
-
cwd,
|
|
930
|
-
namespace: executionPlan.namespace,
|
|
931
|
-
sessionName: executionPlan.sessionName,
|
|
932
|
-
signal,
|
|
933
|
-
target: priorSessionTabTarget,
|
|
934
|
-
});
|
|
935
|
-
if (plannedSessionTabSelection && executionPlan.sessionName) {
|
|
936
|
-
if (executionPlan.commandInfo.command === "eval" && runtimeToolStdin !== undefined) {
|
|
937
|
-
const appliedSessionTabSelection = await applyOpenResultTabCorrection({
|
|
938
|
-
correction: plannedSessionTabSelection,
|
|
939
|
-
cwd,
|
|
940
|
-
namespace: executionPlan.namespace,
|
|
941
|
-
sessionName: executionPlan.sessionName,
|
|
942
|
-
signal,
|
|
943
|
-
});
|
|
944
|
-
if (!appliedSessionTabSelection) {
|
|
945
|
-
const error = "agent-browser could not re-select the intended tab before running the command.";
|
|
946
|
-
return { kind: "early-result", statePatch, result: {
|
|
947
|
-
content: [{ type: "text", text: error }],
|
|
948
|
-
details: {
|
|
949
|
-
args: redactedArgs,
|
|
950
|
-
command: executionPlan.commandInfo.command,
|
|
951
|
-
compatibilityWorkaround,
|
|
952
|
-
effectiveArgs: redactedEffectiveArgs,
|
|
953
|
-
sessionMode,
|
|
954
|
-
sessionTabCorrection: plannedSessionTabSelection,
|
|
955
|
-
...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: error, failureCategory: "tab-drift", succeeded: false, tabDrift: true, validationError: error }),
|
|
956
|
-
nextActions: applyNamespaceToNextActions(buildSessionTabRecoveryNextActions({
|
|
957
|
-
kind: "tab-drift",
|
|
958
|
-
resultCategory: "failure",
|
|
959
|
-
sessionName: executionPlan.sessionName,
|
|
960
|
-
tabCorrection: plannedSessionTabSelection,
|
|
961
|
-
target: priorSessionTabTarget,
|
|
962
|
-
}), executionPlan.namespace),
|
|
963
|
-
validationError: error,
|
|
964
|
-
...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
|
|
965
|
-
},
|
|
966
|
-
isError: true,
|
|
967
|
-
} };
|
|
968
|
-
}
|
|
969
|
-
sessionTabCorrection = appliedSessionTabSelection;
|
|
970
|
-
}
|
|
971
|
-
else {
|
|
972
|
-
const pinnedBatchPlan = buildPinnedBatchPlan({
|
|
973
|
-
command: executionPlan.commandInfo.command,
|
|
974
|
-
commandTokens,
|
|
975
|
-
selectedTab: plannedSessionTabSelection.selectedTab,
|
|
976
|
-
stdin: runtimeToolStdin,
|
|
977
|
-
});
|
|
978
|
-
if (pinnedBatchPlan && "error" in pinnedBatchPlan) {
|
|
979
|
-
return { kind: "early-result", statePatch, result: {
|
|
980
|
-
content: [{ type: "text", text: pinnedBatchPlan.error }],
|
|
981
|
-
details: {
|
|
982
|
-
args: redactedArgs,
|
|
983
|
-
command: executionPlan.commandInfo.command,
|
|
984
|
-
compatibilityWorkaround,
|
|
985
|
-
effectiveArgs: redactedEffectiveArgs,
|
|
986
|
-
sessionMode,
|
|
987
|
-
sessionTabCorrection: plannedSessionTabSelection,
|
|
988
|
-
...buildAgentBrowserResultCategoryDetails({ args: redactedEffectiveArgs, command: executionPlan.commandInfo.command, errorText: pinnedBatchPlan.error, failureCategory: "tab-drift", succeeded: false, tabDrift: true, validationError: pinnedBatchPlan.error }),
|
|
989
|
-
nextActions: applyNamespaceToNextActions(buildSessionTabRecoveryNextActions({
|
|
990
|
-
kind: "tab-drift",
|
|
991
|
-
resultCategory: "failure",
|
|
992
|
-
sessionName: executionPlan.sessionName,
|
|
993
|
-
tabCorrection: plannedSessionTabSelection,
|
|
994
|
-
target: priorSessionTabTarget,
|
|
995
|
-
}), executionPlan.namespace),
|
|
996
|
-
validationError: pinnedBatchPlan.error,
|
|
997
|
-
...buildSessionDetailFields(executionPlan.sessionName, executionPlan.usedImplicitSession, executionPlan.namespace, managedSessionRestoreDisabled()),
|
|
998
|
-
},
|
|
999
|
-
isError: true,
|
|
1000
|
-
} };
|
|
1001
|
-
}
|
|
1002
|
-
if (pinnedBatchPlan) {
|
|
1003
|
-
sessionTabCorrection = plannedSessionTabSelection;
|
|
1004
|
-
// The rewritten batch must keep the caller's fail-fast semantics:
|
|
1005
|
-
// upstream reads only the exact --bail token, so re-emit it whenever
|
|
1006
|
-
// the original batch tokens carried it (argv or stdin/job/qa mode).
|
|
1007
|
-
const pinnedBailArgs = commandTokens.includes("--bail") ? ["--bail"] : [];
|
|
1008
|
-
processArgs = ["--json", ...(executionPlan.namespace !== undefined ? ["--namespace", executionPlan.namespace] : []), "--session", executionPlan.sessionName, "batch", ...pinnedBailArgs];
|
|
1009
|
-
processStdin = JSON.stringify(pinnedBatchPlan.steps);
|
|
1010
|
-
includePinnedNavigationSummary = pinnedBatchPlan.includeNavigationSummary;
|
|
1011
|
-
pinnedBatchUnwrapMode = pinnedBatchPlan.unwrapMode;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
const clickDispatchProbe = pinnedBatchUnwrapMode === undefined && compiledElectron === undefined
|
|
1003
|
+
const processArgs = executionPlan.effectiveArgs;
|
|
1004
|
+
const processStdin = preparedArgs.stdin ?? runtimeToolStdin;
|
|
1005
|
+
const clickDispatchProbe = compiledElectron === undefined
|
|
1017
1006
|
? await prepareClickDispatchProbe({ commandTokens, cwd, namespace: executionPlan.namespace, refSnapshot: promptRefSnapshot, sessionName: executionPlan.sessionName, signal })
|
|
1018
1007
|
: undefined;
|
|
1019
1008
|
let readTimeoutPageUrl = priorSessionTabTarget?.url;
|
|
@@ -1061,9 +1050,7 @@ export async function prepareBrowserRun(options) {
|
|
|
1061
1050
|
electronLaunch,
|
|
1062
1051
|
exactSensitiveValues,
|
|
1063
1052
|
executionPlan,
|
|
1064
|
-
includePinnedNavigationSummary,
|
|
1065
1053
|
ownedManagedSessionContext: ownedManagedSession,
|
|
1066
|
-
pinnedBatchUnwrapMode,
|
|
1067
1054
|
preparedArgs,
|
|
1068
1055
|
priorRefSnapshotState,
|
|
1069
1056
|
priorSessionTabTarget,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
|
+
import { parseArgvDescriptor } from "../../argv-descriptor.js";
|
|
3
|
+
import { needsManagedSession } from "../../command-policy.js";
|
|
2
4
|
import { getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace } from "../../argv-grammar.js";
|
|
3
5
|
import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "../../batch-lifecycle.js";
|
|
4
|
-
import { isCloseAllCommand, isCloseCommand, isOpenNavigationCommand, isUnverifiedPageTransitionCommand } from "../../command-taxonomy.js";
|
|
6
|
+
import { isCloseAllCommand, isCloseCommand, isOpenNavigationCommand, isRecordPageTransitionCommand, isUnverifiedPageTransitionCommand, isWindowOrDiffPageTransitionCommand } from "../../command-taxonomy.js";
|
|
5
7
|
import { OPEN_RESULT_TAB_CORRECTION_FLAGS } from "../../launch-scoped-flags.js";
|
|
6
8
|
import { cleanupElectronLaunchResources, inspectElectronLaunchStatus } from "../../electron/cleanup.js";
|
|
7
9
|
import { getResultingPageTargetState, commandRequiresLivePageVerification } from "../../page-target-validation.js";
|
|
@@ -9,6 +11,7 @@ import { analyzeNetworkSourceLookupResults, analyzeSourceLookupResults, redactNe
|
|
|
9
11
|
import { analyzeQaPresetResults, analyzeQaPresetTimeout, buildQaCompactFailureText, buildQaCompactPassText, extractQaPageContext } from "../../input-modes/job.js";
|
|
10
12
|
import { applyNetworkRouteRecords, buildNetworkRouteDiagnostics } from "../../results/network-routes.js";
|
|
11
13
|
import { buildToolPresentation } from "../../results/presentation.js";
|
|
14
|
+
import { compactLargePresentationOutput } from "../../results/presentation/large-output.js";
|
|
12
15
|
import { getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results/envelope.js";
|
|
13
16
|
import { omitUpstreamLifecycle } from "../../results/presentation/common.js";
|
|
14
17
|
import { getClipboardWritePayloadCandidates, redactClipboardPermissionEcho, redactClipboardPermissionErrorValue } from "../../results/presentation/errors.js";
|
|
@@ -20,7 +23,7 @@ import { isManagedSessionRestoreKey } from "../../managed-session-storage.js";
|
|
|
20
23
|
import { createFreshSessionName, extractUpstreamCommandTokens, resolveManagedSessionState } from "../../runtime.js";
|
|
21
24
|
import { getUpstreamEffectiveBatchSteps } from "../batch-stdin.js";
|
|
22
25
|
import { closeManagedSession, inspectManagedSessionDaemon } from "./managed-session-daemon-policy.js";
|
|
23
|
-
import { applyOpenResultTabCorrection, buildAboutBlankRecoveryHint, buildAboutBlankWarning, buildElectronPostCommandHealthDiagnostic, buildElectronRefFreshnessDiagnostic, buildElectronSessionMismatch, buildManagedSessionOutcome, collectOpenResultTabCorrection, collectSessionTabSelection, extractNavigationSummaryFromData, extractStringResultField, findElectronLaunchRecordForSession, formatElectronPostCommandHealthText, formatElectronSessionMismatchText, getSessionContextKey, getStaleRefArgs, mergeNavigationSummaryIntoData, shouldCaptureNavigationSummary, shouldCorrectSessionTabAfterCommand, shouldInspectElectronPostCommandHealth,
|
|
26
|
+
import { applyOpenResultTabCorrection, buildAboutBlankRecoveryHint, buildAboutBlankWarning, buildElectronPostCommandHealthDiagnostic, buildElectronRefFreshnessDiagnostic, buildElectronSessionMismatch, buildManagedSessionOutcome, collectOpenResultTabCorrection, collectSessionTabSelection, commandChoosesSessionTabTarget, extractNavigationSummaryFromData, extractStringResultField, findElectronLaunchRecordForSession, formatElectronPostCommandHealthText, formatElectronSessionMismatchText, getSessionContextKey, getStaleRefArgs, mergeNavigationSummaryIntoData, shouldCaptureNavigationSummary, shouldCorrectSessionTabAfterCommand, shouldInspectElectronPostCommandHealth, updateTraceOwnerState, } from "./session-state.js";
|
|
24
27
|
import { collectClickDispatchDiagnostic } from "./click-dispatch.js";
|
|
25
28
|
import { buildScrollNoopDiagnostic, collectComboboxFocusDiagnostic, collectElectronBroadGetTextScopeDiagnostics, collectElectronHandoff, collectFillVerificationDiagnostic, collectNavigationSummary, collectOverlayBlockerDiagnostic, collectQaAttachedTarget, collectSnapshotOverlayBlockerDiagnostic, collectRecordingDependencyWarning, collectScrollPositionSnapshot, collectSelectorTextVisibilityDiagnostics, collectTimeoutPartialProgress, sleepMs, formatQaAttachedTargetText, getArtifactCleanupGuidance, getEvalResultWarning, getEvalStdinHint, getSourceLookupElectronContext, } from "./diagnostics.js";
|
|
26
29
|
import { repairScreenshotData } from "./prepare.js";
|
|
@@ -160,17 +163,13 @@ export async function processBrowserOutput(input) {
|
|
|
160
163
|
let networkRoutesBySession = state.networkRoutesBySession;
|
|
161
164
|
try {
|
|
162
165
|
const persistentArtifactStore = getPersistentSessionArtifactStore(ctx);
|
|
163
|
-
|
|
166
|
+
// Native upgrade prints text even with --json; all other command shapes keep strict JSON parsing.
|
|
167
|
+
const plainTextUpgrade = !prepared.executionPlan.plainTextInspection && prepared.executionPlan.commandInfo.command === "upgrade" && !needsManagedSession(parseArgvDescriptor(prepared.runtimeToolArgs));
|
|
168
|
+
const parsed = await parseAgentBrowserEnvelope({ stdout: processResult.stdout, stdoutPath: processResult.stdoutSpillPath, plainText: plainTextUpgrade });
|
|
164
169
|
let parseError = parsed.parseError;
|
|
165
170
|
let presentationEnvelope = parsed.envelope;
|
|
166
171
|
let navigationSummary = undefined;
|
|
167
172
|
let failedTransitionReverification = false;
|
|
168
|
-
if (prepared.pinnedBatchUnwrapMode) {
|
|
169
|
-
const pinnedBatchResult = unwrapPinnedSessionBatchEnvelope({ envelope: parsed.envelope, includeNavigationSummary: prepared.includePinnedNavigationSummary, mode: prepared.pinnedBatchUnwrapMode });
|
|
170
|
-
parseError = pinnedBatchResult.parseError ?? parseError;
|
|
171
|
-
presentationEnvelope = pinnedBatchResult.envelope ?? presentationEnvelope;
|
|
172
|
-
navigationSummary = pinnedBatchResult.navigationSummary;
|
|
173
|
-
}
|
|
174
173
|
const repairedScreenshot = await repairScreenshotArtifact({ cwd, envelope: presentationEnvelope, request: prepared.preparedArgs.screenshotPathRequest });
|
|
175
174
|
presentationEnvelope = repairedScreenshot.envelope;
|
|
176
175
|
const repairedBatchScreenshots = await repairBatchScreenshotArtifacts({ cwd, envelope: presentationEnvelope, requests: prepared.preparedArgs.batchScreenshotPathRequests });
|
|
@@ -180,6 +179,15 @@ export async function processBrowserOutput(input) {
|
|
|
180
179
|
const batchCommandSteps = prepared.executionPlan.commandInfo.command === "batch"
|
|
181
180
|
? getUpstreamEffectiveBatchSteps(prepared.commandTokens, prepared.runtimeToolStdin)
|
|
182
181
|
: [];
|
|
182
|
+
const dispatchedCommands = prepared.executionPlan.commandInfo.command !== "batch"
|
|
183
|
+
? [prepared.commandTokens]
|
|
184
|
+
: Array.isArray(presentationEnvelope?.data)
|
|
185
|
+
? presentationEnvelope.data.flatMap((row, index) => isRecord(row) ? [Array.isArray(row.command) && row.command.every((token) => typeof token === "string") ? row.command : batchCommandSteps[index] ?? []] : [])
|
|
186
|
+
: batchCommandSteps;
|
|
187
|
+
const destinationTransition = dispatchedCommands.some((step) => {
|
|
188
|
+
const [command, subcommand] = extractUpstreamCommandTokens(step);
|
|
189
|
+
return isWindowOrDiffPageTransitionCommand(command, subcommand);
|
|
190
|
+
});
|
|
183
191
|
const nestedBatchClose = prepared.executionPlan.commandInfo.command === "batch"
|
|
184
192
|
? getSuccessfulBatchCloseLifecycle(presentationEnvelope?.data, batchCommandSteps)
|
|
185
193
|
: undefined;
|
|
@@ -198,7 +206,7 @@ export async function processBrowserOutput(input) {
|
|
|
198
206
|
const parseFailureOutput = parseError && processResult.stdoutSpillPath
|
|
199
207
|
? { fullOutputUnavailable: "Malformed upstream output was discarded because it may contain sensitive browser data." }
|
|
200
208
|
: {};
|
|
201
|
-
const processSucceeded = !processResult.aborted && !processResult.spawnError && processResult.exitCode === 0;
|
|
209
|
+
const processSucceeded = !processResult.timedOut && !processResult.aborted && !processResult.spawnError && processResult.exitCode === 0;
|
|
202
210
|
const plainTextInspection = prepared.executionPlan.plainTextInspection && processSucceeded;
|
|
203
211
|
const parseSucceeded = plainTextInspection || parseError === undefined;
|
|
204
212
|
if (isStreamEnableAlreadyEnabledNoop({ command: prepared.executionPlan.commandInfo.command, envelope: presentationEnvelope, processSucceeded, subcommand: prepared.executionPlan.commandInfo.subcommand })) {
|
|
@@ -209,6 +217,10 @@ export async function processBrowserOutput(input) {
|
|
|
209
217
|
const inspectionText = plainTextInspection ? processResult.stdout.trim() : undefined;
|
|
210
218
|
const sessionStateKey = getSessionContextKey(prepared.executionPlan.sessionName, prepared.executionPlan.namespace);
|
|
211
219
|
const closeAllApplied = nestedBatchClosesAll || (directCloseAllRequested && succeeded);
|
|
220
|
+
if (sessionStateKey && processResult.agentBrowserStarted && sessionPageState.get(sessionStateKey).tabReopenPending === true) {
|
|
221
|
+
if (dispatchedCommands.some(commandChoosesSessionTabTarget))
|
|
222
|
+
sessionPageState.setTabReopenPending({ pending: false, sessionName: sessionStateKey, update: sessionPageStateUpdate });
|
|
223
|
+
}
|
|
212
224
|
if (closeAllApplied) {
|
|
213
225
|
networkRoutesBySession = withoutNamespaceEntries(networkRoutesBySession, prepared.executionPlan.namespace);
|
|
214
226
|
deleteNamespaceEntries(state.attachedSessionKeys, prepared.executionPlan.namespace);
|
|
@@ -252,36 +264,32 @@ export async function processBrowserOutput(input) {
|
|
|
252
264
|
}
|
|
253
265
|
}
|
|
254
266
|
const tabTransition = prepared.executionPlan.commandInfo.command === "tab" && prepared.executionPlan.commandInfo.subcommand !== undefined && !["list", "new"].includes(prepared.executionPlan.commandInfo.subcommand);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
currentPageUrl: prepared.priorSessionTabTarget?.url,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
267
|
+
// Non-page rows (including a failed prefix) cannot retire a cold target for a navigation that never ran.
|
|
268
|
+
const resultingPageState = sessionPageState.get(sessionStateKey).tabReopenPending === true
|
|
269
|
+
? { currentPageUrl: prepared.priorSessionTabTarget?.url, pageTargetMayHaveChanged: false, pageUrlUnknown: prepared.priorSessionTabTargetUnknown === true }
|
|
270
|
+
: getResultingPageTargetState({
|
|
271
|
+
args: prepared.executionPlan.effectiveArgs,
|
|
272
|
+
executedBatchSteps: dispatchedCommands,
|
|
273
|
+
currentPageUrl: prepared.priorSessionTabTarget?.url,
|
|
274
|
+
pageUrlUnknown: prepared.priorSessionTabTargetUnknown === true,
|
|
275
|
+
});
|
|
262
276
|
if (succeeded &&
|
|
263
|
-
!navigationSummary &&
|
|
264
277
|
(shouldCaptureNavigationSummary(prepared.executionPlan.commandInfo.command, presentationEnvelope?.data, prepared.executionPlan.commandInfo.subcommand) ||
|
|
265
278
|
shouldCaptureSemanticActionNavigationSummary(prepared.compiledSemanticAction, presentationEnvelope?.data) ||
|
|
266
279
|
commandRequiresLivePageVerification(prepared.executionPlan.effectiveArgs, prepared.runtimeToolStdin) ||
|
|
267
|
-
tabTransition)) {
|
|
280
|
+
(destinationTransition && !nestedBatchClosed) || tabTransition)) {
|
|
268
281
|
navigationSummary = await collectNavigationSummary({ cwd, namespace: prepared.executionPlan.namespace, priorTarget: prepared.priorSessionTabTarget, reusePriorTitle: !tabTransition, sessionName: prepared.executionPlan.sessionName, signal });
|
|
269
282
|
}
|
|
270
|
-
//
|
|
271
|
-
// agent through a manual get url round trip. Probe the live URL ourselves instead: an observed page
|
|
272
|
-
// stays verified, and a failed
|
|
273
|
-
// probe preserves the existing unknown-target behavior.
|
|
283
|
+
// Failed transitions may already have changed the page; keep only a live observed URL.
|
|
274
284
|
if (succeeded === false &&
|
|
275
|
-
!navigationSummary &&
|
|
276
285
|
processResult.agentBrowserStarted &&
|
|
277
286
|
!processResult.aborted &&
|
|
278
287
|
!processResult.timedOut &&
|
|
279
|
-
|
|
280
|
-
|
|
288
|
+
!nestedBatchClosed &&
|
|
289
|
+
(destinationTransition || (prepared.executionPlan.commandInfo.command !== "batch" &&
|
|
290
|
+
isUnverifiedPageTransitionCommand(prepared.executionPlan.commandInfo.command, prepared.executionPlan.commandInfo.subcommand)))) {
|
|
281
291
|
navigationSummary = await collectNavigationSummary({ cwd, namespace: prepared.executionPlan.namespace, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName, signal });
|
|
282
|
-
//
|
|
283
|
-
// keeping the verified URL must not keep the prior snapshot refs (markTabTargetUnknown dropped
|
|
284
|
-
// them before this probe existed); invalidate so the next ref use requires a fresh snapshot.
|
|
292
|
+
// Re-verifying a failed transition's URL does not make its prior refs valid.
|
|
285
293
|
failedTransitionReverification = navigationSummary !== undefined;
|
|
286
294
|
}
|
|
287
295
|
if (navigationSummary && presentationEnvelope && prepared.executionPlan.commandInfo.command !== "eval" && !Array.isArray(presentationEnvelope.data))
|
|
@@ -309,8 +317,9 @@ export async function processBrowserOutput(input) {
|
|
|
309
317
|
const safeObservedSessionTabTarget = observedSessionTabTarget;
|
|
310
318
|
let currentSessionTabTarget = safeObservedSessionTabTarget;
|
|
311
319
|
if (!currentSessionTabTarget && nestedBatchClose === undefined) {
|
|
320
|
+
// Window/diff responses do not report the final URL; URL2 is intent, not redirect evidence.
|
|
312
321
|
currentSessionTabTarget = resultingPageState.pageTargetMayHaveChanged
|
|
313
|
-
? succeeded ? normalizeSessionTabTarget({ url: resultingPageState.currentPageUrl }) : undefined
|
|
322
|
+
? succeeded && !destinationTransition ? normalizeSessionTabTarget({ url: resultingPageState.currentPageUrl }) : undefined
|
|
314
323
|
: deriveSessionTabTarget({ command: prepared.executionPlan.commandInfo.command, data: presentationEnvelope?.data, navigationSummary, previousTarget: prepared.priorSessionTabTarget, subcommand: prepared.executionPlan.commandInfo.subcommand });
|
|
315
324
|
}
|
|
316
325
|
let aboutBlankSessionMismatch;
|
|
@@ -318,7 +327,8 @@ export async function processBrowserOutput(input) {
|
|
|
318
327
|
let electronRefFreshnessDiagnostic;
|
|
319
328
|
let electronSessionMismatch;
|
|
320
329
|
let electronStatusAfterCommand;
|
|
321
|
-
const
|
|
330
|
+
const explicitlyTargetsAboutBlank = dispatchedCommands.some((step) => commandExplicitlyTargetsAboutBlank(extractUpstreamCommandTokens(step)));
|
|
331
|
+
const shouldTreatAboutBlankAsMismatch = succeeded && !tabTransition && !destinationTransition && !explicitlyTargetsAboutBlank && nestedBatchClose === undefined && prepared.priorSessionTabTarget !== undefined && !isAboutBlankSessionTabTarget(prepared.priorSessionTabTarget) && isAboutBlankSessionTabTarget(observedSessionTabTarget ?? currentSessionTabTarget);
|
|
322
332
|
let sessionTabCorrection = prepared.sessionTabCorrection;
|
|
323
333
|
if (shouldTreatAboutBlankAsMismatch && prepared.priorSessionTabTarget) {
|
|
324
334
|
const aboutBlankObservedTarget = observedSessionTabTarget ?? currentSessionTabTarget;
|
|
@@ -536,7 +546,7 @@ export async function processBrowserOutput(input) {
|
|
|
536
546
|
if (sessionStateKey && succeeded) {
|
|
537
547
|
if (openResultTabCorrection || sessionTabCorrection || aboutBlankSessionMismatch?.recoveryApplied)
|
|
538
548
|
sessionPageState.markPinning(sessionStateKey, "drift");
|
|
539
|
-
else if (prepared.sessionTabPinningReason === "restore")
|
|
549
|
+
else if (prepared.sessionTabPinningReason === "restore" && observedSessionTabTarget)
|
|
540
550
|
sessionPageState.clearRestorePinning(sessionStateKey);
|
|
541
551
|
}
|
|
542
552
|
if (replacedManagedSessionName) {
|
|
@@ -626,14 +636,23 @@ export async function processBrowserOutput(input) {
|
|
|
626
636
|
electronLaunchRecord = electronFailedConnectCleanup.record;
|
|
627
637
|
}
|
|
628
638
|
}
|
|
629
|
-
let errorText = getAgentBrowserErrorText({ aborted: processResult.aborted, command: prepared.executionPlan.commandInfo.command, effectiveArgs: prepared.redactedProcessArgs, envelope: presentationEnvelope, exitCode: processResult.exitCode, parseError, plainTextInspection, staleRefArgs: getStaleRefArgs(prepared.commandTokens, prepared.runtimeToolStdin), spawnError: processResult.spawnError, stderr: processResult.stderr, timedOut: processResult.timedOut, timeoutMs: processResult.timeoutMs, wrapperRecoveryHint: buildWrapperRecoveryHint({
|
|
639
|
+
let errorText = getAgentBrowserErrorText({ aborted: processResult.aborted, command: prepared.executionPlan.commandInfo.command, effectiveArgs: prepared.redactedProcessArgs, envelope: presentationEnvelope, exitCode: processResult.exitCode, parseError, plainTextInspection, staleRefArgs: getStaleRefArgs(prepared.commandTokens, prepared.runtimeToolStdin), spawnError: processResult.spawnError, stderr: processResult.stderr, timedOut: processResult.timedOut, timeoutMs: processResult.timeoutMs, wrapperRecoveryHint: buildWrapperRecoveryHint({ sessionTabCorrection }) });
|
|
630
640
|
if (errorText) {
|
|
631
641
|
const clipboardWritePayloadCandidates = getClipboardWritePayloadCandidates(prepared.commandTokens);
|
|
632
642
|
errorText = redactClipboardPermissionEcho(prepared.executionPlan.commandInfo, errorText);
|
|
633
643
|
if (presentationEnvelope?.error !== undefined)
|
|
634
644
|
presentationEnvelope = { ...presentationEnvelope, error: redactClipboardPermissionErrorValue(prepared.executionPlan.commandInfo, presentationEnvelope.error, clipboardWritePayloadCandidates) };
|
|
635
645
|
}
|
|
636
|
-
|
|
646
|
+
if (plainTextUpgrade && errorText)
|
|
647
|
+
presentationEnvelope = { ...presentationEnvelope, success: false, error: errorText };
|
|
648
|
+
let presentation = plainTextInspection ? { artifacts: undefined, batchFailure: undefined, batchSteps: undefined, content: [{ type: "text", text: inspectionText ?? "" }], data: undefined, fullOutputPath: undefined, fullOutputPaths: undefined, imagePath: undefined, imagePaths: undefined, savedFile: undefined, savedFilePath: undefined, summary: `${prepared.redactedArgs.join(" ")} completed` } : await buildToolPresentation({ args: prepared.redactedProcessArgs, artifactManifest, artifactMaxUpdatedAtMs: Date.now(), artifactMinUpdatedAtMs: input.artifactRunStartedAtMs, artifactRequest: screenshotArtifactRequest, batchArtifactRequests: batchScreenshotArtifactRequests, commandInfo: prepared.executionPlan.commandInfo, compiledSemanticAction: prepared.compiledSemanticAction, cwd, envelope: presentationEnvelope, errorText, namespace: prepared.executionPlan.namespace, networkRouteDiagnostics, networkRoutes: activeNetworkRoutes, persistentArtifactStore, sessionName: prepared.executionPlan.sessionName });
|
|
649
|
+
if (plainTextUpgrade && !succeeded && typeof presentationEnvelope?.data === "string") {
|
|
650
|
+
presentation.data = presentationEnvelope.data;
|
|
651
|
+
const errorContent = presentation.content[0];
|
|
652
|
+
if (errorContent?.type === "text" && presentationEnvelope.data)
|
|
653
|
+
errorContent.text += `\n\n${presentationEnvelope.data}`;
|
|
654
|
+
presentation = await compactLargePresentationOutput({ artifactManifest, commandInfo: prepared.executionPlan.commandInfo, data: presentation.data, persistentArtifactStore, presentation });
|
|
655
|
+
}
|
|
637
656
|
if (electronHandoff?.error && electronHandoff.failureCategory)
|
|
638
657
|
presentation.failureCategory = electronHandoff.failureCategory;
|
|
639
658
|
networkRoutesBySession = applyBatchNetworkRouteState({ data: presentationEnvelope?.data, routesBySession: networkRoutesBySession, sessionName: sessionStateKey, succeeded });
|
|
@@ -738,7 +757,14 @@ export async function processBrowserOutput(input) {
|
|
|
738
757
|
const evalResultWarning = getEvalResultWarning({ command: prepared.executionPlan.commandInfo.command, data: presentationEnvelope?.data, navigationSummary: evalNavigationSummary, pageUrl: evalPageUrl, stdin: prepared.runtimeToolStdin });
|
|
739
758
|
const resultArtifactManifest = presentation.artifactManifest ?? artifactManifest;
|
|
740
759
|
const artifactCleanup = await getArtifactCleanupGuidance({ command: prepared.executionPlan.commandInfo.command, cwd, manifest: resultArtifactManifest, succeeded });
|
|
741
|
-
const
|
|
760
|
+
const recordingTransitionReached = prepared.executionPlan.commandInfo.command === "batch"
|
|
761
|
+
? presentation.batchSteps?.some((step) => isRecordPageTransitionCommand(extractUpstreamCommandTokens(step.command ?? [])))
|
|
762
|
+
: isRecordPageTransitionCommand(prepared.commandTokens);
|
|
763
|
+
const recordingPageWarning = processResult.agentBrowserStarted && !prepared.executionPlan.plainTextInspection && recordingTransitionReached
|
|
764
|
+
? "Page state: this recording command can replace or navigate the active page, even on failure; prior in-page DOM and JavaScript state may not carry over. Take a fresh snapshot before continuing with page-scoped refs."
|
|
765
|
+
: undefined;
|
|
766
|
+
const sessionWarning = electronPostCommandHealth ? formatElectronPostCommandHealthText(electronPostCommandHealth) : electronSessionMismatch ? formatElectronSessionMismatchText(electronSessionMismatch) : aboutBlankSessionMismatch ? buildAboutBlankWarning(aboutBlankSessionMismatch) : undefined;
|
|
767
|
+
const warningText = [sessionWarning, recordingPageWarning].filter(Boolean).join("\n\n") || undefined;
|
|
742
768
|
const redactedContent = buildRedactedPresentationContent({ exactSensitiveValues: prepared.exactSensitiveValues, plainTextInspection, presentation, presentationEnvelope, succeeded, userRequestedJson: prepared.userRequestedJson, warningText });
|
|
743
769
|
const finalRecoveryState = await prepareFinalResultRecoveryState({ aboutBlankSessionMismatch, batchRefSnapshotState, commandTokens: prepared.commandTokens, compiledSemanticAction: prepared.compiledSemanticAction, currentRefSnapshot, currentRefSnapshotInvalidation, currentSessionTabTarget, cwd, electronPostCommandHealth, errorText, executionPlan: prepared.executionPlan, parseError, plainTextInspection, presentation, processResult, redactedProcessArgs: prepared.redactedProcessArgs, runtimeToolArgs: prepared.runtimeToolArgs, sessionPageState, sessionPageStateUpdate, sessionTabCorrection, signal, succeeded });
|
|
744
770
|
currentRefSnapshot = finalRecoveryState.currentRefSnapshot;
|