pi-agent-browser-native 0.6.16 → 0.6.17
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 +5 -1
- package/README.md +11 -5
- package/dist/extensions/agent-browser/index.js +476 -437
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +8 -4
- package/dist/extensions/agent-browser/lib/execution-cwd.js +64 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +27 -20
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +7 -5
- package/dist/extensions/agent-browser/lib/orchestration/operation-paths.js +76 -0
- package/docs/ARCHITECTURE.md +2 -0
- package/docs/COMMAND_REFERENCE.md +2 -2
- package/docs/SUPPORT_MATRIX.md +1 -1
- package/docs/TOOL_CONTRACT.md +9 -3
- package/package.json +4 -4
|
@@ -6,7 +6,7 @@ import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "./
|
|
|
6
6
|
import { PROJECT_RULE_PROMPT, buildBrowserDefaultProfileGuideline, buildBrowserExecutablePathGuideline, buildToolPromptGuidelines, } from "./lib/playbook.js";
|
|
7
7
|
import { SessionPageState } from "./lib/session-page-state.js";
|
|
8
8
|
import { buildExecutionPlan, canUseHeadlessCompatibilityUserAgent, createEphemeralSessionSeed, createFreshSessionName, createImplicitSessionName, extractUpstreamCommandTokens, getImplicitSessionCloseTimeoutMs, getImplicitSessionIdleTimeoutMs, isRestorableManagedSessionName, restoreManagedSessionStateFromBranch, validateToolArgs, redactSensitiveText, redactSensitiveValue, isPlainTextInspectionArgs, } from "./lib/runtime.js";
|
|
9
|
-
import { deleteIdentityKeysInNamespace, extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace, isUpstreamEnvFlagEnabled, resolveAgentBrowserNamespace } from "./lib/argv-grammar.js";
|
|
9
|
+
import { deleteIdentityKeysInNamespace, extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace, isUpstreamEnvFlagEnabled, resolveAgentBrowserNamespace, scanUpstreamGlobalFlagOccurrences } from "./lib/argv-grammar.js";
|
|
10
10
|
import { parseArgvDescriptor } from "./lib/argv-descriptor.js";
|
|
11
11
|
import { needsManagedSession } from "./lib/command-policy.js";
|
|
12
12
|
import { ManagedSessionRestoreState, resolveOwnedManagedSessionContext, withOwnedManagedSessionContext } from "./lib/managed-session-restore.js";
|
|
@@ -14,6 +14,8 @@ import { isRecord } from "./lib/parsing.js";
|
|
|
14
14
|
import { runAgentBrowserProcess } from "./lib/process.js";
|
|
15
15
|
import { getAgentBrowserProcessEnvironment, withIsolatedAgentBrowserEnvironment } from "./lib/process-environment.js";
|
|
16
16
|
import { withNativeSessionDefaults } from "./lib/orchestration/native-session-defaults.js";
|
|
17
|
+
import { getBrowserCwdError, resolveExecutionCwd, restoreManagedSessionCwd } from "./lib/execution-cwd.js";
|
|
18
|
+
import { resolveOperationPaths } from "./lib/orchestration/operation-paths.js";
|
|
17
19
|
import { closeManagedSession, inspectManagedSessionDaemon } from "./lib/orchestration/browser-run/managed-session-daemon-policy.js";
|
|
18
20
|
import { MINIMUM_AGENT_BROWSER_VERSION, SUPPORTED_AGENT_BROWSER_VERSION_LABEL, TARGET_AGENT_BROWSER_VERSION, getAgentBrowserVersionValidationError, parseAgentBrowserVersionOutput, } from "./lib/upstream-version.js";
|
|
19
21
|
import { buildPromptPolicy, getLatestUserPrompt, shouldAppendBrowserSystemPrompt } from "./lib/prompt-policy.js";
|
|
@@ -1108,7 +1110,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1108
1110
|
managedSessionHeadedAutosaveInterval = managedSessionActive
|
|
1109
1111
|
? restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, managedSessionName, managedSessionNamespace)
|
|
1110
1112
|
: undefined;
|
|
1111
|
-
managedSessionCwd = ctx.cwd;
|
|
1113
|
+
managedSessionCwd = restoreManagedSessionCwd(branch, managedSessionName, managedSessionNamespace, ctx.cwd);
|
|
1112
1114
|
freshSessionOrdinal = nextFreshSessionOrdinal;
|
|
1113
1115
|
sessionPageState = SessionPageState.fromBranch(branch);
|
|
1114
1116
|
traceOwners = new Map();
|
|
@@ -1159,7 +1161,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1159
1161
|
continue;
|
|
1160
1162
|
if (!isRestorableManagedSessionName(identity.sessionName, managedSessionBaseName))
|
|
1161
1163
|
continue;
|
|
1162
|
-
trackOwnedManagedSession(ownedManagedSessions, identity.sessionName, ctx.cwd, {
|
|
1164
|
+
trackOwnedManagedSession(ownedManagedSessions, identity.sessionName, restoreManagedSessionCwd(branch, identity.sessionName, identity.namespace, ctx.cwd), {
|
|
1163
1165
|
branchOwned: true,
|
|
1164
1166
|
compatibilityWorkaround: restoreManagedSessionCompatibilityWorkaroundFromBranch(branch, identity.sessionName, identity.namespace),
|
|
1165
1167
|
headedManagedAutosaveDisabled: restoreManagedSessionHeadedAutosaveDisabledFromBranch(branch, identity.sessionName, identity.namespace),
|
|
@@ -1168,7 +1170,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1168
1170
|
});
|
|
1169
1171
|
}
|
|
1170
1172
|
if (restoredState.active) {
|
|
1171
|
-
trackOwnedManagedSession(ownedManagedSessions, restoredState.sessionName,
|
|
1173
|
+
trackOwnedManagedSession(ownedManagedSessions, restoredState.sessionName, managedSessionCwd, {
|
|
1172
1174
|
branchOwned: true,
|
|
1173
1175
|
compatibilityWorkaround: managedSessionCompatibilityWorkaround,
|
|
1174
1176
|
headedManagedAutosaveDisabled: managedSessionHeadedAutosaveDisabled,
|
|
@@ -1184,7 +1186,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1184
1186
|
const closeRank = branchResourceEvents.managedSessionCloseRanks.get(sessionKey);
|
|
1185
1187
|
if (activeRank === undefined || (closeRank !== undefined && closeRank >= activeRank))
|
|
1186
1188
|
continue;
|
|
1187
|
-
trackOwnedManagedSession(ownedManagedSessions, record.sessionName, ctx.cwd, {
|
|
1189
|
+
trackOwnedManagedSession(ownedManagedSessions, record.sessionName, restoreManagedSessionCwd(branch, record.sessionName, undefined, ctx.cwd), {
|
|
1188
1190
|
branchOwned: true,
|
|
1189
1191
|
headedManagedAutosaveDisabled: restoreManagedSessionHeadedAutosaveDisabledFromBranch(branch, record.sessionName),
|
|
1190
1192
|
headedManagedAutosaveInterval: restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, record.sessionName),
|
|
@@ -1243,7 +1245,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1243
1245
|
notifyRecordingPersistence(ctx);
|
|
1244
1246
|
}));
|
|
1245
1247
|
});
|
|
1246
|
-
//
|
|
1248
|
+
// Fork-only checkpoint event; official Pi validation types do not declare it.
|
|
1247
1249
|
// Pi owns awaited tools/events (including their execution queues). Only script
|
|
1248
1250
|
// children and retained browser/recording resources need extension-side checks.
|
|
1249
1251
|
const checkpointPi = pi;
|
|
@@ -1433,477 +1435,514 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1433
1435
|
component.setState(formatAgentBrowserRenderResult(result, options, theme, context.isError), options.expanded, theme);
|
|
1434
1436
|
return component;
|
|
1435
1437
|
},
|
|
1436
|
-
async execute(toolCallId, params, signal, onUpdate, ctx, nativeToolCallId = toolCallId) {
|
|
1438
|
+
async execute(toolCallId, params, signal, onUpdate, ctx, nativeToolCallId = toolCallId, capturedCwd) {
|
|
1439
|
+
let operationCwd;
|
|
1440
|
+
try {
|
|
1441
|
+
operationCwd = capturedCwd ?? resolveExecutionCwd(pi, ctx);
|
|
1442
|
+
}
|
|
1443
|
+
catch (error) {
|
|
1444
|
+
return buildValidationFailureResult({ kind: "invalid", status: "invalid", redactedArgs: [], toolArgs: [], validationError: error instanceof Error ? error.message : String(error) });
|
|
1445
|
+
}
|
|
1437
1446
|
const promptPolicy = buildPromptPolicy(getLatestUserPrompt(ctx.sessionManager.getBranch()));
|
|
1438
1447
|
const outputPath = isRecord(params) && typeof params.outputPath === "string" ? params.outputPath : undefined;
|
|
1439
|
-
const
|
|
1440
|
-
getBatchPreflightValidationError: (args, stdin) => getArtifactPreflightValidationError({ args, cwd:
|
|
1448
|
+
const admittedInput = resolveAgentBrowserInput({
|
|
1449
|
+
getBatchPreflightValidationError: (args, stdin) => getArtifactPreflightValidationError({ args, cwd: operationCwd, outputPath, stdin }),
|
|
1441
1450
|
params,
|
|
1442
1451
|
});
|
|
1443
|
-
if (
|
|
1444
|
-
return buildValidationFailureResult(
|
|
1452
|
+
if (admittedInput.status === "invalid") {
|
|
1453
|
+
return buildValidationFailureResult(admittedInput);
|
|
1445
1454
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1450
|
-
const
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1455
|
+
const explicitConfig = scanUpstreamGlobalFlagOccurrences(admittedInput.toolArgs, "--config").length > 0 || getAgentBrowserProcessEnvironment().AGENT_BROWSER_CONFIG !== undefined;
|
|
1456
|
+
// Managed selection must follow earlier queued fresh launches, while file paths keep their admission snapshot.
|
|
1457
|
+
const managedAtAdmission = admittedInput.kind !== "script" && admittedInput.kind !== "electron"
|
|
1458
|
+
&& !extractExplicitSessionName(admittedInput.toolArgs) && !explicitConfig;
|
|
1459
|
+
const artifactsAtAdmission = managedAtAdmission && commandTouchesArtifactLifecycle(admittedInput.toolArgs, admittedInput.toolStdin, outputPath);
|
|
1460
|
+
const withManagedQueue = (run) => managedAtAdmission ? run() : managedSessionExecutionQueue.run(run);
|
|
1461
|
+
const withArtifactQueue = (run) => artifactsAtAdmission ? run() : artifactExecutionQueue.run(run);
|
|
1462
|
+
const executeAdmitted = async () => {
|
|
1463
|
+
let resolvedInput = admittedInput;
|
|
1464
|
+
const requestedSession = extractExplicitSessionName(resolvedInput.toolArgs);
|
|
1465
|
+
const requestedNamespace = resolveAgentBrowserNamespace(resolvedInput.toolArgs, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE);
|
|
1466
|
+
const requestedOwner = requestedSession ? ownedManagedSessions.get(getSessionContextKey(requestedSession, requestedNamespace) ?? requestedSession) : undefined;
|
|
1467
|
+
const browserCwd = (params.sessionMode === "fresh" && !requestedSession) || explicitConfig ? operationCwd
|
|
1468
|
+
: requestedSession ? requestedOwner?.cwd ?? ctx.cwd : managedSessionActive ? managedSessionCwd : ctx.cwd;
|
|
1469
|
+
const browserCwdError = resolvedInput.kind === "electron" ? undefined : getBrowserCwdError(browserCwd);
|
|
1470
|
+
if (browserCwdError)
|
|
1471
|
+
return buildValidationFailureResult({ ...resolvedInput, attemptedKind: resolvedInput.kind, kind: "invalid", status: "invalid", validationError: browserCwdError });
|
|
1472
|
+
if (!isPlainTextInspectionArgs(resolvedInput.toolArgs) && (operationCwd !== browserCwd || operationCwd !== ctx.cwd)) {
|
|
1473
|
+
const bound = resolveOperationPaths(resolvedInput.toolArgs, resolvedInput.toolStdin, operationCwd);
|
|
1474
|
+
resolvedInput = { ...resolvedInput, toolArgs: bound.args, toolStdin: bound.stdin };
|
|
1459
1475
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
}
|
|
1474
|
-
return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, preserveTextContent, result: warnRecordingPersistence(result) });
|
|
1475
|
-
});
|
|
1476
|
-
};
|
|
1477
|
-
const versionCheckCommand = extractUpstreamCommandTokens(resolvedInput.toolArgs)[0];
|
|
1478
|
-
const electronHostOnlyAction = resolvedInput.kind === "electron" && ["cleanup", "list", "status"].includes(resolvedInput.compiledElectron.action);
|
|
1479
|
-
const browserBackedVersionCheck = readConfirmation?.capabilities?.readRequiresConfirmation !== true && needsManagedSession(parseArgvDescriptor(resolvedInput.toolArgs), resolvedInput.toolStdin);
|
|
1480
|
-
if (resolvedInput.kind !== "script" && !electronHostOnlyAction && browserBackedVersionCheck && !isPlainTextInspectionArgs(resolvedInput.toolArgs) && !isCloseCommand(versionCheckCommand) && signal?.aborted !== true) {
|
|
1481
|
-
const versionFailure = await validateUpstreamVersion(ctx.cwd, signal);
|
|
1482
|
-
if (versionFailure)
|
|
1483
|
-
return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, result: versionFailure });
|
|
1484
|
-
}
|
|
1485
|
-
if (resolvedInput.kind === "script") {
|
|
1486
|
-
if (!ctx.sessionManager.getSessionFile()) {
|
|
1487
|
-
return buildValidationFailureResult({
|
|
1488
|
-
attemptedKind: "script",
|
|
1489
|
-
kind: "invalid",
|
|
1490
|
-
redactedArgs: [],
|
|
1491
|
-
status: "invalid",
|
|
1492
|
-
toolArgs: [],
|
|
1493
|
-
validationError: "script requires a persisted Pi session so its isolated browser-session cleanup lease survives restart; relaunch Pi without --no-session.",
|
|
1494
|
-
});
|
|
1476
|
+
if (resolvedInput.kind !== "script")
|
|
1477
|
+
await beforeExecute?.(nativeToolCallId, { ...ctx, signal });
|
|
1478
|
+
const runtimeBrowserConfig = loadAgentBrowserConfigSync({ cwd: ctx.cwd, includeProjectConfig: shouldIncludeProjectConfig(ctx) });
|
|
1479
|
+
const rootProfile = runtimeBrowserConfig.trustedBrowserDefaultProfile;
|
|
1480
|
+
const pendingReadConfirmation = sessionPageState.findReadConfirmation(resolvedInput.toolArgs, resolveAgentBrowserNamespace(resolvedInput.toolArgs, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE));
|
|
1481
|
+
const rootSessionId = process.env.PI_SUBAGENT_CHILD === "1" && process.env.PI_SUBAGENT_ROOT_SESSION_ID
|
|
1482
|
+
? process.env.PI_SUBAGENT_ROOT_SESSION_ID : ctx.sessionManager.getSessionId();
|
|
1483
|
+
return withNativeSessionDefaults(resolvedInput, browserCwd, signal, async (resolvedInput, withLaunchDefaults) => {
|
|
1484
|
+
const readConfirmation = sessionPageState.findReadConfirmation(resolvedInput.toolArgs, resolveAgentBrowserNamespace(resolvedInput.toolArgs, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE));
|
|
1485
|
+
if (readConfirmation)
|
|
1486
|
+
resolvedInput = { ...resolvedInput, toolArgs: scopeReadConfirmationArgs(resolvedInput.toolArgs, readConfirmation) };
|
|
1487
|
+
if (resolvedInput.kind === "qa" && resolvedInput.compiledQaPreset.checks.attached && !managedSessionActive && !extractExplicitSessionName(resolvedInput.toolArgs)) {
|
|
1488
|
+
return buildValidationFailureResult({ ...resolvedInput, attemptedKind: "qa", kind: "invalid", status: "invalid", validationError: "qa.attached requires an active attached session. Run electron.launch or connect to an Electron debug port first, or configure a native shared session." });
|
|
1495
1489
|
}
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1490
|
+
const applyUnserializedOutputPath = async (result, preserveTextContent = false) => {
|
|
1491
|
+
if (!outputPath || !canWriteAgentBrowserOutput(result))
|
|
1492
|
+
return warnRecordingPersistence(result);
|
|
1493
|
+
return withArtifactQueue(async () => {
|
|
1494
|
+
flushRecordingReservations();
|
|
1495
|
+
const reservationError = getArtifactPreflightValidationError({
|
|
1496
|
+
activeRecordingReservations: activeRecordingReservations.values(),
|
|
1497
|
+
args: [],
|
|
1498
|
+
cwd: operationCwd,
|
|
1499
|
+
outputPath,
|
|
1500
|
+
});
|
|
1501
|
+
if (reservationError) {
|
|
1502
|
+
return warnRecordingPersistence(buildValidationFailureResult({ attemptedKind: resolvedInput.kind, kind: "invalid", redactedArgs: resolvedInput.redactedArgs, status: "invalid", toolArgs: resolvedInput.toolArgs, toolStdin: resolvedInput.toolStdin, validationError: reservationError }));
|
|
1503
|
+
}
|
|
1504
|
+
return applyAgentBrowserOutputPath({ cwd: operationCwd, outputPath, preserveTextContent, result: warnRecordingPersistence(result) });
|
|
1505
|
+
});
|
|
1510
1506
|
};
|
|
1511
|
-
const
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1514
|
-
if (signal?.aborted)
|
|
1515
|
-
|
|
1516
|
-
activeScriptControllers.add(scriptController);
|
|
1517
|
-
let finishScriptExecution;
|
|
1518
|
-
const scriptExecution = new Promise((resolve) => {
|
|
1519
|
-
finishScriptExecution = resolve;
|
|
1520
|
-
});
|
|
1521
|
-
activeScriptExecutions.add(scriptExecution);
|
|
1522
|
-
try {
|
|
1523
|
-
// Keep preflight inside shutdown tracking so quit cannot race into starting the sandbox afterward.
|
|
1524
|
-
const versionFailure = await withIsolatedAgentBrowserEnvironment(() => validateUpstreamVersion(ctx.cwd, scriptController.signal));
|
|
1507
|
+
const versionCheckCommand = extractUpstreamCommandTokens(resolvedInput.toolArgs)[0];
|
|
1508
|
+
const electronHostOnlyAction = resolvedInput.kind === "electron" && ["cleanup", "list", "status"].includes(resolvedInput.compiledElectron.action);
|
|
1509
|
+
const browserBackedVersionCheck = readConfirmation?.capabilities?.readRequiresConfirmation !== true && needsManagedSession(parseArgvDescriptor(resolvedInput.toolArgs), resolvedInput.toolStdin);
|
|
1510
|
+
if (resolvedInput.kind !== "script" && !electronHostOnlyAction && browserBackedVersionCheck && !isPlainTextInspectionArgs(resolvedInput.toolArgs) && !isCloseCommand(versionCheckCommand) && signal?.aborted !== true) {
|
|
1511
|
+
const versionFailure = await validateUpstreamVersion(browserCwd, signal);
|
|
1525
1512
|
if (versionFailure)
|
|
1526
|
-
return applyAgentBrowserOutputPath({ cwd:
|
|
1527
|
-
const pendingRun = runAgentBrowserScript({
|
|
1528
|
-
beforeFirstCall() {
|
|
1529
|
-
appendScriptSessionLease(pi, sessionName, "active");
|
|
1530
|
-
trackOwnedManagedSession(ownedManagedSessions, sessionName, ctx.cwd, { namespace: AGENT_BROWSER_SCRIPT_NAMESPACE });
|
|
1531
|
-
managedSessionRestoreState.disable(sessionName, AGENT_BROWSER_SCRIPT_NAMESPACE);
|
|
1532
|
-
// This fresh, unselectable lease owns any daemon its first read starts.
|
|
1533
|
-
managedSessionRestoreState.recordDaemonRestoreKey(sessionName, AGENT_BROWSER_SCRIPT_NAMESPACE, null);
|
|
1534
|
-
leased = true;
|
|
1535
|
-
},
|
|
1536
|
-
code: resolvedInput.compiledScript.code,
|
|
1537
|
-
dispatch: async (innerParams, innerSignal) => {
|
|
1538
|
-
const remainingMs = Math.max(1, deadline - Date.now());
|
|
1539
|
-
const innerTimeoutMs = Math.min(innerParams.timeoutMs ?? remainingMs, remainingMs);
|
|
1540
|
-
const innerResult = await withIsolatedAgentBrowserEnvironment(() => agentBrowserTool.execute(`${toolCallId}:script:${innerResults.length + 1}`, {
|
|
1541
|
-
args: ["--namespace", AGENT_BROWSER_SCRIPT_NAMESPACE, "--session", sessionName, ...innerParams.args],
|
|
1542
|
-
stdin: innerParams.stdin,
|
|
1543
|
-
timeoutMs: innerTimeoutMs,
|
|
1544
|
-
}, innerSignal, undefined, ctx, nativeToolCallId));
|
|
1545
|
-
innerResults.push(innerResult);
|
|
1546
|
-
return await buildScriptBrowserEnvelope(innerResult, innerParams.args, sessionName);
|
|
1547
|
-
},
|
|
1548
|
-
signal: scriptController.signal,
|
|
1549
|
-
timeoutMs: scriptTimeoutMs,
|
|
1550
|
-
});
|
|
1551
|
-
run = await pendingRun;
|
|
1513
|
+
return applyAgentBrowserOutputPath({ cwd: operationCwd, outputPath, result: versionFailure });
|
|
1552
1514
|
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
}
|
|
1564
|
-
catch {
|
|
1565
|
-
cleanupError = "The isolated script session cleanup operation failed.";
|
|
1566
|
-
try {
|
|
1567
|
-
appendScriptSessionLease(pi, sessionName, "failed");
|
|
1568
|
-
}
|
|
1569
|
-
catch { }
|
|
1570
|
-
}
|
|
1515
|
+
if (resolvedInput.kind === "script") {
|
|
1516
|
+
if (!ctx.sessionManager.getSessionFile()) {
|
|
1517
|
+
return buildValidationFailureResult({
|
|
1518
|
+
attemptedKind: "script",
|
|
1519
|
+
kind: "invalid",
|
|
1520
|
+
redactedArgs: [],
|
|
1521
|
+
status: "invalid",
|
|
1522
|
+
toolArgs: [],
|
|
1523
|
+
validationError: "script requires a persisted Pi session so its isolated browser-session cleanup lease survives restart; relaunch Pi without --no-session.",
|
|
1524
|
+
});
|
|
1571
1525
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1526
|
+
const sessionName = createAgentBrowserScriptSessionName();
|
|
1527
|
+
const innerResults = [];
|
|
1528
|
+
const scriptTimeoutMs = params.timeoutMs ?? AGENT_BROWSER_SCRIPT_DEFAULT_TIMEOUT_MS;
|
|
1529
|
+
const deadline = Date.now() + scriptTimeoutMs;
|
|
1530
|
+
let leased = false;
|
|
1531
|
+
let cleanupError;
|
|
1532
|
+
let run = {
|
|
1533
|
+
callCount: 0,
|
|
1534
|
+
emitCount: 0,
|
|
1535
|
+
error: "Script sandbox execution failed.",
|
|
1536
|
+
failureCategory: "upstream-error",
|
|
1537
|
+
ok: false,
|
|
1538
|
+
rejectedCallCount: 0,
|
|
1539
|
+
steps: [],
|
|
1584
1540
|
};
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1541
|
+
const scriptController = new AbortController();
|
|
1542
|
+
const abortScript = () => scriptController.abort();
|
|
1543
|
+
signal?.addEventListener("abort", abortScript, { once: true });
|
|
1544
|
+
if (signal?.aborted)
|
|
1545
|
+
scriptController.abort();
|
|
1546
|
+
activeScriptControllers.add(scriptController);
|
|
1547
|
+
let finishScriptExecution;
|
|
1548
|
+
const scriptExecution = new Promise((resolve) => {
|
|
1549
|
+
finishScriptExecution = resolve;
|
|
1550
|
+
});
|
|
1551
|
+
activeScriptExecutions.add(scriptExecution);
|
|
1552
|
+
try {
|
|
1553
|
+
// Keep preflight inside shutdown tracking so quit cannot race into starting the sandbox afterward.
|
|
1554
|
+
const versionFailure = await withIsolatedAgentBrowserEnvironment(() => validateUpstreamVersion(browserCwd, scriptController.signal));
|
|
1555
|
+
if (versionFailure)
|
|
1556
|
+
return applyAgentBrowserOutputPath({ cwd: operationCwd, outputPath, result: versionFailure });
|
|
1557
|
+
const pendingRun = runAgentBrowserScript({
|
|
1558
|
+
beforeFirstCall() {
|
|
1559
|
+
appendScriptSessionLease(pi, sessionName, "active");
|
|
1560
|
+
trackOwnedManagedSession(ownedManagedSessions, sessionName, ctx.cwd, { namespace: AGENT_BROWSER_SCRIPT_NAMESPACE });
|
|
1561
|
+
managedSessionRestoreState.disable(sessionName, AGENT_BROWSER_SCRIPT_NAMESPACE);
|
|
1562
|
+
// This fresh, unselectable lease owns any daemon its first read starts.
|
|
1563
|
+
managedSessionRestoreState.recordDaemonRestoreKey(sessionName, AGENT_BROWSER_SCRIPT_NAMESPACE, null);
|
|
1564
|
+
leased = true;
|
|
1565
|
+
},
|
|
1566
|
+
code: resolvedInput.compiledScript.code,
|
|
1567
|
+
dispatch: async (innerParams, innerSignal) => {
|
|
1568
|
+
const remainingMs = Math.max(1, deadline - Date.now());
|
|
1569
|
+
const innerTimeoutMs = Math.min(innerParams.timeoutMs ?? remainingMs, remainingMs);
|
|
1570
|
+
const innerResult = await withIsolatedAgentBrowserEnvironment(() => agentBrowserTool.execute(`${toolCallId}:script:${innerResults.length + 1}`, {
|
|
1571
|
+
args: ["--namespace", AGENT_BROWSER_SCRIPT_NAMESPACE, "--session", sessionName, ...innerParams.args],
|
|
1572
|
+
stdin: innerParams.stdin,
|
|
1573
|
+
timeoutMs: innerTimeoutMs,
|
|
1574
|
+
}, innerSignal, undefined, ctx, nativeToolCallId, operationCwd));
|
|
1575
|
+
innerResults.push(innerResult);
|
|
1576
|
+
return await buildScriptBrowserEnvelope(innerResult, innerParams.args, sessionName);
|
|
1577
|
+
},
|
|
1578
|
+
signal: scriptController.signal,
|
|
1579
|
+
timeoutMs: scriptTimeoutMs,
|
|
1580
|
+
});
|
|
1581
|
+
run = await pendingRun;
|
|
1626
1582
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
managedSessionName = createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal);
|
|
1583
|
+
catch { }
|
|
1584
|
+
finally {
|
|
1585
|
+
activeScriptControllers.delete(scriptController);
|
|
1586
|
+
signal?.removeEventListener("abort", abortScript);
|
|
1587
|
+
if (leased) {
|
|
1588
|
+
try {
|
|
1589
|
+
cleanupError = await artifactExecutionQueue.run(() => managedSessionExecutionQueue.run(() => {
|
|
1590
|
+
flushRecordingReservations();
|
|
1591
|
+
return closeScriptSessionLeaseWithinQueue(sessionName, ctx.cwd);
|
|
1592
|
+
}));
|
|
1593
|
+
}
|
|
1594
|
+
catch {
|
|
1595
|
+
cleanupError = "The isolated script session cleanup operation failed.";
|
|
1596
|
+
try {
|
|
1597
|
+
appendScriptSessionLease(pi, sessionName, "failed");
|
|
1598
|
+
}
|
|
1599
|
+
catch { }
|
|
1600
|
+
}
|
|
1646
1601
|
}
|
|
1602
|
+
activeScriptExecutions.delete(scriptExecution);
|
|
1603
|
+
finishScriptExecution();
|
|
1647
1604
|
}
|
|
1605
|
+
let scriptResult = buildScriptToolResult({ cleanupError, innerResults, run, sessionName: leased ? sessionName : undefined });
|
|
1648
1606
|
if (artifactManifest) {
|
|
1649
|
-
|
|
1650
|
-
...
|
|
1607
|
+
scriptResult = {
|
|
1608
|
+
...scriptResult,
|
|
1651
1609
|
details: {
|
|
1652
|
-
...(isRecord(
|
|
1610
|
+
...(isRecord(scriptResult.details) ? scriptResult.details : {}),
|
|
1653
1611
|
artifactManifest: redactSensitiveValue(artifactManifest),
|
|
1654
1612
|
artifactRetentionSummary: formatSessionArtifactRetentionSummary(artifactManifest),
|
|
1655
1613
|
},
|
|
1656
1614
|
};
|
|
1657
1615
|
}
|
|
1616
|
+
return applyUnserializedOutputPath(scriptResult);
|
|
1658
1617
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1618
|
+
const { toolArgs } = resolvedInput;
|
|
1619
|
+
const compiledElectron = resolvedInput.kind === "electron" ? resolvedInput.compiledElectron : undefined;
|
|
1620
|
+
const redactedCompiledElectron = resolvedInput.kind === "electron" ? resolvedInput.redactedCompiledElectron : undefined;
|
|
1621
|
+
const runElectronHostInput = async () => {
|
|
1622
|
+
const electronHostLaunchRecords = getElectronHostLaunchRecordsForInput({
|
|
1623
|
+
branchRecords: electronLaunchRecords,
|
|
1624
|
+
compiledElectron,
|
|
1625
|
+
ownedRecords: ownedElectronLaunchRecords,
|
|
1626
|
+
});
|
|
1627
|
+
let electronHostResult = await handleElectronHostInput({
|
|
1628
|
+
attachedSessionKeys,
|
|
1629
|
+
compiledElectron,
|
|
1670
1630
|
cwd: ctx.cwd,
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1631
|
+
electronChildProcesses,
|
|
1632
|
+
electronLaunchRecords: electronHostLaunchRecords,
|
|
1633
|
+
implicitSessionCloseTimeoutMs,
|
|
1634
|
+
managedSessionActive,
|
|
1635
|
+
managedSessionName,
|
|
1636
|
+
managedSessionNamespace,
|
|
1637
|
+
managedSessionRestoreState,
|
|
1638
|
+
ownedManagedSessions,
|
|
1639
|
+
redactedCompiledElectron,
|
|
1640
|
+
sessionPageState,
|
|
1641
|
+
signal,
|
|
1642
|
+
});
|
|
1643
|
+
if (electronHostResult && compiledElectron?.action === "cleanup") {
|
|
1644
|
+
branchStateGeneration += 1;
|
|
1645
|
+
const cleanupRecords = isRecord(electronHostResult.details)
|
|
1646
|
+
&& isRecord(electronHostResult.details.electron)
|
|
1647
|
+
&& isRecord(electronHostResult.details.electron.cleanup)
|
|
1648
|
+
&& Array.isArray(electronHostResult.details.electron.cleanup.results)
|
|
1649
|
+
? electronHostResult.details.electron.cleanup.results
|
|
1650
|
+
: [];
|
|
1651
|
+
const cleanedLaunchIds = new Set();
|
|
1652
|
+
for (const cleanupResult of cleanupRecords) {
|
|
1653
|
+
if (isRecord(cleanupResult) && isElectronLaunchRecord(cleanupResult.record)) {
|
|
1654
|
+
cleanedLaunchIds.add(cleanupResult.record.launchId);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
replaceWithActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronHostLaunchRecords, branchOwnedElectronLaunchIds, cleanedLaunchIds);
|
|
1658
|
+
mergeElectronCleanupRecords(electronLaunchRecords, cleanupRecords);
|
|
1659
|
+
const cleanupNamespace = isRecord(electronHostResult.details) && typeof electronHostResult.details.namespace === "string"
|
|
1660
|
+
? electronHostResult.details.namespace
|
|
1661
|
+
: undefined;
|
|
1662
|
+
const closedSessionIdentities = getCleanupResultsClosedManagedSessionIdentities(cleanupRecords, cleanupNamespace);
|
|
1663
|
+
syncElectronCleanupManagedSessions(ownedManagedSessions, cleanupRecords, cleanupNamespace);
|
|
1664
|
+
for (const identity of closedSessionIdentities) {
|
|
1665
|
+
retireRecordingSession(identity.sessionName, identity.namespace);
|
|
1666
|
+
const closedSessionKey = getSessionContextKey(identity.sessionName, identity.namespace) ?? identity.sessionName;
|
|
1667
|
+
clearSessionScopedBrowserState(closedSessionKey);
|
|
1668
|
+
if (closedSessionKey === (getSessionContextKey(managedSessionName, managedSessionNamespace) ?? managedSessionName)) {
|
|
1669
|
+
managedSessionActive = false;
|
|
1670
|
+
managedSessionCompatibilityWorkaround = undefined;
|
|
1671
|
+
managedSessionHeadedAutosaveDisabled = false;
|
|
1672
|
+
managedSessionHeadedAutosaveInterval = undefined;
|
|
1673
|
+
managedSessionNamespace = undefined;
|
|
1674
|
+
freshSessionOrdinal += 1;
|
|
1675
|
+
managedSessionName = createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if (artifactManifest) {
|
|
1679
|
+
electronHostResult = {
|
|
1680
|
+
...electronHostResult,
|
|
1681
|
+
details: {
|
|
1682
|
+
...(isRecord(electronHostResult.details) ? electronHostResult.details : {}),
|
|
1683
|
+
artifactManifest: redactSensitiveValue(artifactManifest),
|
|
1684
|
+
artifactRetentionSummary: formatSessionArtifactRetentionSummary(artifactManifest),
|
|
1685
|
+
},
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1675
1688
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
const
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
artifactManifest,
|
|
1708
|
-
attachedSessionKeys,
|
|
1709
|
-
closedManagedSessionNames: new Set(),
|
|
1710
|
-
electronChildProcesses,
|
|
1711
|
-
electronLaunchRecords,
|
|
1712
|
-
ephemeralSessionSeed,
|
|
1713
|
-
freshSessionOrdinal,
|
|
1714
|
-
managedSessionActive,
|
|
1715
|
-
managedSessionBaseName,
|
|
1716
|
-
managedSessionCompatibilityWorkaround,
|
|
1717
|
-
managedSessionHeadedAutosaveDisabled,
|
|
1718
|
-
managedSessionHeadedAutosaveInterval,
|
|
1719
|
-
managedSessionCwd,
|
|
1689
|
+
return electronHostResult;
|
|
1690
|
+
};
|
|
1691
|
+
const runSerializedElectronHostInput = () => shouldSerializeElectronHostInput(compiledElectron)
|
|
1692
|
+
? managedSessionExecutionQueue.run(runElectronHostInput)
|
|
1693
|
+
: runElectronHostInput();
|
|
1694
|
+
const electronHostResult = compiledElectron?.action === "cleanup"
|
|
1695
|
+
? await artifactExecutionQueue.run(async () => {
|
|
1696
|
+
flushRecordingReservations();
|
|
1697
|
+
const reservationError = outputPath ? getArtifactPreflightValidationError({
|
|
1698
|
+
activeRecordingReservations: activeRecordingReservations.values(),
|
|
1699
|
+
args: [],
|
|
1700
|
+
cwd: operationCwd,
|
|
1701
|
+
outputPath,
|
|
1702
|
+
}) : undefined;
|
|
1703
|
+
if (reservationError) {
|
|
1704
|
+
return warnRecordingPersistence(buildValidationFailureResult({ attemptedKind: resolvedInput.kind, kind: "invalid", redactedArgs: resolvedInput.redactedArgs, status: "invalid", toolArgs: resolvedInput.toolArgs, toolStdin: resolvedInput.toolStdin, validationError: reservationError }));
|
|
1705
|
+
}
|
|
1706
|
+
const result = await runSerializedElectronHostInput();
|
|
1707
|
+
return result ? applyAgentBrowserOutputPath({ cwd: operationCwd, outputPath, result: warnRecordingPersistence(result) }) : result;
|
|
1708
|
+
})
|
|
1709
|
+
: await runSerializedElectronHostInput();
|
|
1710
|
+
if (electronHostResult) {
|
|
1711
|
+
return compiledElectron?.action === "cleanup" ? electronHostResult : applyUnserializedOutputPath(electronHostResult);
|
|
1712
|
+
}
|
|
1713
|
+
const explicitSessionName = extractExplicitSessionName(toolArgs);
|
|
1714
|
+
const callerOwnedSessionNamespace = explicitSessionName
|
|
1715
|
+
? resolveAgentBrowserNamespace(toolArgs, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE)
|
|
1716
|
+
: undefined;
|
|
1717
|
+
const serializeBrowserCommand = shouldSerializeBrowserCommand({
|
|
1718
|
+
namespace: callerOwnedSessionNamespace,
|
|
1719
|
+
explicitSessionName,
|
|
1720
1720
|
managedSessionName,
|
|
1721
|
-
|
|
1722
|
-
managedSessionRestoreState,
|
|
1723
|
-
networkRoutesBySession,
|
|
1721
|
+
ownedElectronLaunchRecords,
|
|
1724
1722
|
ownedManagedSessions,
|
|
1725
|
-
sessionPageState,
|
|
1726
|
-
traceOwners,
|
|
1727
|
-
};
|
|
1728
|
-
const selectedPlan = buildExecutionPlan(toolArgs, {
|
|
1729
|
-
freshSessionName: createFreshSessionName(browserRunState.managedSessionBaseName, browserRunState.ephemeralSessionSeed, browserRunState.freshSessionOrdinal + 1),
|
|
1730
|
-
managedSessionActive: browserRunState.managedSessionActive,
|
|
1731
|
-
managedSessionCompatibilityWorkaround: browserRunState.managedSessionCompatibilityWorkaround,
|
|
1732
|
-
managedSessionName: browserRunState.managedSessionName,
|
|
1733
|
-
managedSessionNamespace: browserRunState.managedSessionNamespace,
|
|
1734
|
-
sessionMode: compiledElectron?.action === "launch" ? "fresh" : params.sessionMode ?? "auto",
|
|
1735
|
-
stdin: resolvedInput.toolStdin,
|
|
1736
|
-
browserIndependentReadConfirmation: readConfirmation?.capabilities?.readRequiresConfirmation === true,
|
|
1737
|
-
});
|
|
1738
|
-
const initialArtifactManifest = browserRunState.artifactManifest;
|
|
1739
|
-
const initialNetworkRoutesBySession = browserRunState.networkRoutesBySession;
|
|
1740
|
-
const attachedSessionRequested = isAttachedBrowserInvocation(toolArgs)
|
|
1741
|
-
|| (resolvedInput.kind === "electron" && resolvedInput.compiledElectron.action === "launch");
|
|
1742
|
-
const allocatesFreshManagedSession = explicitSessionName === undefined
|
|
1743
|
-
&& (params.sessionMode === "fresh" || (resolvedInput.kind === "electron" && resolvedInput.compiledElectron.action === "launch"));
|
|
1744
|
-
const reusableSessionKey = allocatesFreshManagedSession
|
|
1745
|
-
? undefined
|
|
1746
|
-
: getSessionContextKey(selectedPlan.sessionName, selectedPlan.namespace)
|
|
1747
|
-
?? getSessionContextKey(browserRunState.managedSessionName, browserRunState.managedSessionNamespace);
|
|
1748
|
-
const attachedSessionKnown = reusableSessionKey !== undefined && attachedSessionKeys.has(reusableSessionKey);
|
|
1749
|
-
let result = await runAgentBrowserTool({
|
|
1750
|
-
daemonInactive,
|
|
1751
|
-
ctx,
|
|
1752
|
-
cwd: ctx.cwd,
|
|
1753
|
-
electronPostCommandStatusSettleMs: ELECTRON_POST_COMMAND_STATUS_SETTLE_MS,
|
|
1754
|
-
electronProfileIsolationDetails: ELECTRON_PROFILE_ISOLATION_DETAILS,
|
|
1755
|
-
implicitSessionCloseTimeoutMs,
|
|
1756
|
-
implicitSessionIdleTimeoutMs,
|
|
1757
|
-
input: resolvedInput,
|
|
1758
|
-
onUpdate,
|
|
1759
|
-
params,
|
|
1760
|
-
establishAttachedBrowserSession: attachedSessionRequested && !attachedSessionKnown,
|
|
1761
|
-
preserveAttachedBrowserSession: attachedSessionRequested || attachedSessionKnown,
|
|
1762
|
-
promptPolicy,
|
|
1763
|
-
sessionPageStateUpdate,
|
|
1764
|
-
signal,
|
|
1765
|
-
state: browserRunState,
|
|
1766
1723
|
});
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
const
|
|
1777
|
-
const
|
|
1778
|
-
const
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1724
|
+
const callerOwnedSessionQueueKey = !serializeBrowserCommand && explicitSessionName
|
|
1725
|
+
? getSessionContextKey(explicitSessionName, callerOwnedSessionNamespace) ?? explicitSessionName
|
|
1726
|
+
: undefined;
|
|
1727
|
+
const runBrowserCommand = async (daemonInactive) => {
|
|
1728
|
+
// Load execution-only preparation/output code inside the existing session queue,
|
|
1729
|
+
// before capturing state. Registration, rendering, and restore stay synchronous/eager.
|
|
1730
|
+
const { runAgentBrowserTool } = await import("./lib/orchestration/browser-run/index.js");
|
|
1731
|
+
flushRecordingReservations();
|
|
1732
|
+
const branchRestoreGenerationAtStart = branchRestoreGeneration;
|
|
1733
|
+
const generationAtStart = branchStateGeneration;
|
|
1734
|
+
const sessionPageStateUpdate = sessionPageState.beginUpdate();
|
|
1735
|
+
const browserRunState = {
|
|
1736
|
+
activeRecordingReservations,
|
|
1737
|
+
artifactManifest,
|
|
1738
|
+
attachedSessionKeys,
|
|
1739
|
+
closedManagedSessionNames: new Set(),
|
|
1740
|
+
electronChildProcesses,
|
|
1741
|
+
electronLaunchRecords,
|
|
1742
|
+
ephemeralSessionSeed,
|
|
1743
|
+
freshSessionOrdinal,
|
|
1744
|
+
managedSessionActive,
|
|
1745
|
+
managedSessionBaseName,
|
|
1746
|
+
managedSessionCompatibilityWorkaround,
|
|
1747
|
+
managedSessionHeadedAutosaveDisabled,
|
|
1748
|
+
managedSessionHeadedAutosaveInterval,
|
|
1749
|
+
managedSessionCwd,
|
|
1750
|
+
managedSessionName,
|
|
1751
|
+
managedSessionNamespace,
|
|
1752
|
+
managedSessionRestoreState,
|
|
1753
|
+
networkRoutesBySession,
|
|
1754
|
+
ownedManagedSessions,
|
|
1755
|
+
sessionPageState,
|
|
1756
|
+
traceOwners,
|
|
1757
|
+
};
|
|
1758
|
+
const selectedPlan = buildExecutionPlan(toolArgs, {
|
|
1759
|
+
freshSessionName: createFreshSessionName(browserRunState.managedSessionBaseName, browserRunState.ephemeralSessionSeed, browserRunState.freshSessionOrdinal + 1),
|
|
1760
|
+
managedSessionActive: browserRunState.managedSessionActive,
|
|
1761
|
+
managedSessionCompatibilityWorkaround: browserRunState.managedSessionCompatibilityWorkaround,
|
|
1762
|
+
managedSessionName: browserRunState.managedSessionName,
|
|
1763
|
+
managedSessionNamespace: browserRunState.managedSessionNamespace,
|
|
1764
|
+
sessionMode: compiledElectron?.action === "launch" ? "fresh" : params.sessionMode ?? "auto",
|
|
1765
|
+
stdin: resolvedInput.toolStdin,
|
|
1766
|
+
browserIndependentReadConfirmation: readConfirmation?.capabilities?.readRequiresConfirmation === true,
|
|
1767
|
+
});
|
|
1768
|
+
const initialArtifactManifest = browserRunState.artifactManifest;
|
|
1769
|
+
const initialNetworkRoutesBySession = browserRunState.networkRoutesBySession;
|
|
1770
|
+
const attachedSessionRequested = isAttachedBrowserInvocation(toolArgs)
|
|
1771
|
+
|| (resolvedInput.kind === "electron" && resolvedInput.compiledElectron.action === "launch");
|
|
1772
|
+
const allocatesFreshManagedSession = explicitSessionName === undefined
|
|
1773
|
+
&& (params.sessionMode === "fresh" || (resolvedInput.kind === "electron" && resolvedInput.compiledElectron.action === "launch"));
|
|
1774
|
+
const reusableSessionKey = allocatesFreshManagedSession
|
|
1775
|
+
? undefined
|
|
1776
|
+
: getSessionContextKey(selectedPlan.sessionName, selectedPlan.namespace)
|
|
1777
|
+
?? getSessionContextKey(browserRunState.managedSessionName, browserRunState.managedSessionNamespace);
|
|
1778
|
+
const attachedSessionKnown = reusableSessionKey !== undefined && attachedSessionKeys.has(reusableSessionKey);
|
|
1779
|
+
let result = await runAgentBrowserTool({
|
|
1780
|
+
daemonInactive,
|
|
1781
|
+
ctx,
|
|
1782
|
+
cwd: browserCwd,
|
|
1783
|
+
operationCwd,
|
|
1784
|
+
electronPostCommandStatusSettleMs: ELECTRON_POST_COMMAND_STATUS_SETTLE_MS,
|
|
1785
|
+
electronProfileIsolationDetails: ELECTRON_PROFILE_ISOLATION_DETAILS,
|
|
1786
|
+
implicitSessionCloseTimeoutMs,
|
|
1787
|
+
implicitSessionIdleTimeoutMs,
|
|
1788
|
+
input: resolvedInput,
|
|
1789
|
+
onUpdate,
|
|
1790
|
+
params,
|
|
1791
|
+
establishAttachedBrowserSession: attachedSessionRequested && !attachedSessionKnown,
|
|
1792
|
+
preserveAttachedBrowserSession: attachedSessionRequested || attachedSessionKnown,
|
|
1793
|
+
promptPolicy,
|
|
1794
|
+
sessionPageStateUpdate,
|
|
1795
|
+
signal,
|
|
1796
|
+
state: browserRunState,
|
|
1797
|
+
});
|
|
1798
|
+
const branchRestoreStillCurrent = branchRestoreGenerationAtStart === branchRestoreGeneration;
|
|
1799
|
+
const resultDetails = isRecord(result.details) ? result.details : undefined;
|
|
1800
|
+
if (resultDetails?.sessionName === browserRunState.managedSessionName && resultDetails.namespace === browserRunState.managedSessionNamespace && browserRunState.managedSessionActive) {
|
|
1801
|
+
resultDetails.managedSessionCwd = browserRunState.managedSessionCwd;
|
|
1802
|
+
}
|
|
1803
|
+
const resultSessionName = typeof resultDetails?.sessionName === "string"
|
|
1804
|
+
? resultDetails.sessionName
|
|
1805
|
+
: extractExplicitSessionName(toolArgs);
|
|
1806
|
+
const resultNamespace = typeof resultDetails?.namespace === "string"
|
|
1807
|
+
? resultDetails.namespace
|
|
1808
|
+
: selectedPlan.namespace;
|
|
1809
|
+
if (branchRestoreStillCurrent) {
|
|
1810
|
+
const resultBatchCloseLifecycle = getSuccessfulBatchCloseLifecycle(resultDetails?.batchSteps);
|
|
1811
|
+
const resultSessionKey = getSessionContextKey(resultSessionName, resultNamespace) ?? resultSessionName;
|
|
1812
|
+
const managedSessionOutcome = isRecord(resultDetails?.managedSessionOutcome) ? resultDetails.managedSessionOutcome : undefined;
|
|
1813
|
+
const closeAllApplied = resultDetails?.closeAllApplied === true;
|
|
1814
|
+
const attachedSessionRemainsActive = result.isError !== true
|
|
1815
|
+
|| ((attachedSessionRequested || attachedSessionKnown) && managedSessionOutcome?.activeAfter === true);
|
|
1816
|
+
const closesAttachedSession = (result.isError !== true && isCloseCommand(extractUpstreamCommandTokens(toolArgs)[0]))
|
|
1817
|
+
|| resultBatchCloseLifecycle?.endsClosed === true;
|
|
1818
|
+
if (closeAllApplied) {
|
|
1819
|
+
deleteIdentityKeysInNamespace(attachedSessionKeys, resultNamespace);
|
|
1820
|
+
if (resultSessionKey && attachedSessionRemainsActive && (attachedSessionRequested || attachedSessionKnown) && resultBatchCloseLifecycle?.endsClosed === false) {
|
|
1821
|
+
attachedSessionKeys.add(resultSessionKey);
|
|
1822
|
+
result = { ...result, details: { ...(resultDetails ?? {}), attachedBrowserSession: true } };
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
else if (resultSessionKey && closesAttachedSession)
|
|
1826
|
+
attachedSessionKeys.delete(resultSessionKey);
|
|
1827
|
+
else if (resultSessionKey && attachedSessionRemainsActive && (attachedSessionRequested || attachedSessionKnown)) {
|
|
1787
1828
|
attachedSessionKeys.add(resultSessionKey);
|
|
1788
1829
|
result = { ...result, details: { ...(resultDetails ?? {}), attachedBrowserSession: true } };
|
|
1789
1830
|
}
|
|
1790
1831
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
artifactManifest = mergeBrowserRunArtifactManifest(artifactManifest, initialArtifactManifest, browserRunState.artifactManifest);
|
|
1801
|
-
const handledBatchCloseKeys = syncRecordingReservationsFromResult(result);
|
|
1802
|
-
if (resultDetails?.closeAllApplied === true) {
|
|
1803
|
-
for (const [sessionKey, reservation] of [...activeRecordingReservations]) {
|
|
1804
|
-
if (isAgentBrowserSessionIdentityKeyInNamespace(sessionKey, resultNamespace)) {
|
|
1805
|
-
retireRecordingSession(reservation.sessionName, reservation.namespace);
|
|
1832
|
+
if (branchRestoreStillCurrent) {
|
|
1833
|
+
networkRoutesBySession = mergeBrowserRunMap(networkRoutesBySession, initialNetworkRoutesBySession, browserRunState.networkRoutesBySession);
|
|
1834
|
+
artifactManifest = mergeBrowserRunArtifactManifest(artifactManifest, initialArtifactManifest, browserRunState.artifactManifest);
|
|
1835
|
+
const handledBatchCloseKeys = syncRecordingReservationsFromResult(result);
|
|
1836
|
+
if (resultDetails?.closeAllApplied === true) {
|
|
1837
|
+
for (const [sessionKey, reservation] of [...activeRecordingReservations]) {
|
|
1838
|
+
if (isAgentBrowserSessionIdentityKeyInNamespace(sessionKey, resultNamespace)) {
|
|
1839
|
+
retireRecordingSession(reservation.sessionName, reservation.namespace);
|
|
1840
|
+
}
|
|
1806
1841
|
}
|
|
1807
1842
|
}
|
|
1843
|
+
for (const closedSessionKey of browserRunState.closedManagedSessionNames) {
|
|
1844
|
+
if (handledBatchCloseKeys.has(closedSessionKey))
|
|
1845
|
+
continue;
|
|
1846
|
+
const reservation = activeRecordingReservations.get(closedSessionKey);
|
|
1847
|
+
if (reservation)
|
|
1848
|
+
retireRecordingSession(reservation.sessionName, reservation.namespace);
|
|
1849
|
+
}
|
|
1850
|
+
if (resultSessionName) {
|
|
1851
|
+
const reservation = activeRecordingReservations.get(getAgentBrowserSessionIdentityKey(resultSessionName, resultNamespace));
|
|
1852
|
+
if (reservation)
|
|
1853
|
+
result = appendActiveRecordingCleanupAction(result, reservation);
|
|
1854
|
+
}
|
|
1855
|
+
if (artifactManifest) {
|
|
1856
|
+
result = {
|
|
1857
|
+
...result,
|
|
1858
|
+
details: {
|
|
1859
|
+
...(isRecord(result.details) ? result.details : {}),
|
|
1860
|
+
artifactManifest: redactSensitiveValue(artifactManifest),
|
|
1861
|
+
artifactRetentionSummary: formatSessionArtifactRetentionSummary(artifactManifest),
|
|
1862
|
+
},
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1808
1865
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
result
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
}
|
|
1832
|
-
const branchStateStillCurrent = generationAtStart === branchStateGeneration;
|
|
1833
|
-
if (serializeBrowserCommand || branchStateStillCurrent) {
|
|
1834
|
-
freshSessionOrdinal = Math.max(freshSessionOrdinal, browserRunState.freshSessionOrdinal);
|
|
1835
|
-
managedSessionActive = browserRunState.managedSessionActive;
|
|
1836
|
-
managedSessionCompatibilityWorkaround = browserRunState.managedSessionCompatibilityWorkaround;
|
|
1837
|
-
managedSessionHeadedAutosaveDisabled = browserRunState.managedSessionHeadedAutosaveDisabled === true;
|
|
1838
|
-
managedSessionHeadedAutosaveInterval = browserRunState.managedSessionHeadedAutosaveInterval;
|
|
1839
|
-
managedSessionCwd = browserRunState.managedSessionCwd;
|
|
1840
|
-
managedSessionName = browserRunState.managedSessionName;
|
|
1841
|
-
managedSessionNamespace = browserRunState.managedSessionNamespace;
|
|
1842
|
-
for (const closedSessionName of browserRunState.closedManagedSessionNames) {
|
|
1843
|
-
untrackOwnedManagedSession(ownedManagedSessions, closedSessionName);
|
|
1866
|
+
const branchStateStillCurrent = generationAtStart === branchStateGeneration;
|
|
1867
|
+
if (serializeBrowserCommand || branchStateStillCurrent) {
|
|
1868
|
+
freshSessionOrdinal = Math.max(freshSessionOrdinal, browserRunState.freshSessionOrdinal);
|
|
1869
|
+
managedSessionActive = browserRunState.managedSessionActive;
|
|
1870
|
+
managedSessionCompatibilityWorkaround = browserRunState.managedSessionCompatibilityWorkaround;
|
|
1871
|
+
managedSessionHeadedAutosaveDisabled = browserRunState.managedSessionHeadedAutosaveDisabled === true;
|
|
1872
|
+
managedSessionHeadedAutosaveInterval = browserRunState.managedSessionHeadedAutosaveInterval;
|
|
1873
|
+
managedSessionCwd = browserRunState.managedSessionCwd;
|
|
1874
|
+
managedSessionName = browserRunState.managedSessionName;
|
|
1875
|
+
managedSessionNamespace = browserRunState.managedSessionNamespace;
|
|
1876
|
+
for (const closedSessionName of browserRunState.closedManagedSessionNames) {
|
|
1877
|
+
untrackOwnedManagedSession(ownedManagedSessions, closedSessionName);
|
|
1878
|
+
}
|
|
1879
|
+
syncOwnedManagedSessionsFromResult(ownedManagedSessions, result, browserRunState.managedSessionCwd);
|
|
1880
|
+
mergeActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronLaunchRecords, {
|
|
1881
|
+
branchOwnedLaunchIds: branchOwnedElectronLaunchIds,
|
|
1882
|
+
touchedLaunchIds: !result.isError
|
|
1883
|
+
? getTouchedElectronLaunchIds(explicitSessionName ?? browserRunState.managedSessionName, electronLaunchRecords, resultNamespace)
|
|
1884
|
+
: undefined,
|
|
1885
|
+
});
|
|
1886
|
+
if (serializeBrowserCommand)
|
|
1887
|
+
branchStateGeneration += 1;
|
|
1844
1888
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
const plan = buildExecutionPlan(toolArgs, {
|
|
1863
|
-
freshSessionName: createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal + 1),
|
|
1864
|
-
managedSessionActive,
|
|
1865
|
-
managedSessionCompatibilityWorkaround,
|
|
1866
|
-
managedSessionName,
|
|
1867
|
-
managedSessionNamespace,
|
|
1868
|
-
sessionMode: params.sessionMode ?? "auto",
|
|
1869
|
-
stdin: resolvedInput.toolStdin,
|
|
1889
|
+
return applyAgentBrowserOutputPath({ cwd: operationCwd, outputPath, preserveTextContent: Array.isArray(params.args) && params.args.includes("--json"), result: warnRecordingPersistence(result) });
|
|
1890
|
+
};
|
|
1891
|
+
const closesAllSessions = commandClosesAllSessions(toolArgs, resolvedInput.toolStdin);
|
|
1892
|
+
const runWithLaunchDefaults = () => withLaunchDefaults ? withLaunchDefaults(runBrowserCommand) : runBrowserCommand();
|
|
1893
|
+
const runWithinSessionQueue = () => {
|
|
1894
|
+
if (closesAllSessions)
|
|
1895
|
+
return withManagedQueue(() => {
|
|
1896
|
+
const plan = buildExecutionPlan(toolArgs, {
|
|
1897
|
+
freshSessionName: createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal + 1),
|
|
1898
|
+
managedSessionActive,
|
|
1899
|
+
managedSessionCompatibilityWorkaround,
|
|
1900
|
+
managedSessionName,
|
|
1901
|
+
managedSessionNamespace,
|
|
1902
|
+
sessionMode: params.sessionMode ?? "auto",
|
|
1903
|
+
stdin: resolvedInput.toolStdin,
|
|
1904
|
+
});
|
|
1905
|
+
return callerOwnedSessionExecutionQueues.runExclusive(plan.namespace, runWithLaunchDefaults);
|
|
1870
1906
|
});
|
|
1871
|
-
|
|
1907
|
+
if (serializeBrowserCommand)
|
|
1908
|
+
return withManagedQueue(runWithLaunchDefaults);
|
|
1909
|
+
return callerOwnedSessionQueueKey
|
|
1910
|
+
? callerOwnedSessionExecutionQueues.run(callerOwnedSessionQueueKey, callerOwnedSessionNamespace, runWithLaunchDefaults)
|
|
1911
|
+
: runWithLaunchDefaults();
|
|
1912
|
+
};
|
|
1913
|
+
if (!commandTouchesArtifactLifecycle(toolArgs, resolvedInput.toolStdin, outputPath))
|
|
1914
|
+
return runWithinSessionQueue();
|
|
1915
|
+
return withArtifactQueue(async () => {
|
|
1916
|
+
const artifactValidationError = getArtifactPreflightValidationError({
|
|
1917
|
+
activeRecordingReservations: activeRecordingReservations.values(),
|
|
1918
|
+
args: toolArgs,
|
|
1919
|
+
cwd: operationCwd,
|
|
1920
|
+
outputPath,
|
|
1921
|
+
stdin: resolvedInput.toolStdin,
|
|
1872
1922
|
});
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
cwd: ctx.cwd,
|
|
1886
|
-
outputPath,
|
|
1887
|
-
stdin: resolvedInput.toolStdin,
|
|
1923
|
+
if (!artifactValidationError)
|
|
1924
|
+
return runWithinSessionQueue();
|
|
1925
|
+
flushRecordingReservations();
|
|
1926
|
+
return warnRecordingPersistence(buildValidationFailureResult({
|
|
1927
|
+
attemptedKind: resolvedInput.kind,
|
|
1928
|
+
kind: "invalid",
|
|
1929
|
+
redactedArgs: resolvedInput.redactedArgs,
|
|
1930
|
+
status: "invalid",
|
|
1931
|
+
toolArgs: resolvedInput.toolArgs,
|
|
1932
|
+
toolStdin: resolvedInput.toolStdin,
|
|
1933
|
+
validationError: artifactValidationError,
|
|
1934
|
+
}));
|
|
1888
1935
|
});
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
attemptedKind: resolvedInput.kind,
|
|
1894
|
-
kind: "invalid",
|
|
1895
|
-
redactedArgs: resolvedInput.redactedArgs,
|
|
1896
|
-
status: "invalid",
|
|
1897
|
-
toolArgs: resolvedInput.toolArgs,
|
|
1898
|
-
toolStdin: resolvedInput.toolStdin,
|
|
1899
|
-
validationError: artifactValidationError,
|
|
1900
|
-
}));
|
|
1936
|
+
}, managedSessionActive || freshSessionOrdinal > 0 || params.sessionMode === "fresh" || pendingReadConfirmation ? undefined : {
|
|
1937
|
+
id: rootSessionId,
|
|
1938
|
+
profile: rootProfile?.policy === "always" && !/[\\/~]/.test(rootProfile.name) ? rootProfile.name : undefined,
|
|
1939
|
+
executablePath: runtimeBrowserConfig.trustedBrowserExecutablePath,
|
|
1901
1940
|
});
|
|
1902
|
-
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1941
|
+
};
|
|
1942
|
+
if (!managedAtAdmission)
|
|
1943
|
+
return executeAdmitted();
|
|
1944
|
+
const executeManaged = () => managedSessionExecutionQueue.run(executeAdmitted);
|
|
1945
|
+
return artifactsAtAdmission ? artifactExecutionQueue.run(executeManaged) : executeManaged();
|
|
1907
1946
|
},
|
|
1908
1947
|
};
|
|
1909
1948
|
pi.registerTool(beforeExecute ? { ...agentBrowserTool, executionMode: "sequential" } : agentBrowserTool);
|