pi-agent-browser-native 0.2.77 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +69 -35
  3. package/dist/extensions/agent-browser/index.js +911 -121
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +35 -8
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +48 -7
  6. package/dist/extensions/agent-browser/lib/batch-lifecycle.js +71 -0
  7. package/dist/extensions/agent-browser/lib/command-policy.js +1 -6
  8. package/dist/extensions/agent-browser/lib/command-taxonomy.js +35 -8
  9. package/dist/extensions/agent-browser/lib/config-policy.js +0 -5
  10. package/dist/extensions/agent-browser/lib/config.js +0 -6
  11. package/dist/extensions/agent-browser/lib/electron/cdp.js +6 -7
  12. package/dist/extensions/agent-browser/lib/electron/cleanup.js +0 -7
  13. package/dist/extensions/agent-browser/lib/electron/discovery.js +0 -7
  14. package/dist/extensions/agent-browser/lib/electron/launch.js +0 -7
  15. package/dist/extensions/agent-browser/lib/input-modes/electron.js +0 -5
  16. package/dist/extensions/agent-browser/lib/input-modes/job.js +61 -9
  17. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +2 -7
  18. package/dist/extensions/agent-browser/lib/input-modes/params.js +23 -29
  19. package/dist/extensions/agent-browser/lib/input-modes/script.js +462 -0
  20. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +51 -17
  21. package/dist/extensions/agent-browser/lib/input-modes/shared.js +0 -5
  22. package/dist/extensions/agent-browser/lib/input-modes/types.js +0 -5
  23. package/dist/extensions/agent-browser/lib/json-schema.js +0 -5
  24. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +12 -5
  25. package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -2
  26. package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +3 -4
  27. package/dist/extensions/agent-browser/lib/managed-session-restore.js +55 -48
  28. package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +2 -9
  29. package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +48 -36
  30. package/dist/extensions/agent-browser/lib/managed-session-storage.js +50 -29
  31. package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -5
  32. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +26 -5
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +110 -30
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +2 -1
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +27 -26
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +58 -33
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +2 -1
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +22 -14
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
  40. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
  41. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
  42. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
  43. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +148 -34
  44. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +225 -61
  45. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +74 -30
  46. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +47 -26
  47. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +40 -19
  48. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +42 -1
  49. package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +299 -0
  50. package/dist/extensions/agent-browser/lib/parsing.js +0 -7
  51. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +36 -11
  52. package/dist/extensions/agent-browser/lib/playbook.js +20 -24
  53. package/dist/extensions/agent-browser/lib/process-environment.js +14 -0
  54. package/dist/extensions/agent-browser/lib/process-identity.js +4 -9
  55. package/dist/extensions/agent-browser/lib/process.js +131 -48
  56. package/dist/extensions/agent-browser/lib/prompt-policy.js +171 -27
  57. package/dist/extensions/agent-browser/lib/recording-reservations.js +183 -0
  58. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +63 -13
  59. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +68 -11
  60. package/dist/extensions/agent-browser/lib/results/categories.js +7 -9
  61. package/dist/extensions/agent-browser/lib/results/confirmation.js +0 -7
  62. package/dist/extensions/agent-browser/lib/results/contracts.js +0 -7
  63. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +0 -7
  64. package/dist/extensions/agent-browser/lib/results/envelope.js +0 -7
  65. package/dist/extensions/agent-browser/lib/results/network.js +0 -7
  66. package/dist/extensions/agent-browser/lib/results/next-actions.js +19 -35
  67. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +85 -44
  68. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +66 -11
  69. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +0 -5
  70. package/dist/extensions/agent-browser/lib/results/presentation/common.js +18 -5
  71. package/dist/extensions/agent-browser/lib/results/presentation/content.js +0 -5
  72. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +7 -6
  73. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +2 -1
  74. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +0 -5
  75. package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -5
  76. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +26 -16
  77. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +58 -13
  78. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +1 -15
  79. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +0 -5
  80. package/dist/extensions/agent-browser/lib/results/presentation.js +6 -8
  81. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +2 -7
  82. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +0 -7
  83. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +51 -15
  84. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +13 -14
  85. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +0 -7
  86. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +0 -7
  87. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +0 -7
  88. package/dist/extensions/agent-browser/lib/results/snapshot.js +0 -7
  89. package/dist/extensions/agent-browser/lib/results/text.js +0 -7
  90. package/dist/extensions/agent-browser/lib/runtime.js +119 -52
  91. package/dist/extensions/agent-browser/lib/session-page-state.js +62 -17
  92. package/dist/extensions/agent-browser/lib/string-enum-schema.js +0 -5
  93. package/dist/extensions/agent-browser/lib/temp.js +0 -7
  94. package/dist/extensions/agent-browser/lib/upstream-version.js +14 -0
  95. package/dist/extensions/agent-browser/lib/web-search.js +0 -5
  96. package/dist/extensions/agent-browser/script-worker.js +169 -0
  97. package/dist/scripts/agent-browser-target.mjs +3 -0
  98. package/docs/ARCHITECTURE.md +50 -31
  99. package/docs/COMMAND_REFERENCE.md +97 -41
  100. package/docs/ELECTRON.md +3 -19
  101. package/docs/RELEASE.md +8 -20
  102. package/docs/REQUIREMENTS.md +8 -6
  103. package/docs/SUPPORT_MATRIX.md +32 -25
  104. package/docs/TOOL_CONTRACT.md +107 -64
  105. package/docs/platform-smoke.md +1 -1
  106. package/package.json +7 -6
  107. package/platform-smoke.config.mjs +2 -2
  108. package/scripts/agent-browser-capability-baseline.mjs +24 -6
  109. package/scripts/agent-browser-target.mjs +3 -0
  110. package/scripts/build.mjs +41 -0
  111. package/scripts/doctor.mjs +8 -7
  112. package/scripts/platform-smoke/browser-dogfood-windows.ps1 +9 -3
  113. package/scripts/platform-smoke/targets.mjs +12 -6
  114. package/dist/extensions/agent-browser/lib/electron/text.js +0 -13
  115. package/dist/extensions/agent-browser/lib/input-modes.js +0 -10
  116. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +0 -5
  117. package/dist/extensions/agent-browser/lib/results/artifact-state.js +0 -13
  118. package/dist/extensions/agent-browser/lib/results.js +0 -14
