pi-agent-browser-native 0.2.48 → 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 +17 -0
  2. package/README.md +16 -6
  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/RELEASE.md +22 -11
  95. package/docs/SUPPORT_MATRIX.md +4 -3
  96. package/package.json +9 -5
  97. package/scripts/config.mjs +8 -2
  98. package/scripts/doctor.mjs +8 -7
  99. package/extensions/agent-browser/index.ts +0 -961
  100. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  101. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  102. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  103. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  104. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  105. package/extensions/agent-browser/lib/config-policy.js +0 -690
  106. package/extensions/agent-browser/lib/config.ts +0 -211
  107. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  108. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  109. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  110. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  111. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  112. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  113. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  114. package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
  115. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  116. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  117. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  118. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  119. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  120. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  121. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  122. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  123. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  124. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  125. package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
  126. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
  127. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
  128. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
  129. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  130. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
  131. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
  132. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
  133. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
  134. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
  135. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
  136. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
  137. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  138. package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
  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 -565
  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 -267
  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 -728
  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
+ }