pi-agent-browser-native 0.2.77 → 0.5.0
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 +154 -0
- package/README.md +69 -35
- package/dist/extensions/agent-browser/index.js +911 -121
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +35 -8
- package/dist/extensions/agent-browser/lib/argv-grammar.js +48 -7
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +71 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +1 -6
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +35 -8
- package/dist/extensions/agent-browser/lib/config-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/config.js +0 -6
- package/dist/extensions/agent-browser/lib/electron/cdp.js +6 -7
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/discovery.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/launch.js +0 -7
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/job.js +61 -9
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +2 -7
- package/dist/extensions/agent-browser/lib/input-modes/params.js +23 -29
- package/dist/extensions/agent-browser/lib/input-modes/script.js +462 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +51 -17
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/types.js +0 -5
- package/dist/extensions/agent-browser/lib/json-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +12 -5
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -2
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +3 -4
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +55 -48
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +2 -9
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +48 -36
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +50 -29
- package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +26 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +110 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +27 -26
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +58 -33
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +22 -14
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +148 -34
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +225 -61
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +74 -30
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +47 -26
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +40 -19
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +42 -1
- package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +299 -0
- package/dist/extensions/agent-browser/lib/parsing.js +0 -7
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +36 -11
- package/dist/extensions/agent-browser/lib/playbook.js +20 -24
- package/dist/extensions/agent-browser/lib/process-environment.js +14 -0
- package/dist/extensions/agent-browser/lib/process-identity.js +4 -9
- package/dist/extensions/agent-browser/lib/process.js +131 -48
- package/dist/extensions/agent-browser/lib/prompt-policy.js +171 -27
- package/dist/extensions/agent-browser/lib/recording-reservations.js +183 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +63 -13
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +68 -11
- package/dist/extensions/agent-browser/lib/results/categories.js +7 -9
- package/dist/extensions/agent-browser/lib/results/confirmation.js +0 -7
- package/dist/extensions/agent-browser/lib/results/contracts.js +0 -7
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +0 -7
- package/dist/extensions/agent-browser/lib/results/envelope.js +0 -7
- package/dist/extensions/agent-browser/lib/results/network.js +0 -7
- package/dist/extensions/agent-browser/lib/results/next-actions.js +19 -35
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +85 -44
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +66 -11
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +18 -5
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +7 -6
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +26 -16
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +58 -13
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +1 -15
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation.js +6 -8
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +2 -7
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +51 -15
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +13 -14
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot.js +0 -7
- package/dist/extensions/agent-browser/lib/results/text.js +0 -7
- package/dist/extensions/agent-browser/lib/runtime.js +119 -52
- package/dist/extensions/agent-browser/lib/session-page-state.js +62 -17
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/temp.js +0 -7
- package/dist/extensions/agent-browser/lib/upstream-version.js +14 -0
- package/dist/extensions/agent-browser/lib/web-search.js +0 -5
- package/dist/extensions/agent-browser/script-worker.js +169 -0
- package/dist/scripts/agent-browser-target.mjs +3 -0
- package/docs/ARCHITECTURE.md +50 -31
- package/docs/COMMAND_REFERENCE.md +97 -41
- package/docs/ELECTRON.md +3 -19
- package/docs/RELEASE.md +8 -20
- package/docs/REQUIREMENTS.md +8 -6
- package/docs/SUPPORT_MATRIX.md +32 -25
- package/docs/TOOL_CONTRACT.md +107 -64
- package/docs/platform-smoke.md +1 -1
- package/package.json +7 -6
- package/platform-smoke.config.mjs +2 -2
- package/scripts/agent-browser-capability-baseline.mjs +24 -6
- package/scripts/agent-browser-target.mjs +3 -0
- package/scripts/build.mjs +41 -0
- package/scripts/doctor.mjs +8 -7
- package/scripts/platform-smoke/browser-dogfood-windows.ps1 +9 -3
- package/scripts/platform-smoke/targets.mjs +12 -6
- package/dist/extensions/agent-browser/lib/electron/text.js +0 -13
- package/dist/extensions/agent-browser/lib/input-modes.js +0 -10
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +0 -5
- package/dist/extensions/agent-browser/lib/results/artifact-state.js +0 -13
- package/dist/extensions/agent-browser/lib/results.js +0 -14
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
|
-
import { getAgentBrowserSessionIdentityKey } from "../../argv-grammar.js";
|
|
3
|
-
import {
|
|
2
|
+
import { getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace } from "../../argv-grammar.js";
|
|
3
|
+
import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "../../batch-lifecycle.js";
|
|
4
|
+
import { isCloseAllCommand, isCloseCommand, isNavigationObservableCommandName, isOpenNavigationCommand, isRecordPageTransitionCommand, isUnverifiedPageTransitionCommand } from "../../command-taxonomy.js";
|
|
4
5
|
import { OPEN_RESULT_TAB_CORRECTION_FLAGS } from "../../launch-scoped-flags.js";
|
|
5
6
|
import { cleanupElectronLaunchResources, inspectElectronLaunchStatus } from "../../electron/cleanup.js";
|
|
6
7
|
import { getAllowedDomainsViolation, parseAllowedDomainsPolicyFromArgs } from "../../navigation-policy.js";
|
|
7
8
|
import { getManagedSessionResultingPageState, getObservedBrowserPageValidationError, managedSessionCommandRequiresLivePageVerification } from "../../managed-session-state-policy.js";
|
|
8
|
-
import { analyzeNetworkSourceLookupResults,
|
|
9
|
-
import {
|
|
9
|
+
import { analyzeNetworkSourceLookupResults, analyzeSourceLookupResults, redactNetworkSourceLookupAnalysis } from "../../input-modes/lookups.js";
|
|
10
|
+
import { analyzeQaPresetResults, analyzeQaPresetTimeout, buildQaCompactFailureText, buildQaCompactPassText, extractQaPageContext } from "../../input-modes/job.js";
|
|
11
|
+
import { applyNetworkRouteRecords, buildNetworkRouteDiagnostics } from "../../results/network-routes.js";
|
|
12
|
+
import { buildToolPresentation } from "../../results/presentation.js";
|
|
13
|
+
import { getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results/envelope.js";
|
|
14
|
+
import { omitUpstreamLifecycle } from "../../results/presentation/common.js";
|
|
10
15
|
import { getClipboardWritePayloadCandidates, redactClipboardPermissionEcho, redactClipboardPermissionErrorValue } from "../../results/presentation/errors.js";
|
|
11
16
|
import { shouldCaptureSemanticActionNavigationSummary } from "../../results/presentation/semantic-action.js";
|
|
12
|
-
import { commandExplicitlyTargetsAboutBlank, deriveSessionTabTarget, extractLatestRefSnapshotStateFromBatchResults, extractRefSnapshotFromData, extractSessionTabTargetFromBatchResults, extractSessionTabTargetFromCommandData, isAboutBlankSessionTabTarget, normalizeSessionTabTarget, } from "../../session-page-state.js";
|
|
17
|
+
import { buildPageTransitionRefSnapshotInvalidation, commandExplicitlyTargetsAboutBlank, deriveSessionTabTarget, extractLatestRefSnapshotStateFromBatchResults, extractRefSnapshotFromData, extractSessionTabTargetFromBatchResults, extractSessionTabTargetFromCommandData, isAboutBlankSessionTabTarget, normalizeSessionTabTarget, } from "../../session-page-state.js";
|
|
13
18
|
import { isRecord } from "../../parsing.js";
|
|
14
19
|
import { pruneOwnedManagedSessionRestoreSnapshots } from "../../managed-session-restore.js";
|
|
15
20
|
import { isManagedSessionRestoreKey } from "../../managed-session-storage.js";
|
|
16
|
-
import { createFreshSessionName,
|
|
21
|
+
import { createFreshSessionName, extractUpstreamCommandTokens, resolveManagedSessionState } from "../../runtime.js";
|
|
22
|
+
import { getUpstreamEffectiveBatchSteps } from "../batch-stdin.js";
|
|
17
23
|
import { closeManagedSession, inspectManagedSessionDaemon } from "./managed-session-daemon-policy.js";
|
|
18
24
|
import { applyOpenResultTabCorrection, buildAboutBlankRecoveryHint, buildAboutBlankWarning, buildElectronPostCommandHealthDiagnostic, buildElectronRefFreshnessDiagnostic, buildElectronSessionMismatch, buildManagedSessionOutcome, collectOpenResultTabCorrection, collectSessionTabSelection, extractNavigationSummaryFromData, extractStringResultField, findElectronLaunchRecordForSession, formatElectronPostCommandHealthText, formatElectronSessionMismatchText, getSessionContextKey, getStaleRefArgs, mergeNavigationSummaryIntoData, shouldCaptureNavigationSummary, shouldCorrectSessionTabAfterCommand, shouldInspectElectronPostCommandHealth, unwrapPinnedSessionBatchEnvelope, updateTraceOwnerState, } from "./session-state.js";
|
|
19
25
|
import { collectClickDispatchDiagnostic } from "./click-dispatch.js";
|
|
20
26
|
import { buildScrollNoopDiagnostic, collectComboboxFocusDiagnostic, collectElectronBroadGetTextScopeDiagnostics, collectElectronHandoff, collectFillVerificationDiagnostic, collectNavigationSummary, collectOverlayBlockerDiagnostic, collectQaAttachedTarget, collectSnapshotOverlayBlockerDiagnostic, collectRecordingDependencyWarning, collectScrollPositionSnapshot, collectSelectorTextVisibilityDiagnostics, collectTimeoutPartialProgress, sleepMs, formatQaAttachedTargetText, getArtifactCleanupGuidance, getEvalResultWarning, getEvalStdinHint, getSourceLookupElectronContext, } from "./diagnostics.js";
|
|
21
27
|
import { repairScreenshotData } from "./prepare.js";
|
|
22
|
-
import { getPersistentSessionArtifactStore } from "./session-
|
|
28
|
+
import { getPersistentSessionArtifactStore } from "./session-state.js";
|
|
23
29
|
import { buildFinalAgentBrowserToolResult, buildRedactedPresentationContent, buildWrapperRecoveryHint, prepareFinalResultRecoveryState, redactExactSensitiveValue, } from "./final-result.js";
|
|
24
30
|
async function repairScreenshotArtifact(options) {
|
|
25
31
|
const { cwd, envelope, request } = options;
|
|
@@ -68,6 +74,15 @@ function batchStartedManagedBrowser(data) {
|
|
|
68
74
|
return command === "connect" || command === "goto" || command === "navigate" || isOpenNavigationCommand(command);
|
|
69
75
|
});
|
|
70
76
|
}
|
|
77
|
+
function withoutNamespaceEntries(entries, namespace) {
|
|
78
|
+
return new Map([...entries].filter(([key]) => !isAgentBrowserSessionIdentityKeyInNamespace(key, namespace)));
|
|
79
|
+
}
|
|
80
|
+
function deleteNamespaceEntries(entries, namespace) {
|
|
81
|
+
for (const key of entries.keys()) {
|
|
82
|
+
if (isAgentBrowserSessionIdentityKeyInNamespace(key, namespace))
|
|
83
|
+
entries.delete(key);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
71
86
|
function setNetworkRouteState(options) {
|
|
72
87
|
if (!options.sessionName)
|
|
73
88
|
return options.routesBySession;
|
|
@@ -92,7 +107,12 @@ function applyBatchNetworkRouteState(options) {
|
|
|
92
107
|
for (const item of options.data) {
|
|
93
108
|
if (!isRecord(item) || !Array.isArray(item.command) || !item.command.every((token) => typeof token === "string"))
|
|
94
109
|
continue;
|
|
95
|
-
|
|
110
|
+
const commandTokens = extractUpstreamCommandTokens(item.command);
|
|
111
|
+
const stepSucceeded = item.success !== false;
|
|
112
|
+
if (stepSucceeded && isCloseCommand(commandTokens[0]))
|
|
113
|
+
routes = undefined;
|
|
114
|
+
else
|
|
115
|
+
routes = applyNetworkRouteRecords(routes, commandTokens, stepSucceeded);
|
|
96
116
|
}
|
|
97
117
|
return setNetworkRouteState({ routes, routesBySession: options.routesBySession, sessionName: options.sessionName });
|
|
98
118
|
}
|
|
@@ -105,6 +125,8 @@ export async function processBrowserOutput(input) {
|
|
|
105
125
|
let freshSessionOrdinal = state.freshSessionOrdinal;
|
|
106
126
|
let managedSessionActive = state.managedSessionActive;
|
|
107
127
|
let managedSessionCompatibilityWorkaround = state.managedSessionCompatibilityWorkaround;
|
|
128
|
+
let managedSessionHeadedAutosaveDisabled = state.managedSessionHeadedAutosaveDisabled === true;
|
|
129
|
+
let managedSessionHeadedAutosaveInterval = state.managedSessionHeadedAutosaveInterval;
|
|
108
130
|
let managedSessionCwd = state.managedSessionCwd;
|
|
109
131
|
let managedSessionName = state.managedSessionName;
|
|
110
132
|
let managedSessionNamespace = state.managedSessionNamespace;
|
|
@@ -115,6 +137,7 @@ export async function processBrowserOutput(input) {
|
|
|
115
137
|
let parseError = parsed.parseError;
|
|
116
138
|
let presentationEnvelope = parsed.envelope;
|
|
117
139
|
let navigationSummary = undefined;
|
|
140
|
+
let failedTransitionReverification = false;
|
|
118
141
|
if (prepared.pinnedBatchUnwrapMode) {
|
|
119
142
|
const pinnedBatchResult = unwrapPinnedSessionBatchEnvelope({ envelope: parsed.envelope, includeNavigationSummary: prepared.includePinnedNavigationSummary, mode: prepared.pinnedBatchUnwrapMode });
|
|
120
143
|
parseError = pinnedBatchResult.parseError ?? parseError;
|
|
@@ -127,11 +150,22 @@ export async function processBrowserOutput(input) {
|
|
|
127
150
|
presentationEnvelope = repairedBatchScreenshots.envelope;
|
|
128
151
|
const screenshotArtifactRequest = repairedScreenshot.request;
|
|
129
152
|
const batchScreenshotArtifactRequests = repairedBatchScreenshots.requests;
|
|
153
|
+
const batchCommandSteps = prepared.executionPlan.commandInfo.command === "batch"
|
|
154
|
+
? getUpstreamEffectiveBatchSteps(prepared.commandTokens, prepared.runtimeToolStdin)
|
|
155
|
+
: [];
|
|
156
|
+
const nestedBatchClose = prepared.executionPlan.commandInfo.command === "batch"
|
|
157
|
+
? getSuccessfulBatchCloseLifecycle(presentationEnvelope?.data, batchCommandSteps)
|
|
158
|
+
: undefined;
|
|
159
|
+
const nestedBatchClosed = nestedBatchClose?.endsClosed === true;
|
|
160
|
+
const nestedBatchRemainsActive = nestedBatchClose?.endsClosed === false;
|
|
161
|
+
const nestedBatchClosesAll = prepared.executionPlan.commandInfo.command === "batch"
|
|
162
|
+
&& batchHasSuccessfulCloseAll(presentationEnvelope?.data, batchCommandSteps);
|
|
163
|
+
const directCloseAllRequested = isCloseAllCommand(extractUpstreamCommandTokens(prepared.commandTokens));
|
|
130
164
|
const rawCloseStatePath = isCloseCommand(prepared.executionPlan.commandInfo.command)
|
|
131
165
|
&& isRecord(presentationEnvelope?.data)
|
|
132
166
|
&& typeof presentationEnvelope.data.statePath === "string"
|
|
133
167
|
? presentationEnvelope.data.statePath
|
|
134
|
-
:
|
|
168
|
+
: nestedBatchClose?.statePath;
|
|
135
169
|
if (presentationEnvelope && prepared.exactSensitiveValues.length > 0)
|
|
136
170
|
presentationEnvelope = redactExactSensitiveValue(presentationEnvelope, prepared.exactSensitiveValues);
|
|
137
171
|
const parseFailureOutput = parseError && processResult.stdoutSpillPath
|
|
@@ -147,7 +181,44 @@ export async function processBrowserOutput(input) {
|
|
|
147
181
|
let succeeded = processSucceeded && parseSucceeded && envelopeSuccess;
|
|
148
182
|
const inspectionText = plainTextInspection ? processResult.stdout.trim() : undefined;
|
|
149
183
|
const sessionStateKey = getSessionContextKey(prepared.executionPlan.sessionName, prepared.executionPlan.namespace);
|
|
150
|
-
|
|
184
|
+
const closeAllApplied = nestedBatchClosesAll || (directCloseAllRequested && succeeded);
|
|
185
|
+
if (closeAllApplied) {
|
|
186
|
+
allowedDomainsBySession = withoutNamespaceEntries(allowedDomainsBySession, prepared.executionPlan.namespace);
|
|
187
|
+
networkRoutesBySession = withoutNamespaceEntries(networkRoutesBySession, prepared.executionPlan.namespace);
|
|
188
|
+
deleteNamespaceEntries(state.attachedSessionKeys, prepared.executionPlan.namespace);
|
|
189
|
+
deleteNamespaceEntries(traceOwners, prepared.executionPlan.namespace);
|
|
190
|
+
sessionPageState.clearNamespace(prepared.executionPlan.namespace);
|
|
191
|
+
const retainedSessionKey = nestedBatchRemainsActive ? sessionStateKey : undefined;
|
|
192
|
+
for (const [key, owner] of state.ownedManagedSessions) {
|
|
193
|
+
if (!isAgentBrowserSessionIdentityKeyInNamespace(key, prepared.executionPlan.namespace) || key === retainedSessionKey)
|
|
194
|
+
continue;
|
|
195
|
+
state.closedManagedSessionNames.add(key);
|
|
196
|
+
state.managedSessionRestoreState.clear(owner.sessionName, owner.namespace);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else if (nestedBatchClose && sessionStateKey) {
|
|
200
|
+
allowedDomainsBySession = new Map(allowedDomainsBySession);
|
|
201
|
+
allowedDomainsBySession.delete(sessionStateKey);
|
|
202
|
+
networkRoutesBySession = new Map(networkRoutesBySession);
|
|
203
|
+
networkRoutesBySession.delete(sessionStateKey);
|
|
204
|
+
sessionPageState.clearSession(sessionStateKey);
|
|
205
|
+
}
|
|
206
|
+
if (prepared.executionPlan.commandInfo.command === "batch" && Array.isArray(presentationEnvelope?.data)) {
|
|
207
|
+
for (const [index, row] of presentationEnvelope.data.entries()) {
|
|
208
|
+
if (!isRecord(row))
|
|
209
|
+
continue;
|
|
210
|
+
const rowCommand = Array.isArray(row.command) && row.command.every((token) => typeof token === "string")
|
|
211
|
+
? row.command
|
|
212
|
+
: batchCommandSteps[index];
|
|
213
|
+
if (!rowCommand)
|
|
214
|
+
continue;
|
|
215
|
+
const [command, subcommand] = extractUpstreamCommandTokens(rowCommand);
|
|
216
|
+
updateTraceOwnerState({ command, sessionName: sessionStateKey, subcommand, succeeded: row.success === true, traceOwners });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
updateTraceOwnerState({ command: prepared.executionPlan.commandInfo.command, sessionName: sessionStateKey, subcommand: prepared.executionPlan.commandInfo.subcommand, succeeded, traceOwners });
|
|
221
|
+
}
|
|
151
222
|
let clickDispatchDiagnostic;
|
|
152
223
|
if (succeeded && prepared.clickDispatchProbe) {
|
|
153
224
|
clickDispatchDiagnostic = await collectClickDispatchDiagnostic({ cwd, namespace: prepared.executionPlan.namespace, probe: prepared.clickDispatchProbe, sessionName: prepared.executionPlan.sessionName, signal });
|
|
@@ -178,7 +249,24 @@ export async function processBrowserOutput(input) {
|
|
|
178
249
|
shouldCaptureAllowedDomainNavigationSummary ||
|
|
179
250
|
managedSessionCommandRequiresLivePageVerification(prepared.executionPlan.effectiveArgs, prepared.runtimeToolStdin) ||
|
|
180
251
|
(prepared.executionPlan.commandInfo.command === "tab" && prepared.executionPlan.commandInfo.subcommand === "close"))) {
|
|
181
|
-
navigationSummary = await collectNavigationSummary({ cwd, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, signal });
|
|
252
|
+
navigationSummary = await collectNavigationSummary({ cwd, namespace: prepared.executionPlan.namespace, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName, signal });
|
|
253
|
+
}
|
|
254
|
+
// A failed eval/back/forward/reload/state-load/tab would otherwise drop the page to unverified and force the
|
|
255
|
+
// agent through a manual get url round trip. Probe the live URL ourselves instead: an observed http(s) page
|
|
256
|
+
// stays verified, a file URL keeps the local-file gate closed through currentSessionTabTarget, and a failed
|
|
257
|
+
// probe preserves the existing unknown-target behavior.
|
|
258
|
+
if (succeeded === false &&
|
|
259
|
+
!navigationSummary &&
|
|
260
|
+
processResult.agentBrowserStarted &&
|
|
261
|
+
!processResult.aborted &&
|
|
262
|
+
!processResult.timedOut &&
|
|
263
|
+
prepared.executionPlan.commandInfo.command !== "batch" &&
|
|
264
|
+
isUnverifiedPageTransitionCommand(prepared.executionPlan.commandInfo.command, prepared.executionPlan.commandInfo.subcommand)) {
|
|
265
|
+
navigationSummary = await collectNavigationSummary({ cwd, namespace: prepared.executionPlan.namespace, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName, signal });
|
|
266
|
+
// A failed transition command can still have mutated or replaced the document before throwing, so
|
|
267
|
+
// keeping the verified URL must not keep the prior snapshot refs (markTabTargetUnknown dropped
|
|
268
|
+
// them before this probe existed); invalidate so the next ref use requires a fresh snapshot.
|
|
269
|
+
failedTransitionReverification = navigationSummary !== undefined;
|
|
182
270
|
}
|
|
183
271
|
if (navigationSummary && presentationEnvelope && prepared.executionPlan.commandInfo.command !== "eval" && !Array.isArray(presentationEnvelope.data))
|
|
184
272
|
presentationEnvelope = { ...presentationEnvelope, data: mergeNavigationSummaryIntoData(presentationEnvelope.data, navigationSummary) };
|
|
@@ -202,15 +290,18 @@ export async function processBrowserOutput(input) {
|
|
|
202
290
|
presentationEnvelope = { error: observedPageValidationError, success: false };
|
|
203
291
|
}
|
|
204
292
|
const safeObservedSessionTabTarget = observedPageValidationError ? normalizeSessionTabTarget({ url: observedSessionTabTarget?.url }) : observedSessionTabTarget;
|
|
205
|
-
let currentSessionTabTarget = safeObservedSessionTabTarget
|
|
206
|
-
|
|
207
|
-
|
|
293
|
+
let currentSessionTabTarget = safeObservedSessionTabTarget;
|
|
294
|
+
if (!currentSessionTabTarget && nestedBatchClose === undefined) {
|
|
295
|
+
currentSessionTabTarget = resultingPageState.pageTargetMayHaveChanged
|
|
296
|
+
? succeeded ? normalizeSessionTabTarget({ url: resultingPageState.currentPageUrl }) : undefined
|
|
297
|
+
: deriveSessionTabTarget({ command: prepared.executionPlan.commandInfo.command, data: presentationEnvelope?.data, navigationSummary, previousTarget: prepared.priorSessionTabTarget, subcommand: prepared.executionPlan.commandInfo.subcommand });
|
|
298
|
+
}
|
|
208
299
|
let aboutBlankSessionMismatch;
|
|
209
300
|
let electronPostCommandHealth;
|
|
210
301
|
let electronRefFreshnessDiagnostic;
|
|
211
302
|
let electronSessionMismatch;
|
|
212
303
|
let electronStatusAfterCommand;
|
|
213
|
-
const shouldTreatAboutBlankAsMismatch = succeeded && prepared.priorSessionTabTarget !== undefined && !isAboutBlankSessionTabTarget(prepared.priorSessionTabTarget) && isAboutBlankSessionTabTarget(observedSessionTabTarget ?? currentSessionTabTarget) && !commandExplicitlyTargetsAboutBlank(prepared.commandTokens);
|
|
304
|
+
const shouldTreatAboutBlankAsMismatch = succeeded && nestedBatchClose === undefined && prepared.priorSessionTabTarget !== undefined && !isAboutBlankSessionTabTarget(prepared.priorSessionTabTarget) && isAboutBlankSessionTabTarget(observedSessionTabTarget ?? currentSessionTabTarget) && !commandExplicitlyTargetsAboutBlank(prepared.commandTokens);
|
|
214
305
|
let sessionTabCorrection = prepared.sessionTabCorrection;
|
|
215
306
|
if (shouldTreatAboutBlankAsMismatch && prepared.priorSessionTabTarget) {
|
|
216
307
|
const aboutBlankObservedTarget = observedSessionTabTarget ?? currentSessionTabTarget;
|
|
@@ -223,7 +314,7 @@ export async function processBrowserOutput(input) {
|
|
|
223
314
|
else
|
|
224
315
|
currentSessionTabTarget = aboutBlankObservedTarget ?? normalizeSessionTabTarget({ url: "about:blank" });
|
|
225
316
|
aboutBlankSessionMismatch = { activeUrl: "about:blank", recoveryApplied: appliedAboutBlankRecovery !== undefined, recoveryHint: buildAboutBlankRecoveryHint(), targetTitle: prepared.priorSessionTabTarget.title, targetUrl: prepared.priorSessionTabTarget.url };
|
|
226
|
-
const electronRecord = findElectronLaunchRecordForSession(prepared.executionPlan.sessionName, electronLaunchRecords);
|
|
317
|
+
const electronRecord = findElectronLaunchRecordForSession(prepared.executionPlan.sessionName, electronLaunchRecords, prepared.executionPlan.namespace);
|
|
227
318
|
if (electronRecord && prepared.executionPlan.sessionName) {
|
|
228
319
|
electronStatusAfterCommand = await inspectElectronLaunchStatus(electronRecord);
|
|
229
320
|
electronSessionMismatch = buildElectronSessionMismatch({ managedSession: { sessionName: prepared.executionPlan.sessionName, title: aboutBlankObservedTarget?.title, url: aboutBlankObservedTarget?.url ?? "about:blank" }, record: electronRecord, statusTargets: electronStatusAfterCommand.targets });
|
|
@@ -249,7 +340,7 @@ export async function processBrowserOutput(input) {
|
|
|
249
340
|
succeeded = false;
|
|
250
341
|
presentationEnvelope = { ...(presentationEnvelope ?? {}), error: allowedDomainsViolation.summary, success: false };
|
|
251
342
|
}
|
|
252
|
-
const electronRecordForCommand = findElectronLaunchRecordForSession(prepared.executionPlan.sessionName, electronLaunchRecords);
|
|
343
|
+
const electronRecordForCommand = findElectronLaunchRecordForSession(prepared.executionPlan.sessionName, electronLaunchRecords, prepared.executionPlan.namespace);
|
|
253
344
|
if (succeeded && electronRecordForCommand && shouldInspectElectronPostCommandHealth(prepared.executionPlan.commandInfo.command)) {
|
|
254
345
|
electronStatusAfterCommand ??= await inspectElectronLaunchStatus(electronRecordForCommand);
|
|
255
346
|
electronPostCommandHealth = buildElectronPostCommandHealthDiagnostic({ command: prepared.executionPlan.commandInfo.command, record: electronRecordForCommand, status: electronStatusAfterCommand, target: observedSessionTabTarget ?? currentSessionTabTarget });
|
|
@@ -283,7 +374,7 @@ export async function processBrowserOutput(input) {
|
|
|
283
374
|
if (succeeded) {
|
|
284
375
|
selectorTextVisibilityDiagnostics = await collectSelectorTextVisibilityDiagnostics({ commandInfo: prepared.executionPlan.commandInfo, commandTokens: prepared.commandTokens, cwd, data: presentationEnvelope?.data, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, signal });
|
|
285
376
|
if (electronRecordForCommand)
|
|
286
|
-
electronBroadGetTextScopeDiagnostics = collectElectronBroadGetTextScopeDiagnostics({ commandInfo: prepared.executionPlan.commandInfo, commandTokens: prepared.commandTokens, currentTarget: currentSessionTabTarget, data: presentationEnvelope?.data, electronLaunchRecords, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName });
|
|
377
|
+
electronBroadGetTextScopeDiagnostics = collectElectronBroadGetTextScopeDiagnostics({ commandInfo: prepared.executionPlan.commandInfo, commandTokens: prepared.commandTokens, currentTarget: currentSessionTabTarget, data: presentationEnvelope?.data, electronLaunchRecords, namespace: prepared.executionPlan.namespace, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName });
|
|
287
378
|
}
|
|
288
379
|
const activeNetworkRoutes = sessionStateKey ? networkRoutesBySession.get(sessionStateKey) : undefined;
|
|
289
380
|
const networkRouteDiagnostics = succeeded && prepared.executionPlan.commandInfo.command === "network" && prepared.executionPlan.commandInfo.subcommand === "requests" && prepared.executionPlan.sessionName
|
|
@@ -293,11 +384,13 @@ export async function processBrowserOutput(input) {
|
|
|
293
384
|
const comboboxFocusDiagnostic = succeeded ? await collectComboboxFocusDiagnostic({ command: prepared.executionPlan.commandInfo.command, commandTokens: prepared.commandTokens, cwd, namespace: prepared.executionPlan.namespace, semanticAction: prepared.compiledSemanticAction, sessionName: prepared.executionPlan.sessionName, signal }) : undefined;
|
|
294
385
|
const recordingDependencyWarning = await collectRecordingDependencyWarning({ command: prepared.executionPlan.commandInfo.command, commandTokens: prepared.commandTokens, succeeded });
|
|
295
386
|
const scrollNoopDiagnostic = succeeded && prepared.shouldProbeScrollNoop ? buildScrollNoopDiagnostic(prepared.scrollPositionBefore, await collectScrollPositionSnapshot({ cwd, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, signal })) : undefined;
|
|
387
|
+
const batchRefSnapshotState = prepared.executionPlan.commandInfo.command === "batch" ? extractLatestRefSnapshotStateFromBatchResults(presentationEnvelope?.data) : undefined;
|
|
296
388
|
let currentRefSnapshot;
|
|
297
389
|
let currentRefSnapshotInvalidation;
|
|
298
|
-
const batchRefSnapshotState = prepared.executionPlan.commandInfo.command === "batch" ? extractLatestRefSnapshotStateFromBatchResults(presentationEnvelope?.data) : undefined;
|
|
299
390
|
if (sessionStateKey) {
|
|
300
|
-
|
|
391
|
+
const sessionClosed = (isCloseCommand(prepared.executionPlan.commandInfo.command) && succeeded) || nestedBatchClosed;
|
|
392
|
+
if (sessionClosed) {
|
|
393
|
+
state.attachedSessionKeys.delete(sessionStateKey);
|
|
301
394
|
allowedDomainsBySession = new Map(allowedDomainsBySession);
|
|
302
395
|
allowedDomainsBySession.delete(sessionStateKey);
|
|
303
396
|
networkRoutesBySession = new Map(networkRoutesBySession);
|
|
@@ -305,35 +398,66 @@ export async function processBrowserOutput(input) {
|
|
|
305
398
|
sessionPageState.clearSession(sessionStateKey);
|
|
306
399
|
state.closedManagedSessionNames.add(sessionStateKey);
|
|
307
400
|
}
|
|
308
|
-
else if (currentSessionTabTarget) {
|
|
309
|
-
const tabUpdate = sessionPageState.applyTabTarget({ sessionName: sessionStateKey, target: currentSessionTabTarget, update: sessionPageStateUpdate });
|
|
310
|
-
if (!tabUpdate.applied && succeeded)
|
|
311
|
-
sessionPageState.markPinning(sessionStateKey, "drift");
|
|
312
|
-
}
|
|
313
|
-
else if (processResult.agentBrowserStarted && (resultingPageState.pageUrlUnknown || resultingPageState.pageTargetMayHaveChanged)) {
|
|
314
|
-
sessionPageState.markTabTargetUnknown({ sessionName: sessionStateKey, update: sessionPageStateUpdate });
|
|
315
|
-
}
|
|
316
|
-
const refSnapshot = prepared.executionPlan.commandInfo.command === "batch" ? batchRefSnapshotState?.snapshot : succeeded ? prepared.executionPlan.commandInfo.command === "snapshot" ? extractRefSnapshotFromData(presentationEnvelope?.data) : prepared.resolvedSemanticActionRefSnapshot ?? overlayBlockerDiagnostic?.snapshot : undefined;
|
|
317
|
-
if (refSnapshot) {
|
|
318
|
-
const refUpdate = sessionPageState.applyRefSnapshot({ fallbackTarget: currentSessionTabTarget, sessionName: sessionStateKey, snapshot: refSnapshot, update: sessionPageStateUpdate });
|
|
319
|
-
currentRefSnapshot = refUpdate.refSnapshot;
|
|
320
|
-
currentRefSnapshotInvalidation = refUpdate.refSnapshotInvalidation;
|
|
321
|
-
}
|
|
322
401
|
else {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
402
|
+
// A batch that times out or returns unparseable output yields no result rows, but the daemon
|
|
403
|
+
// may already have executed a recording page swap; fall back to the planned steps then. This
|
|
404
|
+
// also fires for wrapper-replaced envelopes and upstream pre-loop errors where nothing ran,
|
|
405
|
+
// which only over-invalidates (one extra snapshot), never under-invalidates.
|
|
406
|
+
const plannedBatchRecordSwap = !Array.isArray(presentationEnvelope?.data) && batchCommandSteps.some((step) => isRecordPageTransitionCommand(step));
|
|
407
|
+
const pageTransitionInvalidation = processResult.agentBrowserStarted && (isRecordPageTransitionCommand(prepared.commandTokens) || plannedBatchRecordSwap)
|
|
408
|
+
? buildPageTransitionRefSnapshotInvalidation()
|
|
409
|
+
: failedTransitionReverification
|
|
410
|
+
? buildPageTransitionRefSnapshotInvalidation("A failed eval/back/forward/reload/connect/state-load/tab command may still have changed the page, so the prior snapshot refs were invalidated. Run snapshot -i before using page-scoped refs.")
|
|
411
|
+
: batchRefSnapshotState?.invalidation?.reason === "page-transition"
|
|
412
|
+
? batchRefSnapshotState.invalidation
|
|
413
|
+
: undefined;
|
|
414
|
+
if (currentSessionTabTarget) {
|
|
415
|
+
const tabUpdate = sessionPageState.applyTabTarget({ sessionName: sessionStateKey, target: currentSessionTabTarget, update: sessionPageStateUpdate });
|
|
416
|
+
if (!tabUpdate.applied && succeeded)
|
|
417
|
+
sessionPageState.markPinning(sessionStateKey, "drift");
|
|
418
|
+
}
|
|
419
|
+
else if (processResult.agentBrowserStarted && (resultingPageState.pageUrlUnknown || resultingPageState.pageTargetMayHaveChanged)) {
|
|
420
|
+
sessionPageState.markTabTargetUnknown({ sessionName: sessionStateKey, update: sessionPageStateUpdate });
|
|
421
|
+
}
|
|
422
|
+
const refSnapshot = prepared.executionPlan.commandInfo.command === "batch" ? batchRefSnapshotState?.snapshot : succeeded ? prepared.executionPlan.commandInfo.command === "snapshot" ? extractRefSnapshotFromData(presentationEnvelope?.data) : prepared.resolvedSemanticActionRefSnapshot ?? overlayBlockerDiagnostic?.snapshot : undefined;
|
|
423
|
+
if (refSnapshot) {
|
|
424
|
+
const refUpdate = sessionPageState.applyRefSnapshot({ fallbackTarget: currentSessionTabTarget, sessionName: sessionStateKey, snapshot: refSnapshot, update: sessionPageStateUpdate });
|
|
425
|
+
currentRefSnapshot = refUpdate.refSnapshot;
|
|
426
|
+
currentRefSnapshotInvalidation = refUpdate.refSnapshotInvalidation;
|
|
427
|
+
}
|
|
428
|
+
else if (pageTransitionInvalidation) {
|
|
429
|
+
const refUpdate = sessionPageState.applyRefSnapshotInvalidation({ invalidation: pageTransitionInvalidation, sessionName: sessionStateKey, update: sessionPageStateUpdate });
|
|
430
|
+
currentRefSnapshot = refUpdate.refSnapshot;
|
|
431
|
+
currentRefSnapshotInvalidation = refUpdate.refSnapshotInvalidation;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
const stateView = sessionPageState.get(sessionStateKey);
|
|
435
|
+
currentRefSnapshot = stateView.refSnapshot;
|
|
436
|
+
currentRefSnapshotInvalidation = stateView.refSnapshotInvalidation;
|
|
437
|
+
}
|
|
326
438
|
}
|
|
327
439
|
}
|
|
328
440
|
const priorManagedSessionActive = managedSessionActive;
|
|
329
441
|
const priorManagedSessionCwd = managedSessionCwd;
|
|
442
|
+
const priorManagedSessionHeadedAutosaveInterval = managedSessionHeadedAutosaveInterval;
|
|
330
443
|
const priorManagedSessionName = managedSessionName;
|
|
331
444
|
const priorManagedSessionNamespace = managedSessionNamespace;
|
|
332
|
-
const
|
|
445
|
+
const priorManagedSessionKey = getSessionContextKey(priorManagedSessionName, priorManagedSessionNamespace) ?? priorManagedSessionName;
|
|
446
|
+
const closeAllTargetsPriorManagedSession = closeAllApplied
|
|
447
|
+
&& priorManagedSessionActive
|
|
448
|
+
&& isAgentBrowserSessionIdentityKeyInNamespace(priorManagedSessionKey, prepared.executionPlan.namespace);
|
|
449
|
+
const closeAllRetainsPriorManagedSession = closeAllTargetsPriorManagedSession
|
|
450
|
+
&& nestedBatchRemainsActive
|
|
451
|
+
&& sessionStateKey === priorManagedSessionKey;
|
|
452
|
+
const closeAllClosesPriorManagedSession = closeAllTargetsPriorManagedSession && !closeAllRetainsPriorManagedSession;
|
|
453
|
+
const commandClosesSession = isCloseCommand(prepared.executionPlan.commandInfo.command) || nestedBatchClosed || closeAllClosesPriorManagedSession;
|
|
454
|
+
const closeCommandSucceeded = (isCloseCommand(prepared.executionPlan.commandInfo.command) && succeeded) || nestedBatchClosed || closeAllClosesPriorManagedSession;
|
|
333
455
|
const closeTargetsPriorManagedNamespace = prepared.executionPlan.namespace === priorManagedSessionNamespace;
|
|
334
|
-
const managedCloseSessionName =
|
|
335
|
-
?
|
|
336
|
-
: prepared.executionPlan.
|
|
456
|
+
const managedCloseSessionName = closeAllClosesPriorManagedSession
|
|
457
|
+
? priorManagedSessionName
|
|
458
|
+
: closeCommandSucceeded && prepared.executionPlan.sessionName === priorManagedSessionName && closeTargetsPriorManagedNamespace
|
|
459
|
+
? prepared.executionPlan.sessionName
|
|
460
|
+
: prepared.executionPlan.managedSessionName;
|
|
337
461
|
const policyBlockedFreshManagedSession = allowedDomainsViolation !== undefined && prepared.sessionMode === "fresh" && prepared.executionPlan.managedSessionName === prepared.executionPlan.sessionName;
|
|
338
462
|
const postLaunchBatchFailure = !succeeded && processSucceeded && parseSucceeded && prepared.sessionMode === "fresh" && prepared.executionPlan.commandInfo.command === "batch" && batchStartedManagedBrowser(presentationEnvelope?.data);
|
|
339
463
|
const postLaunchTimeoutWithPage = !succeeded && processResult.timedOut && prepared.sessionMode === "fresh" && prepared.executionPlan.commandInfo.command === "batch" && timeoutPartialProgress?.liveUrlRecovered === true;
|
|
@@ -344,6 +468,7 @@ export async function processBrowserOutput(input) {
|
|
|
344
468
|
const failedFreshDaemon = failedFreshSessionMayHaveStarted && prepared.executionPlan.sessionName
|
|
345
469
|
? await inspectManagedSessionDaemon({
|
|
346
470
|
cwd,
|
|
471
|
+
headedManagedAutosaveInterval: prepared.ownedManagedSessionContext?.headedManagedAutosaveInterval,
|
|
347
472
|
namespace: prepared.executionPlan.namespace,
|
|
348
473
|
sessionName: prepared.executionPlan.sessionName,
|
|
349
474
|
timeoutMs: Math.min(implicitSessionCloseTimeoutMs, 2_000),
|
|
@@ -354,8 +479,8 @@ export async function processBrowserOutput(input) {
|
|
|
354
479
|
}
|
|
355
480
|
// Only a confirmed inactive daemon proves that a started fresh command did not establish browser ownership.
|
|
356
481
|
const postLaunchFreshFailure = failedFreshDaemon !== undefined && failedFreshDaemon.status !== "inactive";
|
|
357
|
-
const managedTransitionSucceeded = succeeded || policyBlockedFreshManagedSession || postLaunchBatchFailure || postLaunchTimeoutWithPage || postLaunchFreshFailure;
|
|
358
|
-
const managedSessionState = resolveManagedSessionState({ command: prepared.executionPlan.commandInfo.command, managedSessionName: managedCloseSessionName, managedSessionNamespace: prepared.executionPlan.namespace, priorActive: priorManagedSessionActive, priorNamespace: priorManagedSessionNamespace, priorSessionName: priorManagedSessionName, succeeded: managedTransitionSucceeded });
|
|
482
|
+
const managedTransitionSucceeded = succeeded || nestedBatchClosed || nestedBatchRemainsActive || policyBlockedFreshManagedSession || postLaunchBatchFailure || postLaunchTimeoutWithPage || postLaunchFreshFailure;
|
|
483
|
+
const managedSessionState = resolveManagedSessionState({ command: commandClosesSession ? "close" : prepared.executionPlan.commandInfo.command, managedSessionName: managedCloseSessionName, managedSessionNamespace: prepared.executionPlan.namespace, priorActive: priorManagedSessionActive, priorNamespace: priorManagedSessionNamespace, priorSessionName: priorManagedSessionName, succeeded: managedTransitionSucceeded });
|
|
359
484
|
if (!managedTransitionSucceeded && prepared.sessionMode === "fresh" && prepared.executionPlan.managedSessionName) {
|
|
360
485
|
state.managedSessionRestoreState.clear(prepared.executionPlan.managedSessionName, prepared.executionPlan.namespace);
|
|
361
486
|
}
|
|
@@ -368,11 +493,15 @@ export async function processBrowserOutput(input) {
|
|
|
368
493
|
=== getAgentBrowserSessionIdentityKey(managedSessionName, managedSessionNamespace);
|
|
369
494
|
if (!managedSessionActive) {
|
|
370
495
|
managedSessionCompatibilityWorkaround = undefined;
|
|
496
|
+
managedSessionHeadedAutosaveDisabled = false;
|
|
497
|
+
managedSessionHeadedAutosaveInterval = undefined;
|
|
371
498
|
}
|
|
372
499
|
else if (managedTransitionSucceeded && executionTargetsManagedSession) {
|
|
373
500
|
managedSessionCompatibilityWorkaround = prepared.compatibilityWorkaround;
|
|
501
|
+
managedSessionHeadedAutosaveDisabled = prepared.ownedManagedSessionContext?.headedManagedAutosaveDisabled === true;
|
|
502
|
+
managedSessionHeadedAutosaveInterval = prepared.ownedManagedSessionContext?.headedManagedAutosaveInterval;
|
|
374
503
|
}
|
|
375
|
-
if (
|
|
504
|
+
if (closeCommandSucceeded && managedCloseSessionName === priorManagedSessionName && !managedSessionActive) {
|
|
376
505
|
const daemonRestoreKey = state.managedSessionRestoreState.getDaemonRestoreKey(managedCloseSessionName, priorManagedSessionNamespace);
|
|
377
506
|
const ownedRestoreKey = !state.managedSessionRestoreState.isDisabled(managedCloseSessionName, priorManagedSessionNamespace)
|
|
378
507
|
&& isManagedSessionRestoreKey(daemonRestoreKey) ? daemonRestoreKey : null;
|
|
@@ -387,7 +516,7 @@ export async function processBrowserOutput(input) {
|
|
|
387
516
|
managedSessionName = createFreshSessionName(state.managedSessionBaseName, state.ephemeralSessionSeed, freshSessionOrdinal);
|
|
388
517
|
managedSessionNamespace = undefined;
|
|
389
518
|
}
|
|
390
|
-
let managedSessionOutcome = buildManagedSessionOutcome({ activeAfter: managedSessionActive, activeBefore: priorManagedSessionActive, attemptedSessionName: managedCloseSessionName, command: prepared.executionPlan.commandInfo.command, currentSessionName: managedSessionName, currentSessionNamespace: managedSessionNamespace, previousSessionName: priorManagedSessionName, replacedSessionName: replacedManagedSessionName, replacedSessionNamespace: priorManagedSessionNamespace, sessionMode: prepared.sessionMode, succeeded: managedTransitionSucceeded });
|
|
519
|
+
let managedSessionOutcome = buildManagedSessionOutcome({ activeAfter: managedSessionActive, activeBefore: priorManagedSessionActive, attemptedSessionName: managedCloseSessionName, command: commandClosesSession ? "close" : prepared.executionPlan.commandInfo.command, currentSessionName: managedSessionName, currentSessionNamespace: managedSessionNamespace, previousSessionName: priorManagedSessionName, replacedSessionName: replacedManagedSessionName, replacedSessionNamespace: priorManagedSessionNamespace, sessionMode: prepared.sessionMode, succeeded: managedTransitionSucceeded });
|
|
391
520
|
if (prepared.executionPlan.managedSessionName && managedTransitionSucceeded && managedSessionActive) {
|
|
392
521
|
managedSessionCwd = cwd;
|
|
393
522
|
managedSessionNamespace = prepared.executionPlan.namespace;
|
|
@@ -406,7 +535,16 @@ export async function processBrowserOutput(input) {
|
|
|
406
535
|
networkRoutesBySession.delete(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
407
536
|
sessionPageState.clearSession(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
408
537
|
const replacedSessionKey = replacedSessionStateKey ?? replacedManagedSessionName;
|
|
409
|
-
const replacedCloseError = await closeManagedSession({ cwd: priorManagedSessionCwd, namespace: priorManagedSessionNamespace, preserveAttachedBrowserSession: state.attachedSessionKeys.has(replacedSessionKey), restoreState: state.managedSessionRestoreState, sessionName: replacedManagedSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
538
|
+
const replacedCloseError = await closeManagedSession({ cwd: priorManagedSessionCwd, headedManagedAutosaveInterval: priorManagedSessionHeadedAutosaveInterval, namespace: priorManagedSessionNamespace, preserveAttachedBrowserSession: state.attachedSessionKeys.has(replacedSessionKey), restoreState: state.managedSessionRestoreState, sessionName: replacedManagedSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
539
|
+
if (managedSessionOutcome) {
|
|
540
|
+
managedSessionOutcome = {
|
|
541
|
+
...managedSessionOutcome,
|
|
542
|
+
replacedSessionClosed: !replacedCloseError,
|
|
543
|
+
summary: replacedCloseError
|
|
544
|
+
? `${managedSessionOutcome.summary} Previous session ${replacedManagedSessionName} remains wrapper-owned because automatic close failed; retry an explicit close.`
|
|
545
|
+
: managedSessionOutcome.summary,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
410
548
|
if (!replacedCloseError) {
|
|
411
549
|
state.attachedSessionKeys.delete(replacedSessionKey);
|
|
412
550
|
state.closedManagedSessionNames.add(replacedSessionKey);
|
|
@@ -419,7 +557,7 @@ export async function processBrowserOutput(input) {
|
|
|
419
557
|
if (succeeded && prepared.executionPlan.sessionName) {
|
|
420
558
|
const electronSessionName = prepared.executionPlan.sessionName;
|
|
421
559
|
const electronSessionStateKey = sessionStateKey ?? electronSessionName;
|
|
422
|
-
electronLaunchRecord = { ...prepared.electronLaunch.record, sessionName: electronSessionName };
|
|
560
|
+
electronLaunchRecord = { ...prepared.electronLaunch.record, namespace: prepared.executionPlan.namespace, sessionName: electronSessionName };
|
|
423
561
|
const electronHandoffMode = prepared.compiledElectron?.action === "launch" ? prepared.compiledElectron.handoff : "connect";
|
|
424
562
|
try {
|
|
425
563
|
electronHandoff = await collectElectronHandoff({ cwd, handoff: electronHandoffMode, namespace: prepared.executionPlan.namespace, sessionName: electronSessionName, signal });
|
|
@@ -434,7 +572,7 @@ export async function processBrowserOutput(input) {
|
|
|
434
572
|
if (electronHandoff.error) {
|
|
435
573
|
succeeded = false;
|
|
436
574
|
presentationEnvelope = { error: electronHandoff.error, success: false };
|
|
437
|
-
const closeError = await closeManagedSession({ cwd, namespace: prepared.executionPlan.namespace, policyLock: prepared.managedSessionPolicyLock, preserveAttachedBrowserSession: input.preserveAttachedBrowserSession, restoreState: state.managedSessionRestoreState, sessionName: electronSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
575
|
+
const closeError = await closeManagedSession({ cwd, headedManagedAutosaveInterval: prepared.ownedManagedSessionContext?.headedManagedAutosaveInterval, namespace: prepared.executionPlan.namespace, policyLock: prepared.managedSessionPolicyLock, preserveAttachedBrowserSession: input.preserveAttachedBrowserSession, restoreState: state.managedSessionRestoreState, sessionName: electronSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
438
576
|
electronFailedConnectCleanup = await cleanupElectronLaunchResources({ child: prepared.electronLaunch.child, record: electronLaunchRecord, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
439
577
|
electronLaunchRecord = electronFailedConnectCleanup.record;
|
|
440
578
|
if (electronFailedConnectCleanup.partial) {
|
|
@@ -487,7 +625,7 @@ export async function processBrowserOutput(input) {
|
|
|
487
625
|
if (presentationEnvelope?.error !== undefined)
|
|
488
626
|
presentationEnvelope = { ...presentationEnvelope, error: redactClipboardPermissionErrorValue(prepared.executionPlan.commandInfo, presentationEnvelope.error, clipboardWritePayloadCandidates) };
|
|
489
627
|
}
|
|
490
|
-
const 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, 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 });
|
|
628
|
+
const 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 });
|
|
491
629
|
if (observedPageValidationError)
|
|
492
630
|
presentation.failureCategory = "validation-error";
|
|
493
631
|
if (electronHandoff?.error && electronHandoff.failureCategory)
|
|
@@ -498,13 +636,20 @@ export async function processBrowserOutput(input) {
|
|
|
498
636
|
presentationEnvelope = { ...(presentationEnvelope ?? {}), error: presentation.summary, success: false };
|
|
499
637
|
}
|
|
500
638
|
if (scrollNoopDiagnostic) {
|
|
639
|
+
succeeded = false;
|
|
640
|
+
presentation.resultCategory = "failure";
|
|
641
|
+
presentation.failureCategory = "upstream-error";
|
|
642
|
+
presentationEnvelope = { ...(presentationEnvelope ?? {}), error: "Scroll completed with no observed movement.", success: false };
|
|
501
643
|
presentation.summary = "Scroll completed with no observed movement.";
|
|
502
644
|
if (isRecord(presentation.data))
|
|
503
645
|
presentation.data = { ...presentation.data, noMovement: true, scrolled: false };
|
|
504
|
-
if (presentation.content[0]?.type === "text")
|
|
505
|
-
presentation.
|
|
506
|
-
|
|
646
|
+
if (presentation.content[0]?.type === "text") {
|
|
647
|
+
const details = isRecord(presentation.data) ? JSON.stringify(omitUpstreamLifecycle(presentation.data), null, 2) : presentation.content[0].text;
|
|
648
|
+
presentation.content[0] = { ...presentation.content[0], text: `Scroll completed with no observed movement.\n\n${details}` };
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
507
651
|
presentation.content.unshift({ type: "text", text: "Scroll completed with no observed movement." });
|
|
652
|
+
}
|
|
508
653
|
}
|
|
509
654
|
if (parseFailureOutput.artifactManifest) {
|
|
510
655
|
presentation.artifactManifest = parseFailureOutput.artifactManifest;
|
|
@@ -524,7 +669,7 @@ export async function processBrowserOutput(input) {
|
|
|
524
669
|
let qaAttachedTarget = prepared.compiledQaPreset?.checks.attached
|
|
525
670
|
? await collectQaAttachedTarget({ currentTarget: currentSessionTabTarget ?? prepared.priorSessionTabTarget, cwd, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, signal })
|
|
526
671
|
: undefined;
|
|
527
|
-
const sourceLookupElectronContext = prepared.compiledSourceLookup ? getSourceLookupElectronContext({ currentTarget: currentSessionTabTarget, electronLaunchRecords, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName }) : undefined;
|
|
672
|
+
const sourceLookupElectronContext = prepared.compiledSourceLookup ? getSourceLookupElectronContext({ currentTarget: currentSessionTabTarget, electronLaunchRecords, namespace: prepared.executionPlan.namespace, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName }) : undefined;
|
|
528
673
|
const sourceLookup = prepared.compiledSourceLookup ? await analyzeSourceLookupResults(presentationEnvelope?.data, prepared.compiledSourceLookup, cwd, { electronContext: sourceLookupElectronContext, workspaceRoot: cwd }) : undefined;
|
|
529
674
|
const networkSourceLookup = prepared.compiledNetworkSourceLookup ? redactNetworkSourceLookupAnalysis(await analyzeNetworkSourceLookupResults(presentationEnvelope?.data, prepared.compiledNetworkSourceLookup, cwd)) : undefined;
|
|
530
675
|
if (networkSourceLookup && presentation.content[0]?.type === "text")
|
|
@@ -535,14 +680,22 @@ export async function processBrowserOutput(input) {
|
|
|
535
680
|
presentation.content[0] = { ...presentation.content[0], text: `${sourceLookup.summary}\n\n${presentation.content[0].text}` };
|
|
536
681
|
else if (sourceLookup)
|
|
537
682
|
presentation.content.unshift({ type: "text", text: sourceLookup.summary });
|
|
538
|
-
if (qaPreset && !qaPreset.passed && presentation.failureCategory !== "artifact-missing") {
|
|
683
|
+
if (qaPreset && !qaPreset.passed && prepared.compiledQaPreset && presentation.failureCategory !== "artifact-missing") {
|
|
539
684
|
succeeded = false;
|
|
540
685
|
presentation.failureCategory = "qa-failure";
|
|
541
686
|
presentation.summary = qaPreset.summary;
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
687
|
+
const compactText = buildQaCompactFailureText({
|
|
688
|
+
batchStepCount: presentation.batchSteps?.length ?? prepared.compiledQaPreset.steps.length,
|
|
689
|
+
checks: prepared.compiledQaPreset.checks,
|
|
690
|
+
page: extractQaPageContext({
|
|
691
|
+
attachedTarget: qaAttachedTarget,
|
|
692
|
+
batchData: presentationEnvelope?.data,
|
|
693
|
+
compiled: prepared.compiledQaPreset,
|
|
694
|
+
}),
|
|
695
|
+
qaPreset,
|
|
696
|
+
});
|
|
697
|
+
const nonTextContent = presentation.content.filter((item) => item.type !== "text");
|
|
698
|
+
presentation.content = [{ type: "text", text: compactText }, ...nonTextContent];
|
|
546
699
|
}
|
|
547
700
|
else if (qaPreset?.passed && prepared.compiledQaPreset && succeeded) {
|
|
548
701
|
const compactText = buildQaCompactPassText({
|
|
@@ -588,9 +741,20 @@ export async function processBrowserOutput(input) {
|
|
|
588
741
|
if (sessionStateKey)
|
|
589
742
|
currentSessionTabTarget = authoritativePageState?.tabTarget;
|
|
590
743
|
const currentSessionTabTargetUnknown = authoritativePageState?.tabTargetUnknown === true ? true : undefined;
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
744
|
+
const resultRetainsPreparedManagedSession = !managedSessionOutcome || (managedSessionOutcome.activeAfter
|
|
745
|
+
&& managedSessionOutcome.attemptedSessionName === managedSessionOutcome.currentSessionName);
|
|
746
|
+
const resultHeadedManagedAutosaveDisabled = prepared.ownedManagedSessionContext?.headedManagedAutosaveDisabled === true
|
|
747
|
+
&& resultRetainsPreparedManagedSession
|
|
748
|
+
&& !(commandClosesSession && succeeded);
|
|
749
|
+
const resultHeadedManagedAutosaveInterval = resultRetainsPreparedManagedSession && !(commandClosesSession && succeeded)
|
|
750
|
+
? prepared.ownedManagedSessionContext?.headedManagedAutosaveInterval
|
|
751
|
+
: undefined;
|
|
752
|
+
const result = buildFinalAgentBrowserToolResult({ aboutBlankSessionMismatch, artifactCleanup, categoryDetails: finalRecoveryState.categoryDetails, clickDispatchDiagnostic, commandTokens: prepared.commandTokens, comboboxFocusDiagnostic, compiledNetworkSourceLookup: prepared.compiledNetworkSourceLookup, compiledSemanticAction: prepared.compiledSemanticAction, compatibilityWorkaround: prepared.compatibilityWorkaround, currentRefSnapshot, currentRefSnapshotInvalidation, currentSessionTabTarget, currentSessionTabTargetUnknown, electronBroadGetTextScopeDiagnostics, electronFailedConnectCleanup, electronHandoff, electronLaunch: prepared.electronLaunch, electronLaunchRecord, electronLaunchRecords, electronPostCommandHealth, electronProfileIsolationDetails: input.electronProfileIsolationDetails, electronRefFreshnessDiagnostic, electronSessionMismatch, errorText, evalResultWarning, evalStdinHint, exactSensitiveValues: prepared.exactSensitiveValues, executionPlan: prepared.executionPlan, fillVerificationDiagnostic, inspectionText, managedSessionHeadedAutosaveDisabled: resultHeadedManagedAutosaveDisabled || undefined, managedSessionHeadedAutosaveInterval: resultHeadedManagedAutosaveInterval, managedSessionOutcome, managedSessionRestoreDisabled: state.managedSessionRestoreState.isDisabled(prepared.executionPlan.sessionName, prepared.executionPlan.namespace), navigationSummary, networkSourceLookup, noActivePageSnapshotFailure: finalRecoveryState.noActivePageSnapshotFailure, openResultTabCorrection, overlayBlockerDiagnostic, parseError, parseFailureOutput, parseSucceeded, plainTextInspection, presentation, presentationEnvelope, priorSessionTabTarget: prepared.priorSessionTabTarget, processResult, qaAttachedTarget, qaPreset, recordingDependencyWarning, redactedArgs: prepared.redactedArgs, redactedCompiledElectron: prepared.redactedCompiledElectron, redactedCompiledJob: prepared.redactedCompiledJob, redactedCompiledNetworkSourceLookup: prepared.redactedCompiledNetworkSourceLookup, redactedCompiledQaPreset: prepared.redactedCompiledQaPreset, redactedCompiledSemanticAction: prepared.redactedCompiledSemanticAction, redactedCompiledSourceLookup: prepared.redactedCompiledSourceLookup, redactedContent, redactedProcessArgs: prepared.redactedProcessArgs, redactedRecoveryHint: prepared.redactedRecoveryHint, resultArtifactManifest, richInputRecoveryDiagnostic: finalRecoveryState.richInputRecoveryDiagnostic, scrollNoopDiagnostic, selectorTextVisibilityDiagnostics, sessionMode: prepared.sessionMode, sessionTabCorrection, sourceLookup, succeeded, timeoutPartialProgress, userRequestedJson: prepared.userRequestedJson, visibleRefFallbackDiagnostic: finalRecoveryState.visibleRefFallbackDiagnostic, visibleRefFallbackSessionName: finalRecoveryState.visibleRefFallbackSessionName });
|
|
753
|
+
const resultWithCloseAll = closeAllApplied
|
|
754
|
+
? { ...result, details: { ...(isRecord(result.details) ? result.details : {}), closeAllApplied: true } }
|
|
755
|
+
: result;
|
|
756
|
+
const statePatch = { allowedDomainsBySession, artifactManifest, freshSessionOrdinal, managedSessionActive, managedSessionCompatibilityWorkaround, managedSessionHeadedAutosaveDisabled, managedSessionHeadedAutosaveInterval, managedSessionCwd, managedSessionName, managedSessionNamespace, networkRoutesBySession };
|
|
757
|
+
return { result: resultWithCloseAll, statePatch };
|
|
594
758
|
}
|
|
595
759
|
finally {
|
|
596
760
|
if (processResult.stdoutSpillPath)
|