@@ -1,8 +1,10 @@
1
1
  import { cleanupElectronLaunchResources } from "../../electron/cleanup.js";
2
- import { getCompiledSemanticActionCommandIndex, getCompiledSemanticActionSessionPrefix, isCompiledSemanticActionFindCommand, redactNetworkSourceLookupSurface, } from "../../input-modes.js";
3
- import { buildAgentBrowserNextActions, buildAgentBrowserResultCategoryDetails, } from "../../results.js";
2
+ import { getCompiledSemanticActionCommandIndex, getCompiledSemanticActionSessionPrefix, isCompiledSemanticActionFindCommand } from "../../input-modes/semantic-action.js";
3
+ import { redactNetworkSourceLookupSurface } from "../../input-modes/lookups.js";
4
+ import { buildAgentBrowserNextActions } from "../../results/action-recommendations.js";
5
+ import { buildAgentBrowserResultCategoryDetails } from "../../results/categories.js";
4
6
  import { formatSessionArtifactRetentionSummary } from "../../results/artifact-manifest.js";
5
- import { AgentBrowserNextActionCollector, alignPageChangeSummaryNextActionIds, applyNamespaceToNextActions, isStandaloneSnapshotNextAction, withOptionalSessionArgs, } from "../../results/next-actions.js";
7
+ import { alignPageChangeSummaryNextActionIds, appendUniqueAgentBrowserNextActions, applyNamespaceToNextActions, isStandaloneSnapshotNextAction, withOptionalSessionArgs, } from "../../results/next-actions.js";
6
8
  import { buildConnectedSessionNextActions, buildNoActivePageNextActions, buildSessionAwareStaleRefNextActions, buildSessionTabRecoveryNextActions, } from "../../results/recovery-next-actions.js";
7
9
  import { buildRichInputRecoveryDiagnostic, buildRichInputRecoveryNextActions, buildVisibleRefFallbackNextActions, formatRichInputRecoveryText, formatVisibleRefFallbackText, sanitizeVisibleRefFallbackDiagnostic, } from "../../results/selector-recovery.js";
8
10
  import { buildNoActivePageRefSnapshotInvalidation, isNoActivePageSnapshotFailure, } from "../../session-page-state.js";
@@ -193,7 +195,7 @@ export async function prepareFinalResultRecoveryState(options) {
193
195
  }
194
196
  }
195
197
  const richInputRecoveryDiagnostic = buildRichInputRecoveryDiagnostic(visibleRefFallbackDiagnostic);
196
- const noActivePageSnapshotFailure = categoryDetails.resultCategory === "failure" && (isNoActivePageSnapshotFailure(options.executionPlan.commandInfo.command, options.errorText ?? options.presentation.summary) || options.batchRefSnapshotState?.invalidation !== undefined);
198
+ const noActivePageSnapshotFailure = categoryDetails.resultCategory === "failure" && (isNoActivePageSnapshotFailure(options.executionPlan.commandInfo.command, options.errorText ?? options.presentation.summary) || options.batchRefSnapshotState?.invalidation?.reason === "no-active-page");
197
199
  const executionSessionKey = getSessionContextKey(options.executionPlan.sessionName, options.executionPlan.namespace);
198
200
  if (noActivePageSnapshotFailure && executionSessionKey) {
199
201
  const refUpdate = options.sessionPageState.applyRefSnapshotInvalidation({ invalidation: buildNoActivePageRefSnapshotInvalidation(), sessionName: executionSessionKey, update: options.sessionPageStateUpdate });
@@ -257,68 +259,87 @@ function buildDialogTimeoutNextActions(options) {
257
259
  ];
258
260
  }
