pi-agent-browser-native 0.2.47 → 0.2.49

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 (185) hide show
  1. package/CHANGELOG.md +63 -19
  2. package/README.md +52 -19
  3. package/dist/extensions/agent-browser/index.js +785 -0
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
  6. package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
  7. package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
  8. package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
  9. package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
  10. package/dist/extensions/agent-browser/lib/config.js +122 -0
  11. package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
  12. package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
  13. package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
  14. package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
  15. package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
  16. package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
  17. package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
  18. package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
  19. package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
  20. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
  21. package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
  22. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
  23. package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
  24. package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
  25. package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
  26. package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
  27. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
  28. package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
  29. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
  30. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
  31. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
  32. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
  40. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
  41. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
  42. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
  43. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
  44. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
  45. package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
  46. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
  47. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
  48. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
  49. package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
  50. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
  51. package/dist/extensions/agent-browser/lib/playbook.js +121 -0
  52. package/dist/extensions/agent-browser/lib/process.js +448 -0
  53. package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
  54. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
  55. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
  56. package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
  57. package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
  58. package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
  59. package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
  60. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
  61. package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
  62. package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
  63. package/dist/extensions/agent-browser/lib/results/network.js +73 -0
  64. package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
  65. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
  66. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
  67. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
  68. package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
  69. package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
  70. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
  71. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
  72. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
  73. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
  74. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
  75. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
  76. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
  77. package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
  78. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
  79. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
  80. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
  81. package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
  82. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
  83. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
  84. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
  85. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
  86. package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
  87. package/dist/extensions/agent-browser/lib/results/text.js +40 -0
  88. package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
  89. package/dist/extensions/agent-browser/lib/runtime.js +816 -0
  90. package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
  91. package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
  92. package/dist/extensions/agent-browser/lib/temp.js +498 -0
  93. package/dist/extensions/agent-browser/lib/web-search.js +562 -0
  94. package/docs/ARCHITECTURE.md +10 -10
  95. package/docs/COMMAND_REFERENCE.md +35 -21
  96. package/docs/ELECTRON.md +3 -3
  97. package/docs/RELEASE.md +46 -26
  98. package/docs/REQUIREMENTS.md +1 -1
  99. package/docs/SUPPORT_MATRIX.md +35 -106
  100. package/docs/TOOL_CONTRACT.md +23 -21
  101. package/package.json +12 -8
  102. package/scripts/agent-browser-capability-baseline.mjs +6 -3
  103. package/scripts/config.mjs +8 -2
  104. package/scripts/doctor.mjs +19 -17
  105. package/scripts/platform-smoke.mjs +1 -1
  106. package/extensions/agent-browser/index.ts +0 -952
  107. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  108. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  109. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  110. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  111. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  112. package/extensions/agent-browser/lib/config-policy.js +0 -690
  113. package/extensions/agent-browser/lib/config.ts +0 -209
  114. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  115. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  116. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  117. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  118. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  119. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  120. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  121. package/extensions/agent-browser/lib/input-modes/job.ts +0 -451
  122. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  123. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  124. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  125. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  126. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  127. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  128. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  129. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  130. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  131. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  132. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -257
  133. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -912
  134. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -512
  135. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  136. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -1481
  137. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -564
  138. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  139. package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
  140. package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -564
  141. package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
  142. package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
  143. package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
  144. package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -252
  145. package/extensions/agent-browser/lib/playbook.ts +0 -142
  146. package/extensions/agent-browser/lib/process.ts +0 -516
  147. package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
  148. package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
  149. package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
  150. package/extensions/agent-browser/lib/results/categories.ts +0 -106
  151. package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
  152. package/extensions/agent-browser/lib/results/contracts.ts +0 -241
  153. package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
  154. package/extensions/agent-browser/lib/results/envelope.ts +0 -195
  155. package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
  156. package/extensions/agent-browser/lib/results/network.ts +0 -78
  157. package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
  158. package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
  159. package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
  160. package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
  161. package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
  162. package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
  163. package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
  164. package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
  165. package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
  166. package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
  167. package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
  168. package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
  169. package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
  170. package/extensions/agent-browser/lib/results/presentation.ts +0 -257
  171. package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
  172. package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
  173. package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
  174. package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
  175. package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
  176. package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
  177. package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
  178. package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
  179. package/extensions/agent-browser/lib/results/text.ts +0 -40
  180. package/extensions/agent-browser/lib/runtime.ts +0 -988
  181. package/extensions/agent-browser/lib/session-page-state.ts +0 -512
  182. package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
  183. package/extensions/agent-browser/lib/temp.ts +0 -577
  184. package/extensions/agent-browser/lib/web-search.ts +0 -721
  185. /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
