pi-agent-browser-native 0.2.76 → 0.3.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 +33 -0
- package/README.md +10 -19
- package/dist/extensions/agent-browser/index.js +221 -23
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +0 -5
- package/dist/extensions/agent-browser/lib/argv-grammar.js +4 -5
- package/dist/extensions/agent-browser/lib/command-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +0 -6
- 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 +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -6
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +0 -5
- 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 +4 -5
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -2
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +0 -3
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +30 -13
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +0 -5
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +1 -7
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +0 -5
- package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +41 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +4 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +20 -11
- 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 +42 -20
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +40 -9
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +17 -1
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +42 -18
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +4 -1
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +42 -1
- package/dist/extensions/agent-browser/lib/parsing.js +0 -7
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +4 -1
- package/dist/extensions/agent-browser/lib/playbook.js +4 -10
- package/dist/extensions/agent-browser/lib/process-identity.js +0 -5
- package/dist/extensions/agent-browser/lib/process.js +12 -9
- package/dist/extensions/agent-browser/lib/prompt-policy.js +171 -27
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +1 -8
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +6 -7
- package/dist/extensions/agent-browser/lib/results/categories.js +1 -8
- 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 +0 -30
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +1 -7
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +0 -5
- 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 +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation.js +0 -5
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +0 -7
- 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 +4 -14
- package/dist/extensions/agent-browser/lib/session-page-state.js +0 -7
- 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/web-search.js +0 -5
- package/docs/ARCHITECTURE.md +17 -16
- package/docs/COMMAND_REFERENCE.md +10 -9
- package/docs/ELECTRON.md +3 -19
- package/docs/RELEASE.md +5 -17
- package/docs/REQUIREMENTS.md +4 -4
- package/docs/SUPPORT_MATRIX.md +18 -17
- package/docs/TOOL_CONTRACT.md +23 -19
- package/docs/platform-smoke.md +1 -1
- package/package.json +5 -6
- package/scripts/doctor.mjs +1 -1
- 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,26 @@
|
|
|
1
1
|
import { copyFile, mkdir } from "node:fs/promises";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { getBooleanFlagValue, isUpstreamEnvFlagEnabled } from "../../argv-grammar.js";
|
|
3
4
|
import { isCloseCommand } from "../../command-taxonomy.js";
|
|
4
5
|
import { cleanupElectronLaunchResources } from "../../electron/cleanup.js";
|
|
5
6
|
import { launchElectronApp } from "../../electron/launch.js";
|
|
6
7
|
import { pathExists } from "../../fs-utils.js";
|
|
7
|
-
import { getCompiledSemanticActionSessionPrefix } from "../../input-modes.js";
|
|
8
|
+
import { getCompiledSemanticActionSessionPrefix } from "../../input-modes/semantic-action.js";
|
|
8
9
|
import { tryDirectAnchorDownload } from "./prepare/direct-anchor-download.js";
|
|
9
10
|
import { tryNetworkRequestsPageFilter } from "./prepare/network-page-filter.js";
|
|
10
11
|
import { tryContainerScroll, tryPageScrollTo } from "./prepare/scroll-shims.js";
|
|
11
12
|
import { trySnapshotFilter } from "./prepare/snapshot-filter.js";
|
|
12
13
|
import { commandTimeoutNeedsActivePageUrl, getCommandAwareProcessTimeoutMs } from "./prepare/wait-timeouts.js";
|
|
13
|
-
import { getPersistentSessionArtifactStore } from "./session-
|
|
14
|
-
import { buildAgentBrowserResultCategoryDetails } from "../../results.js";
|
|
14
|
+
import { getPersistentSessionArtifactStore } from "./session-state.js";
|
|
15
|
+
import { buildAgentBrowserResultCategoryDetails } from "../../results/categories.js";
|
|
15
16
|
import { applyNamespaceToNextActions } from "../../results/next-actions.js";
|
|
16
17
|
import { buildSessionAwareStaleRefNextActions, buildSessionTabRecoveryNextActions } from "../../results/recovery-next-actions.js";
|
|
17
18
|
import { resolveVisibleRefActionFromSnapshot } from "../../results/selector-recovery.js";
|
|
18
19
|
import { extractRefSnapshotFromData } from "../../session-page-state.js";
|
|
19
20
|
import { buildExecutionPlan, createFreshSessionName, extractCommandTokens, getDefaultHeadlessCompatUserAgent, redactInvocationArgs, } from "../../runtime.js";
|
|
20
|
-
import { buildOwnedManagedSessionRestoreContext, canonicalizeOwnedManagedSessionCloseArgs, withOwnedManagedSessionContext, } from "../../managed-session-restore.js";
|
|
21
|
+
import { buildOwnedManagedSessionRestoreContext, canonicalizeOwnedManagedSessionCloseArgs, resolveExplicitAutosaveInterval, withOwnedManagedSessionContext, } from "../../managed-session-restore.js";
|
|
21
22
|
import { getCallerOwnedSessionLivePageVerificationRequirement, getManagedSessionStateAccessValidationError, getManagedSessionTargetAccessValidationError, } from "../../managed-session-state-policy.js";
|
|
22
|
-
import { acquireOwnedManagedSessionDaemonPolicy } from "./managed-session-daemon-policy.js";
|
|
23
|
+
import { acquireOwnedManagedSessionDaemonPolicy, getRunningHeadedAutosavePolicyChangeError } from "./managed-session-daemon-policy.js";
|
|
23
24
|
import { applyOpenResultTabCorrection, buildManagedSessionOutcome, buildPinnedBatchPlan, buildSessionDetailFields, buildStaleRefPreflight, getSessionContextKey, extractStringResultField, collectAnySessionTabSelection, collectSessionTabSelection, getGuardedRefUsage, getTraceOwnerGuardMessage, runSessionCommandData, shouldPinSessionTabForCommand, } from "./session-state.js";
|
|
24
25
|
import { parseBatchStdinJsonArray } from "../batch-stdin.js";
|
|
25
26
|
import { buildElectronHostFailureResult, getElectronLaunchFailureCategory, redactRecoveryHint } from "./final-result.js";
|
|
@@ -397,11 +398,27 @@ export async function prepareBrowserRun(options) {
|
|
|
397
398
|
if (!executionPlan.validationError && managedStateAccessError)
|
|
398
399
|
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: managedStateAccessError };
|
|
399
400
|
const recordedOwnedSession = ownedSessionKey ? state.ownedManagedSessions.get(ownedSessionKey) : undefined;
|
|
401
|
+
const targetsCurrentManagedSession = state.managedSessionActive
|
|
402
|
+
&& ownedSessionKey === getSessionContextKey(state.managedSessionName, state.managedSessionNamespace);
|
|
403
|
+
const retainedHeadedAutosaveDisabled = recordedOwnedSession?.headedManagedAutosaveDisabled === true
|
|
404
|
+
|| (targetsCurrentManagedSession && state.managedSessionHeadedAutosaveDisabled === true);
|
|
405
|
+
const retainedHeadedAutosaveInterval = recordedOwnedSession?.headedManagedAutosaveInterval
|
|
406
|
+
?? (targetsCurrentManagedSession ? state.managedSessionHeadedAutosaveInterval : undefined);
|
|
407
|
+
const explicitAutosaveInterval = resolveExplicitAutosaveInterval(process.env.AGENT_BROWSER_AUTOSAVE_INTERVAL_MS);
|
|
408
|
+
const autosavePolicyChangeError = getRunningHeadedAutosavePolicyChangeError(retainedHeadedAutosaveInterval, isCloseCommand(executionPlan.commandInfo.command));
|
|
409
|
+
if (!executionPlan.validationError && autosavePolicyChangeError) {
|
|
410
|
+
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: autosavePolicyChangeError };
|
|
411
|
+
}
|
|
412
|
+
const headedLaunch = getBooleanFlagValue(executionPlan.effectiveArgs, "--headed") ?? isUpstreamEnvFlagEnabled(process.env.AGENT_BROWSER_HEADED);
|
|
413
|
+
const headedManagedAutosaveDisabled = retainedHeadedAutosaveDisabled || (explicitAutosaveInterval === undefined && headedLaunch);
|
|
414
|
+
const headedManagedAutosaveInterval = retainedHeadedAutosaveInterval ?? (headedLaunch ? explicitAutosaveInterval ?? "0" : undefined);
|
|
400
415
|
const ownedManagedSession = buildOwnedManagedSessionRestoreContext({
|
|
401
416
|
args: executionPlan.effectiveArgs,
|
|
402
417
|
cwd: recordedOwnedSession?.cwd ?? cwd,
|
|
403
418
|
currentManagedSessionName: state.managedSessionName,
|
|
404
419
|
currentManagedSessionNamespace: state.managedSessionNamespace,
|
|
420
|
+
headedManagedAutosaveDisabled,
|
|
421
|
+
headedManagedAutosaveInterval,
|
|
405
422
|
managedSessionName: executionPlan.managedSessionName,
|
|
406
423
|
namespace: executionPlan.namespace,
|
|
407
424
|
recordedOwnedSession,
|
|
@@ -451,13 +468,18 @@ export async function prepareBrowserRun(options) {
|
|
|
451
468
|
const priorRefSnapshotState = priorSessionPageState.refSnapshot;
|
|
452
469
|
const priorRefSnapshotInvalidation = priorSessionPageState.refSnapshotInvalidation;
|
|
453
470
|
let semanticActionVisibleRefResolution;
|
|
454
|
-
let
|
|
471
|
+
let livePageVerified = false;
|
|
455
472
|
const isCallerOwnedExplicitSession = () => executionPlan.sessionName !== undefined
|
|
456
473
|
&& executionPlan.usedImplicitSession === false
|
|
457
474
|
&& ownedManagedSession === undefined;
|
|
458
|
-
const
|
|
459
|
-
|
|
475
|
+
const requiresLivePageVerification = () => isCallerOwnedExplicitSession() || options.preserveAttachedBrowserSession === true;
|
|
476
|
+
const verifyLivePage = async (request) => {
|
|
477
|
+
if (!request.requirement || !executionPlan.sessionName)
|
|
478
|
+
return;
|
|
479
|
+
if (options.establishAttachedBrowserSession) {
|
|
480
|
+
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: request.requirement };
|
|
460
481
|
return;
|
|
482
|
+
}
|
|
461
483
|
let liveUrl;
|
|
462
484
|
try {
|
|
463
485
|
const liveUrlData = await runSessionCommandData({
|
|
@@ -475,27 +497,27 @@ export async function prepareBrowserRun(options) {
|
|
|
475
497
|
throw signal.reason ?? error;
|
|
476
498
|
}
|
|
477
499
|
if (liveUrl === undefined) {
|
|
478
|
-
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError:
|
|
500
|
+
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: request.requirement };
|
|
479
501
|
return;
|
|
480
502
|
}
|
|
481
503
|
const livePageValidationError = getManagedSessionStateAccessValidationError({
|
|
482
|
-
args:
|
|
504
|
+
args: request.args,
|
|
483
505
|
currentPageUrl: liveUrl,
|
|
484
506
|
cwd,
|
|
485
507
|
pageUrlUnknown: false,
|
|
486
|
-
stdin:
|
|
508
|
+
stdin: request.stdin,
|
|
487
509
|
});
|
|
488
510
|
if (livePageValidationError) {
|
|
489
511
|
executionPlan = { ...executionPlan, recoveryHint: undefined, validationError: livePageValidationError };
|
|
490
512
|
return;
|
|
491
513
|
}
|
|
492
|
-
|
|
514
|
+
livePageVerified = true;
|
|
493
515
|
priorSessionTabTarget ??= { url: liveUrl };
|
|
494
516
|
priorSessionTabTargetUnknown = undefined;
|
|
495
517
|
};
|
|
496
518
|
const mayResolveSemanticVisibleRef = executionPlan.managedSessionName !== freshSessionName && canResolveSemanticVisibleRef(compiledSemanticAction);
|
|
497
|
-
if (!executionPlan.validationError && mayResolveSemanticVisibleRef &&
|
|
498
|
-
await
|
|
519
|
+
if (!executionPlan.validationError && mayResolveSemanticVisibleRef && requiresLivePageVerification()) {
|
|
520
|
+
await verifyLivePage({
|
|
499
521
|
args: ["snapshot", "-i"],
|
|
500
522
|
requirement: getCallerOwnedSessionLivePageVerificationRequirement({ args: ["snapshot", "-i"], cwd }),
|
|
501
523
|
});
|
|
@@ -530,21 +552,21 @@ export async function prepareBrowserRun(options) {
|
|
|
530
552
|
refSnapshotInvalidation: resolvedSemanticActionRefSnapshot ? undefined : priorRefSnapshotInvalidation,
|
|
531
553
|
stdin: runtimeToolStdin,
|
|
532
554
|
});
|
|
533
|
-
const
|
|
555
|
+
const livePageAccessEligible = !executionPlan.validationError
|
|
534
556
|
&& preLiveStaleRefPreflight === undefined
|
|
535
557
|
&& validateStdinCommandContract({ command: executionPlan.commandInfo.command, commandTokens, stdin: runtimeToolStdin }) === undefined
|
|
536
|
-
&&
|
|
537
|
-
const
|
|
538
|
-
&& !
|
|
558
|
+
&& requiresLivePageVerification();
|
|
559
|
+
const livePageRequirement = livePageAccessEligible
|
|
560
|
+
&& !livePageVerified
|
|
539
561
|
? getCallerOwnedSessionLivePageVerificationRequirement({
|
|
540
562
|
args: executionPlan.effectiveArgs,
|
|
541
563
|
cwd,
|
|
542
564
|
stdin: runtimeToolStdin,
|
|
543
565
|
})
|
|
544
566
|
: undefined;
|
|
545
|
-
await
|
|
567
|
+
await verifyLivePage({
|
|
546
568
|
args: executionPlan.effectiveArgs,
|
|
547
|
-
requirement:
|
|
569
|
+
requirement: livePageRequirement,
|
|
548
570
|
stdin: runtimeToolStdin,
|
|
549
571
|
});
|
|
550
572
|
const redactedEffectiveArgs = redactInvocationArgs(executionPlan.effectiveArgs);
|
|
@@ -5,8 +5,11 @@ import { OPEN_RESULT_TAB_CORRECTION_FLAGS } from "../../launch-scoped-flags.js";
|
|
|
5
5
|
import { cleanupElectronLaunchResources, inspectElectronLaunchStatus } from "../../electron/cleanup.js";
|
|
6
6
|
import { getAllowedDomainsViolation, parseAllowedDomainsPolicyFromArgs } from "../../navigation-policy.js";
|
|
7
7
|
import { getManagedSessionResultingPageState, getObservedBrowserPageValidationError, managedSessionCommandRequiresLivePageVerification } from "../../managed-session-state-policy.js";
|
|
8
|
-
import { analyzeNetworkSourceLookupResults,
|
|
9
|
-
import {
|
|
8
|
+
import { analyzeNetworkSourceLookupResults, analyzeSourceLookupResults, redactNetworkSourceLookupAnalysis } from "../../input-modes/lookups.js";
|
|
9
|
+
import { analyzeQaPresetResults, analyzeQaPresetTimeout, buildQaCompactPassText, extractQaPageContext } from "../../input-modes/job.js";
|
|
10
|
+
import { applyNetworkRouteRecords, buildNetworkRouteDiagnostics } from "../../results/network-routes.js";
|
|
11
|
+
import { buildToolPresentation } from "../../results/presentation.js";
|
|
12
|
+
import { getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results/envelope.js";
|
|
10
13
|
import { getClipboardWritePayloadCandidates, redactClipboardPermissionEcho, redactClipboardPermissionErrorValue } from "../../results/presentation/errors.js";
|
|
11
14
|
import { shouldCaptureSemanticActionNavigationSummary } from "../../results/presentation/semantic-action.js";
|
|
12
15
|
import { commandExplicitlyTargetsAboutBlank, deriveSessionTabTarget, extractLatestRefSnapshotStateFromBatchResults, extractRefSnapshotFromData, extractSessionTabTargetFromBatchResults, extractSessionTabTargetFromCommandData, isAboutBlankSessionTabTarget, normalizeSessionTabTarget, } from "../../session-page-state.js";
|
|
@@ -19,7 +22,7 @@ import { applyOpenResultTabCorrection, buildAboutBlankRecoveryHint, buildAboutBl
|
|
|
19
22
|
import { collectClickDispatchDiagnostic } from "./click-dispatch.js";
|
|
20
23
|
import { buildScrollNoopDiagnostic, collectComboboxFocusDiagnostic, collectElectronBroadGetTextScopeDiagnostics, collectElectronHandoff, collectFillVerificationDiagnostic, collectNavigationSummary, collectOverlayBlockerDiagnostic, collectQaAttachedTarget, collectSnapshotOverlayBlockerDiagnostic, collectRecordingDependencyWarning, collectScrollPositionSnapshot, collectSelectorTextVisibilityDiagnostics, collectTimeoutPartialProgress, sleepMs, formatQaAttachedTargetText, getArtifactCleanupGuidance, getEvalResultWarning, getEvalStdinHint, getSourceLookupElectronContext, } from "./diagnostics.js";
|
|
21
24
|
import { repairScreenshotData } from "./prepare.js";
|
|
22
|
-
import { getPersistentSessionArtifactStore } from "./session-
|
|
25
|
+
import { getPersistentSessionArtifactStore } from "./session-state.js";
|
|
23
26
|
import { buildFinalAgentBrowserToolResult, buildRedactedPresentationContent, buildWrapperRecoveryHint, prepareFinalResultRecoveryState, redactExactSensitiveValue, } from "./final-result.js";
|
|
24
27
|
async function repairScreenshotArtifact(options) {
|
|
25
28
|
const { cwd, envelope, request } = options;
|
|
@@ -105,6 +108,8 @@ export async function processBrowserOutput(input) {
|
|
|
105
108
|
let freshSessionOrdinal = state.freshSessionOrdinal;
|
|
106
109
|
let managedSessionActive = state.managedSessionActive;
|
|
107
110
|
let managedSessionCompatibilityWorkaround = state.managedSessionCompatibilityWorkaround;
|
|
111
|
+
let managedSessionHeadedAutosaveDisabled = state.managedSessionHeadedAutosaveDisabled === true;
|
|
112
|
+
let managedSessionHeadedAutosaveInterval = state.managedSessionHeadedAutosaveInterval;
|
|
108
113
|
let managedSessionCwd = state.managedSessionCwd;
|
|
109
114
|
let managedSessionName = state.managedSessionName;
|
|
110
115
|
let managedSessionNamespace = state.managedSessionNamespace;
|
|
@@ -327,6 +332,7 @@ export async function processBrowserOutput(input) {
|
|
|
327
332
|
}
|
|
328
333
|
const priorManagedSessionActive = managedSessionActive;
|
|
329
334
|
const priorManagedSessionCwd = managedSessionCwd;
|
|
335
|
+
const priorManagedSessionHeadedAutosaveInterval = managedSessionHeadedAutosaveInterval;
|
|
330
336
|
const priorManagedSessionName = managedSessionName;
|
|
331
337
|
const priorManagedSessionNamespace = managedSessionNamespace;
|
|
332
338
|
const commandClosesSession = isCloseCommand(prepared.executionPlan.commandInfo.command);
|
|
@@ -344,6 +350,7 @@ export async function processBrowserOutput(input) {
|
|
|
344
350
|
const failedFreshDaemon = failedFreshSessionMayHaveStarted && prepared.executionPlan.sessionName
|
|
345
351
|
? await inspectManagedSessionDaemon({
|
|
346
352
|
cwd,
|
|
353
|
+
headedManagedAutosaveInterval: prepared.ownedManagedSessionContext?.headedManagedAutosaveInterval,
|
|
347
354
|
namespace: prepared.executionPlan.namespace,
|
|
348
355
|
sessionName: prepared.executionPlan.sessionName,
|
|
349
356
|
timeoutMs: Math.min(implicitSessionCloseTimeoutMs, 2_000),
|
|
@@ -368,9 +375,13 @@ export async function processBrowserOutput(input) {
|
|
|
368
375
|
=== getAgentBrowserSessionIdentityKey(managedSessionName, managedSessionNamespace);
|
|
369
376
|
if (!managedSessionActive) {
|
|
370
377
|
managedSessionCompatibilityWorkaround = undefined;
|
|
378
|
+
managedSessionHeadedAutosaveDisabled = false;
|
|
379
|
+
managedSessionHeadedAutosaveInterval = undefined;
|
|
371
380
|
}
|
|
372
381
|
else if (managedTransitionSucceeded && executionTargetsManagedSession) {
|
|
373
382
|
managedSessionCompatibilityWorkaround = prepared.compatibilityWorkaround;
|
|
383
|
+
managedSessionHeadedAutosaveDisabled = prepared.ownedManagedSessionContext?.headedManagedAutosaveDisabled === true;
|
|
384
|
+
managedSessionHeadedAutosaveInterval = prepared.ownedManagedSessionContext?.headedManagedAutosaveInterval;
|
|
374
385
|
}
|
|
375
386
|
if (commandClosesSession && succeeded && managedCloseSessionName === priorManagedSessionName && !managedSessionActive) {
|
|
376
387
|
const daemonRestoreKey = state.managedSessionRestoreState.getDaemonRestoreKey(managedCloseSessionName, priorManagedSessionNamespace);
|
|
@@ -405,9 +416,21 @@ export async function processBrowserOutput(input) {
|
|
|
405
416
|
networkRoutesBySession = new Map(networkRoutesBySession);
|
|
406
417
|
networkRoutesBySession.delete(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
407
418
|
sessionPageState.clearSession(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
419
|
+
const replacedSessionKey = replacedSessionStateKey ?? replacedManagedSessionName;
|
|
420
|
+
const replacedCloseError = await closeManagedSession({ cwd: priorManagedSessionCwd, headedManagedAutosaveInterval: priorManagedSessionHeadedAutosaveInterval, namespace: priorManagedSessionNamespace, preserveAttachedBrowserSession: state.attachedSessionKeys.has(replacedSessionKey), restoreState: state.managedSessionRestoreState, sessionName: replacedManagedSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
421
|
+
if (managedSessionOutcome) {
|
|
422
|
+
managedSessionOutcome = {
|
|
423
|
+
...managedSessionOutcome,
|
|
424
|
+
replacedSessionClosed: !replacedCloseError,
|
|
425
|
+
summary: replacedCloseError
|
|
426
|
+
? `${managedSessionOutcome.summary} Previous session ${replacedManagedSessionName} remains wrapper-owned because automatic close failed; retry an explicit close.`
|
|
427
|
+
: managedSessionOutcome.summary,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
if (!replacedCloseError) {
|
|
431
|
+
state.attachedSessionKeys.delete(replacedSessionKey);
|
|
432
|
+
state.closedManagedSessionNames.add(replacedSessionKey);
|
|
433
|
+
}
|
|
411
434
|
}
|
|
412
435
|
let electronLaunchRecord;
|
|
413
436
|
let electronFailedConnectCleanup = prepared.electronFailedConnectCleanup;
|
|
@@ -431,7 +454,7 @@ export async function processBrowserOutput(input) {
|
|
|
431
454
|
if (electronHandoff.error) {
|
|
432
455
|
succeeded = false;
|
|
433
456
|
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 });
|
|
457
|
+
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 });
|
|
435
458
|
electronFailedConnectCleanup = await cleanupElectronLaunchResources({ child: prepared.electronLaunch.child, record: electronLaunchRecord, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
436
459
|
electronLaunchRecord = electronFailedConnectCleanup.record;
|
|
437
460
|
if (electronFailedConnectCleanup.partial) {
|
|
@@ -585,8 +608,16 @@ export async function processBrowserOutput(input) {
|
|
|
585
608
|
if (sessionStateKey)
|
|
586
609
|
currentSessionTabTarget = authoritativePageState?.tabTarget;
|
|
587
610
|
const currentSessionTabTargetUnknown = authoritativePageState?.tabTargetUnknown === true ? true : undefined;
|
|
588
|
-
const
|
|
589
|
-
|
|
611
|
+
const resultRetainsPreparedManagedSession = !managedSessionOutcome || (managedSessionOutcome.activeAfter
|
|
612
|
+
&& managedSessionOutcome.attemptedSessionName === managedSessionOutcome.currentSessionName);
|
|
613
|
+
const resultHeadedManagedAutosaveDisabled = prepared.ownedManagedSessionContext?.headedManagedAutosaveDisabled === true
|
|
614
|
+
&& resultRetainsPreparedManagedSession
|
|
615
|
+
&& !(commandClosesSession && succeeded);
|
|
616
|
+
const resultHeadedManagedAutosaveInterval = resultRetainsPreparedManagedSession && !(commandClosesSession && succeeded)
|
|
617
|
+
? prepared.ownedManagedSessionContext?.headedManagedAutosaveInterval
|
|
618
|
+
: undefined;
|
|
619
|
+
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 });
|
|
620
|
+
const statePatch = { allowedDomainsBySession, artifactManifest, freshSessionOrdinal, managedSessionActive, managedSessionCompatibilityWorkaround, managedSessionHeadedAutosaveDisabled, managedSessionHeadedAutosaveInterval, managedSessionCwd, managedSessionName, managedSessionNamespace, networkRoutesBySession };
|
|
590
621
|
return { result, statePatch };
|
|
591
622
|
}
|
|
592
623
|
finally {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
2
|
import { runAgentBrowserProcess } from "../../process.js";
|
|
3
|
-
import { buildAgentBrowserNextActions
|
|
3
|
+
import { buildAgentBrowserNextActions } from "../../results/action-recommendations.js";
|
|
4
|
+
import { parseAgentBrowserEnvelope } from "../../results/envelope.js";
|
|
4
5
|
import { buildNextToolAction, withOptionalNamespaceArgs, withOptionalSessionArgs } from "../../results/next-actions.js";
|
|
5
6
|
import { getSessionPageStateKey, isAboutBlankUrl, normalizeComparableUrl, normalizeSessionTabTarget, targetsMatch, } from "../../session-page-state.js";
|
|
6
7
|
import { isCloseCommand, isElectronPostCommandHealthCommand, isNavigationObservableCommandName, isRefGuardedCommand, isRefInvalidatingBatchCommand, isSessionTabPinningExcludedCommand, isSessionTabPostCommandCorrectionExcludedCommand, } from "../../command-taxonomy.js";
|
|
@@ -21,6 +22,10 @@ export function applyBrowserRunStatePatch(state, patch) {
|
|
|
21
22
|
state.managedSessionActive = patch.managedSessionActive;
|
|
22
23
|
if ("managedSessionCompatibilityWorkaround" in patch)
|
|
23
24
|
state.managedSessionCompatibilityWorkaround = patch.managedSessionCompatibilityWorkaround;
|
|
25
|
+
if (patch.managedSessionHeadedAutosaveDisabled !== undefined)
|
|
26
|
+
state.managedSessionHeadedAutosaveDisabled = patch.managedSessionHeadedAutosaveDisabled;
|
|
27
|
+
if ("managedSessionHeadedAutosaveInterval" in patch)
|
|
28
|
+
state.managedSessionHeadedAutosaveInterval = patch.managedSessionHeadedAutosaveInterval;
|
|
24
29
|
if (patch.managedSessionCwd !== undefined)
|
|
25
30
|
state.managedSessionCwd = patch.managedSessionCwd;
|
|
26
31
|
if (patch.managedSessionName !== undefined)
|
|
@@ -136,6 +141,12 @@ function formatManagedSessionOutcomeRecoveryGuidance(outcome) {
|
|
|
136
141
|
export function formatManagedSessionOutcomeText(outcome) {
|
|
137
142
|
if (!outcome)
|
|
138
143
|
return undefined;
|
|
144
|
+
if (outcome.replacedSessionClosed === false) {
|
|
145
|
+
const cleanupWarning = "Cleanup warning: Automatic close of the previous wrapper-managed session failed, so it remains wrapper-owned. Use details.managedSessionOutcome for its exact identity and close it explicitly when safe.";
|
|
146
|
+
return outcome.succeeded
|
|
147
|
+
? ["Managed session outcome: The fresh browser became current.", cleanupWarning].join("\n")
|
|
148
|
+
: [formatManagedSessionOutcomeHeadline(outcome), formatManagedSessionOutcomeRecoveryGuidance(outcome), cleanupWarning].join("\n");
|
|
149
|
+
}
|
|
139
150
|
if (outcome.status === "closed" && outcome.succeeded) {
|
|
140
151
|
return [
|
|
141
152
|
"Managed session outcome: The current wrapper-managed browser session was closed.",
|
|
@@ -753,3 +764,8 @@ export function formatElectronRefFreshnessText(diagnostic) {
|
|
|
753
764
|
return diagnostic?.summary;
|
|
754
765
|
}
|
|
755
766
|
export { extractBatchResultCommand };
|
|
767
|
+
export function getPersistentSessionArtifactStore(ctx) {
|
|
768
|
+
const sessionDir = typeof ctx.sessionManager.getSessionDir === "function" ? ctx.sessionManager.getSessionDir() : undefined;
|
|
769
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
770
|
+
return sessionDir && sessionId ? { sessionDir, sessionId } : undefined;
|
|
771
|
+
}
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Own wrapper-side Electron host orchestration for agent_browser structured electron input.
|
|
3
|
-
* Responsibilities: Discover Electron apps, inspect/probe/cleanup wrapper-tracked Electron launches, and build Pi-facing Electron host results.
|
|
4
|
-
* Scope: Electron host actions that do not spawn the main upstream browser command; generic agent_browser execution stays in browser-run.
|
|
5
|
-
*/
|
|
6
1
|
import { cleanupElectronLaunchResources, inspectElectronLaunchStatus } from "../../electron/cleanup.js";
|
|
7
2
|
import { discoverElectronApps } from "../../electron/discovery.js";
|
|
8
|
-
import { boundElectronProbeString } from "../../electron/
|
|
3
|
+
import { boundElectronProbeString } from "../../electron/cdp.js";
|
|
9
4
|
import { buildOwnedManagedSessionRestoreContext, withOwnedManagedSessionContext, } from "../../managed-session-restore.js";
|
|
10
5
|
import { getManagedSessionStateAccessValidationError } from "../../managed-session-state-policy.js";
|
|
11
6
|
import { isRecord } from "../../parsing.js";
|
|
12
|
-
import {
|
|
7
|
+
import { withAttachedBrowserSessionContext } from "../../process.js";
|
|
8
|
+
import { buildAgentBrowserNextActions } from "../../results/action-recommendations.js";
|
|
9
|
+
import { buildAgentBrowserResultCategoryDetails } from "../../results/categories.js";
|
|
13
10
|
import { appendUniqueAgentBrowserNextActions } from "../../results/next-actions.js";
|
|
14
11
|
import { extractRefSnapshotFromData, getSessionPageStateKey, isAboutBlankUrl, normalizeSessionTabTarget } from "../../session-page-state.js";
|
|
15
12
|
import { redactSensitiveText } from "../../runtime.js";
|
|
16
13
|
import { collectElectronManagedSessionTarget } from "../browser-run/diagnostics.js";
|
|
17
14
|
import { buildElectronHostFailureResult, formatElectronTargetLines, redactToolDetails } from "../browser-run/final-result.js";
|
|
18
|
-
import { acquireOwnedManagedSessionDaemonPolicy, closeManagedSession } from "../browser-run/managed-session-daemon-policy.js";
|
|
15
|
+
import { acquireOwnedManagedSessionDaemonPolicy, closeManagedSession, getRunningHeadedAutosavePolicyChangeError } from "../browser-run/managed-session-daemon-policy.js";
|
|
19
16
|
import { buildElectronIdentifiers, buildElectronMismatchNextActions, buildElectronSessionMismatch, extractStringResultField, findElectronLaunchRecordForSession, formatElectronSessionMismatchText, getActiveElectronRecords, getLiveElectronRendererTargets, runSessionCommandData, } from "../browser-run/session-state.js";
|
|
20
17
|
const ELECTRON_PROFILE_ISOLATION_NOTE = "Profile note: electron.launch starts an isolated temporary profile; it does not reuse the app's normal signed-in profile or attach to an already-running authenticated app.";
|
|
21
18
|
const ELECTRON_EXISTING_AUTH_GUIDANCE = "For already-authenticated desktop app content, do not stop here: if host tools are allowed and the app is not running, launch the normal app with --remote-debugging-port=<port>, verify the port, then run agent_browser connect <port>; if it is already running without a debug port, ask before relaunching it.";
|
|
@@ -397,9 +394,14 @@ function getElectronProbeSummary(probe) {
|
|
|
397
394
|
class ElectronManagedSessionPolicyError extends Error {
|
|
398
395
|
}
|
|
399
396
|
async function withOwnedElectronManagedSessionPolicy(options, run) {
|
|
397
|
+
const autosavePolicyChangeError = getRunningHeadedAutosavePolicyChangeError(options.headedManagedAutosaveInterval);
|
|
398
|
+
if (autosavePolicyChangeError)
|
|
399
|
+
throw new ElectronManagedSessionPolicyError(autosavePolicyChangeError);
|
|
400
400
|
const context = buildOwnedManagedSessionRestoreContext({
|
|
401
401
|
args: ["--namespace", options.namespace ?? "", "--session", options.sessionName, ...options.args],
|
|
402
402
|
cwd: options.cwd,
|
|
403
|
+
headedManagedAutosaveDisabled: options.headedManagedAutosaveDisabled,
|
|
404
|
+
headedManagedAutosaveInterval: options.headedManagedAutosaveInterval,
|
|
403
405
|
managedSessionName: options.sessionName,
|
|
404
406
|
namespace: options.namespace,
|
|
405
407
|
restoreState: options.restoreState,
|
|
@@ -580,6 +582,8 @@ function buildElectronProbeResult(options) {
|
|
|
580
582
|
},
|
|
581
583
|
nextActions: nextActions.length > 0 ? nextActions : undefined,
|
|
582
584
|
...buildAgentBrowserResultCategoryDetails({ args: [], succeeded: true }),
|
|
585
|
+
managedSessionHeadedAutosaveDisabled: options.headedManagedAutosaveDisabled === true ? true : undefined,
|
|
586
|
+
managedSessionHeadedAutosaveInterval: options.headedManagedAutosaveInterval,
|
|
583
587
|
namespace: options.namespace,
|
|
584
588
|
refSnapshot: options.probe.refSnapshot,
|
|
585
589
|
sessionName: options.probe.sessionName,
|
|
@@ -596,8 +600,10 @@ function buildElectronProbeResult(options) {
|
|
|
596
600
|
export async function cleanupTrackedElectronHostLaunches(options) {
|
|
597
601
|
const results = [];
|
|
598
602
|
for (const record of options.records) {
|
|
603
|
+
const sessionKey = getSessionPageStateKey(record.sessionName) ?? record.sessionName;
|
|
604
|
+
const managedSessionOwner = sessionKey ? options.ownedManagedSessions.get(sessionKey) : undefined;
|
|
599
605
|
const managedSessionCloseError = record.sessionName
|
|
600
|
-
? await closeManagedSession({ cwd: options.cwd, restoreState: options.managedSessionRestoreState, sessionName: record.sessionName, timeoutMs: options.timeoutMs })
|
|
606
|
+
? await closeManagedSession({ cwd: options.cwd, headedManagedAutosaveInterval: managedSessionOwner?.headedManagedAutosaveInterval, preserveAttachedBrowserSession: options.attachedSessionKeys.has(sessionKey ?? record.sessionName), restoreState: options.managedSessionRestoreState, sessionName: record.sessionName, timeoutMs: options.timeoutMs })
|
|
601
607
|
: undefined;
|
|
602
608
|
const managedSessionStep = record.sessionName
|
|
603
609
|
? managedSessionCloseError
|
|
@@ -640,7 +646,13 @@ export async function cleanupActiveElectronHostLaunches(options) {
|
|
|
640
646
|
: [];
|
|
641
647
|
}
|
|
642
648
|
export async function handleElectronHostInput(options) {
|
|
643
|
-
const
|
|
649
|
+
const currentSessionKey = getSessionPageStateKey(options.managedSessionName, options.managedSessionNamespace) ?? options.managedSessionName;
|
|
650
|
+
const preserveAttachedBrowserSession = options.attachedSessionKeys.has(currentSessionKey)
|
|
651
|
+
|| [...options.electronLaunchRecords.values()].some((record) => record.sessionName !== undefined && options.attachedSessionKeys.has(getSessionPageStateKey(record.sessionName) ?? record.sessionName));
|
|
652
|
+
return await withAttachedBrowserSessionContext(preserveAttachedBrowserSession, () => handleElectronHostInputInContext(options));
|
|
653
|
+
}
|
|
654
|
+
async function handleElectronHostInputInContext(options) {
|
|
655
|
+
const { attachedSessionKeys, compiledElectron, cwd, electronChildProcesses, electronLaunchRecords, implicitSessionCloseTimeoutMs, managedSessionActive, managedSessionName, managedSessionNamespace, managedSessionRestoreState, ownedManagedSessions, redactedCompiledElectron, sessionPageState, signal, } = options;
|
|
644
656
|
if (compiledElectron?.action === "list") {
|
|
645
657
|
try {
|
|
646
658
|
const discovery = await discoverElectronApps({ maxResults: compiledElectron.maxResults, query: compiledElectron.query });
|
|
@@ -658,13 +670,18 @@ export async function handleElectronHostInput(options) {
|
|
|
658
670
|
const statuses = await Promise.all(records.map((record) => inspectElectronLaunchStatus(record)));
|
|
659
671
|
const managedSessions = await Promise.all(records
|
|
660
672
|
.filter((record) => typeof record.sessionName === "string")
|
|
661
|
-
.map((record) =>
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
673
|
+
.map((record) => {
|
|
674
|
+
const sessionKey = getSessionPageStateKey(record.sessionName) ?? record.sessionName;
|
|
675
|
+
return collectOwnedElectronManagedSessionTarget({
|
|
676
|
+
cwd,
|
|
677
|
+
headedManagedAutosaveDisabled: ownedManagedSessions.get(sessionKey)?.headedManagedAutosaveDisabled,
|
|
678
|
+
headedManagedAutosaveInterval: ownedManagedSessions.get(sessionKey)?.headedManagedAutosaveInterval,
|
|
679
|
+
restoreState: managedSessionRestoreState,
|
|
680
|
+
sessionName: record.sessionName,
|
|
681
|
+
signal,
|
|
682
|
+
timeoutMs: compiledElectron.timeoutMs,
|
|
683
|
+
});
|
|
684
|
+
}));
|
|
668
685
|
const mismatches = managedSessions
|
|
669
686
|
.map((managedSession) => {
|
|
670
687
|
const record = records.find((candidate) => candidate.sessionName === managedSession.sessionName);
|
|
@@ -726,9 +743,14 @@ export async function handleElectronHostInput(options) {
|
|
|
726
743
|
});
|
|
727
744
|
if (fileAccessError)
|
|
728
745
|
throw new ElectronManagedSessionPolicyError(fileAccessError);
|
|
746
|
+
const managedSessionOwner = ownedManagedSessions.get(pageStateKey);
|
|
747
|
+
const headedManagedAutosaveDisabled = managedSessionOwner?.headedManagedAutosaveDisabled === true;
|
|
748
|
+
const headedManagedAutosaveInterval = managedSessionOwner?.headedManagedAutosaveInterval;
|
|
729
749
|
const probe = await withOwnedElectronManagedSessionPolicy({
|
|
730
750
|
args: ["snapshot", "-i"],
|
|
731
751
|
cwd,
|
|
752
|
+
headedManagedAutosaveDisabled,
|
|
753
|
+
headedManagedAutosaveInterval,
|
|
732
754
|
namespace: probeNamespace,
|
|
733
755
|
restoreState: managedSessionRestoreState,
|
|
734
756
|
sessionName: probeSessionName,
|
|
@@ -772,6 +794,8 @@ export async function handleElectronHostInput(options) {
|
|
|
772
794
|
}
|
|
773
795
|
return buildElectronProbeResult({
|
|
774
796
|
compiledElectron: redactedCompiledElectron ?? compiledElectron,
|
|
797
|
+
headedManagedAutosaveDisabled,
|
|
798
|
+
headedManagedAutosaveInterval,
|
|
775
799
|
mismatch: sessionMismatch,
|
|
776
800
|
namespace: probeNamespace,
|
|
777
801
|
probe,
|
|
@@ -794,7 +818,7 @@ export async function handleElectronHostInput(options) {
|
|
|
794
818
|
const selection = selectElectronRecords(compiledElectron, electronLaunchRecords);
|
|
795
819
|
if (selection.error)
|
|
796
820
|
return buildElectronHostFailureResult({ compiledElectron: redactedCompiledElectron ?? compiledElectron, errorText: selection.error, failureCategory: "validation-error" });
|
|
797
|
-
const cleanupResults = await cleanupTrackedElectronHostLaunches({ cwd, electronChildProcesses, electronLaunchRecords, managedSessionRestoreState, records: selection.records ?? [], timeoutMs: compiledElectron.timeoutMs ?? implicitSessionCloseTimeoutMs });
|
|
821
|
+
const cleanupResults = await cleanupTrackedElectronHostLaunches({ attachedSessionKeys, cwd, electronChildProcesses, electronLaunchRecords, managedSessionRestoreState, ownedManagedSessions, records: selection.records ?? [], timeoutMs: compiledElectron.timeoutMs ?? implicitSessionCloseTimeoutMs });
|
|
798
822
|
return buildElectronCleanupResult(redactedCompiledElectron ?? compiledElectron, cleanupResults);
|
|
799
823
|
}
|
|
800
824
|
return undefined;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { parseArgvDescriptor } from "../argv-descriptor.js";
|
|
2
2
|
import { validateToolArgs, redactInvocationArgs, redactSensitiveText } from "../runtime.js";
|
|
3
3
|
import { buildAgentBrowserResultCategoryDetails } from "../results/categories.js";
|
|
4
|
-
import { compileAgentBrowserElectron
|
|
4
|
+
import { compileAgentBrowserElectron } from "../input-modes/electron.js";
|
|
5
|
+
import { compileAgentBrowserJob, compileAgentBrowserQaPreset } from "../input-modes/job.js";
|
|
6
|
+
import { compileAgentBrowserNetworkSourceLookup, compileAgentBrowserSourceLookup, redactNetworkSourceLookupArgs, redactNetworkSourceLookupUrl } from "../input-modes/lookups.js";
|
|
7
|
+
import { compileAgentBrowserSemanticAction } from "../input-modes/semantic-action.js";
|
|
5
8
|
function redactCompiledElectron(compiled) {
|
|
6
9
|
if (!compiled)
|
|
7
10
|
return undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, realpath, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
3
|
import { getAgentBrowserStoragePathValidationError } from "../managed-session-state-policy.js";
|
|
4
4
|
import { isRecord } from "../parsing.js";
|
|
@@ -31,6 +31,33 @@ function appendOutputFileNotice(result, message) {
|
|
|
31
31
|
export function getAgentBrowserOutputPathValidationError(outputPath, cwd) {
|
|
32
32
|
return outputPath ? getAgentBrowserStoragePathValidationError(normalizeRequestedOutputPath(outputPath), cwd) : undefined;
|
|
33
33
|
}
|
|
34
|
+
function getArtifactPaths(result, cwd) {
|
|
35
|
+
const details = isRecord(result.details) ? result.details : undefined;
|
|
36
|
+
if (!details || !Array.isArray(details.artifacts))
|
|
37
|
+
return [];
|
|
38
|
+
return details.artifacts.flatMap((artifact) => {
|
|
39
|
+
if (!isRecord(artifact))
|
|
40
|
+
return [];
|
|
41
|
+
const path = typeof artifact.absolutePath === "string" ? artifact.absolutePath : typeof artifact.path === "string" ? artifact.path : undefined;
|
|
42
|
+
return path ? [isAbsolute(path) ? path : resolve(cwd, path)] : [];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function pathsReferToSameFile(left, right) {
|
|
46
|
+
if (resolve(left) === resolve(right))
|
|
47
|
+
return true;
|
|
48
|
+
try {
|
|
49
|
+
if (await realpath(left) === await realpath(right))
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
try {
|
|
54
|
+
const [leftStat, rightStat] = await Promise.all([stat(left), stat(right)]);
|
|
55
|
+
return leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
34
61
|
export async function applyAgentBrowserOutputPath(options) {
|
|
35
62
|
if (!options.outputPath)
|
|
36
63
|
return options.result;
|
|
@@ -47,6 +74,20 @@ export async function applyAgentBrowserOutputPath(options) {
|
|
|
47
74
|
const requestedPath = normalizeRequestedOutputPath(options.outputPath);
|
|
48
75
|
const absolutePath = isAbsolute(requestedPath) ? requestedPath : resolve(options.cwd, requestedPath);
|
|
49
76
|
const payload = getOutputPayload(options.result);
|
|
77
|
+
for (const artifactPath of getArtifactPaths(options.result, options.cwd)) {
|
|
78
|
+
if (!await pathsReferToSameFile(absolutePath, artifactPath))
|
|
79
|
+
continue;
|
|
80
|
+
const message = "outputPath resolves to the same file as a browser artifact destination; choose a separate outputPath or omit it. The browser artifact was preserved.";
|
|
81
|
+
const outputFile = { absolutePath, error: message, path: requestedPath, source: payload.source, status: "failed" };
|
|
82
|
+
const details = isRecord(options.result.details) ? { ...options.result.details } : {};
|
|
83
|
+
delete details.successCategory;
|
|
84
|
+
return {
|
|
85
|
+
...options.result,
|
|
86
|
+
content: appendOutputFileNotice(options.result, `Output file rejected: ${message}`),
|
|
87
|
+
details: { ...details, failureCategory: "validation-error", outputFile, resultCategory: "failure" },
|
|
88
|
+
isError: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
50
91
|
try {
|
|
51
92
|
const serialized = serializeOutputPayload(payload.value);
|
|
52
93
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Centralize low-level boundary parsing helpers shared by runtime planning, temp-artifact lifecycle, and result rendering.
|
|
3
|
-
* Responsibilities: Identify non-null object records and normalize positive-integer string configuration values.
|
|
4
|
-
* Scope: Tiny generic parsing predicates only; module-specific validation and error handling stay with their owning modules.
|
|
5
|
-
* Usage: Imported by agent-browser wrapper modules that parse untyped JSON, persisted state, or environment variables.
|
|
6
|
-
* Invariants/Assumptions: Arrays intentionally count as records to preserve existing object-boundary semantics, and positive integers must be safe base-10 integer strings greater than zero.
|
|
7
|
-
*/
|
|
8
1
|
export function isRecord(value) {
|
|
9
2
|
return typeof value === "object" && value !== null;
|
|
10
3
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getKeybindings, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
2
|
-
import { compileAgentBrowserElectron
|
|
2
|
+
import { compileAgentBrowserElectron } from "./input-modes/electron.js";
|
|
3
|
+
import { compileAgentBrowserJob, compileAgentBrowserQaPreset } from "./input-modes/job.js";
|
|
4
|
+
import { compileAgentBrowserNetworkSourceLookup, compileAgentBrowserSourceLookup } from "./input-modes/lookups.js";
|
|
5
|
+
import { compileAgentBrowserSemanticAction } from "./input-modes/semantic-action.js";
|
|
3
6
|
import { isRecord } from "./parsing.js";
|
|
4
7
|
import { redactInvocationArgs } from "./runtime.js";
|
|
5
8
|
const TUI_INVOCATION_PREVIEW_MAX_CHARS = 160;
|