259
261
  function buildResultNextActions(options) {
260
- const nextActionCollector = new AgentBrowserNextActionCollector(options.presentation.nextActions);
262
+ let nextActions = options.presentation.nextActions ? [...options.presentation.nextActions] : [];
263
+ const append = (actions) => {
264
+ if (actions && actions.length > 0)
265
+ nextActions.push(...actions);
266
+ };
267
+ const appendUnique = (actions) => {
268
+ appendUniqueAgentBrowserNextActions(nextActions, actions);
269
+ };
261
270
  if (options.categoryDetails.resultCategory === "success" && options.executionPlan.commandInfo.command === "connect" && !options.electronLaunchRecord)
262
- nextActionCollector.appendUnique(buildConnectedSessionNextActions(options.executionPlan.sessionName));
271
+ appendUnique(buildConnectedSessionNextActions(options.executionPlan.sessionName));
263
272
  if (options.noActivePageSnapshotFailure)
264
- nextActionCollector.appendUnique(buildNoActivePageNextActions(options.executionPlan.sessionName));
273
+ appendUnique(buildNoActivePageNextActions(options.executionPlan.sessionName));
265
274
  if (options.aboutBlankSessionMismatch) {
266
- nextActionCollector.appendUnique(buildSessionTabRecoveryNextActions({ kind: "about-blank", recoveryApplied: options.aboutBlankSessionMismatch.recoveryApplied, sessionName: options.executionPlan.sessionName, tabCorrection: options.aboutBlankSessionMismatch.recoveryApplied ? options.sessionTabCorrection : undefined, target: { title: options.aboutBlankSessionMismatch.targetTitle, url: options.aboutBlankSessionMismatch.targetUrl } }));
275
+ appendUnique(buildSessionTabRecoveryNextActions({ kind: "about-blank", recoveryApplied: options.aboutBlankSessionMismatch.recoveryApplied, sessionName: options.executionPlan.sessionName, tabCorrection: options.aboutBlankSessionMismatch.recoveryApplied ? options.sessionTabCorrection : undefined, target: { title: options.aboutBlankSessionMismatch.targetTitle, url: options.aboutBlankSessionMismatch.targetUrl } }));
267
276
  if (!options.aboutBlankSessionMismatch.recoveryApplied)
268
- nextActionCollector.removeWhere(isStandaloneSnapshotNextAction);
277
+ nextActions = nextActions.filter((action) => !isStandaloneSnapshotNextAction(action));
269
278
  }
270
279
  else if (options.categoryDetails.resultCategory === "success" && (options.sessionTabCorrection || options.openResultTabCorrection))
271
- nextActionCollector.appendUnique(buildSessionTabRecoveryNextActions({ kind: "tab-drift", recoveryApplied: true, sessionName: options.executionPlan.sessionName, tabCorrection: options.sessionTabCorrection ?? options.openResultTabCorrection, target: options.currentSessionTabTarget ?? options.priorSessionTabTarget }));
280
+ appendUnique(buildSessionTabRecoveryNextActions({ kind: "tab-drift", recoveryApplied: true, sessionName: options.executionPlan.sessionName, tabCorrection: options.sessionTabCorrection ?? options.openResultTabCorrection, target: options.currentSessionTabTarget ?? options.priorSessionTabTarget }));
272
281
  if (options.categoryDetails.failureCategory === "stale-ref")
273
- nextActionCollector.replace(buildSessionAwareStaleRefNextActions(options.executionPlan.sessionName));
282
+ nextActions = [...buildSessionAwareStaleRefNextActions(options.executionPlan.sessionName)];
274
283
  if (options.visibleRefFallbackDiagnostic)
275
- nextActionCollector.append(buildVisibleRefFallbackNextActions({ diagnostic: options.visibleRefFallbackDiagnostic, sessionName: options.visibleRefFallbackSessionName }));
284
+ append(buildVisibleRefFallbackNextActions({ diagnostic: options.visibleRefFallbackDiagnostic, sessionName: options.visibleRefFallbackSessionName }));
276
285
  if (options.richInputRecoveryDiagnostic)
277
- nextActionCollector.append(buildRichInputRecoveryNextActions({ diagnostic: options.richInputRecoveryDiagnostic, sessionName: options.visibleRefFallbackSessionName }));
286
+ append(buildRichInputRecoveryNextActions({ diagnostic: options.richInputRecoveryDiagnostic, sessionName: options.visibleRefFallbackSessionName }));
278
287
  if (options.electronPostCommandHealth) {
279
288
  const electronRecord = options.electronLaunchRecords.get(options.electronPostCommandHealth.launchId);
280
289
  if (electronRecord)
281
- nextActionCollector.appendUnique(buildElectronLifecycleNextActions(electronRecord));
290
+ appendUnique(buildElectronLifecycleNextActions(electronRecord));
282
291
  }
283
292
  if (options.electronSessionMismatch) {
284
293
  const electronRecord = options.electronLaunchRecords.get(options.electronSessionMismatch.launchId);
285
294
  if (electronRecord)
286
- nextActionCollector.appendUnique(buildElectronMismatchNextActions(electronRecord, options.electronSessionMismatch.liveTarget));
295
+ appendUnique(buildElectronMismatchNextActions(electronRecord, options.electronSessionMismatch.liveTarget));
287
296
  }
288
297
  if (options.categoryDetails.failureCategory === "selector-not-found" && options.redactedCompiledSemanticAction) {
289
298
  const candidateActions = buildSemanticActionCandidateActions(options.redactedCompiledSemanticAction);
290
299
  if (candidateActions.length > 0)
291
- nextActionCollector.append(candidateActions);
300
+ append(candidateActions);
292
301
  }
293
302
  if (options.overlayBlockerDiagnostic)
294
- nextActionCollector.append(buildOverlayBlockerNextActions({ diagnostic: options.overlayBlockerDiagnostic, sessionName: options.executionPlan.sessionName }));
303
+ append(buildOverlayBlockerNextActions({ diagnostic: options.overlayBlockerDiagnostic, sessionName: options.executionPlan.sessionName }));
295
304
  if (options.fillVerificationDiagnostic)
296
- nextActionCollector.appendUnique(buildFillVerificationNextActions(options.fillVerificationDiagnostic, options.executionPlan.sessionName));
305
+ appendUnique(buildFillVerificationNextActions(options.fillVerificationDiagnostic, options.executionPlan.sessionName));
297
306
  if (options.electronRefFreshnessDiagnostic)
298
- nextActionCollector.appendUnique(buildElectronRefFreshnessNextActions(options.executionPlan.sessionName));
307
+ appendUnique(buildElectronRefFreshnessNextActions(options.executionPlan.sessionName));
299
308
  if (options.selectorTextVisibilityDiagnostics.length > 0)
300
- nextActionCollector.append(buildSelectorTextVisibilityNextActions({ diagnostics: options.selectorTextVisibilityDiagnostics, sessionName: options.executionPlan.sessionName }));
309
+ append(buildSelectorTextVisibilityNextActions({ diagnostics: options.selectorTextVisibilityDiagnostics, sessionName: options.executionPlan.sessionName }));
301
310
  if (options.electronBroadGetTextScopeDiagnostics.length > 0)
302
- nextActionCollector.append(buildElectronBroadGetTextScopeNextActions({ diagnostics: options.electronBroadGetTextScopeDiagnostics, sessionName: options.executionPlan.sessionName }));
311
+ append(buildElectronBroadGetTextScopeNextActions({ diagnostics: options.electronBroadGetTextScopeDiagnostics, sessionName: options.executionPlan.sessionName }));
303
312
  if (options.sourceLookup?.electronContext)
304
- nextActionCollector.appendUnique(buildSourceLookupElectronNextActions(options.sourceLookup));
313
+ appendUnique(buildSourceLookupElectronNextActions(options.sourceLookup));
305
314
  if (options.clickDispatchDiagnostic)
306
- nextActionCollector.append(buildClickDispatchNextActions({ commandTokens: options.commandTokens, diagnostic: options.clickDispatchDiagnostic, sessionName: options.executionPlan.sessionName }));
315
+ append(buildClickDispatchNextActions({ commandTokens: options.commandTokens, diagnostic: options.clickDispatchDiagnostic, sessionName: options.executionPlan.sessionName }));
307
316
  if (options.scrollNoopDiagnostic)
308
- nextActionCollector.append(buildScrollNoopNextActions(options.executionPlan.sessionName));
317
+ append(buildScrollNoopNextActions(options.executionPlan.sessionName));
309
318
  if (options.comboboxFocusDiagnostic)
310
- nextActionCollector.append(buildComboboxFocusNextActions(options.executionPlan.sessionName));
319
+ append(buildComboboxFocusNextActions(options.executionPlan.sessionName));
311
320
  if (options.managedSessionOutcome)
312
- nextActionCollector.appendUnique(buildManagedSessionFreshFailureNextActions(options.managedSessionOutcome));
321
+ appendUnique(buildManagedSessionFreshFailureNextActions(options.managedSessionOutcome));
313
322
  if (options.categoryDetails.failureCategory === "timeout" && options.processResult.timedOut) {
314
- nextActionCollector.appendUnique(buildTimeoutPartialProgressNextActions(options));
315
- nextActionCollector.appendUnique(buildDialogTimeoutNextActions({ command: options.executionPlan.commandInfo.command, sessionName: options.executionPlan.sessionName }));
323
+ appendUnique(buildTimeoutPartialProgressNextActions(options));
324
+ appendUnique(buildDialogTimeoutNextActions({ command: options.executionPlan.commandInfo.command, sessionName: options.executionPlan.sessionName }));
316
325
  }
317
326
  if (options.categoryDetails.failureCategory === "stale-ref" && options.redactedCompiledSemanticAction && isCompiledSemanticActionFindCommand(options.compiledSemanticAction))
318
- nextActionCollector.append([{ id: "retry-semantic-action-after-stale-ref", params: { args: options.redactedCompiledSemanticAction.args }, reason: "Retry the same semantic target via its compiled find command after the upstream stale-ref failure proves the prior action did not execute.", safety: "Use only for the same intended target; direct stale @refs still require a fresh snapshot or stable locator before retrying.", tool: "agent_browser" }]);
327
+ append([{ id: "retry-semantic-action-after-stale-ref", params: { args: options.redactedCompiledSemanticAction.args }, reason: "Retry the same semantic target via its compiled find command after the upstream stale-ref failure proves the prior action did not execute.", safety: "Use only for the same intended target; direct stale @refs still require a fresh snapshot or stable locator before retrying.", tool: "agent_browser" }]);
319
328
  if (options.electronLaunchRecord)
320
- nextActionCollector.append(buildAgentBrowserNextActions({ electron: { launchId: options.electronLaunchRecord.launchId, sessionName: options.electronLaunchRecord.sessionName, status: options.electronLaunchRecord.cleanupState }, failureCategory: options.categoryDetails.failureCategory, resultCategory: options.categoryDetails.resultCategory, successCategory: options.categoryDetails.successCategory }));
321
- return nextActionCollector.toArray();
329
+ append(buildAgentBrowserNextActions({ electron: { launchId: options.electronLaunchRecord.launchId, sessionName: options.electronLaunchRecord.sessionName, status: options.electronLaunchRecord.cleanupState }, failureCategory: options.categoryDetails.failureCategory, resultCategory: options.categoryDetails.resultCategory, successCategory: options.categoryDetails.successCategory }));
330
+ return nextActions.length > 0 ? nextActions : undefined;
331
+ }
332
+ function formatFailureNextActionsText(options, nextActions) {
333
+ if (options.categoryDetails.resultCategory !== "failure" || !nextActions || nextActions.length === 0)
334
+ return undefined;
335
+ const lines = nextActions.slice(0, 6).map((action) => {
336
+ const params = action.params
337
+ ? { ...action.params, ...(action.params.stdin === undefined ? {} : { stdin: "[omitted; use details.nextActions]" }) }
338
+ : undefined;
339
+ const payload = action.artifactPath ? { artifactPath: action.artifactPath } : params;
340
+ return `- ${action.id}${payload ? ` ${JSON.stringify(payload)}` : ""}: ${action.reason}`;
341
+ });
342
+ return ["Next actions:", ...lines, "Use the exact redacted payloads in details.nextActions when available."].join("\n");
322
343
  }