@@ -0,0 +1,491 @@
1
+ import { readFile, rm } from "node:fs/promises";
2
+ import { isCloseCommand, isOpenNavigationCommand } from "../../command-taxonomy.js";
3
+ import { cleanupElectronLaunchResources, inspectElectronLaunchStatus } from "../../electron/cleanup.js";
4
+ import { getAllowedDomainsViolation, parseAllowedDomainsPolicyFromArgs } from "../../navigation-policy.js";
5
+ import { analyzeNetworkSourceLookupResults, analyzeQaPresetResults, analyzeQaPresetTimeout, analyzeSourceLookupResults, buildQaCompactPassText, extractQaPageContext, redactNetworkSourceLookupAnalysis, } from "../../input-modes.js";
6
+ import { applyNetworkRouteRecords, buildNetworkRouteDiagnostics, buildToolPresentation, getAgentBrowserErrorText, parseAgentBrowserEnvelope, } from "../../results.js";
7
+ import { buildEvictedSessionArtifactEntries, formatSessionArtifactRetentionSummary, mergeSessionArtifactManifest, } from "../../results/artifact-manifest.js";
8
+ import { getClipboardWritePayloadCandidates, redactClipboardPermissionEcho, redactClipboardPermissionErrorValue } from "../../results/presentation/errors.js";
9
+ import { shouldCaptureSemanticActionNavigationSummary } from "../../results/presentation/semantic-action.js";
10
+ import { commandExplicitlyTargetsAboutBlank, deriveSessionTabTarget, extractLatestRefSnapshotStateFromBatchResults, extractRefSnapshotFromData, extractSessionTabTargetFromBatchResults, extractSessionTabTargetFromCommandData, isAboutBlankSessionTabTarget, normalizeSessionTabTarget, } from "../../session-page-state.js";
11
+ import { writePersistentSessionArtifactFile, writeSecureTempFile } from "../../temp.js";
12
+ import { isRecord } from "../../parsing.js";
13
+ import { createFreshSessionName, extractCommandTokens, hasLaunchScopedTabCorrectionFlag, resolveManagedSessionState } from "../../runtime.js";
14
+ import { applyOpenResultTabCorrection, buildAboutBlankRecoveryHint, buildAboutBlankWarning, buildElectronPostCommandHealthDiagnostic, buildElectronRefFreshnessDiagnostic, buildElectronSessionMismatch, buildManagedSessionOutcome, closeManagedSession, collectOpenResultTabCorrection, collectSessionTabSelection, extractNavigationSummaryFromData, extractStringResultField, findElectronLaunchRecordForSession, formatElectronPostCommandHealthText, formatElectronSessionMismatchText, getStaleRefArgs, mergeNavigationSummaryIntoData, shouldCaptureNavigationSummary, shouldCorrectSessionTabAfterCommand, shouldInspectElectronPostCommandHealth, unwrapPinnedSessionBatchEnvelope, updateTraceOwnerState, } from "./session-state.js";
15
+ import { collectClickDispatchDiagnostic } from "./click-dispatch.js";
16
+ import { buildScrollNoopDiagnostic, collectComboboxFocusDiagnostic, collectElectronBroadGetTextScopeDiagnostics, collectElectronHandoff, collectFillVerificationDiagnostic, collectNavigationSummary, collectOverlayBlockerDiagnostic, collectQaAttachedTarget, collectSnapshotOverlayBlockerDiagnostic, collectRecordingDependencyWarning, collectScrollPositionSnapshot, collectSelectorTextVisibilityDiagnostics, collectTimeoutPartialProgress, formatQaAttachedTargetText, getArtifactCleanupGuidance, getEvalResultWarning, getEvalStdinHint, getSourceLookupElectronContext, sleepMs, } from "./diagnostics.js";
17
+ import { repairScreenshotData } from "./prepare.js";
18
+ import { getPersistentSessionArtifactStore } from "./session-artifacts.js";
19
+ import { buildFinalAgentBrowserToolResult, buildRedactedPresentationContent, buildWrapperRecoveryHint, prepareFinalResultRecoveryState, redactExactSensitiveText, redactExactSensitiveValue, } from "./final-result.js";
20
+ async function repairScreenshotArtifact(options) {
21
+ const { cwd, envelope, request } = options;
22
+ if (!request || !envelope || !isRecord(envelope.data))
23
+ return { envelope, request };
24
+ const repaired = await repairScreenshotData({ cwd, data: envelope.data, request });
25
+ return { envelope: { ...envelope, data: repaired.data }, request: repaired.request };
26
+ }
27
+ async function repairBatchScreenshotArtifacts(options) {
28
+ const { cwd, envelope, requests } = options;
29
+ if (!envelope || !Array.isArray(envelope.data) || !requests?.some((request) => request !== undefined))
30
+ return { envelope, requests };
31
+ const repairedRequests = [];
32
+ const repairedData = await Promise.all(envelope.data.map(async (item, index) => {
33
+ const request = requests[index];
34
+ if (!request || !isRecord(item) || !isRecord(item.result))
35
+ return item;
36
+ const repaired = await repairScreenshotData({ cwd, data: item.result, request });
37
+ repairedRequests[index] = repaired.request;
38
+ return { ...item, result: repaired.data };
39
+ }));
40
+ return { envelope: { ...envelope, data: repairedData }, requests: repairedRequests };
41
+ }
42
+ function getEnvelopeErrorString(envelope) {
43
+ if (!envelope?.error)
44
+ return undefined;
45
+ if (typeof envelope.error === "string")
46
+ return envelope.error;
47
+ if (isRecord(envelope.error) && typeof envelope.error.message === "string")
48
+ return envelope.error.message;
49
+ return String(envelope.error);
50
+ }
51
+ function isStreamEnableAlreadyEnabledNoop(options) {
52
+ if (!options.processSucceeded || options.command !== "stream" || options.subcommand !== "enable" || options.envelope?.success !== false)
53
+ return false;
54
+ const message = (getEnvelopeErrorString(options.envelope) ?? "").trim().replace(/[.!]+$/, "").toLowerCase();
55
+ return message === "streaming is already enabled for this session" || message === "streaming is already enabled" || message === "stream already enabled";
56
+ }
57
+ function batchStartedManagedBrowser(data) {
58
+ if (!Array.isArray(data))
59
+ return false;
60
+ return data.some((entry) => {
61
+ if (!isRecord(entry) || entry.success !== true || !Array.isArray(entry.command))
62
+ return false;
63
+ const command = typeof entry.command[0] === "string" ? entry.command[0] : undefined;
64
+ return command === "connect" || command === "goto" || command === "navigate" || isOpenNavigationCommand(command);
65
+ });
66
+ }
67
+ function setNetworkRouteState(options) {
68
+ if (!options.sessionName)
69
+ return options.routesBySession;
70
+ const previousRoutes = options.routesBySession.get(options.sessionName);
71
+ if (options.routes === previousRoutes)
72
+ return options.routesBySession;
73
+ const next = new Map(options.routesBySession);
74
+ if (options.routes && options.routes.length > 0)
75
+ next.set(options.sessionName, options.routes);
76
+ else
77
+ next.delete(options.sessionName);
78
+ return next;
79
+ }
80
+ function applyNetworkRouteState(options) {
81
+ const routes = options.sessionName ? applyNetworkRouteRecords(options.routesBySession.get(options.sessionName), options.commandTokens, options.succeeded) : undefined;
82
+ return setNetworkRouteState({ routes, routesBySession: options.routesBySession, sessionName: options.sessionName });
83
+ }
84
+ function applyBatchNetworkRouteState(options) {
85
+ if (!options.succeeded || !options.sessionName || !Array.isArray(options.data))
86
+ return options.routesBySession;
87
+ let routes = options.routesBySession.get(options.sessionName);
88
+ for (const item of options.data) {
89
+ if (!isRecord(item) || !Array.isArray(item.command) || !item.command.every((token) => typeof token === "string"))
90
+ continue;
91
+ routes = applyNetworkRouteRecords(routes, extractCommandTokens(item.command), item.success !== false);
92
+ }
93
+ return setNetworkRouteState({ routes, routesBySession: options.routesBySession, sessionName: options.sessionName });
94
+ }
95
+ export async function preserveParseFailureOutput(options) {
96
+ if (!options.stdoutSpillPath)
97
+ return {};
98
+ try {
99
+ const rawOutput = redactExactSensitiveText(await readFile(options.stdoutSpillPath, "utf8"), options.exactSensitiveValues ?? []);
100
+ const nowMs = Date.now();
101
+ let evictedArtifacts = [];
102
+ let fullOutputPath;
103
+ let storageScope;
104
+ if (options.persistentArtifactStore) {
105
+ const result = await writePersistentSessionArtifactFile({ content: rawOutput, prefix: "pi-agent-browser-parse-failure-output", store: options.persistentArtifactStore, suffix: ".txt" });
106
+ fullOutputPath = result.path;
107
+ evictedArtifacts = result.evictedArtifacts;
108
+ storageScope = "persistent-session";
109
+ }
110
+ else {
111
+ fullOutputPath = await writeSecureTempFile({ content: rawOutput, prefix: "pi-agent-browser-parse-failure-output", suffix: ".txt" });
112
+ storageScope = "process-temp";
113
+ }
114
+ const artifactManifest = mergeSessionArtifactManifest({
115
+ base: options.artifactManifest,
116
+ entries: [{ command: "agent-browser", createdAtMs: nowMs, kind: "spill", path: fullOutputPath, retentionState: storageScope === "persistent-session" ? "live" : "ephemeral", storageScope }, ...buildEvictedSessionArtifactEntries(evictedArtifacts, nowMs)],
117
+ nowMs,
118
+ });
119
+ return { artifactManifest, artifactRetentionSummary: artifactManifest ? formatSessionArtifactRetentionSummary(artifactManifest) : undefined, fullOutputPath };
120
+ }
121
+ catch (error) {
122
+ const message = error instanceof Error ? error.message : String(error);
123
+ return { fullOutputUnavailable: message };
124
+ }
125
+ }
126
+ export async function processBrowserOutput(input) {
127
+ const { ctx, cwd, electronPostCommandStatusSettleMs, implicitSessionCloseTimeoutMs, sessionPageStateUpdate, signal, state } = input;
128
+ const { prepared, processResult } = input;
129
+ const { electronChildProcesses, electronLaunchRecords, sessionPageState, traceOwners } = state;
130
+ let allowedDomainsBySession = state.allowedDomainsBySession;
131
+ let artifactManifest = state.artifactManifest;
132
+ let freshSessionOrdinal = state.freshSessionOrdinal;
133
+ let managedSessionActive = state.managedSessionActive;
134
+ let managedSessionCwd = state.managedSessionCwd;
135
+ let managedSessionName = state.managedSessionName;
136
+ let networkRoutesBySession = state.networkRoutesBySession;
137
+ try {
138
+ const persistentArtifactStore = getPersistentSessionArtifactStore(ctx);
139
+ const parsed = await parseAgentBrowserEnvelope({ stdout: processResult.stdout, stdoutPath: processResult.stdoutSpillPath });
140
+ let parseError = parsed.parseError;
141
+ let presentationEnvelope = parsed.envelope;
142
+ let navigationSummary = undefined;
143
+ if (prepared.pinnedBatchUnwrapMode) {
144
+ const pinnedBatchResult = unwrapPinnedSessionBatchEnvelope({ envelope: parsed.envelope, includeNavigationSummary: prepared.includePinnedNavigationSummary, mode: prepared.pinnedBatchUnwrapMode });
145
+ parseError = pinnedBatchResult.parseError ?? parseError;
146
+ presentationEnvelope = pinnedBatchResult.envelope ?? presentationEnvelope;
147
+ navigationSummary = pinnedBatchResult.navigationSummary;
148
+ }
149
+ const repairedScreenshot = await repairScreenshotArtifact({ cwd, envelope: presentationEnvelope, request: prepared.preparedArgs.screenshotPathRequest });
150
+ presentationEnvelope = repairedScreenshot.envelope;
151
+ const repairedBatchScreenshots = await repairBatchScreenshotArtifacts({ cwd, envelope: presentationEnvelope, requests: prepared.preparedArgs.batchScreenshotPathRequests });
152
+ presentationEnvelope = repairedBatchScreenshots.envelope;
153
+ const screenshotArtifactRequest = repairedScreenshot.request;
154
+ const batchScreenshotArtifactRequests = repairedBatchScreenshots.requests;
155
+ if (presentationEnvelope && prepared.exactSensitiveValues.length > 0)
156
+ presentationEnvelope = redactExactSensitiveValue(presentationEnvelope, prepared.exactSensitiveValues);
157
+ const parseFailureOutput = parseError ? await preserveParseFailureOutput({ artifactManifest, exactSensitiveValues: prepared.exactSensitiveValues, persistentArtifactStore, stdoutSpillPath: processResult.stdoutSpillPath }) : {};
158
+ const processSucceeded = !processResult.aborted && !processResult.spawnError && processResult.exitCode === 0;
159
+ const plainTextInspection = prepared.executionPlan.plainTextInspection && processSucceeded;
160
+ const parseSucceeded = plainTextInspection || parseError === undefined;
161
+ if (isStreamEnableAlreadyEnabledNoop({ command: prepared.executionPlan.commandInfo.command, envelope: presentationEnvelope, processSucceeded, subcommand: prepared.executionPlan.commandInfo.subcommand })) {
162
+ presentationEnvelope = { success: true, data: { alreadyEnabled: true, enabled: true, message: getEnvelopeErrorString(presentationEnvelope) ?? "Stream already enabled" } };
163
+ }
164
+ const envelopeSuccess = plainTextInspection ? true : presentationEnvelope?.success !== false;
165
+ let succeeded = processSucceeded && parseSucceeded && envelopeSuccess;
166
+ const inspectionText = plainTextInspection ? processResult.stdout.trim() : undefined;
167
+ updateTraceOwnerState({ command: prepared.executionPlan.commandInfo.command, sessionName: prepared.executionPlan.sessionName, subcommand: prepared.executionPlan.commandInfo.subcommand, succeeded, traceOwners });
168
+ let clickDispatchDiagnostic;
169
+ if (succeeded && prepared.clickDispatchProbe) {
170
+ clickDispatchDiagnostic = await collectClickDispatchDiagnostic({ cwd, probe: prepared.clickDispatchProbe, sessionName: prepared.executionPlan.sessionName, signal });
171
+ if (clickDispatchDiagnostic) {
172
+ succeeded = false;
173
+ presentationEnvelope = { ...(presentationEnvelope ?? {}), error: clickDispatchDiagnostic.summary, success: false };
174
+ }
175
+ }
176
+ const parsedAllowedDomainsPolicy = parseAllowedDomainsPolicyFromArgs(prepared.runtimeToolArgs);
177
+ const sessionAllowedDomainsPolicy = prepared.executionPlan.sessionName
178
+ ? parsedAllowedDomainsPolicy ?? allowedDomainsBySession.get(prepared.executionPlan.sessionName)
179
+ : parsedAllowedDomainsPolicy;
180
+ const shouldCaptureAllowedDomainNavigationSummary = prepared.executionPlan.commandInfo.command === "batch" && sessionAllowedDomainsPolicy !== undefined;
181
+ if (succeeded &&
182
+ !navigationSummary &&
183
+ (shouldCaptureNavigationSummary(prepared.executionPlan.commandInfo.command, presentationEnvelope?.data) ||
184
+ shouldCaptureSemanticActionNavigationSummary(prepared.compiledSemanticAction, presentationEnvelope?.data) ||
185
+ shouldCaptureAllowedDomainNavigationSummary)) {
186
+ navigationSummary = await collectNavigationSummary({ cwd, sessionName: prepared.executionPlan.sessionName, signal });
187
+ }
188
+ if (navigationSummary && presentationEnvelope && !Array.isArray(presentationEnvelope.data))
189
+ presentationEnvelope = { ...presentationEnvelope, data: mergeNavigationSummaryIntoData(presentationEnvelope.data, navigationSummary) };
190
+ let overlayBlockerDiagnostic;
191
+ let openResultTabCorrection;
192
+ if (succeeded && prepared.executionPlan.sessionName && hasLaunchScopedTabCorrectionFlag(prepared.runtimeToolArgs) && isOpenNavigationCommand(prepared.executionPlan.commandInfo.command)) {
193
+ const targetTitle = extractStringResultField(presentationEnvelope?.data, "title");
194
+ const targetUrl = extractStringResultField(presentationEnvelope?.data, "url");
195
+ const plannedTabCorrection = await collectOpenResultTabCorrection({ cwd, sessionName: prepared.executionPlan.sessionName, signal, targetTitle, targetUrl });
196
+ if (plannedTabCorrection)
197
+ openResultTabCorrection = await applyOpenResultTabCorrection({ correction: plannedTabCorrection, cwd, sessionName: prepared.executionPlan.sessionName, signal });
198
+ }
199
+ const observedSessionTabTarget = normalizeSessionTabTarget(navigationSummary) ?? extractSessionTabTargetFromBatchResults(presentationEnvelope?.data) ?? extractSessionTabTargetFromCommandData(prepared.commandTokens, presentationEnvelope?.data);
200
+ let currentSessionTabTarget = deriveSessionTabTarget({ command: prepared.executionPlan.commandInfo.command, data: presentationEnvelope?.data, navigationSummary, previousTarget: prepared.priorSessionTabTarget, subcommand: prepared.executionPlan.commandInfo.subcommand });
201
+ let aboutBlankSessionMismatch;
202
+ let electronPostCommandHealth;
203
+ let electronRefFreshnessDiagnostic;
204
+ let electronSessionMismatch;
205
+ let electronStatusAfterCommand;
206
+ const shouldTreatAboutBlankAsMismatch = succeeded && prepared.priorSessionTabTarget !== undefined && !isAboutBlankSessionTabTarget(prepared.priorSessionTabTarget) && isAboutBlankSessionTabTarget(observedSessionTabTarget ?? currentSessionTabTarget) && !commandExplicitlyTargetsAboutBlank(prepared.commandTokens);
207
+ let sessionTabCorrection = prepared.sessionTabCorrection;
208
+ if (shouldTreatAboutBlankAsMismatch && prepared.priorSessionTabTarget) {
209
+ const aboutBlankObservedTarget = observedSessionTabTarget ?? currentSessionTabTarget;
210
+ const aboutBlankRecovery = await collectSessionTabSelection({ cwd, sessionName: prepared.executionPlan.sessionName, signal, target: prepared.priorSessionTabTarget });
211
+ const appliedAboutBlankRecovery = aboutBlankRecovery ? await applyOpenResultTabCorrection({ correction: aboutBlankRecovery, cwd, sessionName: prepared.executionPlan.sessionName, signal }) : undefined;
212
+ if (appliedAboutBlankRecovery) {
213
+ sessionTabCorrection = appliedAboutBlankRecovery;
214
+ currentSessionTabTarget = prepared.priorSessionTabTarget;
215
+ }
216
+ else
217
+ currentSessionTabTarget = aboutBlankObservedTarget ?? normalizeSessionTabTarget({ url: "about:blank" });
218
+ aboutBlankSessionMismatch = { activeUrl: "about:blank", recoveryApplied: appliedAboutBlankRecovery !== undefined, recoveryHint: buildAboutBlankRecoveryHint(), targetTitle: prepared.priorSessionTabTarget.title, targetUrl: prepared.priorSessionTabTarget.url };
219
+ const electronRecord = findElectronLaunchRecordForSession(prepared.executionPlan.sessionName, electronLaunchRecords);
220
+ if (electronRecord && prepared.executionPlan.sessionName) {
221
+ electronStatusAfterCommand = await inspectElectronLaunchStatus(electronRecord);
222
+ electronSessionMismatch = buildElectronSessionMismatch({ managedSession: { sessionName: prepared.executionPlan.sessionName, title: aboutBlankObservedTarget?.title, url: aboutBlankObservedTarget?.url ?? "about:blank" }, record: electronRecord, statusTargets: electronStatusAfterCommand.targets });
223
+ }
224
+ }
225
+ if (succeeded && prepared.priorSessionTabTarget && !sessionTabCorrection && !aboutBlankSessionMismatch && !commandExplicitlyTargetsAboutBlank(prepared.commandTokens) && observedSessionTabTarget && shouldCorrectSessionTabAfterCommand({ command: prepared.executionPlan.commandInfo.command, pinningRequired: prepared.sessionTabPinningReason !== undefined, sessionName: prepared.executionPlan.sessionName })) {
226
+ const postCommandTabCorrection = await collectSessionTabSelection({ cwd, sessionName: prepared.executionPlan.sessionName, signal, target: observedSessionTabTarget });
227
+ if (postCommandTabCorrection) {
228
+ const appliedPostCommandCorrection = await applyOpenResultTabCorrection({ correction: postCommandTabCorrection, cwd, sessionName: prepared.executionPlan.sessionName, signal });
229
+ if (appliedPostCommandCorrection && !sessionTabCorrection)
230
+ sessionTabCorrection = appliedPostCommandCorrection;
231
+ }
232
+ }
233
+ if (succeeded && prepared.executionPlan.sessionName && parsedAllowedDomainsPolicy) {
234
+ allowedDomainsBySession = new Map(allowedDomainsBySession);
235
+ allowedDomainsBySession.set(prepared.executionPlan.sessionName, parsedAllowedDomainsPolicy);
236
+ }
237
+ const allowedDomainsViolation = succeeded ? getAllowedDomainsViolation({
238
+ policy: sessionAllowedDomainsPolicy,
239
+ url: currentSessionTabTarget?.url ?? observedSessionTabTarget?.url ?? navigationSummary?.url,
240
+ }) : undefined;
241
+ if (allowedDomainsViolation) {
242
+ succeeded = false;
243
+ presentationEnvelope = { ...(presentationEnvelope ?? {}), error: allowedDomainsViolation.summary, success: false };
244
+ }
245
+ const electronRecordForCommand = findElectronLaunchRecordForSession(prepared.executionPlan.sessionName, electronLaunchRecords);
246
+ if (succeeded && electronRecordForCommand && shouldInspectElectronPostCommandHealth(prepared.executionPlan.commandInfo.command)) {
247
+ electronStatusAfterCommand ??= await inspectElectronLaunchStatus(electronRecordForCommand);
248
+ electronPostCommandHealth = buildElectronPostCommandHealthDiagnostic({ command: prepared.executionPlan.commandInfo.command, record: electronRecordForCommand, status: electronStatusAfterCommand, target: observedSessionTabTarget ?? currentSessionTabTarget });
249
+ if (electronPostCommandHealth && electronPostCommandHealth.reason !== "process-dead") {
250
+ await sleepMs(electronPostCommandStatusSettleMs);
251
+ electronStatusAfterCommand = await inspectElectronLaunchStatus(electronRecordForCommand);
252
+ electronPostCommandHealth = buildElectronPostCommandHealthDiagnostic({ command: prepared.executionPlan.commandInfo.command, record: electronRecordForCommand, status: electronStatusAfterCommand, target: observedSessionTabTarget ?? currentSessionTabTarget });
253
+ }
254
+ if (electronPostCommandHealth)
255
+ succeeded = false;
256
+ }
257
+ let fillVerificationDiagnostic;
258
+ let selectorTextVisibilityDiagnostics = [];
259
+ let electronBroadGetTextScopeDiagnostics = [];
260
+ const timeoutPartialProgress = processResult.timedOut ? await collectTimeoutPartialProgress({ command: prepared.executionPlan.commandInfo.command, compiledJob: prepared.compiledJob, cwd, sessionName: prepared.executionPlan.sessionName, stdin: prepared.runtimeToolStdin }) : undefined;
261
+ if (succeeded) {
262
+ const fillRefSnapshot = prepared.resolvedSemanticActionRefSnapshot ?? prepared.priorRefSnapshotState;
263
+ fillVerificationDiagnostic = await collectFillVerificationDiagnostic({ commandTokens: prepared.commandTokens, cwd, forceValueVerification: electronRecordForCommand !== undefined, refSnapshot: fillRefSnapshot, sessionName: prepared.executionPlan.sessionName, signal });
264
+ }
265
+ if (succeeded && electronRecordForCommand) {
266
+ electronRefFreshnessDiagnostic = buildElectronRefFreshnessDiagnostic({ command: prepared.executionPlan.commandInfo.command, commandTokens: prepared.commandTokens, record: electronRecordForCommand, sessionName: prepared.executionPlan.sessionName, stdin: prepared.runtimeToolStdin });
267
+ }
268
+ if (succeeded && prepared.executionPlan.commandInfo.command === "snapshot") {
269
+ overlayBlockerDiagnostic = collectSnapshotOverlayBlockerDiagnostic(presentationEnvelope?.data);
270
+ }
271
+ if (succeeded && !overlayBlockerDiagnostic && !sessionTabCorrection && !aboutBlankSessionMismatch && !electronRecordForCommand && !clickDispatchDiagnostic)
272
+ overlayBlockerDiagnostic = await collectOverlayBlockerDiagnostic({ command: prepared.executionPlan.commandInfo.command, cwd, data: presentationEnvelope?.data, navigationSummary, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName, signal });
273
+ if (succeeded) {
274
+ selectorTextVisibilityDiagnostics = await collectSelectorTextVisibilityDiagnostics({ commandInfo: prepared.executionPlan.commandInfo, commandTokens: prepared.commandTokens, cwd, data: presentationEnvelope?.data, sessionName: prepared.executionPlan.sessionName, signal });
275
+ if (electronRecordForCommand)
276
+ electronBroadGetTextScopeDiagnostics = collectElectronBroadGetTextScopeDiagnostics({ commandInfo: prepared.executionPlan.commandInfo, commandTokens: prepared.commandTokens, currentTarget: currentSessionTabTarget, data: presentationEnvelope?.data, electronLaunchRecords, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName });
277
+ }
278
+ const activeNetworkRoutes = prepared.executionPlan.sessionName ? networkRoutesBySession.get(prepared.executionPlan.sessionName) : undefined;
279
+ const networkRouteDiagnostics = succeeded && prepared.executionPlan.commandInfo.command === "network" && prepared.executionPlan.commandInfo.subcommand === "requests" && prepared.executionPlan.sessionName
280
+ ? buildNetworkRouteDiagnostics(presentationEnvelope?.data, activeNetworkRoutes)
281
+ : undefined;
282
+ networkRoutesBySession = applyNetworkRouteState({ commandTokens: prepared.commandTokens, routesBySession: networkRoutesBySession, sessionName: prepared.executionPlan.sessionName, succeeded });
283
+ const comboboxFocusDiagnostic = succeeded ? await collectComboboxFocusDiagnostic({ command: prepared.executionPlan.commandInfo.command, commandTokens: prepared.commandTokens, cwd, semanticAction: prepared.compiledSemanticAction, sessionName: prepared.executionPlan.sessionName, signal }) : undefined;
284
+ const recordingDependencyWarning = await collectRecordingDependencyWarning({ command: prepared.executionPlan.commandInfo.command, commandTokens: prepared.commandTokens, succeeded });
285
+ const scrollNoopDiagnostic = succeeded && prepared.shouldProbeScrollNoop ? buildScrollNoopDiagnostic(prepared.scrollPositionBefore, await collectScrollPositionSnapshot({ cwd, sessionName: prepared.executionPlan.sessionName, signal })) : undefined;
286
+ let currentRefSnapshot;
287
+ let currentRefSnapshotInvalidation;
288
+ const batchRefSnapshotState = prepared.executionPlan.commandInfo.command === "batch" ? extractLatestRefSnapshotStateFromBatchResults(presentationEnvelope?.data) : undefined;
289
+ if (prepared.executionPlan.sessionName) {
290
+ if (isCloseCommand(prepared.executionPlan.commandInfo.command) && succeeded) {
291
+ allowedDomainsBySession = new Map(allowedDomainsBySession);
292
+ allowedDomainsBySession.delete(prepared.executionPlan.sessionName);
293
+ networkRoutesBySession = new Map(networkRoutesBySession);
294
+ networkRoutesBySession.delete(prepared.executionPlan.sessionName);
295
+ sessionPageState.clearSession(prepared.executionPlan.sessionName);
296
+ state.closedManagedSessionNames.add(prepared.executionPlan.sessionName);
297
+ }
298
+ else if (currentSessionTabTarget) {
299
+ const tabUpdate = sessionPageState.applyTabTarget({ sessionName: prepared.executionPlan.sessionName, target: currentSessionTabTarget, update: sessionPageStateUpdate });
300
+ if (!tabUpdate.applied && succeeded)
301
+ sessionPageState.markPinning(prepared.executionPlan.sessionName, "drift");
302
+ }
303
+ const refSnapshot = prepared.executionPlan.commandInfo.command === "batch" ? batchRefSnapshotState?.snapshot : succeeded ? prepared.executionPlan.commandInfo.command === "snapshot" ? extractRefSnapshotFromData(presentationEnvelope?.data) : prepared.resolvedSemanticActionRefSnapshot ?? overlayBlockerDiagnostic?.snapshot : undefined;
304
+ if (refSnapshot) {
305
+ const refUpdate = sessionPageState.applyRefSnapshot({ fallbackTarget: currentSessionTabTarget, sessionName: prepared.executionPlan.sessionName, snapshot: refSnapshot, update: sessionPageStateUpdate });
306
+ currentRefSnapshot = refUpdate.refSnapshot;
307
+ currentRefSnapshotInvalidation = refUpdate.refSnapshotInvalidation;
308
+ }
309
+ else {
310
+ const stateView = sessionPageState.get(prepared.executionPlan.sessionName);
311
+ currentRefSnapshot = stateView.refSnapshot;
312
+ currentRefSnapshotInvalidation = stateView.refSnapshotInvalidation;
313
+ }
314
+ }
315
+ const priorManagedSessionActive = managedSessionActive;
316
+ const priorManagedSessionCwd = managedSessionCwd;
317
+ const priorManagedSessionName = managedSessionName;
318
+ const commandClosesSession = isCloseCommand(prepared.executionPlan.commandInfo.command);
319
+ const managedCloseSessionName = commandClosesSession && succeeded && prepared.executionPlan.sessionName === priorManagedSessionName
320
+ ? prepared.executionPlan.sessionName
321
+ : prepared.executionPlan.managedSessionName;
322
+ const policyBlockedFreshManagedSession = allowedDomainsViolation !== undefined && prepared.sessionMode === "fresh" && prepared.executionPlan.managedSessionName === prepared.executionPlan.sessionName;
323
+ const postLaunchBatchFailure = !succeeded && processSucceeded && parseSucceeded && prepared.sessionMode === "fresh" && prepared.executionPlan.commandInfo.command === "batch" && batchStartedManagedBrowser(presentationEnvelope?.data);
324
+ const postLaunchTimeoutWithPage = !succeeded && processResult.timedOut && prepared.sessionMode === "fresh" && prepared.executionPlan.commandInfo.command === "batch" && timeoutPartialProgress?.liveUrlRecovered === true;
325
+ const managedTransitionSucceeded = succeeded || policyBlockedFreshManagedSession || postLaunchBatchFailure || postLaunchTimeoutWithPage;
326
+ const managedSessionState = resolveManagedSessionState({ command: prepared.executionPlan.commandInfo.command, managedSessionName: managedCloseSessionName, priorActive: priorManagedSessionActive, priorSessionName: priorManagedSessionName, succeeded: managedTransitionSucceeded });
327
+ const replacedManagedSessionName = managedSessionState.replacedSessionName;
328
+ managedSessionActive = managedSessionState.active;
329
+ managedSessionName = managedSessionState.sessionName;
330
+ if (commandClosesSession && succeeded && managedCloseSessionName === priorManagedSessionName && !managedSessionActive) {
331
+ freshSessionOrdinal += 1;
332
+ managedSessionName = createFreshSessionName(state.managedSessionBaseName, state.ephemeralSessionSeed, freshSessionOrdinal);
333
+ }
334
+ let managedSessionOutcome = buildManagedSessionOutcome({ activeAfter: managedSessionActive, activeBefore: priorManagedSessionActive, attemptedSessionName: managedCloseSessionName, command: prepared.executionPlan.commandInfo.command, currentSessionName: managedSessionName, previousSessionName: priorManagedSessionName, replacedSessionName: replacedManagedSessionName, sessionMode: prepared.sessionMode, succeeded: managedTransitionSucceeded });
335
+ if (prepared.executionPlan.managedSessionName && succeeded)
336
+ managedSessionCwd = cwd;
337
+ if (prepared.executionPlan.sessionName && succeeded) {
338
+ if (openResultTabCorrection || sessionTabCorrection || aboutBlankSessionMismatch?.recoveryApplied)
339
+ sessionPageState.markPinning(prepared.executionPlan.sessionName, "drift");
340
+ else if (prepared.sessionTabPinningReason === "restore")
341
+ sessionPageState.clearRestorePinning(prepared.executionPlan.sessionName);
342
+ }
343
+ if (replacedManagedSessionName) {
344
+ allowedDomainsBySession = new Map(allowedDomainsBySession);
345
+ allowedDomainsBySession.delete(replacedManagedSessionName);
346
+ networkRoutesBySession = new Map(networkRoutesBySession);
347
+ networkRoutesBySession.delete(replacedManagedSessionName);
348
+ sessionPageState.clearSession(replacedManagedSessionName);
349
+ const replacedCloseError = await closeManagedSession({ cwd: priorManagedSessionCwd, sessionName: replacedManagedSessionName, timeoutMs: implicitSessionCloseTimeoutMs });
350
+ if (!replacedCloseError)
351
+ state.closedManagedSessionNames.add(replacedManagedSessionName);
352
+ }
353
+ let electronLaunchRecord;
354
+ let electronFailedConnectCleanup = prepared.electronFailedConnectCleanup;
355
+ let electronHandoff = prepared.electronHandoff;
356
+ if (prepared.electronLaunch) {
357
+ if (succeeded && prepared.executionPlan.sessionName) {
358
+ electronLaunchRecord = { ...prepared.electronLaunch.record, sessionName: prepared.executionPlan.sessionName };
359
+ electronLaunchRecords.set(electronLaunchRecord.launchId, electronLaunchRecord);
360
+ electronChildProcesses.set(electronLaunchRecord.launchId, prepared.electronLaunch.child);
361
+ const electronHandoffMode = prepared.compiledElectron?.action === "launch" ? prepared.compiledElectron.handoff : "connect";
362
+ try {
363
+ electronHandoff = await collectElectronHandoff({ cwd, handoff: electronHandoffMode, sessionName: prepared.executionPlan.sessionName, signal });
364
+ }
365
+ catch (error) {
366
+ electronHandoff = { error: error instanceof Error ? error.message : String(error), handoff: electronHandoffMode };
367
+ }
368
+ if (electronHandoff?.refSnapshot) {
369
+ const refUpdate = sessionPageState.applyRefSnapshot({ sessionName: prepared.executionPlan.sessionName, snapshot: electronHandoff.refSnapshot, update: sessionPageStateUpdate });
370
+ currentRefSnapshot = refUpdate.refSnapshot;
371
+ currentRefSnapshotInvalidation = refUpdate.refSnapshotInvalidation;
372
+ if (electronHandoff.refSnapshot.target) {
373
+ currentSessionTabTarget = electronHandoff.refSnapshot.target;
374
+ sessionPageState.applyTabTarget({ sessionName: prepared.executionPlan.sessionName, target: electronHandoff.refSnapshot.target, update: sessionPageStateUpdate });
375
+ }
376
+ }
377
+ }
378
+ else {
379
+ electronFailedConnectCleanup = await cleanupElectronLaunchResources({ child: prepared.electronLaunch.child, record: prepared.electronLaunch.record, timeoutMs: implicitSessionCloseTimeoutMs });
380
+ electronLaunchRecord = electronFailedConnectCleanup.record;
381
+ }
382
+ }
383
+ let errorText = getAgentBrowserErrorText({ aborted: processResult.aborted, command: prepared.executionPlan.commandInfo.command, effectiveArgs: prepared.redactedProcessArgs, envelope: presentationEnvelope, exitCode: processResult.exitCode, parseError, plainTextInspection, staleRefArgs: getStaleRefArgs(prepared.commandTokens, prepared.runtimeToolStdin), spawnError: processResult.spawnError, stderr: processResult.stderr, timedOut: processResult.timedOut, timeoutMs: processResult.timeoutMs, wrapperRecoveryHint: buildWrapperRecoveryHint({ pinnedBatchUnwrapMode: prepared.pinnedBatchUnwrapMode, sessionTabCorrection }) });
384
+ if (errorText) {
385
+ const clipboardWritePayloadCandidates = getClipboardWritePayloadCandidates(prepared.commandTokens);
386
+ errorText = redactClipboardPermissionEcho(prepared.executionPlan.commandInfo, errorText);
387
+ if (presentationEnvelope?.error !== undefined)
388
+ presentationEnvelope = { ...presentationEnvelope, error: redactClipboardPermissionErrorValue(prepared.executionPlan.commandInfo, presentationEnvelope.error, clipboardWritePayloadCandidates) };
389
+ }
390
+ const presentation = plainTextInspection ? { artifacts: undefined, batchFailure: undefined, batchSteps: undefined, content: [{ type: "text", text: inspectionText ?? "" }], data: undefined, fullOutputPath: undefined, fullOutputPaths: undefined, imagePath: undefined, imagePaths: undefined, savedFile: undefined, savedFilePath: undefined, summary: `${prepared.redactedArgs.join(" ")} completed` } : await buildToolPresentation({ args: prepared.redactedProcessArgs, artifactManifest, artifactRequest: screenshotArtifactRequest, batchArtifactRequests: batchScreenshotArtifactRequests, commandInfo: prepared.executionPlan.commandInfo, compiledSemanticAction: prepared.compiledSemanticAction, cwd, envelope: presentationEnvelope, errorText, networkRouteDiagnostics, networkRoutes: activeNetworkRoutes, persistentArtifactStore, sessionName: prepared.executionPlan.sessionName });
391
+ networkRoutesBySession = applyBatchNetworkRouteState({ data: presentationEnvelope?.data, routesBySession: networkRoutesBySession, sessionName: prepared.executionPlan.sessionName, succeeded });
392
+ if (presentation.resultCategory === "failure" && succeeded) {
393
+ succeeded = false;
394
+ presentationEnvelope = { ...(presentationEnvelope ?? {}), error: presentation.summary, success: false };
395
+ }
396
+ if (scrollNoopDiagnostic) {
397
+ presentation.summary = "Scroll completed with no observed movement.";
398
+ if (isRecord(presentation.data))
399
+ presentation.data = { ...presentation.data, noMovement: true, scrolled: false };
400
+ if (presentation.content[0]?.type === "text")
401
+ presentation.content[0] = { ...presentation.content[0], text: `Scroll completed with no observed movement.\n\n${presentation.content[0].text}` };
402
+ else
403
+ presentation.content.unshift({ type: "text", text: "Scroll completed with no observed movement." });
404
+ }
405
+ if (parseFailureOutput.artifactManifest) {
406
+ presentation.artifactManifest = parseFailureOutput.artifactManifest;
407
+ presentation.artifactRetentionSummary = parseFailureOutput.artifactRetentionSummary;
408
+ }
409
+ if (parseFailureOutput.fullOutputPath || parseFailureOutput.fullOutputUnavailable) {
410
+ const existingText = presentation.content[0]?.type === "text" ? presentation.content[0].text : "";
411
+ const noticeLines = [parseFailureOutput.fullOutputPath ? `Full output path: ${parseFailureOutput.fullOutputPath}` : `Full raw output unavailable: ${parseFailureOutput.fullOutputUnavailable}`, parseFailureOutput.artifactRetentionSummary].filter((item) => item !== undefined);
412
+ const notice = noticeLines.join("\n");
413
+ presentation.content[0] = { type: "text", text: existingText.length > 0 ? `${existingText}\n\n${notice}` : notice };
414
+ }
415
+ if (presentation.artifactManifest)
416
+ artifactManifest = presentation.artifactManifest;
417
+ const qaPreset = prepared.compiledQaPreset
418
+ ? (processResult.timedOut ? analyzeQaPresetTimeout(prepared.compiledQaPreset) ?? analyzeQaPresetResults(presentationEnvelope?.data, prepared.compiledQaPreset) : analyzeQaPresetResults(presentationEnvelope?.data, prepared.compiledQaPreset))
419
+ : undefined;
420
+ let qaAttachedTarget = prepared.compiledQaPreset?.checks.attached
421
+ ? await collectQaAttachedTarget({ currentTarget: currentSessionTabTarget ?? prepared.priorSessionTabTarget, cwd, sessionName: prepared.executionPlan.sessionName, signal })
422
+ : undefined;
423
+ const sourceLookupElectronContext = prepared.compiledSourceLookup ? getSourceLookupElectronContext({ currentTarget: currentSessionTabTarget, electronLaunchRecords, priorTarget: prepared.priorSessionTabTarget, sessionName: prepared.executionPlan.sessionName }) : undefined;
424
+ const sourceLookup = prepared.compiledSourceLookup ? await analyzeSourceLookupResults(presentationEnvelope?.data, prepared.compiledSourceLookup, cwd, { electronContext: sourceLookupElectronContext, workspaceRoot: cwd }) : undefined;
425
+ const networkSourceLookup = prepared.compiledNetworkSourceLookup ? redactNetworkSourceLookupAnalysis(await analyzeNetworkSourceLookupResults(presentationEnvelope?.data, prepared.compiledNetworkSourceLookup, cwd)) : undefined;
426
+ if (networkSourceLookup && presentation.content[0]?.type === "text")
427
+ presentation.content[0] = { ...presentation.content[0], text: `${networkSourceLookup.summary}\n\n${presentation.content[0].text}` };
428
+ else if (networkSourceLookup)
429
+ presentation.content.unshift({ type: "text", text: networkSourceLookup.summary });
430
+ if (sourceLookup && presentation.content[0]?.type === "text")
431
+ presentation.content[0] = { ...presentation.content[0], text: `${sourceLookup.summary}\n\n${presentation.content[0].text}` };
432
+ else if (sourceLookup)
433
+ presentation.content.unshift({ type: "text", text: sourceLookup.summary });
434
+ if (qaPreset && !qaPreset.passed && presentation.failureCategory !== "artifact-missing") {
435
+ succeeded = false;
436
+ presentation.failureCategory = "qa-failure";
437
+ presentation.summary = qaPreset.summary;
438
+ if (presentation.content[0]?.type === "text")
439
+ presentation.content[0] = { ...presentation.content[0], text: `${qaPreset.summary}\n\n${presentation.content[0].text}` };
440
+ else
441
+ presentation.content.unshift({ type: "text", text: qaPreset.summary });
442
+ }
443
+ else if (qaPreset?.passed && prepared.compiledQaPreset && succeeded) {
444
+ const compactText = buildQaCompactPassText({
445
+ artifactVerification: presentation.artifactVerification,
446
+ batchStepCount: presentation.batchSteps?.length ?? prepared.compiledQaPreset.steps.length,
447
+ checks: prepared.compiledQaPreset.checks,
448
+ page: extractQaPageContext({
449
+ attachedTarget: qaAttachedTarget,
450
+ batchData: presentationEnvelope?.data,
451
+ compiled: prepared.compiledQaPreset,
452
+ }),
453
+ qaPreset,
454
+ });
455
+ presentation.summary = qaPreset.summary;
456
+ const nonTextContent = presentation.content.filter((item) => item.type !== "text");
457
+ presentation.content = [{ type: "text", text: compactText }, ...nonTextContent];
458
+ }
459
+ const qaAttachedTargetText = formatQaAttachedTargetText(qaAttachedTarget);
460
+ const qaAttachedDiagnosticsText = prepared.compiledQaPreset?.checks.attached && prepared.compiledQaPreset.checks.diagnosticsResetAtStart === false && (prepared.compiledQaPreset.checks.checkNetwork || prepared.compiledQaPreset.checks.checkConsole || prepared.compiledQaPreset.checks.checkErrors)
461
+ ? "Attached diagnostics: existing upstream session console/network/error buffers were preserved; rows may include events from before qa.attached started."
462
+ : undefined;
463
+ const qaAttachedBannerText = [qaAttachedTargetText, qaAttachedDiagnosticsText].filter((part) => typeof part === "string" && part.length > 0).join("\n");
464
+ const skipAttachedTargetBanner = qaPreset?.passed && prepared.compiledQaPreset?.checks.attached;
465
+ if (!skipAttachedTargetBanner && qaAttachedBannerText && presentation.content[0]?.type === "text")
466
+ presentation.content[0] = { ...presentation.content[0], text: `${qaAttachedBannerText}\n\n${presentation.content[0].text}` };
467
+ else if (!skipAttachedTargetBanner && qaAttachedBannerText)
468
+ presentation.content.unshift({ type: "text", text: qaAttachedBannerText });
469
+ if (managedSessionOutcome && managedSessionOutcome.succeeded !== succeeded)
470
+ managedSessionOutcome = { ...managedSessionOutcome, succeeded };
471
+ const evalNavigationSummary = navigationSummary ?? extractNavigationSummaryFromData(presentationEnvelope?.data);
472
+ const evalSessionTabUrl = prepared.executionPlan.sessionName ? sessionPageState.get(prepared.executionPlan.sessionName).tabTarget?.url : undefined;
473
+ const evalPageUrl = evalNavigationSummary?.url ?? currentSessionTabTarget?.url ?? prepared.priorSessionTabTarget?.url ?? evalSessionTabUrl;
474
+ const evalStdinHint = getEvalStdinHint({ command: prepared.executionPlan.commandInfo.command, data: presentationEnvelope?.data, stdin: prepared.runtimeToolStdin });
475
+ const evalResultWarning = getEvalResultWarning({ command: prepared.executionPlan.commandInfo.command, data: presentationEnvelope?.data, navigationSummary: evalNavigationSummary, pageUrl: evalPageUrl, stdin: prepared.runtimeToolStdin });
476
+ const resultArtifactManifest = presentation.artifactManifest ?? artifactManifest;
477
+ const artifactCleanup = await getArtifactCleanupGuidance({ command: prepared.executionPlan.commandInfo.command, cwd, manifest: resultArtifactManifest, succeeded });
478
+ const warningText = electronPostCommandHealth ? formatElectronPostCommandHealthText(electronPostCommandHealth) : electronSessionMismatch ? formatElectronSessionMismatchText(electronSessionMismatch) : aboutBlankSessionMismatch ? buildAboutBlankWarning(aboutBlankSessionMismatch) : undefined;
479
+ const redactedContent = buildRedactedPresentationContent({ exactSensitiveValues: prepared.exactSensitiveValues, plainTextInspection, presentation, presentationEnvelope, succeeded, userRequestedJson: prepared.userRequestedJson, warningText });
480
+ const finalRecoveryState = await prepareFinalResultRecoveryState({ aboutBlankSessionMismatch, batchRefSnapshotState, commandTokens: prepared.commandTokens, compiledSemanticAction: prepared.compiledSemanticAction, currentRefSnapshot, currentRefSnapshotInvalidation, currentSessionTabTarget, cwd, electronPostCommandHealth, errorText, executionPlan: prepared.executionPlan, parseError, plainTextInspection, presentation, processResult, redactedProcessArgs: prepared.redactedProcessArgs, runtimeToolArgs: prepared.runtimeToolArgs, sessionPageState, sessionPageStateUpdate, sessionTabCorrection, signal, succeeded });
481
+ currentRefSnapshot = finalRecoveryState.currentRefSnapshot;
482
+ currentRefSnapshotInvalidation = finalRecoveryState.currentRefSnapshotInvalidation;
483
+ const result = buildFinalAgentBrowserToolResult({ aboutBlankSessionMismatch, artifactCleanup, categoryDetails: finalRecoveryState.categoryDetails, clickDispatchDiagnostic, commandTokens: prepared.commandTokens, comboboxFocusDiagnostic, compiledNetworkSourceLookup: prepared.compiledNetworkSourceLookup, compiledSemanticAction: prepared.compiledSemanticAction, compatibilityWorkaround: prepared.compatibilityWorkaround, currentRefSnapshot, currentRefSnapshotInvalidation, currentSessionTabTarget, electronBroadGetTextScopeDiagnostics, electronFailedConnectCleanup, electronHandoff, electronLaunch: prepared.electronLaunch, electronLaunchRecord, electronLaunchRecords, electronPostCommandHealth, electronProfileIsolationDetails: input.electronProfileIsolationDetails, electronRefFreshnessDiagnostic, electronSessionMismatch, errorText, evalResultWarning, evalStdinHint, exactSensitiveValues: prepared.exactSensitiveValues, executionPlan: prepared.executionPlan, fillVerificationDiagnostic, inspectionText, managedSessionOutcome, navigationSummary, networkSourceLookup, noActivePageSnapshotFailure: finalRecoveryState.noActivePageSnapshotFailure, openResultTabCorrection, overlayBlockerDiagnostic, parseError, parseFailureOutput, parseSucceeded, plainTextInspection, presentation, presentationEnvelope, priorSessionTabTarget: prepared.priorSessionTabTarget, processResult, qaAttachedTarget, qaPreset, recordingDependencyWarning, redactedArgs: prepared.redactedArgs, redactedCompiledElectron: prepared.redactedCompiledElectron, redactedCompiledJob: prepared.redactedCompiledJob, redactedCompiledNetworkSourceLookup: prepared.redactedCompiledNetworkSourceLookup, redactedCompiledQaPreset: prepared.redactedCompiledQaPreset, redactedCompiledSemanticAction: prepared.redactedCompiledSemanticAction, redactedCompiledSourceLookup: prepared.redactedCompiledSourceLookup, redactedContent, redactedProcessArgs: prepared.redactedProcessArgs, redactedRecoveryHint: prepared.redactedRecoveryHint, resultArtifactManifest, richInputRecoveryDiagnostic: finalRecoveryState.richInputRecoveryDiagnostic, scrollNoopDiagnostic, selectorTextVisibilityDiagnostics, sessionMode: prepared.sessionMode, sessionTabCorrection, sourceLookup, succeeded, timeoutPartialProgress, userRequestedJson: prepared.userRequestedJson, visibleRefFallbackDiagnostic: finalRecoveryState.visibleRefFallbackDiagnostic, visibleRefFallbackSessionName: finalRecoveryState.visibleRefFallbackSessionName });
484
+ const statePatch = { allowedDomainsBySession, artifactManifest, freshSessionOrdinal, managedSessionActive, managedSessionCwd, managedSessionName, networkRoutesBySession };
485
+ return { result, statePatch };
486
+ }
487
+ finally {
488
+ if (processResult.stdoutSpillPath)
489
+ await rm(processResult.stdoutSpillPath, { force: true }).catch(() => undefined);
490
+ }
491
+ }
@@ -0,0 +1,40 @@
1
+ import { isAbsolute, resolve } from "node:path";
2
+ import { isCloseCommand } from "../../command-taxonomy.js";
3
+ import { executableExistsOnPath } from "../../executable-path.js";
4
+ function resolveArtifactPath(cwd, path) {
5
+ return isAbsolute(path) ? path : resolve(cwd, path);
6
+ }
7
+ function manifestContainsArtifact(manifest, cwd, artifact) {
8
+ if (!manifest)
9
+ return false;
10
+ const requestedAbsolutePath = resolveArtifactPath(cwd, artifact.path);
11
+ const expectedKind = artifact.kind === "screenshot" ? "image" : "video";
12
+ return manifest.entries.some((entry) => {
13
+ const entryAbsolutePath = entry.absolutePath ?? resolveArtifactPath(cwd, entry.path);
14
+ return entry.storageScope === "explicit-path" && entry.kind === expectedKind && entryAbsolutePath === requestedAbsolutePath && entry.retentionState === "live" && entry.exists === true;
15
+ });
16
+ }
17
+ async function isArtifactRequired(artifact) {
18
+ if (artifact.required)
19
+ return true;
20
+ return artifact.kind === "recording" && await executableExistsOnPath("ffmpeg");
21
+ }
22
+ export async function findRequestedArtifactCloseViolation(options) {
23
+ if (!isCloseCommand(options.command))
24
+ return undefined;
25
+ const missingArtifacts = [];
26
+ for (const artifact of options.promptPolicy.requestedArtifacts) {
27
+ if (!await isArtifactRequired(artifact))
28
+ continue;
29
+ if (!manifestContainsArtifact(options.artifactManifest, options.cwd, artifact))
30
+ missingArtifacts.push(artifact);
31
+ }
32
+ if (missingArtifacts.length === 0)
33
+ return undefined;
34
+ const missingList = missingArtifacts.map((artifact) => `${artifact.kind}: ${artifact.path}`).join(", ");
35
+ return {
36
+ message: `Blocked browser close because requested artifact path${missingArtifacts.length === 1 ? " is" : "s are"} missing or unverified: ${missingList}. Save the requested artifact path first, or report why an optional artifact is unavailable before closing.`,
37
+ missingArtifacts,
38
+ reason: "requested-artifacts-missing-before-close",
39
+ };
40
+ }
@@ -0,0 +1,5 @@
1
+ export function getPersistentSessionArtifactStore(ctx) {
2
+ const sessionDir = typeof ctx.sessionManager.getSessionDir === "function" ? ctx.sessionManager.getSessionDir() : undefined;
3
+ const sessionId = ctx.sessionManager.getSessionId();
4
+ return sessionDir && sessionId ? { sessionDir, sessionId } : undefined;
5
+ }