pi-agent-browser-native 0.6.15 → 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 +27 -1
- package/README.md +22 -4
- package/dist/extensions/agent-browser/index.js +551 -437
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +8 -4
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +21 -6
- 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/managed-session-policy-lock.js +47 -11
- 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/dist/extensions/agent-browser/lib/process-identity.js +2 -1
- package/dist/extensions/agent-browser/lib/process.js +56 -20
- package/dist/extensions/agent-browser/lib/runtime.js +1 -1
- package/dist/extensions/agent-browser/lib/windows-stock-launcher.js +70 -0
- package/docs/ARCHITECTURE.md +14 -1
- package/docs/COMMAND_REFERENCE.md +2 -2
- package/docs/ELECTRON.md +4 -2
- package/docs/RELEASE.md +35 -1
- package/docs/SUPPORT_MATRIX.md +13 -3
- package/docs/TOOL_CONTRACT.md +11 -3
- package/docs/platform-smoke.md +5 -3
- package/package.json +10 -6
- package/scripts/config.mjs +1 -1
- package/scripts/doctor.mjs +2 -2
|
@@ -6,14 +6,17 @@ 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
|
-
import { ManagedSessionRestoreState } from "./lib/managed-session-restore.js";
|
|
12
|
+
import { ManagedSessionRestoreState, resolveOwnedManagedSessionContext, withOwnedManagedSessionContext } from "./lib/managed-session-restore.js";
|
|
13
13
|
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";
|
|
19
|
+
import { closeManagedSession, inspectManagedSessionDaemon } from "./lib/orchestration/browser-run/managed-session-daemon-policy.js";
|
|
17
20
|
import { MINIMUM_AGENT_BROWSER_VERSION, SUPPORTED_AGENT_BROWSER_VERSION_LABEL, TARGET_AGENT_BROWSER_VERSION, getAgentBrowserVersionValidationError, parseAgentBrowserVersionOutput, } from "./lib/upstream-version.js";
|
|
18
21
|
import { buildPromptPolicy, getLatestUserPrompt, shouldAppendBrowserSystemPrompt } from "./lib/prompt-policy.js";
|
|
19
22
|
import { isCloseAllCommand, isCloseCommand } from "./lib/command-taxonomy.js";
|
|
@@ -21,9 +24,8 @@ import { hasLaunchScopedFlagToken } from "./lib/launch-scoped-flags.js";
|
|
|
21
24
|
import { cleanupSecureTempArtifacts } from "./lib/temp.js";
|
|
22
25
|
import { AGENT_BROWSER_PARAMS } from "./lib/input-modes/params.js";
|
|
23
26
|
import { AGENT_BROWSER_SCRIPT_DEFAULT_TIMEOUT_MS, AGENT_BROWSER_SCRIPT_NAMESPACE, createAgentBrowserScriptSessionName, isAgentBrowserScriptSessionName, runAgentBrowserScript, } from "./lib/input-modes/script.js";
|
|
24
|
-
import { closeManagedSession, getSessionContextKey, runAgentBrowserTool } from "./lib/orchestration/browser-run/index.js";
|
|
25
27
|
import { canonicalizeExplicitArtifactDestination, getExplicitArtifactDestination, getRecordContactSheetDestination } from "./lib/orchestration/browser-run/artifact-paths.js";
|
|
26
|
-
import { findElectronLaunchRecordForSession, getActiveElectronRecords } from "./lib/orchestration/browser-run/session-state.js";
|
|
28
|
+
import { findElectronLaunchRecordForSession, getActiveElectronRecords, getSessionContextKey } from "./lib/orchestration/browser-run/session-state.js";
|
|
27
29
|
import { parseBatchCommandArgument, parseUserBatchStdin } from "./lib/orchestration/batch-stdin.js";
|
|
28
30
|
import { ELECTRON_POST_COMMAND_STATUS_SETTLE_MS, ELECTRON_PROFILE_ISOLATION_DETAILS, cleanupActiveElectronHostLaunches, handleElectronHostInput, restoreElectronLaunchRecordsFromBranch, } from "./lib/orchestration/electron-host/index.js";
|
|
29
31
|
import { buildValidationFailureResult, resolveAgentBrowserInput } from "./lib/orchestration/input-plan.js";
|
|
@@ -571,7 +573,7 @@ function collectBranchManagedResourceEvents(branch) {
|
|
|
571
573
|
const replacedSessionNamespace = typeof outcome.replacedSessionNamespace === "string" ? outcome.replacedSessionNamespace : namespace;
|
|
572
574
|
setBranchRankForString(events.managedSessionCloseRanks, getSessionContextKey(typeof outcome.replacedSessionName === "string" ? outcome.replacedSessionName : undefined, replacedSessionNamespace), eventRank);
|
|
573
575
|
}
|
|
574
|
-
if (succeeded && !isCloseCommand(command) && sessionName && (usedImplicitSession || sessionMode === "fresh" || details.managedSessionHeadedAutosaveDisabled === true || typeof details.managedSessionHeadedAutosaveInterval === "string")) {
|
|
576
|
+
if (succeeded && !isCloseCommand(command) && sessionName && ((!explicitSessionName && (usedImplicitSession || sessionMode === "fresh")) || details.managedSessionHeadedAutosaveDisabled === true || typeof details.managedSessionHeadedAutosaveInterval === "string")) {
|
|
575
577
|
setBranchManagedSessionActive(events, sessionName, namespace, eventRank);
|
|
576
578
|
}
|
|
577
579
|
if (succeeded && isCloseCommand(command)) {
|
|
@@ -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,6 +1245,78 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1243
1245
|
notifyRecordingPersistence(ctx);
|
|
1244
1246
|
}));
|
|
1245
1247
|
});
|
|
1248
|
+
// Fork-only checkpoint event; official Pi validation types do not declare it.
|
|
1249
|
+
// Pi owns awaited tools/events (including their execution queues). Only script
|
|
1250
|
+
// children and retained browser/recording resources need extension-side checks.
|
|
1251
|
+
const checkpointPi = pi;
|
|
1252
|
+
checkpointPi.on("session_checkpoint", async (event, ctx) => {
|
|
1253
|
+
const blocked = (reason) => ({ sleepReady: false, reason });
|
|
1254
|
+
if (activeScriptControllers.size || activeScriptExecutions.size)
|
|
1255
|
+
return blocked("Browser script execution or cleanup is pending");
|
|
1256
|
+
flushRecordingReservations();
|
|
1257
|
+
if (recordingReservationsDirty)
|
|
1258
|
+
return blocked("Browser recording journal persistence is dirty");
|
|
1259
|
+
if (activeRecordingReservations.size)
|
|
1260
|
+
return blocked("Browser recording is pending; finish it explicitly before sleep");
|
|
1261
|
+
const entries = ctx.sessionManager.getEntries();
|
|
1262
|
+
if ([...getScriptSessionLeasesFromBranch(entries).values()].some(lease => lease.cleanup !== "closed")
|
|
1263
|
+
|| [...ownedManagedSessions.values()].some(owner => isAgentBrowserScriptSessionName(owner.sessionName))) {
|
|
1264
|
+
return blocked("Browser script cleanup lease is unresolved");
|
|
1265
|
+
}
|
|
1266
|
+
if (getActiveElectronRecords(ownedElectronLaunchRecords).length || getActiveElectronRecords(electronLaunchRecords).length
|
|
1267
|
+
|| [...electronChildProcesses.values()].some(child => child.exitCode === null && child.signalCode === null)) {
|
|
1268
|
+
return blocked("Electron launch is still active");
|
|
1269
|
+
}
|
|
1270
|
+
if (attachedSessionKeys.size || restoreAttachedSessionKeysFromBranch(entries).size)
|
|
1271
|
+
return blocked("Attached browser state is caller-owned and not checkpointed");
|
|
1272
|
+
if (traceOwners.size || [...networkRoutesBySession.values()].some(routes => routes.length))
|
|
1273
|
+
return blocked("Browser trace or network routes are still active");
|
|
1274
|
+
// Include off-branch and caller-owned/root identities, without acquiring
|
|
1275
|
+
// cleanup ownership. Transcript page/ref details do not serialize a browser.
|
|
1276
|
+
const historicalResources = collectBranchManagedResourceEvents(entries);
|
|
1277
|
+
const sessions = new Map(ownedManagedSessions);
|
|
1278
|
+
if (managedSessionActive)
|
|
1279
|
+
trackOwnedManagedSession(sessions, managedSessionName, managedSessionCwd, { namespace: managedSessionNamespace });
|
|
1280
|
+
for (const entry of entries) {
|
|
1281
|
+
if (entry.type !== "message" || entry.message.role !== "toolResult" || entry.message.toolName !== "agent_browser")
|
|
1282
|
+
continue;
|
|
1283
|
+
const details = isRecord(entry.message.details) ? entry.message.details : undefined;
|
|
1284
|
+
// Helpers can launch before a main-process failure. Conversely, native
|
|
1285
|
+
// launch flags on sessionless reads can open the unnamed default browser.
|
|
1286
|
+
const sessionName = typeof details?.sessionName === "string" ? details.sessionName
|
|
1287
|
+
: details?.agentBrowserStarted === true ? "default" : undefined;
|
|
1288
|
+
if (!sessionName)
|
|
1289
|
+
continue;
|
|
1290
|
+
const namespace = typeof details?.namespace === "string" ? details.namespace : undefined;
|
|
1291
|
+
const key = getSessionContextKey(sessionName, namespace) ?? sessionName;
|
|
1292
|
+
if (!sessions.has(key))
|
|
1293
|
+
trackOwnedManagedSession(sessions, sessionName, ctx.cwd, { namespace });
|
|
1294
|
+
}
|
|
1295
|
+
for (const [key, owner] of sessions) {
|
|
1296
|
+
event.signal.throwIfAborted();
|
|
1297
|
+
// An abnormal restart can leave a wrapper-created browser only off-branch.
|
|
1298
|
+
// Reuse native ownership events for inspection, never cleanup ownership.
|
|
1299
|
+
// A later close can refer to the same identity in a different socket root.
|
|
1300
|
+
const historicalIdentity = historicalResources.managedSessionActiveIdentities.get(key);
|
|
1301
|
+
const historicallyOwned = historicalIdentity && isRestorableManagedSessionName(historicalIdentity.sessionName, managedSessionBaseName);
|
|
1302
|
+
const context = resolveOwnedManagedSessionContext({
|
|
1303
|
+
...owner,
|
|
1304
|
+
currentManagedSessionName: managedSessionActive ? managedSessionName : undefined,
|
|
1305
|
+
currentManagedSessionNamespace: managedSessionNamespace,
|
|
1306
|
+
recordedOwnedSession: ownedManagedSessions.get(key) ?? (historicallyOwned ? { ...historicalIdentity, cwd: ctx.cwd } : undefined),
|
|
1307
|
+
restoreState: managedSessionRestoreState,
|
|
1308
|
+
});
|
|
1309
|
+
// Owned routing must not mask an explicit caller's same-name ambient daemon.
|
|
1310
|
+
for (const inspectionContext of context ? [{ ...context, reuseOnly: true }, undefined] : [undefined]) {
|
|
1311
|
+
const daemon = await withOwnedManagedSessionContext(inspectionContext, () => inspectManagedSessionDaemon({ ...owner, signal: event.signal, timeoutMs: 2_000 }));
|
|
1312
|
+
if (daemon.status !== "inactive")
|
|
1313
|
+
return blocked("Browser daemon is live or unverified; finish browser work explicitly before sleep");
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
// No detached writer remains to pause/resume. Native ingress stays held;
|
|
1317
|
+
// checkpoint never closes a browser or changes ordinary shutdown ownership.
|
|
1318
|
+
return { sleepReady: true };
|
|
1319
|
+
});
|
|
1246
1320
|
pi.on("session_shutdown", async (event, ctx) => {
|
|
1247
1321
|
for (const controller of activeScriptControllers)
|
|
1248
1322
|
controller.abort();
|
|
@@ -1361,474 +1435,514 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1361
1435
|
component.setState(formatAgentBrowserRenderResult(result, options, theme, context.isError), options.expanded, theme);
|
|
1362
1436
|
return component;
|
|
1363
1437
|
},
|
|
1364
|
-
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
|
+
}
|
|
1365
1446
|
const promptPolicy = buildPromptPolicy(getLatestUserPrompt(ctx.sessionManager.getBranch()));
|
|
1366
1447
|
const outputPath = isRecord(params) && typeof params.outputPath === "string" ? params.outputPath : undefined;
|
|
1367
|
-
const
|
|
1368
|
-
getBatchPreflightValidationError: (args, stdin) => getArtifactPreflightValidationError({ args, cwd:
|
|
1448
|
+
const admittedInput = resolveAgentBrowserInput({
|
|
1449
|
+
getBatchPreflightValidationError: (args, stdin) => getArtifactPreflightValidationError({ args, cwd: operationCwd, outputPath, stdin }),
|
|
1369
1450
|
params,
|
|
1370
1451
|
});
|
|
1371
|
-
if (
|
|
1372
|
-
return buildValidationFailureResult(
|
|
1452
|
+
if (admittedInput.status === "invalid") {
|
|
1453
|
+
return buildValidationFailureResult(admittedInput);
|
|
1373
1454
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
const
|
|
1377
|
-
|
|
1378
|
-
const
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
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 };
|
|
1387
1475
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
}
|
|
1402
|
-
return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, preserveTextContent, result: warnRecordingPersistence(result) });
|
|
1403
|
-
});
|
|
1404
|
-
};
|
|
1405
|
-
const versionCheckCommand = extractUpstreamCommandTokens(resolvedInput.toolArgs)[0];
|
|
1406
|
-
const electronHostOnlyAction = resolvedInput.kind === "electron" && ["cleanup", "list", "status"].includes(resolvedInput.compiledElectron.action);
|
|
1407
|
-
const browserBackedVersionCheck = readConfirmation?.capabilities?.readRequiresConfirmation !== true && needsManagedSession(parseArgvDescriptor(resolvedInput.toolArgs), resolvedInput.toolStdin);
|
|
1408
|
-
if (resolvedInput.kind !== "script" && !electronHostOnlyAction && browserBackedVersionCheck && !isPlainTextInspectionArgs(resolvedInput.toolArgs) && !isCloseCommand(versionCheckCommand) && signal?.aborted !== true) {
|
|
1409
|
-
const versionFailure = await validateUpstreamVersion(ctx.cwd, signal);
|
|
1410
|
-
if (versionFailure)
|
|
1411
|
-
return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, result: versionFailure });
|
|
1412
|
-
}
|
|
1413
|
-
if (resolvedInput.kind === "script") {
|
|
1414
|
-
if (!ctx.sessionManager.getSessionFile()) {
|
|
1415
|
-
return buildValidationFailureResult({
|
|
1416
|
-
attemptedKind: "script",
|
|
1417
|
-
kind: "invalid",
|
|
1418
|
-
redactedArgs: [],
|
|
1419
|
-
status: "invalid",
|
|
1420
|
-
toolArgs: [],
|
|
1421
|
-
validationError: "script requires a persisted Pi session so its isolated browser-session cleanup lease survives restart; relaunch Pi without --no-session.",
|
|
1422
|
-
});
|
|
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." });
|
|
1423
1489
|
}
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
+
});
|
|
1438
1506
|
};
|
|
1439
|
-
const
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1442
|
-
if (signal?.aborted)
|
|
1443
|
-
|
|
1444
|
-
activeScriptControllers.add(scriptController);
|
|
1445
|
-
let finishScriptExecution;
|
|
1446
|
-
const scriptExecution = new Promise((resolve) => {
|
|
1447
|
-
finishScriptExecution = resolve;
|
|
1448
|
-
});
|
|
1449
|
-
activeScriptExecutions.add(scriptExecution);
|
|
1450
|
-
try {
|
|
1451
|
-
// Keep preflight inside shutdown tracking so quit cannot race into starting the sandbox afterward.
|
|
1452
|
-
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);
|
|
1453
1512
|
if (versionFailure)
|
|
1454
|
-
return applyAgentBrowserOutputPath({ cwd:
|
|
1455
|
-
const pendingRun = runAgentBrowserScript({
|
|
1456
|
-
beforeFirstCall() {
|
|
1457
|
-
appendScriptSessionLease(pi, sessionName, "active");
|
|
1458
|
-
trackOwnedManagedSession(ownedManagedSessions, sessionName, ctx.cwd, { namespace: AGENT_BROWSER_SCRIPT_NAMESPACE });
|
|
1459
|
-
managedSessionRestoreState.disable(sessionName, AGENT_BROWSER_SCRIPT_NAMESPACE);
|
|
1460
|
-
// This fresh, unselectable lease owns any daemon its first read starts.
|
|
1461
|
-
managedSessionRestoreState.recordDaemonRestoreKey(sessionName, AGENT_BROWSER_SCRIPT_NAMESPACE, null);
|
|
1462
|
-
leased = true;
|
|
1463
|
-
},
|
|
1464
|
-
code: resolvedInput.compiledScript.code,
|
|
1465
|
-
dispatch: async (innerParams, innerSignal) => {
|
|
1466
|
-
const remainingMs = Math.max(1, deadline - Date.now());
|
|
1467
|
-
const innerTimeoutMs = Math.min(innerParams.timeoutMs ?? remainingMs, remainingMs);
|
|
1468
|
-
const innerResult = await withIsolatedAgentBrowserEnvironment(() => agentBrowserTool.execute(`${toolCallId}:script:${innerResults.length + 1}`, {
|
|
1469
|
-
args: ["--namespace", AGENT_BROWSER_SCRIPT_NAMESPACE, "--session", sessionName, ...innerParams.args],
|
|
1470
|
-
stdin: innerParams.stdin,
|
|
1471
|
-
timeoutMs: innerTimeoutMs,
|
|
1472
|
-
}, innerSignal, undefined, ctx, nativeToolCallId));
|
|
1473
|
-
innerResults.push(innerResult);
|
|
1474
|
-
return await buildScriptBrowserEnvelope(innerResult, innerParams.args, sessionName);
|
|
1475
|
-
},
|
|
1476
|
-
signal: scriptController.signal,
|
|
1477
|
-
timeoutMs: scriptTimeoutMs,
|
|
1478
|
-
});
|
|
1479
|
-
run = await pendingRun;
|
|
1513
|
+
return applyAgentBrowserOutputPath({ cwd: operationCwd, outputPath, result: versionFailure });
|
|
1480
1514
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
}
|
|
1492
|
-
catch {
|
|
1493
|
-
cleanupError = "The isolated script session cleanup operation failed.";
|
|
1494
|
-
try {
|
|
1495
|
-
appendScriptSessionLease(pi, sessionName, "failed");
|
|
1496
|
-
}
|
|
1497
|
-
catch { }
|
|
1498
|
-
}
|
|
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
|
+
});
|
|
1499
1525
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
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: [],
|
|
1512
1540
|
};
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
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;
|
|
1554
1582
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
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
|
+
}
|
|
1574
1601
|
}
|
|
1602
|
+
activeScriptExecutions.delete(scriptExecution);
|
|
1603
|
+
finishScriptExecution();
|
|
1575
1604
|
}
|
|
1605
|
+
let scriptResult = buildScriptToolResult({ cleanupError, innerResults, run, sessionName: leased ? sessionName : undefined });
|
|
1576
1606
|
if (artifactManifest) {
|
|
1577
|
-
|
|
1578
|
-
...
|
|
1607
|
+
scriptResult = {
|
|
1608
|
+
...scriptResult,
|
|
1579
1609
|
details: {
|
|
1580
|
-
...(isRecord(
|
|
1610
|
+
...(isRecord(scriptResult.details) ? scriptResult.details : {}),
|
|
1581
1611
|
artifactManifest: redactSensitiveValue(artifactManifest),
|
|
1582
1612
|
artifactRetentionSummary: formatSessionArtifactRetentionSummary(artifactManifest),
|
|
1583
1613
|
},
|
|
1584
1614
|
};
|
|
1585
1615
|
}
|
|
1616
|
+
return applyUnserializedOutputPath(scriptResult);
|
|
1586
1617
|
}
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
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,
|
|
1598
1630
|
cwd: ctx.cwd,
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
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
|
+
}
|
|
1603
1688
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
const
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
electronChildProcesses,
|
|
1636
|
-
electronLaunchRecords,
|
|
1637
|
-
ephemeralSessionSeed,
|
|
1638
|
-
freshSessionOrdinal,
|
|
1639
|
-
managedSessionActive,
|
|
1640
|
-
managedSessionBaseName,
|
|
1641
|
-
managedSessionCompatibilityWorkaround,
|
|
1642
|
-
managedSessionHeadedAutosaveDisabled,
|
|
1643
|
-
managedSessionHeadedAutosaveInterval,
|
|
1644
|
-
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,
|
|
1645
1720
|
managedSessionName,
|
|
1646
|
-
|
|
1647
|
-
managedSessionRestoreState,
|
|
1648
|
-
networkRoutesBySession,
|
|
1721
|
+
ownedElectronLaunchRecords,
|
|
1649
1722
|
ownedManagedSessions,
|
|
1650
|
-
sessionPageState,
|
|
1651
|
-
traceOwners,
|
|
1652
|
-
};
|
|
1653
|
-
const selectedPlan = buildExecutionPlan(toolArgs, {
|
|
1654
|
-
freshSessionName: createFreshSessionName(browserRunState.managedSessionBaseName, browserRunState.ephemeralSessionSeed, browserRunState.freshSessionOrdinal + 1),
|
|
1655
|
-
managedSessionActive: browserRunState.managedSessionActive,
|
|
1656
|
-
managedSessionCompatibilityWorkaround: browserRunState.managedSessionCompatibilityWorkaround,
|
|
1657
|
-
managedSessionName: browserRunState.managedSessionName,
|
|
1658
|
-
managedSessionNamespace: browserRunState.managedSessionNamespace,
|
|
1659
|
-
sessionMode: compiledElectron?.action === "launch" ? "fresh" : params.sessionMode ?? "auto",
|
|
1660
|
-
stdin: resolvedInput.toolStdin,
|
|
1661
|
-
browserIndependentReadConfirmation: readConfirmation?.capabilities?.readRequiresConfirmation === true,
|
|
1662
1723
|
});
|
|
1663
|
-
const
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
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
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
const
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
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)) {
|
|
1712
1828
|
attachedSessionKeys.add(resultSessionKey);
|
|
1713
1829
|
result = { ...result, details: { ...(resultDetails ?? {}), attachedBrowserSession: true } };
|
|
1714
1830
|
}
|
|
1715
1831
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
artifactManifest = mergeBrowserRunArtifactManifest(artifactManifest, initialArtifactManifest, browserRunState.artifactManifest);
|
|
1726
|
-
const handledBatchCloseKeys = syncRecordingReservationsFromResult(result);
|
|
1727
|
-
if (resultDetails?.closeAllApplied === true) {
|
|
1728
|
-
for (const [sessionKey, reservation] of [...activeRecordingReservations]) {
|
|
1729
|
-
if (isAgentBrowserSessionIdentityKeyInNamespace(sessionKey, resultNamespace)) {
|
|
1730
|
-
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
|
+
}
|
|
1731
1841
|
}
|
|
1732
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
|
+
}
|
|
1733
1865
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
result
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
}
|
|
1757
|
-
const branchStateStillCurrent = generationAtStart === branchStateGeneration;
|
|
1758
|
-
if (serializeBrowserCommand || branchStateStillCurrent) {
|
|
1759
|
-
freshSessionOrdinal = Math.max(freshSessionOrdinal, browserRunState.freshSessionOrdinal);
|
|
1760
|
-
managedSessionActive = browserRunState.managedSessionActive;
|
|
1761
|
-
managedSessionCompatibilityWorkaround = browserRunState.managedSessionCompatibilityWorkaround;
|
|
1762
|
-
managedSessionHeadedAutosaveDisabled = browserRunState.managedSessionHeadedAutosaveDisabled === true;
|
|
1763
|
-
managedSessionHeadedAutosaveInterval = browserRunState.managedSessionHeadedAutosaveInterval;
|
|
1764
|
-
managedSessionCwd = browserRunState.managedSessionCwd;
|
|
1765
|
-
managedSessionName = browserRunState.managedSessionName;
|
|
1766
|
-
managedSessionNamespace = browserRunState.managedSessionNamespace;
|
|
1767
|
-
for (const closedSessionName of browserRunState.closedManagedSessionNames) {
|
|
1768
|
-
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;
|
|
1769
1888
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
const plan = buildExecutionPlan(toolArgs, {
|
|
1788
|
-
freshSessionName: createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal + 1),
|
|
1789
|
-
managedSessionActive,
|
|
1790
|
-
managedSessionCompatibilityWorkaround,
|
|
1791
|
-
managedSessionName,
|
|
1792
|
-
managedSessionNamespace,
|
|
1793
|
-
sessionMode: params.sessionMode ?? "auto",
|
|
1794
|
-
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);
|
|
1795
1906
|
});
|
|
1796
|
-
|
|
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,
|
|
1797
1922
|
});
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
cwd: ctx.cwd,
|
|
1811
|
-
outputPath,
|
|
1812
|
-
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
|
+
}));
|
|
1813
1935
|
});
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
attemptedKind: resolvedInput.kind,
|
|
1819
|
-
kind: "invalid",
|
|
1820
|
-
redactedArgs: resolvedInput.redactedArgs,
|
|
1821
|
-
status: "invalid",
|
|
1822
|
-
toolArgs: resolvedInput.toolArgs,
|
|
1823
|
-
toolStdin: resolvedInput.toolStdin,
|
|
1824
|
-
validationError: artifactValidationError,
|
|
1825
|
-
}));
|
|
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,
|
|
1826
1940
|
});
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1941
|
+
};
|
|
1942
|
+
if (!managedAtAdmission)
|
|
1943
|
+
return executeAdmitted();
|
|
1944
|
+
const executeManaged = () => managedSessionExecutionQueue.run(executeAdmitted);
|
|
1945
|
+
return artifactsAtAdmission ? artifactExecutionQueue.run(executeManaged) : executeManaged();
|
|
1832
1946
|
},
|
|
1833
1947
|
};
|
|
1834
1948
|
pi.registerTool(beforeExecute ? { ...agentBrowserTool, executionMode: "sequential" } : agentBrowserTool);
|