323
344
  function buildAgentBrowserResultDetails(options, nextActions) {
324
345
  const publicVisibleRefFallbackDiagnostic = options.visibleRefFallbackDiagnostic ? sanitizeVisibleRefFallbackDiagnostic(options.visibleRefFallbackDiagnostic) : undefined;
@@ -348,6 +369,7 @@ function buildAgentBrowserResultDetails(options, nextActions) {
348
369
  navigationSummary: options.navigationSummary,
349
370
  electron: options.electronLaunchRecord ? { action: "launch", cleanup: options.electronFailedConnectCleanup, handoff: options.electronHandoff, identifiers: buildElectronIdentifiers(options.electronLaunchRecord), launch: options.electronLaunchRecord, profileIsolation: options.electronProfileIsolationDetails, status: options.succeeded ? "succeeded" : "failed", targets: options.electronLaunch?.targets, version: options.electronLaunch?.version } : undefined,
350
371
  ...options.categoryDetails,
372
+ agentBrowserStarted: options.processResult.agentBrowserStarted,
351
373
  aboutBlankSessionMismatch: options.aboutBlankSessionMismatch,
352
374
  electronPostCommandHealth: options.electronPostCommandHealth,
353
375
  electronRefFreshness: options.electronRefFreshnessDiagnostic,
@@ -358,6 +380,8 @@ function buildAgentBrowserResultDetails(options, nextActions) {
358
380
  fullOutputPath: options.parseFailureOutput.fullOutputPath ?? options.presentation.fullOutputPath,
359
381
  fullOutputPaths: options.presentation.fullOutputPaths,
360
382
  fullOutputUnavailable: options.parseFailureOutput.fullOutputUnavailable,
383
+ managedSessionHeadedAutosaveDisabled: options.managedSessionHeadedAutosaveDisabled,
384
+ managedSessionHeadedAutosaveInterval: options.managedSessionHeadedAutosaveInterval,
361
385
  managedSessionOutcome: options.managedSessionOutcome,
362
386
  imagePath: options.presentation.imagePath,
363
387
  imagePaths: options.presentation.imagePaths,
@@ -423,7 +447,8 @@ export function buildFinalAgentBrowserToolResult(options) {
423
447
  const artifactCleanupText = formatArtifactCleanupGuidanceText(options.artifactCleanup);
424
448
  const timeoutPartialProgressText = options.timeoutPartialProgress ? formatTimeoutPartialProgressText(options.timeoutPartialProgress) : undefined;
425
449
  const managedSessionOutcomeText = formatManagedSessionOutcomeText(options.managedSessionOutcome);
426
- const rawAppendedDiagnosticText = [visibleRefFallbackText, richInputRecoveryText, semanticActionCandidateText, clickDispatchText, overlayBlockerText, fillVerificationText, electronRefFreshnessText, selectorTextVisibilityText, electronBroadGetTextScopeText, scrollNoopDiagnosticText, comboboxFocusDiagnosticText, recordingDependencyWarningText, evalStdinHintText, evalResultWarningText, artifactCleanupText, timeoutPartialProgressText, managedSessionOutcomeText].filter((item) => item !== undefined).join("\n\n");
450
+ const failureNextActionsText = formatFailureNextActionsText(options, nextActions);
451
+ const rawAppendedDiagnosticText = [visibleRefFallbackText, richInputRecoveryText, semanticActionCandidateText, clickDispatchText, overlayBlockerText, fillVerificationText, electronRefFreshnessText, selectorTextVisibilityText, electronBroadGetTextScopeText, scrollNoopDiagnosticText, comboboxFocusDiagnosticText, recordingDependencyWarningText, evalStdinHintText, evalResultWarningText, artifactCleanupText, timeoutPartialProgressText, managedSessionOutcomeText, failureNextActionsText].filter((item) => item !== undefined).join("\n\n");
427
452
  const appendedDiagnosticText = redactSensitiveText(redactExactSensitiveText(rawAppendedDiagnosticText, options.exactSensitiveValues));
428
453
  const shouldAppendDiagnosticText = appendedDiagnosticText.length > 0 && (!options.userRequestedJson || options.plainTextInspection);
429
454
  let content = shouldAppendDiagnosticText && options.redactedContent[0]?.type === "text" ? [{ ...options.redactedContent[0], text: `${options.redactedContent[0].text}\n\n${appendedDiagnosticText}` }, ...options.redactedContent.slice(1)] : options.redactedContent;
@@ -451,5 +476,5 @@ export async function buildMissingBinaryFailureResult(options) {
451
476
  missingBinaryElectronRecord = missingBinaryElectronCleanup.record;
452
477
  }
453
478
  const textParts = [errorText, managedSessionOutcomeText, missingBinaryElectronCleanup ? `Electron cleanup after failed attach: ${missingBinaryElectronCleanup.summary}` : undefined].filter((part) => part !== undefined && part.length > 0);
454
- return { content: [{ type: "text", text: textParts.join("\n\n") }], details: { args: options.redactedArgs, compatibilityWorkaround: options.compatibilityWorkaround, effectiveArgs: options.redactedProcessArgs, electron: missingBinaryElectronRecord ? { action: "launch", cleanup: missingBinaryElectronCleanup, launch: missingBinaryElectronRecord, status: "failed", targets: options.electronLaunch?.targets, version: options.electronLaunch?.version } : undefined, managedSessionOutcome, namespace: options.executionPlan.namespace, nextActions: managedSessionRecoveryNextActions.length > 0 ? managedSessionRecoveryNextActions : undefined, sessionMode: options.sessionMode, sessionTabCorrection: options.sessionTabCorrection, ...buildAgentBrowserResultCategoryDetails({ args: options.redactedProcessArgs, command: options.executionPlan.commandInfo.command, errorText, failureCategory: "missing-binary", spawnError, succeeded: false }), spawnError }, isError: true };
479
+ return { content: [{ type: "text", text: textParts.join("\n\n") }], details: { agentBrowserStarted: options.processResult.agentBrowserStarted, args: options.redactedArgs, compatibilityWorkaround: options.compatibilityWorkaround, effectiveArgs: options.redactedProcessArgs, electron: missingBinaryElectronRecord ? { action: "launch", cleanup: missingBinaryElectronCleanup, launch: missingBinaryElectronRecord, status: "failed", targets: options.electronLaunch?.targets, version: options.electronLaunch?.version } : undefined, managedSessionOutcome, namespace: options.executionPlan.namespace, nextActions: managedSessionRecoveryNextActions.length > 0 ? managedSessionRecoveryNextActions : undefined, sessionMode: options.sessionMode, sessionTabCorrection: options.sessionTabCorrection, ...buildAgentBrowserResultCategoryDetails({ args: options.redactedProcessArgs, command: options.executionPlan.commandInfo.command, errorText, failureCategory: "missing-binary", spawnError, succeeded: false }), spawnError }, isError: true };
455
480
  }
@@ -20,6 +20,7 @@ async function runAgentBrowserToolInContext(options) {
20
20
  const ownedManagedSession = prepared.ownedManagedSessionContext;
21
21
  return await withOwnedManagedSessionContext(ownedManagedSession, async () => {
22
22
  try {
23
+ const artifactRunStartedAtMs = Date.now();
23
24
  const processResult = await runAgentBrowserProcess({
24
25
  args: prepared.processArgs,
25
26
  cwd: options.cwd,
@@ -51,7 +52,7 @@ async function runAgentBrowserToolInContext(options) {
51
52
  });
52
53
  if (missingBinaryResult)
53
54
  return missingBinaryResult;
54
- const output = await processBrowserOutput({ ...options, prepared, processResult });
55
+ const output = await processBrowserOutput({ ...options, artifactRunStartedAtMs, prepared, processResult });
55
56
  applyBrowserRunStatePatch(options.state, output.statePatch);
56
57
  return output.result;
57
58
  }
@@ -1,24 +1,29 @@
1
- /**
2
- * Purpose: Coordinate wrapper-owned managed-session daemon inspection, reuse, and cleanup policy.
3
- * Responsibilities: Serialize daemon policy decisions, verify live restore keys, and close owned sessions with snapshot cleanup.
4
- * Scope: Managed-session daemon policy only; page state and browser-result shaping live in sibling modules.
5
- * Usage: Called by browser-run preparation, Electron host probes, result handling, and extension shutdown cleanup.
6
- * Invariants/Assumptions: Every owned daemon decision holds the cross-process policy lock until its caller finishes the related operation.
7
- */
8
1
  import { rm } from "node:fs/promises";
9
2
  import { acquireManagedSessionPolicyLock } from "../../managed-session-policy-lock.js";
10
- import { pruneOwnedManagedSessionRestoreSnapshots, } from "../../managed-session-restore.js";
3
+ import { pruneOwnedManagedSessionRestoreSnapshots, resolveExplicitAutosaveInterval, } from "../../managed-session-restore.js";
11
4
  import { isManagedSessionRestoreKey } from "../../managed-session-storage.js";
12
5
  import { isRecord } from "../../parsing.js";
6
+ import { getAgentBrowserProcessEnvironment } from "../../process-environment.js";
13
7
  import { runAgentBrowserProcess } from "../../process.js";
14
- import { getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results.js";
8
+ import { getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results/envelope.js";
15
9
  import { redactInvocationArgs } from "../../runtime.js";
16
- const MANAGED_SESSION_DAEMON_INSPECTION_TIMEOUT_MS = 5_000;
10
+ const MANAGED_SESSION_DAEMON_INSPECTION_TIMEOUT_MS = 35_000;
11
+ const RUNNING_HEADED_AUTOSAVE_POLICY_CHANGE_ERROR = "AGENT_BROWSER_AUTOSAVE_INTERVAL_MS cannot change a running wrapper-owned headed session's launch-time periodic autosave interval. Close that session first, then retry with sessionMode: \"fresh\" so the new daemon starts with the requested interval.";
12
+ export function getRunningHeadedAutosavePolicyChangeError(recordedInterval, closeCommand = false) {
13
+ const explicitInterval = resolveExplicitAutosaveInterval(getAgentBrowserProcessEnvironment().AGENT_BROWSER_AUTOSAVE_INTERVAL_MS);
14
+ return recordedInterval !== undefined && !closeCommand && explicitInterval !== undefined && explicitInterval !== recordedInterval
15
+ ? RUNNING_HEADED_AUTOSAVE_POLICY_CHANGE_ERROR
16
+ : undefined;
17
+ }
18
+ function getHeadedManagedAutosaveEnv(interval) {
19
+ return interval !== undefined ? { AGENT_BROWSER_AUTOSAVE_INTERVAL_MS: interval } : undefined;
20
+ }
17
21
  export async function inspectManagedSessionDaemon(options) {
18
22
  const processResult = await runAgentBrowserProcess({
19
23
  allowManagedSessionTarget: options.allowManagedSessionTarget,
20
24
  args: ["--json", "--namespace", options.namespace ?? "", "--session", options.sessionName, "session", "info"],
21
25
  cwd: options.cwd,
26
+ env: getHeadedManagedAutosaveEnv(options.headedManagedAutosaveInterval),
22
27
  preserveAttachedBrowserSession: options.preserveAttachedBrowserSession,
23
28
  signal: options.signal,
24
29
  timeoutMs: options.timeoutMs ?? MANAGED_SESSION_DAEMON_INSPECTION_TIMEOUT_MS,
@@ -65,6 +70,7 @@ export async function acquireOwnedManagedSessionDaemonPolicy(options) {
65
70
  const daemon = await inspectManagedSessionDaemon({
66
71
  allowManagedSessionTarget: true,
67
72
  cwd: context.cwd,
73
+ headedManagedAutosaveInterval: context.headedManagedAutosaveInterval,
68
74
  namespace: context.namespace,
69
75
  sessionName: context.sessionName,
70
76
  signal,
@@ -74,7 +80,7 @@ export async function acquireOwnedManagedSessionDaemonPolicy(options) {
74
80
  if (options.mode === "close") {
75
81
  if (daemon.status === "active")
76
82
  context.restoreState.recordDaemonRestoreKey(context.sessionName, context.namespace, daemon.restoreKey);
77
- return { lock };
83
+ return { daemonStatus: daemon.status, lock };
78
84
  }
79
85
  const stickyDisabled = context.restoreState.isDisabled(context.sessionName, context.namespace);
80
86
  const hasKnownDaemonRestoreKey = context.restoreState.hasDaemonRestoreKey(context.sessionName, context.namespace);
@@ -96,13 +102,14 @@ export async function acquireOwnedManagedSessionDaemonPolicy(options) {
96
102
  context.restoreState.recordDaemonRestoreKey(context.sessionName, context.namespace, daemon.restoreKey);
97
103
  return !["inactive", "missing-binary"].includes(daemon.status) && !activePolicyMatches
98
104
  ? {
105
+ daemonStatus: daemon.status,
99
106
  error: [
100
107
  "This wrapper-owned session's live daemon does not match the requested managed-restore policy.",
101
108
  "Close that session first, retry with sessionMode: \"fresh\", or use a distinct explicit --session.",
102
109
  ].join(" "),
103
110
  lock,
104
111
  }
105
- : { lock };
112
+ : { daemonStatus: daemon.status, lock };
106
113
  }
107
114
  catch (error) {
108
115
  await lock.release();
@@ -113,7 +120,7 @@ export async function closeManagedSession(options) {
113
120
  const controller = new AbortController();
114
121
  const timer = setTimeout(() => controller.abort(), options.timeoutMs);
115
122
  let stdoutSpillPath;
116
- const closeArgs = [...(options.namespace ? ["--namespace", options.namespace] : []), "--session", options.sessionName, "close"];
123
+ const closeArgs = [...(options.namespace !== undefined ? ["--namespace", options.namespace] : []), "--session", options.sessionName, "close"];
117
124
  const policyLock = options.policyLock ?? await acquireManagedSessionPolicyLock({
118
125
  namespace: options.namespace,
119
126
  sessionName: options.sessionName,
@@ -128,6 +135,7 @@ export async function closeManagedSession(options) {
128
135
  const daemon = await inspectManagedSessionDaemon({
129
136
  allowManagedSessionTarget: true,
130
137
  cwd: options.cwd,
138
+ headedManagedAutosaveInterval: options.headedManagedAutosaveInterval,
131
139
  namespace: options.namespace,
132
140
  preserveAttachedBrowserSession: options.preserveAttachedBrowserSession,
133
141
  sessionName: options.sessionName,
@@ -142,7 +150,7 @@ export async function closeManagedSession(options) {
142
150
  const processResult = await runAgentBrowserProcess({
143
151
  args: closeArgs,
144
152
  cwd: options.cwd,
145
- env: { AGENT_BROWSER_JSON: "1" },
153
+ env: { AGENT_BROWSER_JSON: "1", ...getHeadedManagedAutosaveEnv(options.headedManagedAutosaveInterval) },
146
154
  managedSessionRestoreState: options.restoreState,
147
155
  ownedManagedSession: true,
148
156
  preserveAttachedBrowserSession: options.preserveAttachedBrowserSession,
@@ -1,7 +1,7 @@
1
1
  import { mkdir, stat, writeFile } from "node:fs/promises";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import { isRecord } from "../../../parsing.js";
4
- import { buildAgentBrowserResultCategoryDetails } from "../../../results.js";
4
+ import { buildAgentBrowserResultCategoryDetails } from "../../../results/categories.js";
5
5
  import { formatSessionArtifactRetentionSummary, mergeSessionArtifactManifest } from "../../../results/artifact-manifest.js";
6
6
  import { redactSensitiveText } from "../../../runtime.js";
7
7
  import { buildSessionDetailFields, runSessionCommandData } from "../session-state.js";
@@ -1,5 +1,5 @@
1
1
  import { isRecord } from "../../../parsing.js";
2
- import { buildAgentBrowserResultCategoryDetails } from "../../../results.js";
2
+ import { buildAgentBrowserResultCategoryDetails } from "../../../results/categories.js";
3
3
  import { redactSensitiveText } from "../../../runtime.js";
4
4
  import { buildSessionDetailFields, runSessionCommandData } from "../session-state.js";
5
5
  function parseNetworkRequestsPageFilterRequest(commandTokens) {
@@ -1,5 +1,5 @@
1
1
  import { isRecord } from "../../../parsing.js";
2
- import { buildAgentBrowserResultCategoryDetails } from "../../../results.js";
2
+ import { buildAgentBrowserResultCategoryDetails } from "../../../results/categories.js";
3
3
  import { applyNamespaceToNextActions } from "../../../results/next-actions.js";
4
4
  import { buildScrollNoopNextActions } from "../diagnostics.js";
5
5
  import { buildSessionDetailFields, runSessionCommandData } from "../session-state.js";
@@ -1,5 +1,5 @@
1
1
  import { isRecord } from "../../../parsing.js";
2
- import { buildAgentBrowserResultCategoryDetails } from "../../../results.js";
2
+ import { buildAgentBrowserResultCategoryDetails } from "../../../results/categories.js";
3
3
  import { buildSnapshotPresentation } from "../../../results/snapshot.js";
4
4
  import { extractRefSnapshotFromData } from "../../../session-page-state.js";
5
5
  import { collectScrollPositionSnapshot } from "../diagnostics.js";