pi-agent-browser-native 0.2.72 → 0.2.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +14 -12
- package/dist/extensions/agent-browser/index.js +148 -16
- package/dist/extensions/agent-browser/lib/argv-grammar.js +122 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +11 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +2 -2
- package/dist/extensions/agent-browser/lib/electron/launch.js +48 -12
- package/dist/extensions/agent-browser/lib/input-modes/params.js +96 -98
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +88 -2
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +22 -0
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +432 -0
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +374 -0
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +367 -0
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +589 -0
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +299 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +35 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +9 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +40 -22
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +15 -6
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +54 -33
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +182 -0
- 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 +629 -430
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +148 -57
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +30 -40
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +102 -19
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +13 -1
- package/dist/extensions/agent-browser/lib/playbook.js +9 -8
- package/dist/extensions/agent-browser/lib/process-identity.js +82 -0
- package/dist/extensions/agent-browser/lib/process.js +271 -34
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +5 -3
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +35 -12
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +42 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +7 -0
- package/dist/extensions/agent-browser/lib/runtime.js +114 -107
- package/dist/extensions/agent-browser/lib/session-page-state.js +48 -17
- package/dist/extensions/agent-browser/lib/temp.js +13 -25
- package/docs/ARCHITECTURE.md +10 -9
- package/docs/COMMAND_REFERENCE.md +43 -23
- package/docs/ELECTRON.md +10 -10
- package/docs/RELEASE.md +3 -2
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +19 -18
- package/docs/TOOL_CONTRACT.md +29 -26
- package/docs/platform-smoke.md +2 -2
- package/package.json +1 -1
- package/platform-smoke.config.mjs +1 -1
- package/scripts/agent-browser-capability-baseline.mjs +11 -3
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { getAgentBrowserSessionIdentityKey } from "../../argv-grammar.js";
|
|
2
3
|
import { isCloseCommand, isNavigationObservableCommandName, isOpenNavigationCommand } from "../../command-taxonomy.js";
|
|
3
4
|
import { OPEN_RESULT_TAB_CORRECTION_FLAGS } from "../../launch-scoped-flags.js";
|
|
4
5
|
import { cleanupElectronLaunchResources, inspectElectronLaunchStatus } from "../../electron/cleanup.js";
|
|
5
6
|
import { getAllowedDomainsViolation, parseAllowedDomainsPolicyFromArgs } from "../../navigation-policy.js";
|
|
7
|
+
import { getManagedSessionResultingPageState, getObservedBrowserPageValidationError, managedSessionCommandRequiresLivePageVerification } from "../../managed-session-state-policy.js";
|
|
6
8
|
import { analyzeNetworkSourceLookupResults, analyzeQaPresetResults, analyzeQaPresetTimeout, analyzeSourceLookupResults, buildQaCompactPassText, extractQaPageContext, redactNetworkSourceLookupAnalysis, } from "../../input-modes.js";
|
|
7
9
|
import { applyNetworkRouteRecords, buildNetworkRouteDiagnostics, buildToolPresentation, getAgentBrowserErrorText, parseAgentBrowserEnvelope, } from "../../results.js";
|
|
8
|
-
import { buildEvictedSessionArtifactEntries, formatSessionArtifactRetentionSummary, mergeSessionArtifactManifest, } from "../../results/artifact-manifest.js";
|
|
9
10
|
import { getClipboardWritePayloadCandidates, redactClipboardPermissionEcho, redactClipboardPermissionErrorValue } from "../../results/presentation/errors.js";
|
|
10
11
|
import { shouldCaptureSemanticActionNavigationSummary } from "../../results/presentation/semantic-action.js";
|
|
11
12
|
import { commandExplicitlyTargetsAboutBlank, deriveSessionTabTarget, extractLatestRefSnapshotStateFromBatchResults, extractRefSnapshotFromData, extractSessionTabTargetFromBatchResults, extractSessionTabTargetFromCommandData, isAboutBlankSessionTabTarget, normalizeSessionTabTarget, } from "../../session-page-state.js";
|
|
12
|
-
import { writePersistentSessionArtifactFile, writeSecureTempFile } from "../../temp.js";
|
|
13
13
|
import { isRecord } from "../../parsing.js";
|
|
14
|
+
import { pruneOwnedManagedSessionRestoreSnapshots } from "../../managed-session-restore.js";
|
|
15
|
+
import { isManagedSessionRestoreKey } from "../../managed-session-storage.js";
|
|
14
16
|
import { createFreshSessionName, extractCommandTokens, resolveManagedSessionState } from "../../runtime.js";
|
|
15
|
-
import {
|
|
17
|
+
import { closeManagedSession, inspectManagedSessionDaemon } from "./managed-session-daemon-policy.js";
|
|
18
|
+
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";
|
|
16
19
|
import { collectClickDispatchDiagnostic } from "./click-dispatch.js";
|
|
17
20
|
import { buildScrollNoopDiagnostic, collectComboboxFocusDiagnostic, collectElectronBroadGetTextScopeDiagnostics, collectElectronHandoff, collectFillVerificationDiagnostic, collectNavigationSummary, collectOverlayBlockerDiagnostic, collectQaAttachedTarget, collectSnapshotOverlayBlockerDiagnostic, collectRecordingDependencyWarning, collectScrollPositionSnapshot, collectSelectorTextVisibilityDiagnostics, collectTimeoutPartialProgress, sleepMs, formatQaAttachedTargetText, getArtifactCleanupGuidance, getEvalResultWarning, getEvalStdinHint, getSourceLookupElectronContext, } from "./diagnostics.js";
|
|
18
21
|
import { repairScreenshotData } from "./prepare.js";
|
|
19
22
|
import { getPersistentSessionArtifactStore } from "./session-artifacts.js";
|
|
20
|
-
import { buildFinalAgentBrowserToolResult, buildRedactedPresentationContent, buildWrapperRecoveryHint, prepareFinalResultRecoveryState,
|
|
23
|
+
import { buildFinalAgentBrowserToolResult, buildRedactedPresentationContent, buildWrapperRecoveryHint, prepareFinalResultRecoveryState, redactExactSensitiveValue, } from "./final-result.js";
|
|
21
24
|
async function repairScreenshotArtifact(options) {
|
|
22
25
|
const { cwd, envelope, request } = options;
|
|
23
26
|
if (!request || !envelope || !isRecord(envelope.data))
|
|
@@ -93,37 +96,6 @@ function applyBatchNetworkRouteState(options) {
|
|
|
93
96
|
}
|
|
94
97
|
return setNetworkRouteState({ routes, routesBySession: options.routesBySession, sessionName: options.sessionName });
|
|
95
98
|
}
|
|
96
|
-
export async function preserveParseFailureOutput(options) {
|
|
97
|
-
if (!options.stdoutSpillPath)
|
|
98
|
-
return {};
|
|
99
|
-
try {
|
|
100
|
-
const rawOutput = redactExactSensitiveText(await readFile(options.stdoutSpillPath, "utf8"), options.exactSensitiveValues ?? []);
|
|
101
|
-
const nowMs = Date.now();
|
|
102
|
-
let evictedArtifacts = [];
|
|
103
|
-
let fullOutputPath;
|
|
104
|
-
let storageScope;
|
|
105
|
-
if (options.persistentArtifactStore) {
|
|
106
|
-
const result = await writePersistentSessionArtifactFile({ content: rawOutput, prefix: "pi-agent-browser-parse-failure-output", store: options.persistentArtifactStore, suffix: ".txt" });
|
|
107
|
-
fullOutputPath = result.path;
|
|
108
|
-
evictedArtifacts = result.evictedArtifacts;
|
|
109
|
-
storageScope = "persistent-session";
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
fullOutputPath = await writeSecureTempFile({ content: rawOutput, prefix: "pi-agent-browser-parse-failure-output", suffix: ".txt" });
|
|
113
|
-
storageScope = "process-temp";
|
|
114
|
-
}
|
|
115
|
-
const artifactManifest = mergeSessionArtifactManifest({
|
|
116
|
-
base: options.artifactManifest,
|
|
117
|
-
entries: [{ command: "agent-browser", createdAtMs: nowMs, kind: "spill", path: fullOutputPath, retentionState: storageScope === "persistent-session" ? "live" : "ephemeral", storageScope }, ...buildEvictedSessionArtifactEntries(evictedArtifacts, nowMs)],
|
|
118
|
-
nowMs,
|
|
119
|
-
});
|
|
120
|
-
return { artifactManifest, artifactRetentionSummary: artifactManifest ? formatSessionArtifactRetentionSummary(artifactManifest) : undefined, fullOutputPath };
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
124
|
-
return { fullOutputUnavailable: message };
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
99
|
export async function processBrowserOutput(input) {
|
|
128
100
|
const { ctx, cwd, electronPostCommandStatusSettleMs, implicitSessionCloseTimeoutMs, sessionPageStateUpdate, signal, state } = input;
|
|
129
101
|
const { prepared, processResult } = input;
|
|
@@ -132,6 +104,7 @@ export async function processBrowserOutput(input) {
|
|
|
132
104
|
let artifactManifest = state.artifactManifest;
|
|
133
105
|
let freshSessionOrdinal = state.freshSessionOrdinal;
|
|
134
106
|
let managedSessionActive = state.managedSessionActive;
|
|
107
|
+
let managedSessionCompatibilityWorkaround = state.managedSessionCompatibilityWorkaround;
|
|
135
108
|
let managedSessionCwd = state.managedSessionCwd;
|
|
136
109
|
let managedSessionName = state.managedSessionName;
|
|
137
110
|
let managedSessionNamespace = state.managedSessionNamespace;
|
|
@@ -154,9 +127,16 @@ export async function processBrowserOutput(input) {
|
|
|
154
127
|
presentationEnvelope = repairedBatchScreenshots.envelope;
|
|
155
128
|
const screenshotArtifactRequest = repairedScreenshot.request;
|
|
156
129
|
const batchScreenshotArtifactRequests = repairedBatchScreenshots.requests;
|
|
130
|
+
const rawCloseStatePath = isCloseCommand(prepared.executionPlan.commandInfo.command)
|
|
131
|
+
&& isRecord(presentationEnvelope?.data)
|
|
132
|
+
&& typeof presentationEnvelope.data.statePath === "string"
|
|
133
|
+
? presentationEnvelope.data.statePath
|
|
134
|
+
: undefined;
|
|
157
135
|
if (presentationEnvelope && prepared.exactSensitiveValues.length > 0)
|
|
158
136
|
presentationEnvelope = redactExactSensitiveValue(presentationEnvelope, prepared.exactSensitiveValues);
|
|
159
|
-
const parseFailureOutput = parseError
|
|
137
|
+
const parseFailureOutput = parseError && processResult.stdoutSpillPath
|
|
138
|
+
? { fullOutputUnavailable: "Malformed upstream output was discarded because it may contain sensitive browser data." }
|
|
139
|
+
: {};
|
|
160
140
|
const processSucceeded = !processResult.aborted && !processResult.spawnError && processResult.exitCode === 0;
|
|
161
141
|
const plainTextInspection = prepared.executionPlan.plainTextInspection && processSucceeded;
|
|
162
142
|
const parseSucceeded = plainTextInspection || parseError === undefined;
|
|
@@ -176,6 +156,13 @@ export async function processBrowserOutput(input) {
|
|
|
176
156
|
presentationEnvelope = { ...(presentationEnvelope ?? {}), error: clickDispatchDiagnostic.summary, success: false };
|
|
177
157
|
}
|
|
178
158
|
}
|
|
159
|
+
const resultingPageState = getManagedSessionResultingPageState({
|
|
160
|
+
args: prepared.executionPlan.effectiveArgs,
|
|
161
|
+
currentPageUrl: prepared.priorSessionTabTarget?.url,
|
|
162
|
+
pageUrlUnknown: prepared.priorSessionTabTargetUnknown === true,
|
|
163
|
+
stdin: prepared.runtimeToolStdin,
|
|
164
|
+
trustedFirstBatchTabSelection: prepared.pinnedBatchUnwrapMode !== undefined,
|
|
165
|
+
});
|
|
179
166
|
const presentationDataRecord = isRecord(presentationEnvelope?.data) ? presentationEnvelope.data : undefined;
|
|
180
167
|
const dataClicked = typeof presentationDataRecord?.clicked === "string" ? presentationDataRecord.clicked : undefined;
|
|
181
168
|
const cssClickWithoutHref = prepared.executionPlan.commandInfo.command === "click" && dataClicked !== undefined && !dataClicked.startsWith("@") && !dataClicked.startsWith("ref=") && typeof presentationDataRecord?.href !== "string";
|
|
@@ -189,10 +176,11 @@ export async function processBrowserOutput(input) {
|
|
|
189
176
|
(shouldCaptureNavigationSummary(prepared.executionPlan.commandInfo.command, presentationEnvelope?.data) ||
|
|
190
177
|
shouldCaptureSemanticActionNavigationSummary(prepared.compiledSemanticAction, presentationEnvelope?.data) ||
|
|
191
178
|
shouldCaptureAllowedDomainNavigationSummary ||
|
|
179
|
+
managedSessionCommandRequiresLivePageVerification(prepared.executionPlan.effectiveArgs, prepared.runtimeToolStdin) ||
|
|
192
180
|
(prepared.executionPlan.commandInfo.command === "tab" && prepared.executionPlan.commandInfo.subcommand === "close"))) {
|
|
193
181
|
navigationSummary = await collectNavigationSummary({ cwd, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, signal });
|
|
194
182
|
}
|
|
195
|
-
if (navigationSummary && presentationEnvelope && !Array.isArray(presentationEnvelope.data))
|
|
183
|
+
if (navigationSummary && presentationEnvelope && prepared.executionPlan.commandInfo.command !== "eval" && !Array.isArray(presentationEnvelope.data))
|
|
196
184
|
presentationEnvelope = { ...presentationEnvelope, data: mergeNavigationSummaryIntoData(presentationEnvelope.data, navigationSummary) };
|
|
197
185
|
let overlayBlockerDiagnostic;
|
|
198
186
|
let openResultTabCorrection;
|
|
@@ -203,8 +191,20 @@ export async function processBrowserOutput(input) {
|
|
|
203
191
|
if (plannedTabCorrection)
|
|
204
192
|
openResultTabCorrection = await applyOpenResultTabCorrection({ correction: plannedTabCorrection, cwd, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, signal });
|
|
205
193
|
}
|
|
206
|
-
const
|
|
207
|
-
|
|
194
|
+
const verifiesCurrentUrl = prepared.executionPlan.commandInfo.command === "get" && prepared.executionPlan.commandInfo.subcommand === "url";
|
|
195
|
+
const trustsReportedPageTarget = !resultingPageState.pageUrlUnknown || verifiesCurrentUrl;
|
|
196
|
+
const observedSessionTabTarget = normalizeSessionTabTarget(navigationSummary)
|
|
197
|
+
?? (trustsReportedPageTarget ? extractSessionTabTargetFromBatchResults(presentationEnvelope?.data) : undefined)
|
|
198
|
+
?? (succeeded && trustsReportedPageTarget ? extractSessionTabTargetFromCommandData(prepared.commandTokens, presentationEnvelope?.data) : undefined);
|
|
199
|
+
const observedPageValidationError = succeeded ? getObservedBrowserPageValidationError(prepared.executionPlan.effectiveArgs, observedSessionTabTarget?.url, cwd) : undefined;
|
|
200
|
+
if (observedPageValidationError) {
|
|
201
|
+
succeeded = false;
|
|
202
|
+
presentationEnvelope = { error: observedPageValidationError, success: false };
|
|
203
|
+
}
|
|
204
|
+
const safeObservedSessionTabTarget = observedPageValidationError ? normalizeSessionTabTarget({ url: observedSessionTabTarget?.url }) : observedSessionTabTarget;
|
|
205
|
+
let currentSessionTabTarget = safeObservedSessionTabTarget ?? (resultingPageState.pageTargetMayHaveChanged
|
|
206
|
+
? succeeded ? normalizeSessionTabTarget({ url: resultingPageState.currentPageUrl }) : undefined
|
|
207
|
+
: deriveSessionTabTarget({ command: prepared.executionPlan.commandInfo.command, data: presentationEnvelope?.data, navigationSummary, previousTarget: prepared.priorSessionTabTarget, subcommand: prepared.executionPlan.commandInfo.subcommand }));
|
|
208
208
|
let aboutBlankSessionMismatch;
|
|
209
209
|
let electronPostCommandHealth;
|
|
210
210
|
let electronRefFreshnessDiagnostic;
|
|
@@ -265,6 +265,9 @@ export async function processBrowserOutput(input) {
|
|
|
265
265
|
let selectorTextVisibilityDiagnostics = [];
|
|
266
266
|
let electronBroadGetTextScopeDiagnostics = [];
|
|
267
267
|
const timeoutPartialProgress = processResult.timedOut ? await collectTimeoutPartialProgress({ command: prepared.executionPlan.commandInfo.command, compiledJob: prepared.compiledJob, cwd, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, stdin: prepared.runtimeToolStdin }) : undefined;
|
|
268
|
+
if (!currentSessionTabTarget && timeoutPartialProgress?.currentPage?.source === "live") {
|
|
269
|
+
currentSessionTabTarget = normalizeSessionTabTarget(timeoutPartialProgress.currentPage);
|
|
270
|
+
}
|
|
268
271
|
if (succeeded) {
|
|
269
272
|
const fillRefSnapshot = prepared.resolvedSemanticActionRefSnapshot ?? prepared.priorRefSnapshotState;
|
|
270
273
|
fillVerificationDiagnostic = await collectFillVerificationDiagnostic({ commandTokens: prepared.commandTokens, cwd, forceValueVerification: electronRecordForCommand !== undefined, namespace: prepared.executionPlan.namespace, refSnapshot: fillRefSnapshot, sessionName: prepared.executionPlan.sessionName, signal });
|
|
@@ -307,6 +310,9 @@ export async function processBrowserOutput(input) {
|
|
|
307
310
|
if (!tabUpdate.applied && succeeded)
|
|
308
311
|
sessionPageState.markPinning(sessionStateKey, "drift");
|
|
309
312
|
}
|
|
313
|
+
else if (processResult.agentBrowserStarted && (resultingPageState.pageUrlUnknown || resultingPageState.pageTargetMayHaveChanged)) {
|
|
314
|
+
sessionPageState.markTabTargetUnknown({ sessionName: sessionStateKey, update: sessionPageStateUpdate });
|
|
315
|
+
}
|
|
310
316
|
const refSnapshot = prepared.executionPlan.commandInfo.command === "batch" ? batchRefSnapshotState?.snapshot : succeeded ? prepared.executionPlan.commandInfo.command === "snapshot" ? extractRefSnapshotFromData(presentationEnvelope?.data) : prepared.resolvedSemanticActionRefSnapshot ?? overlayBlockerDiagnostic?.snapshot : undefined;
|
|
311
317
|
if (refSnapshot) {
|
|
312
318
|
const refUpdate = sessionPageState.applyRefSnapshot({ fallbackTarget: currentSessionTabTarget, sessionName: sessionStateKey, snapshot: refSnapshot, update: sessionPageStateUpdate });
|
|
@@ -331,19 +337,58 @@ export async function processBrowserOutput(input) {
|
|
|
331
337
|
const policyBlockedFreshManagedSession = allowedDomainsViolation !== undefined && prepared.sessionMode === "fresh" && prepared.executionPlan.managedSessionName === prepared.executionPlan.sessionName;
|
|
332
338
|
const postLaunchBatchFailure = !succeeded && processSucceeded && parseSucceeded && prepared.sessionMode === "fresh" && prepared.executionPlan.commandInfo.command === "batch" && batchStartedManagedBrowser(presentationEnvelope?.data);
|
|
333
339
|
const postLaunchTimeoutWithPage = !succeeded && processResult.timedOut && prepared.sessionMode === "fresh" && prepared.executionPlan.commandInfo.command === "batch" && timeoutPartialProgress?.liveUrlRecovered === true;
|
|
334
|
-
const
|
|
340
|
+
const failedFreshSessionMayHaveStarted = !succeeded
|
|
341
|
+
&& (processResult.agentBrowserStarted || (!processResult.aborted && processResult.spawnError === undefined))
|
|
342
|
+
&& prepared.sessionMode === "fresh"
|
|
343
|
+
&& prepared.executionPlan.managedSessionName === prepared.executionPlan.sessionName;
|
|
344
|
+
const failedFreshDaemon = failedFreshSessionMayHaveStarted && prepared.executionPlan.sessionName
|
|
345
|
+
? await inspectManagedSessionDaemon({
|
|
346
|
+
cwd,
|
|
347
|
+
namespace: prepared.executionPlan.namespace,
|
|
348
|
+
sessionName: prepared.executionPlan.sessionName,
|
|
349
|
+
timeoutMs: Math.min(implicitSessionCloseTimeoutMs, 2_000),
|
|
350
|
+
})
|
|
351
|
+
: undefined;
|
|
352
|
+
if (failedFreshDaemon?.status === "active") {
|
|
353
|
+
state.managedSessionRestoreState.recordDaemonRestoreKey(prepared.executionPlan.sessionName, prepared.executionPlan.namespace, failedFreshDaemon.restoreKey);
|
|
354
|
+
}
|
|
355
|
+
// Only a confirmed inactive daemon proves that a started fresh command did not establish browser ownership.
|
|
356
|
+
const postLaunchFreshFailure = failedFreshDaemon !== undefined && failedFreshDaemon.status !== "inactive";
|
|
357
|
+
const managedTransitionSucceeded = succeeded || policyBlockedFreshManagedSession || postLaunchBatchFailure || postLaunchTimeoutWithPage || postLaunchFreshFailure;
|
|
335
358
|
const managedSessionState = resolveManagedSessionState({ command: prepared.executionPlan.commandInfo.command, managedSessionName: managedCloseSessionName, managedSessionNamespace: prepared.executionPlan.namespace, priorActive: priorManagedSessionActive, priorNamespace: priorManagedSessionNamespace, priorSessionName: priorManagedSessionName, succeeded: managedTransitionSucceeded });
|
|
359
|
+
if (!managedTransitionSucceeded && prepared.sessionMode === "fresh" && prepared.executionPlan.managedSessionName) {
|
|
360
|
+
state.managedSessionRestoreState.clear(prepared.executionPlan.managedSessionName, prepared.executionPlan.namespace);
|
|
361
|
+
}
|
|
336
362
|
const replacedManagedSessionName = managedSessionState.replacedSessionName;
|
|
337
363
|
managedSessionActive = managedSessionState.active;
|
|
338
364
|
managedSessionName = managedSessionState.sessionName;
|
|
339
365
|
managedSessionNamespace = managedSessionState.namespace;
|
|
366
|
+
const executionTargetsManagedSession = prepared.executionPlan.sessionName
|
|
367
|
+
&& getAgentBrowserSessionIdentityKey(prepared.executionPlan.sessionName, prepared.executionPlan.namespace)
|
|
368
|
+
=== getAgentBrowserSessionIdentityKey(managedSessionName, managedSessionNamespace);
|
|
369
|
+
if (!managedSessionActive) {
|
|
370
|
+
managedSessionCompatibilityWorkaround = undefined;
|
|
371
|
+
}
|
|
372
|
+
else if (managedTransitionSucceeded && executionTargetsManagedSession) {
|
|
373
|
+
managedSessionCompatibilityWorkaround = prepared.compatibilityWorkaround;
|
|
374
|
+
}
|
|
340
375
|
if (commandClosesSession && succeeded && managedCloseSessionName === priorManagedSessionName && !managedSessionActive) {
|
|
376
|
+
const daemonRestoreKey = state.managedSessionRestoreState.getDaemonRestoreKey(managedCloseSessionName, priorManagedSessionNamespace);
|
|
377
|
+
const ownedRestoreKey = !state.managedSessionRestoreState.isDisabled(managedCloseSessionName, priorManagedSessionNamespace)
|
|
378
|
+
&& isManagedSessionRestoreKey(daemonRestoreKey) ? daemonRestoreKey : null;
|
|
379
|
+
state.managedSessionRestoreState.clear(managedCloseSessionName, priorManagedSessionNamespace);
|
|
380
|
+
pruneOwnedManagedSessionRestoreSnapshots({
|
|
381
|
+
cwd,
|
|
382
|
+
namespace: priorManagedSessionNamespace,
|
|
383
|
+
restoreKey: ownedRestoreKey,
|
|
384
|
+
statePath: rawCloseStatePath,
|
|
385
|
+
});
|
|
341
386
|
freshSessionOrdinal += 1;
|
|
342
387
|
managedSessionName = createFreshSessionName(state.managedSessionBaseName, state.ephemeralSessionSeed, freshSessionOrdinal);
|
|
343
388
|
managedSessionNamespace = undefined;
|
|
344
389
|
}
|
|
345
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 });
|
|
346
|
-
if (prepared.executionPlan.managedSessionName &&
|
|
391
|
+
if (prepared.executionPlan.managedSessionName && managedTransitionSucceeded && managedSessionActive) {
|
|
347
392
|
managedSessionCwd = cwd;
|
|
348
393
|
managedSessionNamespace = prepared.executionPlan.namespace;
|
|
349
394
|
}
|
|
@@ -360,7 +405,7 @@ export async function processBrowserOutput(input) {
|
|
|
360
405
|
networkRoutesBySession = new Map(networkRoutesBySession);
|
|
361
406
|
networkRoutesBySession.delete(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
362
407
|
sessionPageState.clearSession(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
363
|
-
const replacedCloseError = await closeManagedSession({ cwd: priorManagedSessionCwd, namespace: priorManagedSessionNamespace, sessionName: replacedManagedSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
408
|
+
const replacedCloseError = await closeManagedSession({ cwd: priorManagedSessionCwd, namespace: priorManagedSessionNamespace, restoreState: state.managedSessionRestoreState, sessionName: replacedManagedSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
364
409
|
if (!replacedCloseError)
|
|
365
410
|
state.closedManagedSessionNames.add(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
366
411
|
}
|
|
@@ -369,23 +414,61 @@ export async function processBrowserOutput(input) {
|
|
|
369
414
|
let electronHandoff = prepared.electronHandoff;
|
|
370
415
|
if (prepared.electronLaunch) {
|
|
371
416
|
if (succeeded && prepared.executionPlan.sessionName) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
417
|
+
const electronSessionName = prepared.executionPlan.sessionName;
|
|
418
|
+
const electronSessionStateKey = sessionStateKey ?? electronSessionName;
|
|
419
|
+
electronLaunchRecord = { ...prepared.electronLaunch.record, sessionName: electronSessionName };
|
|
375
420
|
const electronHandoffMode = prepared.compiledElectron?.action === "launch" ? prepared.compiledElectron.handoff : "connect";
|
|
376
421
|
try {
|
|
377
|
-
electronHandoff = await collectElectronHandoff({ cwd, handoff: electronHandoffMode, namespace: prepared.executionPlan.namespace, sessionName:
|
|
422
|
+
electronHandoff = await collectElectronHandoff({ cwd, handoff: electronHandoffMode, namespace: prepared.executionPlan.namespace, sessionName: electronSessionName, signal });
|
|
378
423
|
}
|
|
379
424
|
catch (error) {
|
|
380
|
-
electronHandoff = {
|
|
425
|
+
electronHandoff = {
|
|
426
|
+
error: error instanceof Error ? error.message : String(error),
|
|
427
|
+
failureCategory: signal?.aborted ? "aborted" : "upstream-error",
|
|
428
|
+
handoff: electronHandoffMode,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
if (electronHandoff.error) {
|
|
432
|
+
succeeded = false;
|
|
433
|
+
presentationEnvelope = { error: electronHandoff.error, success: false };
|
|
434
|
+
const closeError = await closeManagedSession({ cwd, namespace: prepared.executionPlan.namespace, policyLock: prepared.managedSessionPolicyLock, restoreState: state.managedSessionRestoreState, sessionName: electronSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
435
|
+
electronFailedConnectCleanup = await cleanupElectronLaunchResources({ child: prepared.electronLaunch.child, record: electronLaunchRecord, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
436
|
+
electronLaunchRecord = electronFailedConnectCleanup.record;
|
|
437
|
+
if (electronFailedConnectCleanup.partial) {
|
|
438
|
+
electronLaunchRecords.set(electronLaunchRecord.launchId, electronLaunchRecord);
|
|
439
|
+
electronChildProcesses.set(electronLaunchRecord.launchId, prepared.electronLaunch.child);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
electronLaunchRecords.delete(electronLaunchRecord.launchId);
|
|
443
|
+
electronChildProcesses.delete(electronLaunchRecord.launchId);
|
|
444
|
+
}
|
|
445
|
+
if (!closeError) {
|
|
446
|
+
state.closedManagedSessionNames.add(electronSessionStateKey);
|
|
447
|
+
allowedDomainsBySession = new Map(allowedDomainsBySession);
|
|
448
|
+
allowedDomainsBySession.delete(electronSessionStateKey);
|
|
449
|
+
networkRoutesBySession = new Map(networkRoutesBySession);
|
|
450
|
+
networkRoutesBySession.delete(electronSessionStateKey);
|
|
451
|
+
sessionPageState.clearSession(electronSessionStateKey);
|
|
452
|
+
if (managedSessionName === electronSessionName && managedSessionNamespace === prepared.executionPlan.namespace) {
|
|
453
|
+
managedSessionActive = false;
|
|
454
|
+
freshSessionOrdinal += 1;
|
|
455
|
+
managedSessionName = createFreshSessionName(state.managedSessionBaseName, state.ephemeralSessionSeed, freshSessionOrdinal);
|
|
456
|
+
managedSessionNamespace = undefined;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
managedSessionOutcome = buildManagedSessionOutcome({ activeAfter: managedSessionActive, activeBefore: priorManagedSessionActive, attemptedSessionName: electronSessionName, command: prepared.executionPlan.commandInfo.command, currentSessionName: managedSessionName, currentSessionNamespace: managedSessionNamespace, previousSessionName: priorManagedSessionName, replacedSessionName: replacedManagedSessionName, replacedSessionNamespace: priorManagedSessionNamespace, sessionMode: prepared.sessionMode, succeeded: false });
|
|
381
460
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
461
|
+
else {
|
|
462
|
+
electronLaunchRecords.set(electronLaunchRecord.launchId, electronLaunchRecord);
|
|
463
|
+
electronChildProcesses.set(electronLaunchRecord.launchId, prepared.electronLaunch.child);
|
|
464
|
+
if (electronHandoff.refSnapshot) {
|
|
465
|
+
const refUpdate = sessionPageState.applyRefSnapshot({ sessionName: electronSessionStateKey, snapshot: electronHandoff.refSnapshot, update: sessionPageStateUpdate });
|
|
466
|
+
currentRefSnapshot = refUpdate.refSnapshot;
|
|
467
|
+
currentRefSnapshotInvalidation = refUpdate.refSnapshotInvalidation;
|
|
468
|
+
if (electronHandoff.refSnapshot.target) {
|
|
469
|
+
const targetUpdate = sessionPageState.applyTabTarget({ sessionName: electronSessionStateKey, target: electronHandoff.refSnapshot.target, update: sessionPageStateUpdate });
|
|
470
|
+
currentSessionTabTarget = targetUpdate.tabTarget;
|
|
471
|
+
}
|
|
389
472
|
}
|
|
390
473
|
}
|
|
391
474
|
}
|
|
@@ -402,6 +485,10 @@ export async function processBrowserOutput(input) {
|
|
|
402
485
|
presentationEnvelope = { ...presentationEnvelope, error: redactClipboardPermissionErrorValue(prepared.executionPlan.commandInfo, presentationEnvelope.error, clipboardWritePayloadCandidates) };
|
|
403
486
|
}
|
|
404
487
|
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 });
|
|
488
|
+
if (observedPageValidationError)
|
|
489
|
+
presentation.failureCategory = "validation-error";
|
|
490
|
+
if (electronHandoff?.error && electronHandoff.failureCategory)
|
|
491
|
+
presentation.failureCategory = electronHandoff.failureCategory;
|
|
405
492
|
networkRoutesBySession = applyBatchNetworkRouteState({ data: presentationEnvelope?.data, routesBySession: networkRoutesBySession, sessionName: sessionStateKey, succeeded });
|
|
406
493
|
if (presentation.resultCategory === "failure" && succeeded) {
|
|
407
494
|
succeeded = false;
|
|
@@ -494,8 +581,12 @@ export async function processBrowserOutput(input) {
|
|
|
494
581
|
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 });
|
|
495
582
|
currentRefSnapshot = finalRecoveryState.currentRefSnapshot;
|
|
496
583
|
currentRefSnapshotInvalidation = finalRecoveryState.currentRefSnapshotInvalidation;
|
|
497
|
-
const
|
|
498
|
-
|
|
584
|
+
const authoritativePageState = sessionStateKey ? sessionPageState.get(sessionStateKey) : undefined;
|
|
585
|
+
if (sessionStateKey)
|
|
586
|
+
currentSessionTabTarget = authoritativePageState?.tabTarget;
|
|
587
|
+
const currentSessionTabTargetUnknown = authoritativePageState?.tabTargetUnknown === true ? true : undefined;
|
|
588
|
+
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, 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 });
|
|
589
|
+
const statePatch = { allowedDomainsBySession, artifactManifest, freshSessionOrdinal, managedSessionActive, managedSessionCompatibilityWorkaround, managedSessionCwd, managedSessionName, managedSessionNamespace, networkRoutesBySession };
|
|
499
590
|
return { result, statePatch };
|
|
500
591
|
}
|
|
501
592
|
finally {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
2
|
import { runAgentBrowserProcess } from "../../process.js";
|
|
3
|
-
import { buildAgentBrowserNextActions,
|
|
3
|
+
import { buildAgentBrowserNextActions, parseAgentBrowserEnvelope } from "../../results.js";
|
|
4
4
|
import { buildNextToolAction, withOptionalNamespaceArgs, withOptionalSessionArgs } from "../../results/next-actions.js";
|
|
5
5
|
import { getSessionPageStateKey, isAboutBlankUrl, normalizeComparableUrl, normalizeSessionTabTarget, targetsMatch, } from "../../session-page-state.js";
|
|
6
6
|
import { isCloseCommand, isElectronPostCommandHealthCommand, isNavigationObservableCommandName, isRefGuardedCommand, isRefInvalidatingBatchCommand, isSessionTabPinningExcludedCommand, isSessionTabPostCommandCorrectionExcludedCommand, } from "../../command-taxonomy.js";
|
|
7
|
-
import { chooseOpenResultTabCorrection
|
|
7
|
+
import { chooseOpenResultTabCorrection } from "../../runtime.js";
|
|
8
8
|
import { isRecord } from "../../parsing.js";
|
|
9
9
|
import { parseUserBatchStdin } from "../batch-stdin.js";
|
|
10
10
|
export const NAVIGATION_SUMMARY_EVAL = `({ title: document.title, url: location.href })`;
|
|
@@ -19,6 +19,8 @@ export function applyBrowserRunStatePatch(state, patch) {
|
|
|
19
19
|
state.freshSessionOrdinal = patch.freshSessionOrdinal;
|
|
20
20
|
if (patch.managedSessionActive !== undefined)
|
|
21
21
|
state.managedSessionActive = patch.managedSessionActive;
|
|
22
|
+
if ("managedSessionCompatibilityWorkaround" in patch)
|
|
23
|
+
state.managedSessionCompatibilityWorkaround = patch.managedSessionCompatibilityWorkaround;
|
|
22
24
|
if (patch.managedSessionCwd !== undefined)
|
|
23
25
|
state.managedSessionCwd = patch.managedSessionCwd;
|
|
24
26
|
if (patch.managedSessionName !== undefined)
|
|
@@ -29,8 +31,17 @@ export function applyBrowserRunStatePatch(state, patch) {
|
|
|
29
31
|
state.networkRoutesBySession = patch.networkRoutesBySession;
|
|
30
32
|
}
|
|
31
33
|
export const getSessionContextKey = getSessionPageStateKey;
|
|
32
|
-
export function buildSessionDetailFields(sessionName, usedImplicitSession, namespace) {
|
|
33
|
-
return {
|
|
34
|
+
export function buildSessionDetailFields(sessionName, usedImplicitSession, namespace, managedSessionRestoreDisabled = false) {
|
|
35
|
+
return {
|
|
36
|
+
...(namespace ? { namespace } : {}),
|
|
37
|
+
...(sessionName
|
|
38
|
+
? {
|
|
39
|
+
sessionName,
|
|
40
|
+
usedImplicitSession,
|
|
41
|
+
...(managedSessionRestoreDisabled ? { managedSessionRestoreDisabled: true } : {}),
|
|
42
|
+
}
|
|
43
|
+
: {}),
|
|
44
|
+
};
|
|
34
45
|
}
|
|
35
46
|
export function buildManagedSessionOutcome(options) {
|
|
36
47
|
const { activeAfter, activeBefore, attemptedSessionName, command, currentSessionName, currentSessionNamespace, previousSessionName, replacedSessionName, replacedSessionNamespace, sessionMode, succeeded } = options;
|
|
@@ -224,6 +235,8 @@ export function extractNavigationSummaryFromData(data) {
|
|
|
224
235
|
return title || url ? { title, url } : undefined;
|
|
225
236
|
}
|
|
226
237
|
export function shouldCaptureNavigationSummary(command, data) {
|
|
238
|
+
if (command === "eval")
|
|
239
|
+
return true;
|
|
227
240
|
if (isRecord(data) && typeof data.clicked === "string" && !data.clicked.startsWith("@") && !data.clicked.startsWith("ref=") && typeof data.href !== "string")
|
|
228
241
|
return false;
|
|
229
242
|
return (isNavigationObservableCommandName(command) &&
|
|
@@ -481,11 +494,12 @@ export function unwrapPinnedSessionBatchEnvelope(options) {
|
|
|
481
494
|
};
|
|
482
495
|
}
|
|
483
496
|
export async function runSessionCommandData(options) {
|
|
484
|
-
const { args, cwd, namespace, sessionName, signal, stdin, timeoutMs } = options;
|
|
497
|
+
const { allowManagedSessionTarget, args, cwd, namespace, pinNamespace, sessionName, signal, stdin, throwOnFailure, timeoutMs } = options;
|
|
485
498
|
if (!sessionName)
|
|
486
499
|
return undefined;
|
|
487
500
|
const processResult = await runAgentBrowserProcess({
|
|
488
|
-
|
|
501
|
+
allowManagedSessionTarget,
|
|
502
|
+
args: ["--json", ...(namespace !== undefined || pinNamespace ? ["--namespace", namespace ?? ""] : []), "--session", sessionName, ...args],
|
|
489
503
|
cwd,
|
|
490
504
|
signal,
|
|
491
505
|
stdin,
|
|
@@ -493,6 +507,14 @@ export async function runSessionCommandData(options) {
|
|
|
493
507
|
});
|
|
494
508
|
try {
|
|
495
509
|
if (processResult.aborted || processResult.spawnError || processResult.exitCode !== 0) {
|
|
510
|
+
if (throwOnFailure) {
|
|
511
|
+
const reason = processResult.aborted
|
|
512
|
+
? "command was aborted"
|
|
513
|
+
: processResult.spawnError
|
|
514
|
+
? "process could not start"
|
|
515
|
+
: `process exited with code ${processResult.exitCode}`;
|
|
516
|
+
throw new Error(`agent-browser ${reason}`);
|
|
517
|
+
}
|
|
496
518
|
return undefined;
|
|
497
519
|
}
|
|
498
520
|
const parsed = await parseAgentBrowserEnvelope({
|
|
@@ -500,6 +522,8 @@ export async function runSessionCommandData(options) {
|
|
|
500
522
|
stdoutPath: processResult.stdoutSpillPath,
|
|
501
523
|
});
|
|
502
524
|
if (parsed.parseError || parsed.envelope?.success === false) {
|
|
525
|
+
if (throwOnFailure)
|
|
526
|
+
throw new Error(parsed.parseError ? "agent-browser returned invalid structured output" : "agent-browser reported failure");
|
|
503
527
|
return undefined;
|
|
504
528
|
}
|
|
505
529
|
return parsed.envelope?.data;
|
|
@@ -728,38 +752,4 @@ export function buildElectronRefFreshnessDiagnostic(options) {
|
|
|
728
752
|
export function formatElectronRefFreshnessText(diagnostic) {
|
|
729
753
|
return diagnostic?.summary;
|
|
730
754
|
}
|
|
731
|
-
export async function closeManagedSession(options) {
|
|
732
|
-
const controller = new AbortController();
|
|
733
|
-
const timer = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
734
|
-
let stdoutSpillPath;
|
|
735
|
-
const closeArgs = [...(options.namespace ? ["--namespace", options.namespace] : []), "--session", options.sessionName, "close"];
|
|
736
|
-
try {
|
|
737
|
-
const processResult = await runAgentBrowserProcess({
|
|
738
|
-
args: closeArgs,
|
|
739
|
-
cwd: options.cwd,
|
|
740
|
-
signal: controller.signal,
|
|
741
|
-
});
|
|
742
|
-
stdoutSpillPath = processResult.stdoutSpillPath;
|
|
743
|
-
return getAgentBrowserErrorText({
|
|
744
|
-
aborted: processResult.aborted,
|
|
745
|
-
command: "close",
|
|
746
|
-
effectiveArgs: redactInvocationArgs(closeArgs),
|
|
747
|
-
exitCode: processResult.exitCode,
|
|
748
|
-
plainTextInspection: false,
|
|
749
|
-
spawnError: processResult.spawnError,
|
|
750
|
-
stderr: processResult.stderr,
|
|
751
|
-
timedOut: processResult.timedOut,
|
|
752
|
-
timeoutMs: processResult.timeoutMs,
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
catch (error) {
|
|
756
|
-
return error instanceof Error ? error.message : String(error);
|
|
757
|
-
}
|
|
758
|
-
finally {
|
|
759
|
-
clearTimeout(timer);
|
|
760
|
-
if (stdoutSpillPath) {
|
|
761
|
-
await rm(stdoutSpillPath, { force: true }).catch(() => undefined);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
755
|
export { extractBatchResultCommand };
|