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.
@@ -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, ctx.cwd, {
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 resolvedInput = resolveAgentBrowserInput({
1368
- getBatchPreflightValidationError: (args, stdin) => getArtifactPreflightValidationError({ args, cwd: ctx.cwd, outputPath, stdin }),
1448
+ const admittedInput = resolveAgentBrowserInput({
1449
+ getBatchPreflightValidationError: (args, stdin) => getArtifactPreflightValidationError({ args, cwd: operationCwd, outputPath, stdin }),
1369
1450
  params,
1370
1451
  });
1371
- if (resolvedInput.status === "invalid") {
1372
- return buildValidationFailureResult(resolvedInput);
1452
+ if (admittedInput.status === "invalid") {
1453
+ return buildValidationFailureResult(admittedInput);
1373
1454
  }
1374
- if (resolvedInput.kind !== "script")
1375
- await beforeExecute?.(nativeToolCallId, { ...ctx, signal });
1376
- const runtimeBrowserConfig = loadAgentBrowserConfigSync({ cwd: ctx.cwd, includeProjectConfig: shouldIncludeProjectConfig(ctx) });
1377
- const rootProfile = runtimeBrowserConfig.trustedBrowserDefaultProfile;
1378
- const pendingReadConfirmation = sessionPageState.findReadConfirmation(resolvedInput.toolArgs, resolveAgentBrowserNamespace(resolvedInput.toolArgs, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE));
1379
- const rootSessionId = process.env.PI_SUBAGENT_CHILD === "1" && process.env.PI_SUBAGENT_ROOT_SESSION_ID
1380
- ? process.env.PI_SUBAGENT_ROOT_SESSION_ID : ctx.sessionManager.getSessionId();
1381
- return withNativeSessionDefaults(resolvedInput, ctx.cwd, signal, async (resolvedInput, withLaunchDefaults) => {
1382
- const readConfirmation = sessionPageState.findReadConfirmation(resolvedInput.toolArgs, resolveAgentBrowserNamespace(resolvedInput.toolArgs, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE));
1383
- if (readConfirmation)
1384
- resolvedInput = { ...resolvedInput, toolArgs: scopeReadConfirmationArgs(resolvedInput.toolArgs, readConfirmation) };
1385
- if (resolvedInput.kind === "qa" && resolvedInput.compiledQaPreset.checks.attached && !managedSessionActive && !extractExplicitSessionName(resolvedInput.toolArgs)) {
1386
- 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." });
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
- const applyUnserializedOutputPath = async (result, preserveTextContent = false) => {
1389
- if (!outputPath || !canWriteAgentBrowserOutput(result))
1390
- return warnRecordingPersistence(result);
1391
- return artifactExecutionQueue.run(async () => {
1392
- flushRecordingReservations();
1393
- const reservationError = getArtifactPreflightValidationError({
1394
- activeRecordingReservations: activeRecordingReservations.values(),
1395
- args: [],
1396
- cwd: ctx.cwd,
1397
- outputPath,
1398
- });
1399
- if (reservationError) {
1400
- return warnRecordingPersistence(buildValidationFailureResult({ attemptedKind: resolvedInput.kind, kind: "invalid", redactedArgs: resolvedInput.redactedArgs, status: "invalid", toolArgs: resolvedInput.toolArgs, toolStdin: resolvedInput.toolStdin, validationError: reservationError }));
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 sessionName = createAgentBrowserScriptSessionName();
1425
- const innerResults = [];
1426
- const scriptTimeoutMs = params.timeoutMs ?? AGENT_BROWSER_SCRIPT_DEFAULT_TIMEOUT_MS;
1427
- const deadline = Date.now() + scriptTimeoutMs;
1428
- let leased = false;
1429
- let cleanupError;
1430
- let run = {
1431
- callCount: 0,
1432
- emitCount: 0,
1433
- error: "Script sandbox execution failed.",
1434
- failureCategory: "upstream-error",
1435
- ok: false,
1436
- rejectedCallCount: 0,
1437
- steps: [],
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 scriptController = new AbortController();
1440
- const abortScript = () => scriptController.abort();
1441
- signal?.addEventListener("abort", abortScript, { once: true });
1442
- if (signal?.aborted)
1443
- scriptController.abort();
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: ctx.cwd, outputPath, result: versionFailure });
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
- catch { }
1482
- finally {
1483
- activeScriptControllers.delete(scriptController);
1484
- signal?.removeEventListener("abort", abortScript);
1485
- if (leased) {
1486
- try {
1487
- cleanupError = await artifactExecutionQueue.run(() => managedSessionExecutionQueue.run(() => {
1488
- flushRecordingReservations();
1489
- return closeScriptSessionLeaseWithinQueue(sessionName, ctx.cwd);
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
- activeScriptExecutions.delete(scriptExecution);
1501
- finishScriptExecution();
1502
- }
1503
- let scriptResult = buildScriptToolResult({ cleanupError, innerResults, run, sessionName: leased ? sessionName : undefined });
1504
- if (artifactManifest) {
1505
- scriptResult = {
1506
- ...scriptResult,
1507
- details: {
1508
- ...(isRecord(scriptResult.details) ? scriptResult.details : {}),
1509
- artifactManifest: redactSensitiveValue(artifactManifest),
1510
- artifactRetentionSummary: formatSessionArtifactRetentionSummary(artifactManifest),
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
- return applyUnserializedOutputPath(scriptResult);
1515
- }
1516
- const { toolArgs } = resolvedInput;
1517
- const compiledElectron = resolvedInput.kind === "electron" ? resolvedInput.compiledElectron : undefined;
1518
- const redactedCompiledElectron = resolvedInput.kind === "electron" ? resolvedInput.redactedCompiledElectron : undefined;
1519
- const runElectronHostInput = async () => {
1520
- const electronHostLaunchRecords = getElectronHostLaunchRecordsForInput({
1521
- branchRecords: electronLaunchRecords,
1522
- compiledElectron,
1523
- ownedRecords: ownedElectronLaunchRecords,
1524
- });
1525
- let electronHostResult = await handleElectronHostInput({
1526
- attachedSessionKeys,
1527
- compiledElectron,
1528
- cwd: ctx.cwd,
1529
- electronChildProcesses,
1530
- electronLaunchRecords: electronHostLaunchRecords,
1531
- implicitSessionCloseTimeoutMs,
1532
- managedSessionActive,
1533
- managedSessionName,
1534
- managedSessionNamespace,
1535
- managedSessionRestoreState,
1536
- ownedManagedSessions,
1537
- redactedCompiledElectron,
1538
- sessionPageState,
1539
- signal,
1540
- });
1541
- if (electronHostResult && compiledElectron?.action === "cleanup") {
1542
- branchStateGeneration += 1;
1543
- const cleanupRecords = isRecord(electronHostResult.details)
1544
- && isRecord(electronHostResult.details.electron)
1545
- && isRecord(electronHostResult.details.electron.cleanup)
1546
- && Array.isArray(electronHostResult.details.electron.cleanup.results)
1547
- ? electronHostResult.details.electron.cleanup.results
1548
- : [];
1549
- const cleanedLaunchIds = new Set();
1550
- for (const cleanupResult of cleanupRecords) {
1551
- if (isRecord(cleanupResult) && isElectronLaunchRecord(cleanupResult.record)) {
1552
- cleanedLaunchIds.add(cleanupResult.record.launchId);
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
- replaceWithActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronHostLaunchRecords, branchOwnedElectronLaunchIds, cleanedLaunchIds);
1556
- mergeElectronCleanupRecords(electronLaunchRecords, cleanupRecords);
1557
- const cleanupNamespace = isRecord(electronHostResult.details) && typeof electronHostResult.details.namespace === "string"
1558
- ? electronHostResult.details.namespace
1559
- : undefined;
1560
- const closedSessionIdentities = getCleanupResultsClosedManagedSessionIdentities(cleanupRecords, cleanupNamespace);
1561
- syncElectronCleanupManagedSessions(ownedManagedSessions, cleanupRecords, cleanupNamespace);
1562
- for (const identity of closedSessionIdentities) {
1563
- retireRecordingSession(identity.sessionName, identity.namespace);
1564
- const closedSessionKey = getSessionContextKey(identity.sessionName, identity.namespace) ?? identity.sessionName;
1565
- clearSessionScopedBrowserState(closedSessionKey);
1566
- if (closedSessionKey === (getSessionContextKey(managedSessionName, managedSessionNamespace) ?? managedSessionName)) {
1567
- managedSessionActive = false;
1568
- managedSessionCompatibilityWorkaround = undefined;
1569
- managedSessionHeadedAutosaveDisabled = false;
1570
- managedSessionHeadedAutosaveInterval = undefined;
1571
- managedSessionNamespace = undefined;
1572
- freshSessionOrdinal += 1;
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
- electronHostResult = {
1578
- ...electronHostResult,
1607
+ scriptResult = {
1608
+ ...scriptResult,
1579
1609
  details: {
1580
- ...(isRecord(electronHostResult.details) ? electronHostResult.details : {}),
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
- return electronHostResult;
1588
- };
1589
- const runSerializedElectronHostInput = () => shouldSerializeElectronHostInput(compiledElectron)
1590
- ? managedSessionExecutionQueue.run(runElectronHostInput)
1591
- : runElectronHostInput();
1592
- const electronHostResult = compiledElectron?.action === "cleanup"
1593
- ? await artifactExecutionQueue.run(async () => {
1594
- flushRecordingReservations();
1595
- const reservationError = outputPath ? getArtifactPreflightValidationError({
1596
- activeRecordingReservations: activeRecordingReservations.values(),
1597
- args: [],
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
- outputPath,
1600
- }) : undefined;
1601
- if (reservationError) {
1602
- return warnRecordingPersistence(buildValidationFailureResult({ attemptedKind: resolvedInput.kind, kind: "invalid", redactedArgs: resolvedInput.redactedArgs, status: "invalid", toolArgs: resolvedInput.toolArgs, toolStdin: resolvedInput.toolStdin, validationError: reservationError }));
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
- const result = await runSerializedElectronHostInput();
1605
- return result ? applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, result: warnRecordingPersistence(result) }) : result;
1606
- })
1607
- : await runSerializedElectronHostInput();
1608
- if (electronHostResult) {
1609
- return compiledElectron?.action === "cleanup" ? electronHostResult : applyUnserializedOutputPath(electronHostResult);
1610
- }
1611
- const explicitSessionName = extractExplicitSessionName(toolArgs);
1612
- const callerOwnedSessionNamespace = explicitSessionName
1613
- ? resolveAgentBrowserNamespace(toolArgs, getAgentBrowserProcessEnvironment().AGENT_BROWSER_NAMESPACE)
1614
- : undefined;
1615
- const serializeBrowserCommand = shouldSerializeBrowserCommand({
1616
- namespace: callerOwnedSessionNamespace,
1617
- explicitSessionName,
1618
- managedSessionName,
1619
- ownedElectronLaunchRecords,
1620
- ownedManagedSessions,
1621
- });
1622
- const callerOwnedSessionQueueKey = !serializeBrowserCommand && explicitSessionName
1623
- ? getSessionContextKey(explicitSessionName, callerOwnedSessionNamespace) ?? explicitSessionName
1624
- : undefined;
1625
- const runBrowserCommand = async (daemonInactive) => {
1626
- flushRecordingReservations();
1627
- const branchRestoreGenerationAtStart = branchRestoreGeneration;
1628
- const generationAtStart = branchStateGeneration;
1629
- const sessionPageStateUpdate = sessionPageState.beginUpdate();
1630
- const browserRunState = {
1631
- activeRecordingReservations,
1632
- artifactManifest,
1633
- attachedSessionKeys,
1634
- closedManagedSessionNames: new Set(),
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
- managedSessionNamespace,
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 initialArtifactManifest = browserRunState.artifactManifest;
1664
- const initialNetworkRoutesBySession = browserRunState.networkRoutesBySession;
1665
- const attachedSessionRequested = isAttachedBrowserInvocation(toolArgs)
1666
- || (resolvedInput.kind === "electron" && resolvedInput.compiledElectron.action === "launch");
1667
- const allocatesFreshManagedSession = explicitSessionName === undefined
1668
- && (params.sessionMode === "fresh" || (resolvedInput.kind === "electron" && resolvedInput.compiledElectron.action === "launch"));
1669
- const reusableSessionKey = allocatesFreshManagedSession
1670
- ? undefined
1671
- : getSessionContextKey(selectedPlan.sessionName, selectedPlan.namespace)
1672
- ?? getSessionContextKey(browserRunState.managedSessionName, browserRunState.managedSessionNamespace);
1673
- const attachedSessionKnown = reusableSessionKey !== undefined && attachedSessionKeys.has(reusableSessionKey);
1674
- let result = await runAgentBrowserTool({
1675
- daemonInactive,
1676
- ctx,
1677
- cwd: ctx.cwd,
1678
- electronPostCommandStatusSettleMs: ELECTRON_POST_COMMAND_STATUS_SETTLE_MS,
1679
- electronProfileIsolationDetails: ELECTRON_PROFILE_ISOLATION_DETAILS,
1680
- implicitSessionCloseTimeoutMs,
1681
- implicitSessionIdleTimeoutMs,
1682
- input: resolvedInput,
1683
- onUpdate,
1684
- params,
1685
- establishAttachedBrowserSession: attachedSessionRequested && !attachedSessionKnown,
1686
- preserveAttachedBrowserSession: attachedSessionRequested || attachedSessionKnown,
1687
- promptPolicy,
1688
- sessionPageStateUpdate,
1689
- signal,
1690
- state: browserRunState,
1691
- });
1692
- const branchRestoreStillCurrent = branchRestoreGenerationAtStart === branchRestoreGeneration;
1693
- const resultDetails = isRecord(result.details) ? result.details : undefined;
1694
- const resultSessionName = typeof resultDetails?.sessionName === "string"
1695
- ? resultDetails.sessionName
1696
- : extractExplicitSessionName(toolArgs);
1697
- const resultNamespace = typeof resultDetails?.namespace === "string"
1698
- ? resultDetails.namespace
1699
- : selectedPlan.namespace;
1700
- if (branchRestoreStillCurrent) {
1701
- const resultBatchCloseLifecycle = getSuccessfulBatchCloseLifecycle(resultDetails?.batchSteps);
1702
- const resultSessionKey = getSessionContextKey(resultSessionName, resultNamespace) ?? resultSessionName;
1703
- const managedSessionOutcome = isRecord(resultDetails?.managedSessionOutcome) ? resultDetails.managedSessionOutcome : undefined;
1704
- const closeAllApplied = resultDetails?.closeAllApplied === true;
1705
- const attachedSessionRemainsActive = result.isError !== true
1706
- || ((attachedSessionRequested || attachedSessionKnown) && managedSessionOutcome?.activeAfter === true);
1707
- const closesAttachedSession = (result.isError !== true && isCloseCommand(extractUpstreamCommandTokens(toolArgs)[0]))
1708
- || resultBatchCloseLifecycle?.endsClosed === true;
1709
- if (closeAllApplied) {
1710
- deleteIdentityKeysInNamespace(attachedSessionKeys, resultNamespace);
1711
- if (resultSessionKey && attachedSessionRemainsActive && (attachedSessionRequested || attachedSessionKnown) && resultBatchCloseLifecycle?.endsClosed === false) {
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
- else if (resultSessionKey && closesAttachedSession)
1717
- attachedSessionKeys.delete(resultSessionKey);
1718
- else if (resultSessionKey && attachedSessionRemainsActive && (attachedSessionRequested || attachedSessionKnown)) {
1719
- attachedSessionKeys.add(resultSessionKey);
1720
- result = { ...result, details: { ...(resultDetails ?? {}), attachedBrowserSession: true } };
1721
- }
1722
- }
1723
- if (branchRestoreStillCurrent) {
1724
- networkRoutesBySession = mergeBrowserRunMap(networkRoutesBySession, initialNetworkRoutesBySession, browserRunState.networkRoutesBySession);
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
- for (const closedSessionKey of browserRunState.closedManagedSessionNames) {
1735
- if (handledBatchCloseKeys.has(closedSessionKey))
1736
- continue;
1737
- const reservation = activeRecordingReservations.get(closedSessionKey);
1738
- if (reservation)
1739
- retireRecordingSession(reservation.sessionName, reservation.namespace);
1740
- }
1741
- if (resultSessionName) {
1742
- const reservation = activeRecordingReservations.get(getAgentBrowserSessionIdentityKey(resultSessionName, resultNamespace));
1743
- if (reservation)
1744
- result = appendActiveRecordingCleanupAction(result, reservation);
1745
- }
1746
- if (artifactManifest) {
1747
- result = {
1748
- ...result,
1749
- details: {
1750
- ...(isRecord(result.details) ? result.details : {}),
1751
- artifactManifest: redactSensitiveValue(artifactManifest),
1752
- artifactRetentionSummary: formatSessionArtifactRetentionSummary(artifactManifest),
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
- syncOwnedManagedSessionsFromResult(ownedManagedSessions, result, browserRunState.managedSessionCwd);
1771
- mergeActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronLaunchRecords, {
1772
- branchOwnedLaunchIds: branchOwnedElectronLaunchIds,
1773
- touchedLaunchIds: !result.isError
1774
- ? getTouchedElectronLaunchIds(explicitSessionName ?? browserRunState.managedSessionName, electronLaunchRecords, resultNamespace)
1775
- : undefined,
1776
- });
1777
- if (serializeBrowserCommand)
1778
- branchStateGeneration += 1;
1779
- }
1780
- return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, preserveTextContent: Array.isArray(params.args) && params.args.includes("--json"), result: warnRecordingPersistence(result) });
1781
- };
1782
- const closesAllSessions = commandClosesAllSessions(toolArgs, resolvedInput.toolStdin);
1783
- const runWithLaunchDefaults = () => withLaunchDefaults ? withLaunchDefaults(runBrowserCommand) : runBrowserCommand();
1784
- const runWithinSessionQueue = () => {
1785
- if (closesAllSessions)
1786
- return managedSessionExecutionQueue.run(() => {
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
- return callerOwnedSessionExecutionQueues.runExclusive(plan.namespace, runWithLaunchDefaults);
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
- if (serializeBrowserCommand)
1799
- return managedSessionExecutionQueue.run(runWithLaunchDefaults);
1800
- return callerOwnedSessionQueueKey
1801
- ? callerOwnedSessionExecutionQueues.run(callerOwnedSessionQueueKey, callerOwnedSessionNamespace, runWithLaunchDefaults)
1802
- : runWithLaunchDefaults();
1803
- };
1804
- if (!commandTouchesArtifactLifecycle(toolArgs, resolvedInput.toolStdin, outputPath))
1805
- return runWithinSessionQueue();
1806
- return artifactExecutionQueue.run(async () => {
1807
- const artifactValidationError = getArtifactPreflightValidationError({
1808
- activeRecordingReservations: activeRecordingReservations.values(),
1809
- args: toolArgs,
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
- if (!artifactValidationError)
1815
- return runWithinSessionQueue();
1816
- flushRecordingReservations();
1817
- return warnRecordingPersistence(buildValidationFailureResult({
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
- }, managedSessionActive || freshSessionOrdinal > 0 || params.sessionMode === "fresh" || pendingReadConfirmation ? undefined : {
1828
- id: rootSessionId,
1829
- profile: rootProfile?.policy === "always" && !/[\\/~]/.test(rootProfile.name) ? rootProfile.name : undefined,
1830
- executablePath: runtimeBrowserConfig.trustedBrowserExecutablePath,
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);