pi-agent-browser-native 0.2.77 → 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 +27 -0
- package/README.md +8 -17
- package/dist/extensions/agent-browser/index.js +127 -19
- 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/managed-session-daemon-policy.js +17 -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 +22 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +35 -7
- 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 +35 -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 +3 -10
- package/dist/extensions/agent-browser/lib/process-identity.js +0 -5
- package/dist/extensions/agent-browser/lib/process.js +0 -7
- 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 +16 -16
- package/docs/COMMAND_REFERENCE.md +8 -7
- package/docs/ELECTRON.md +3 -19
- package/docs/RELEASE.md +5 -17
- package/docs/REQUIREMENTS.md +4 -4
- package/docs/SUPPORT_MATRIX.md +17 -17
- package/docs/TOOL_CONTRACT.md +18 -16
- 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
|
@@ -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);
|
|
@@ -406,7 +417,16 @@ export async function processBrowserOutput(input) {
|
|
|
406
417
|
networkRoutesBySession.delete(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
407
418
|
sessionPageState.clearSession(replacedSessionStateKey ?? replacedManagedSessionName);
|
|
408
419
|
const replacedSessionKey = replacedSessionStateKey ?? replacedManagedSessionName;
|
|
409
|
-
const replacedCloseError = await closeManagedSession({ cwd: priorManagedSessionCwd, namespace: priorManagedSessionNamespace, preserveAttachedBrowserSession: state.attachedSessionKeys.has(replacedSessionKey), restoreState: state.managedSessionRestoreState, sessionName: replacedManagedSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
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
|
+
}
|
|
410
430
|
if (!replacedCloseError) {
|
|
411
431
|
state.attachedSessionKeys.delete(replacedSessionKey);
|
|
412
432
|
state.closedManagedSessionNames.add(replacedSessionKey);
|
|
@@ -434,7 +454,7 @@ export async function processBrowserOutput(input) {
|
|
|
434
454
|
if (electronHandoff.error) {
|
|
435
455
|
succeeded = false;
|
|
436
456
|
presentationEnvelope = { error: electronHandoff.error, success: false };
|
|
437
|
-
const closeError = await closeManagedSession({ cwd, namespace: prepared.executionPlan.namespace, policyLock: prepared.managedSessionPolicyLock, preserveAttachedBrowserSession: input.preserveAttachedBrowserSession, restoreState: state.managedSessionRestoreState, sessionName: electronSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
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 });
|
|
438
458
|
electronFailedConnectCleanup = await cleanupElectronLaunchResources({ child: prepared.electronLaunch.child, record: electronLaunchRecord, timeoutMs: implicitSessionCloseTimeoutMs });
|
|
439
459
|
electronLaunchRecord = electronFailedConnectCleanup.record;
|
|
440
460
|
if (electronFailedConnectCleanup.partial) {
|
|
@@ -588,8 +608,16 @@ export async function processBrowserOutput(input) {
|
|
|
588
608
|
if (sessionStateKey)
|
|
589
609
|
currentSessionTabTarget = authoritativePageState?.tabTarget;
|
|
590
610
|
const currentSessionTabTargetUnknown = authoritativePageState?.tabTargetUnknown === true ? true : undefined;
|
|
591
|
-
const
|
|
592
|
-
|
|
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 };
|
|
593
621
|
return { result, statePatch };
|
|
594
622
|
}
|
|
595
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,22 +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
7
|
import { withAttachedBrowserSessionContext } from "../../process.js";
|
|
13
|
-
import { buildAgentBrowserNextActions
|
|
8
|
+
import { buildAgentBrowserNextActions } from "../../results/action-recommendations.js";
|
|
9
|
+
import { buildAgentBrowserResultCategoryDetails } from "../../results/categories.js";
|
|
14
10
|
import { appendUniqueAgentBrowserNextActions } from "../../results/next-actions.js";
|
|
15
11
|
import { extractRefSnapshotFromData, getSessionPageStateKey, isAboutBlankUrl, normalizeSessionTabTarget } from "../../session-page-state.js";
|
|
16
12
|
import { redactSensitiveText } from "../../runtime.js";
|
|
17
13
|
import { collectElectronManagedSessionTarget } from "../browser-run/diagnostics.js";
|
|
18
14
|
import { buildElectronHostFailureResult, formatElectronTargetLines, redactToolDetails } from "../browser-run/final-result.js";
|
|
19
|
-
import { acquireOwnedManagedSessionDaemonPolicy, closeManagedSession } from "../browser-run/managed-session-daemon-policy.js";
|
|
15
|
+
import { acquireOwnedManagedSessionDaemonPolicy, closeManagedSession, getRunningHeadedAutosavePolicyChangeError } from "../browser-run/managed-session-daemon-policy.js";
|
|
20
16
|
import { buildElectronIdentifiers, buildElectronMismatchNextActions, buildElectronSessionMismatch, extractStringResultField, findElectronLaunchRecordForSession, formatElectronSessionMismatchText, getActiveElectronRecords, getLiveElectronRendererTargets, runSessionCommandData, } from "../browser-run/session-state.js";
|
|
21
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.";
|
|
22
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.";
|
|
@@ -398,9 +394,14 @@ function getElectronProbeSummary(probe) {
|
|
|
398
394
|
class ElectronManagedSessionPolicyError extends Error {
|
|
399
395
|
}
|
|
400
396
|
async function withOwnedElectronManagedSessionPolicy(options, run) {
|
|
397
|
+
const autosavePolicyChangeError = getRunningHeadedAutosavePolicyChangeError(options.headedManagedAutosaveInterval);
|
|
398
|
+
if (autosavePolicyChangeError)
|
|
399
|
+
throw new ElectronManagedSessionPolicyError(autosavePolicyChangeError);
|
|
401
400
|
const context = buildOwnedManagedSessionRestoreContext({
|
|
402
401
|
args: ["--namespace", options.namespace ?? "", "--session", options.sessionName, ...options.args],
|
|
403
402
|
cwd: options.cwd,
|
|
403
|
+
headedManagedAutosaveDisabled: options.headedManagedAutosaveDisabled,
|
|
404
|
+
headedManagedAutosaveInterval: options.headedManagedAutosaveInterval,
|
|
404
405
|
managedSessionName: options.sessionName,
|
|
405
406
|
namespace: options.namespace,
|
|
406
407
|
restoreState: options.restoreState,
|
|
@@ -581,6 +582,8 @@ function buildElectronProbeResult(options) {
|
|
|
581
582
|
},
|
|
582
583
|
nextActions: nextActions.length > 0 ? nextActions : undefined,
|
|
583
584
|
...buildAgentBrowserResultCategoryDetails({ args: [], succeeded: true }),
|
|
585
|
+
managedSessionHeadedAutosaveDisabled: options.headedManagedAutosaveDisabled === true ? true : undefined,
|
|
586
|
+
managedSessionHeadedAutosaveInterval: options.headedManagedAutosaveInterval,
|
|
584
587
|
namespace: options.namespace,
|
|
585
588
|
refSnapshot: options.probe.refSnapshot,
|
|
586
589
|
sessionName: options.probe.sessionName,
|
|
@@ -597,8 +600,10 @@ function buildElectronProbeResult(options) {
|
|
|
597
600
|
export async function cleanupTrackedElectronHostLaunches(options) {
|
|
598
601
|
const results = [];
|
|
599
602
|
for (const record of options.records) {
|
|
603
|
+
const sessionKey = getSessionPageStateKey(record.sessionName) ?? record.sessionName;
|
|
604
|
+
const managedSessionOwner = sessionKey ? options.ownedManagedSessions.get(sessionKey) : undefined;
|
|
600
605
|
const managedSessionCloseError = record.sessionName
|
|
601
|
-
? await closeManagedSession({ cwd: options.cwd, preserveAttachedBrowserSession: options.attachedSessionKeys.has(
|
|
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 })
|
|
602
607
|
: undefined;
|
|
603
608
|
const managedSessionStep = record.sessionName
|
|
604
609
|
? managedSessionCloseError
|
|
@@ -647,7 +652,7 @@ export async function handleElectronHostInput(options) {
|
|
|
647
652
|
return await withAttachedBrowserSessionContext(preserveAttachedBrowserSession, () => handleElectronHostInputInContext(options));
|
|
648
653
|
}
|
|
649
654
|
async function handleElectronHostInputInContext(options) {
|
|
650
|
-
const { attachedSessionKeys, compiledElectron, cwd, electronChildProcesses, electronLaunchRecords, implicitSessionCloseTimeoutMs, managedSessionActive, managedSessionName, managedSessionNamespace, managedSessionRestoreState, redactedCompiledElectron, sessionPageState, signal, } = options;
|
|
655
|
+
const { attachedSessionKeys, compiledElectron, cwd, electronChildProcesses, electronLaunchRecords, implicitSessionCloseTimeoutMs, managedSessionActive, managedSessionName, managedSessionNamespace, managedSessionRestoreState, ownedManagedSessions, redactedCompiledElectron, sessionPageState, signal, } = options;
|
|
651
656
|
if (compiledElectron?.action === "list") {
|
|
652
657
|
try {
|
|
653
658
|
const discovery = await discoverElectronApps({ maxResults: compiledElectron.maxResults, query: compiledElectron.query });
|
|
@@ -665,13 +670,18 @@ async function handleElectronHostInputInContext(options) {
|
|
|
665
670
|
const statuses = await Promise.all(records.map((record) => inspectElectronLaunchStatus(record)));
|
|
666
671
|
const managedSessions = await Promise.all(records
|
|
667
672
|
.filter((record) => typeof record.sessionName === "string")
|
|
668
|
-
.map((record) =>
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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
|
+
}));
|
|
675
685
|
const mismatches = managedSessions
|
|
676
686
|
.map((managedSession) => {
|
|
677
687
|
const record = records.find((candidate) => candidate.sessionName === managedSession.sessionName);
|
|
@@ -733,9 +743,14 @@ async function handleElectronHostInputInContext(options) {
|
|
|
733
743
|
});
|
|
734
744
|
if (fileAccessError)
|
|
735
745
|
throw new ElectronManagedSessionPolicyError(fileAccessError);
|
|
746
|
+
const managedSessionOwner = ownedManagedSessions.get(pageStateKey);
|
|
747
|
+
const headedManagedAutosaveDisabled = managedSessionOwner?.headedManagedAutosaveDisabled === true;
|
|
748
|
+
const headedManagedAutosaveInterval = managedSessionOwner?.headedManagedAutosaveInterval;
|
|
736
749
|
const probe = await withOwnedElectronManagedSessionPolicy({
|
|
737
750
|
args: ["snapshot", "-i"],
|
|
738
751
|
cwd,
|
|
752
|
+
headedManagedAutosaveDisabled,
|
|
753
|
+
headedManagedAutosaveInterval,
|
|
739
754
|
namespace: probeNamespace,
|
|
740
755
|
restoreState: managedSessionRestoreState,
|
|
741
756
|
sessionName: probeSessionName,
|
|
@@ -779,6 +794,8 @@ async function handleElectronHostInputInContext(options) {
|
|
|
779
794
|
}
|
|
780
795
|
return buildElectronProbeResult({
|
|
781
796
|
compiledElectron: redactedCompiledElectron ?? compiledElectron,
|
|
797
|
+
headedManagedAutosaveDisabled,
|
|
798
|
+
headedManagedAutosaveInterval,
|
|
782
799
|
mismatch: sessionMismatch,
|
|
783
800
|
namespace: probeNamespace,
|
|
784
801
|
probe,
|
|
@@ -801,7 +818,7 @@ async function handleElectronHostInputInContext(options) {
|
|
|
801
818
|
const selection = selectElectronRecords(compiledElectron, electronLaunchRecords);
|
|
802
819
|
if (selection.error)
|
|
803
820
|
return buildElectronHostFailureResult({ compiledElectron: redactedCompiledElectron ?? compiledElectron, errorText: selection.error, failureCategory: "validation-error" });
|
|
804
|
-
const cleanupResults = await cleanupTrackedElectronHostLaunches({ attachedSessionKeys, 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 });
|
|
805
822
|
return buildElectronCleanupResult(redactedCompiledElectron ?? compiledElectron, cleanupResults);
|
|
806
823
|
}
|
|
807
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;
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import { LAUNCH_SCOPED_FLAG_LABEL } from "./launch-scoped-flags.js";
|
|
2
|
-
/**
|
|
3
|
-
* Purpose: Provide the canonical agent_browser operating playbook shared by runtime prompt metadata and generated documentation fragments.
|
|
4
|
-
* Responsibilities: Define stable guidance bullets, native tool-call examples, and wrapper-behavior notes without importing runtime/browser process code.
|
|
5
|
-
* Scope: Agent-facing documentation and prompt-guidance text only; command execution and wrapper state behavior live in runtime modules.
|
|
6
|
-
* Usage: Imported by the extension entrypoint for promptGuidelines and by the documentation drift-check script for generated Markdown blocks.
|
|
7
|
-
* Invariants/Assumptions: The native pi tool receives args after the agent-browser binary, stdin is only for batch/eval --stdin/auth save --password-stdin, and wrapper behavior documented here must match implemented behavior.
|
|
8
|
-
*/
|
|
9
2
|
export const PROJECT_RULE_PROMPT = "Project rule: when browser automation is needed, prefer the native `agent_browser` tool. Do not run direct `agent-browser` bash commands unless the user explicitly asks for a bash-oriented workflow or browser-integration debugging.";
|
|
10
3
|
export const TOOL_PROMPT_GUIDELINES_PREFIX = [
|
|
11
4
|
"Use agent_browser for real browser or live web content.",
|
|
@@ -21,7 +14,7 @@ export const QUICK_START_GUIDELINES = [
|
|
|
21
14
|
`Common advanced calls: { args: ["batch", "--bail"], stdin: "[[\"open\",\"https://example.com\"],[\"snapshot\",\"-i\"]]" }, { job: { steps: [{ action: "open", url: "https://example.com" }, { action: "assertText", text: "Example Domain" }, { action: "screenshot", path: ".dogfood/example.png" }] } }, { qa: { url: "https://example.com", expectedText: "Example Domain", screenshotPath: ".dogfood/qa-example.png" } } (example.com smoke only; elsewhere match exact visible text from snapshot -i), { electron: { action: "list", query: "code" } }, { electron: { action: "launch", appName: "Visual Studio Code", handoff: "snapshot" } }, { electron: { action: "probe" } }, { qa: { attached: true, expectedText: "Explorer" } }, { args: ["eval", "--stdin"], stdin: "document.title", outputPath: "logs/page-title.json" }, { args: ["auth", "save", "name", "--password-stdin"], stdin: "<password from user-approved secret source>" }, { args: ["--profile", "Default", "open", "https://example.com/account"], sessionMode: "fresh" }, and { args: ["open", "--enable", "react-devtools", "https://example.com"], sessionMode: "fresh" }. For app pages with a native dropdown, job steps can include { action: "select", selector: "#flavor", value: "chocolate" } before the dependent assertion; for locator-friendly pages, job click/fill steps can use semantic locator fields such as { action: "fill", locator: "role", role: "searchbox", name: "Search", text: "agent browser" }; for human-paced input, job type steps can use { action: "type", selector: "#prompt", text: "hello", delayMs: 20, press: "Enter" }; delayed typing is capped at 200 characters per step, and generated per-character rows are compacted in visible batch prose while full rows remain in details.batchSteps.`,
|
|
22
15
|
"Constrained job navigation is explicit only: click (and select/submit flows that may navigate) does not prove the next page loaded; add assertUrl and/or assertText after navigation-prone steps before screenshot or later interactions. Keep jobs short around navigation, click, and rerender boundaries on dynamic React/product apps; avoid a whole checkout in one job. If a long job times out and details.timeoutPartialProgress shows a mutating incomplete step, inspect current page state and continue with a shorter job or single action instead of blindly retrying the mutating step. Example: { job: { steps: [{ action: \"open\", url: \"https://shop.example/checkout\" }, { action: \"fill\", selector: \"#email\", text: \"user@example.com\" }, { action: \"click\", selector: \"#continue\" }, { action: \"assertUrl\", url: \"**/shipping\" }, { action: \"assertText\", text: \"Shipping address\" }, { action: \"screenshot\", path: \".dogfood/shipping.png\" }] } }. Top-level click may add pageChangeSummary hints, but job never auto-inserts post-click asserts.",
|
|
23
16
|
"High-value command reference: click <selector> --new-tab opens link-like targets in a new tab; select <selector> <value...> changes native dropdown values; wrapper-handled scroll <dir> [px|percent] and scroll to end/top target document scrolling before upstream fallback, while scroll <selector> <dir> [px|percent] targets nested scrollers; download <selector> <path> saves a file triggered by a click; read [url] returns agent-readable text (explicit URLs prefer markdown without launching Chrome; omit the URL for rendered active-tab DOM); get title/url need no selector; get text/html/value/count <selector> and get attr <selector> <name> read elements/page state (use body for whole-page text/html); screenshot [selector] [path] captures a page or element image; pdf <path> saves a PDF; tab list and tab <tab-id-or-label> inspect or recover the active tab; react tree, react inspect <fiberId>, react renders start/stop, and react suspense introspect React after --enable react-devtools; vitals [url] measures Core Web Vitals; pushstate <url> performs SPA navigation; tap <selector> and swipe <direction> [distance] support iOS/provider touch flows.",
|
|
24
|
-
"For artifact-producing commands, read the visible artifact block and details.artifactVerification before using files: check requested path, absolute path, existence, size bytes, artifact kind, optional mediaType, status, optional limitation, and verified/missing/pending/unverified counts. details.artifacts contains per-file metadata; record start rows are pending/openRecording until record stop writes the target. The wrapper creates parent directories for direct artifact paths and can save simple loopback HTTP(S) anchor downloads directly to the requested path before upstream download fallback. Browser close does not delete explicit saved files; if close reports details.artifactCleanup, use host file tools to remove paths listed in explicitArtifactPaths (when non-empty) after inspection. If close fails with details.promptGuard.reason=requested-artifacts-missing-before-close, save the exact required artifact path before closing. For annotated screenshots inside batch, put --annotate in top-level args (for example { args: [\"--annotate\", \"batch\"], stdin: \"[[\\\"screenshot\\\",\\\"/tmp/page.png\\\"]]\" }) rather than inside the screenshot step; if annotation labels crowd a dense page, use a scoped or non-annotated screenshot plus snapshot refs instead.",
|
|
17
|
+
"For artifact-producing commands, read the visible artifact block and details.artifactVerification before using files: check requested path, absolute path, existence, size bytes, artifact kind, optional mediaType, status, optional limitation, and verified/missing/pending/unverified counts. details.artifacts contains per-file metadata; record start rows are pending/openRecording until record stop writes the target. The wrapper creates parent directories for direct artifact paths and can save simple loopback HTTP(S) anchor downloads directly to the requested path before upstream download fallback. Browser close does not delete explicit saved files; if close reports details.artifactCleanup, use host file tools to remove paths listed in explicitArtifactPaths (when non-empty) after inspection. If close fails with details.promptGuard.reason=requested-artifacts-missing-before-close, save the exact required artifact path before closing. A bare inbound image/video path is not a requested output artifact and does not block close. For annotated screenshots inside batch, put --annotate in top-level args (for example { args: [\"--annotate\", \"batch\"], stdin: \"[[\\\"screenshot\\\",\\\"/tmp/page.png\\\"]]\" }) rather than inside the screenshot step; if annotation labels crowd a dense page, use a scoped or non-annotated screenshot plus snapshot refs instead.",
|
|
25
18
|
"When details.nextActions is present, prefer those exact native agent_browser follow-up payloads over prose guidance; they may include args, stdin, sessionMode, networkSourceLookup, safety notes, or artifactPath for saved files.",
|
|
26
19
|
];
|
|
27
20
|
export const WEB_SEARCH_PROMPT_GUIDELINE = "Use agent_browser_web_search for quick live search/URL discovery; prefer it over public search-engine forms that can hit anti-bot/CAPTCHA-gated pages. Use agent_browser after you have a target URL; one query, one follow-up max; stop on HTTP 429.";
|
|
@@ -44,7 +37,7 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
44
37
|
"For React introspection, launch the page with --enable react-devtools before first navigation, then use react tree, react inspect <fiberId>, sourceLookup candidates for local UI source hints, react renders start/stop, or react suspense; sourceLookup is experimental and reports confidence/evidence instead of guaranteed DOM-to-file mappings. For failed fetches and APIs, networkSourceLookup (experimental) correlates failed network requests with initiator metadata and bounded workspace URL literals—candidates only, not definitive blame. Use vitals [url] for Core Web Vitals and hydration timing, and pushstate <url> for client-side SPA navigation.",
|
|
45
38
|
"For first-navigation setup, use open without a URL plus network route --resource-type <csv>, cookies set --curl <file>, or --init-script/--enable before navigate/opening the target page.",
|
|
46
39
|
"For stateful browser context work, prefer purpose-specific page actions before dumping browser data: use auth save --password-stdin with the tool stdin field for credentials, auth list/show/delete/remove for local auth-profile maintenance, auth login when you need the browser to fill a saved profile, state save/load for portable test state, state list/show/rename and targeted `state clear <caller-owned-name>` for saved-state lifecycle cleanup (the native wrapper blocks broad clear/clean and managed targets), cookies get/set/clear and storage local|session only when the task needs those values, and expect cookie/storage/auth/state summaries to redact credential-like fields while allowing benign primitive storage values when useful for local QA.",
|
|
47
|
-
"Upstream restore sessions periodically autosave cookies and localStorage while the browser stays open, including page-driven background changes; AGENT_BROWSER_AUTOSAVE_INTERVAL_MS controls the interval (30000 by default; 0 disables periodic saves but keeps save-on-close), while the never value for --restore-save disables automatic saves for that restore session.",
|
|
40
|
+
"Upstream restore sessions periodically autosave cookies and localStorage while the browser stays open, including page-driven background changes; AGENT_BROWSER_AUTOSAVE_INTERVAL_MS controls the interval (30000 by default; 0 disables periodic saves but keeps save-on-close), while the never value for --restore-save disables automatic saves for that restore session. For wrapper-owned headed launches, the wrapper defaults the interval to 0 because upstream 0.33.2 collects multi-origin storage through visible temporary tabs, then records and reapplies the effective launch-time value to helpers and follow-ups so daemon configuration remains stable. Native close still saves, but direct window close can lose newer state because upstream exempts headed browsers from idle shutdown; set AGENT_BROWSER_AUTOSAVE_INTERVAL_MS before launch when periodic preservation matters, because changing it on a running wrapper-owned headed session requires close plus a fresh launch.",
|
|
48
41
|
"For batch chains that touch cookies, storage, auth, or other secret-bearing commands, use details.batchSteps for per-step artifacts, categories, spill paths, and full structured errors; top-level details.data on batch is only a compact redacted step matrix (success, argv-redacted command, redacted result or scrubbed error text) built from the same presentation rules as standalone calls.",
|
|
49
42
|
"For non-core families, pass current upstream commands through the native tool directly: network requests, network route <url>, network har start/stop [path], diff snapshot, diff screenshot --baseline <file>, diff url <u1> <u2>, trace start, trace stop [path], profiler start, profiler stop [path], record start <path>, record stop, console/errors [--clear], highlight <selector>, inspect, clipboard read, clipboard write <text>, clipboard copy/paste, stream enable/disable/status, dashboard start/stop, device list for iOS simulator inventory, and chat <message>. For compact network requests output, prefer details.nextActions for request detail, route-mock diagnostics, actionable failed-request networkSourceLookup, filtering, clearing the aggregate buffer before repro, or HAR capture follow-ups instead of guessing request-id syntax. Artifact-producing commands report details.artifacts and verification state; long-running starts such as stream, dashboard, trace/profiler, and record should be paired with the matching stop/disable command when the task is done; stream enable already-enabled outcomes are treated as idempotent success with status/disable follow-ups.",
|
|
50
43
|
"For Electron desktop apps, prefer top-level electron for wrapper-owned discovery, isolated launch, status, compact probe, and cleanup: list first, treat likely-sensitive annotations as hints rather than enforcement, launch with the default snapshot handoff unless handoff: \"tabs\" is the safer diagnostic starting point, use electron.probe or snapshot -i/qa.attached for current-session state, and always cleanup the returned launchId when done. electron.launch uses an isolated temporary profile; it does not reuse the app's normal signed-in profile or attach to an already-running authenticated app. For signed-in local app state, host-launch the normal app with --remote-debugging-port when appropriate, then use raw args connect <port|url>; after connect, run get url to verify the active target before page-content reads, inspect tab list, select the stable tab id such as tab t2, verify it again with get url, then run a condition wait or snapshot -i before using refs. close commands (`close`, `quit`, or `exit`) only close the browser/CDP session; leave manually launched app shutdown, profile cleanup, and explicit artifacts to the host owner.",
|
|
@@ -56,7 +49,7 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
56
49
|
"For downloads, prefer download <selector> <path> when an element click should save a file; simple loopback anchor downloads are saved to the requested path when the wrapper can resolve an HTTP(S) href. Do not rely on click alone when you need the downloaded file on disk.",
|
|
57
50
|
"On dashboards with nested scroll containers, verify scroll with a screenshot or fresh snapshot -i; if the viewport did not move, details.data.scrolled may be false/noMovement true and you should prefer scrollintoview <@ref> or target the actual scrollable region with scroll <selector> <dir> [px|percent]. For native selects, use select <selector> <value...> (or semanticAction/job select) instead of clicking option refs; for custom comboboxes, a click/semanticAction may only focus the field, so re-snapshot and fall back to type, press Enter/arrow keys, or visible option refs.",
|
|
58
51
|
"When using eval --stdin, scope checks and actions to the target element or route whenever possible instead of relying on broad page-wide text heuristics.",
|
|
59
|
-
"When using eval --stdin for extraction, pass the JavaScript through the native tool stdin field, not as an extra args token after --stdin, and return the value you want instead of relying on console.log as the primary result channel. Prefer plain expressions like ({ title: document.title }) or explicitly invoked functions like (() => ({ title: document.title }))(); use outputPath when the eval/get/snapshot data should be saved as a durable local file. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called. The native wrapper blocks follow-up inspection, scripting, and interaction on file:// pages to protect authenticated local browser state; use a reachable HTTP(S) fixture instead. If get text on a broad CSS selector surfaces details.selectorTextVisibility or selectorTextVisibilityAll, prefer a visible @ref, a more specific selector, or the inspect-visible-text-candidates nextAction over hidden tab content.",
|
|
52
|
+
"When using eval --stdin for extraction, pass the JavaScript through the native tool stdin field, not as an extra args token after --stdin, and return the value you want instead of relying on console.log as the primary result channel. Prefer plain expressions like ({ title: document.title }) or explicitly invoked functions like (() => ({ title: document.title }))(); use outputPath when the eval/get/snapshot data should be saved as a durable local file, but never reuse a screenshot, download, recording, or other browser artifact destination as outputPath. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called. The native wrapper blocks follow-up inspection, scripting, and interaction on file:// pages to protect authenticated local browser state; use a reachable HTTP(S) fixture instead. If get text on a broad CSS selector surfaces details.selectorTextVisibility or selectorTextVisibilityAll, prefer a visible @ref, a more specific selector, or the inspect-visible-text-candidates nextAction over hidden tab content.",
|
|
60
53
|
"When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.",
|
|
61
54
|
"When commands save or spill files (screenshots, downloads, PDFs, traces, recordings, HAR, large snapshot spills), use the user's exact requested paths when given and treat paths as provisional until details.artifactVerification shows every row verified: branch on missingCount, pendingCount, unverifiedCount, per-entry state, and optional limitation before downstream file use or PASS/FAIL reporting.",
|
|
62
55
|
"For evidence-only screenshots, QA captures, or other audit artifacts, save to an explicit path and branch on details.artifactVerification plus details.artifacts before reporting PASS/FAIL; do not require vision review of inline image attachments unless the user asked for visual inspection.",
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Describe the native command used to read a process start identity for PID-reuse-safe ownership checks.
|
|
3
|
-
* Responsibilities: Keep POSIX and native-Windows process identity probes aligned across policy locks and temp cleanup.
|
|
4
|
-
* Scope: Command construction, output normalization, and cached async lookup for the current process.
|
|
5
|
-
*/
|
|
6
1
|
import { execFile } from "node:child_process";
|
|
7
2
|
import { win32 } from "node:path";
|
|
8
3
|
const WINDOWS_PROCESS_START_IDENTITY_PREFIX = "win32-powershell-ticks-v1:";
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Execute the upstream agent-browser binary for the pi-agent-browser extension.
|
|
3
|
-
* Responsibilities: Validate POSIX socket storage, spawn the agent-browser subprocess, forward parent environment variables plus wrapper overrides, stream optional stdin, bound in-memory output buffering, spill oversized stdout safely to a private temp file under a disk budget, and honor abort signals.
|
|
4
|
-
* Scope: Process execution only; argument planning, output formatting, and pi tool registration live elsewhere.
|
|
5
|
-
* Usage: Called by the extension tool after argument validation and session planning are complete.
|
|
6
|
-
* Invariants/Assumptions: The binary name is always `agent-browser`; Windows routes through PowerShell to invoke npm launchers with escaped argv; callers handle semantic success/error interpretation.
|
|
7
|
-
*/
|
|
8
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
2
|
import { spawn } from "node:child_process";
|
|
10
3
|
import { lstat, mkdir, readdir } from "node:fs/promises";
|