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,166 @@
1
+ /**
2
+ * Purpose: Parse upstream agent-browser output and turn failure envelopes into actionable error text.
3
+ * Responsibilities: Read inline or spilled stdout, parse observed JSON envelope shapes, normalize batch arrays, and extract the most useful error text from nested upstream failures.
4
+ * Scope: Envelope parsing and error derivation only; content rendering and snapshot compaction live in separate modules.
5
+ * Usage: Imported by the public `lib/results.ts` facade and by tests through that facade.
6
+ * Invariants/Assumptions: Upstream `agent-browser --json` responses follow the observed `{ success, data, error }` envelope shape or the array shape returned by `batch --json`.
7
+ */
8
+ import { readFile } from "node:fs/promises";
9
+ import { isRecord } from "../parsing.js";
10
+ import { detectConfirmationRequired } from "./confirmation.js";
11
+ import { stringifyUnknown } from "./text.js";
12
+ function hasStructuredBatchStepFailure(data) {
13
+ return Array.isArray(data) && data.some((item) => isRecord(item) && item.success === false);
14
+ }
15
+ async function readEnvelopeSource(options) {
16
+ if (!options.stdoutPath) {
17
+ return options.stdout;
18
+ }
19
+ try {
20
+ return await readFile(options.stdoutPath, "utf8");
21
+ }
22
+ catch (error) {
23
+ const message = error instanceof Error ? error.message : String(error);
24
+ throw new Error(`agent-browser output spill file could not be read: ${message}`);
25
+ }
26
+ }
27
+ function extractEnvelopeErrorText(error) {
28
+ if (typeof error === "string") {
29
+ return error.trim() || undefined;
30
+ }
31
+ if (typeof error === "number" || typeof error === "boolean") {
32
+ return String(error);
33
+ }
34
+ if (Array.isArray(error)) {
35
+ const parts = error.map((item) => extractEnvelopeErrorText(item) ?? stringifyUnknown(item)).filter((item) => item.length > 0);
36
+ return parts.length > 0 ? parts.join("\n") : undefined;
37
+ }
38
+ if (!isRecord(error)) {
39
+ return error == null ? undefined : stringifyUnknown(error);
40
+ }
41
+ for (const key of ["message", "error", "details", "cause", "stderr"]) {
42
+ const value = extractEnvelopeErrorText(error[key]);
43
+ if (value)
44
+ return value;
45
+ }
46
+ const fallback = stringifyUnknown(error).trim();
47
+ return fallback.length > 0 && fallback !== "{}" ? fallback : undefined;
48
+ }
49
+ export async function parseAgentBrowserEnvelope(options) {
50
+ let stdout;
51
+ try {
52
+ stdout = typeof options === "string" ? options : await readEnvelopeSource(options);
53
+ }
54
+ catch (error) {
55
+ return { parseError: error instanceof Error ? error.message : String(error) };
56
+ }
57
+ const trimmed = stdout.trim();
58
+ if (trimmed.length === 0) {
59
+ return { parseError: "agent-browser returned no JSON output." };
60
+ }
61
+ try {
62
+ const parsed = JSON.parse(trimmed);
63
+ if (Array.isArray(parsed)) {
64
+ return { envelope: { success: parsed.every((item) => !isRecord(item) || item.success !== false), data: parsed } };
65
+ }
66
+ if (!isRecord(parsed)) {
67
+ return { parseError: "agent-browser returned JSON, but it was not an object envelope." };
68
+ }
69
+ if (!("success" in parsed)) {
70
+ return { parseError: "agent-browser returned an invalid JSON envelope: missing boolean success field." };
71
+ }
72
+ if (typeof parsed.success !== "boolean") {
73
+ return { parseError: "agent-browser returned an invalid JSON envelope: success field must be boolean." };
74
+ }
75
+ if (!Object.hasOwn(parsed, "data")) {
76
+ const { success, error, ...topLevelData } = parsed;
77
+ if (Object.keys(topLevelData).length > 0) {
78
+ return { envelope: { error, success, data: topLevelData } };
79
+ }
80
+ }
81
+ return { envelope: parsed };
82
+ }
83
+ catch (error) {
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ return { parseError: `agent-browser returned invalid JSON: ${message}` };
86
+ }
87
+ }
88
+ function buildInvocationLabel(options) {
89
+ if (options.effectiveArgs && options.effectiveArgs.length > 0) {
90
+ return `agent-browser ${options.effectiveArgs.join(" ")}`;
91
+ }
92
+ if (options.command && options.command.trim().length > 0) {
93
+ return `agent-browser ${options.command.trim()}`;
94
+ }
95
+ return "agent-browser";
96
+ }
97
+ function appendWrapperRecoveryHint(message, wrapperRecoveryHint) {
98
+ const hint = wrapperRecoveryHint?.trim();
99
+ return hint ? `${message}\n${hint}` : message;
100
+ }
101
+ function buildFailureFallback(options) {
102
+ const invocation = buildInvocationLabel(options);
103
+ const exitSuffix = options.exitCode !== 0 ? ` (exit code ${options.exitCode})` : "";
104
+ return appendWrapperRecoveryHint(`${invocation} reported failure${exitSuffix}.`, options.wrapperRecoveryHint);
105
+ }
106
+ function buildExitCodeFallback(options) {
107
+ const invocation = buildInvocationLabel(options);
108
+ return appendWrapperRecoveryHint(`${invocation} exited with code ${options.exitCode}.`, options.wrapperRecoveryHint);
109
+ }
110
+ function buildWatchdogTimeoutMessage(options) {
111
+ const timeoutText = options.timeoutMs === undefined ? "the wrapper watchdog" : `the ${options.timeoutMs}ms wrapper watchdog`;
112
+ const ipcTiming = options.timeoutMs !== undefined && options.timeoutMs <= 30_000
113
+ ? "before the upstream CLI entered its 30s IPC retry path"
114
+ : "after waiting beyond the upstream CLI's 30s IPC retry window";
115
+ return [
116
+ `agent-browser exceeded ${timeoutText} and was stopped ${ipcTiming}.`,
117
+ "Prefer a condition wait or split long work into shorter calls; for legitimately long opens or captures, pass agent_browser timeoutMs with a bounded higher value and inspect details.timeoutPartialProgress before retrying.",
118
+ ].join(" ");
119
+ }
120
+ function isUpstreamIpcReadTimeoutMessage(message) {
121
+ return /Failed to read: Resource temporarily unavailable(?: \(os error \d+\))?.*daemon may be busy or unresponsive/i.test(message);
122
+ }
123
+ function buildUpstreamIpcReadTimeoutMessage() {
124
+ return [
125
+ "agent-browser hit the upstream CLI 30s IPC read timeout while waiting for the daemon response.",
126
+ "The daemon may still be alive; do not blindly retry a non-idempotent command. Prefer a shorter command, split long waits, or retry with sessionMode: \"fresh\" after checking tab list.",
127
+ ].join(" ");
128
+ }
129
+ function maybeAppendStaleRefHint(message, args) {
130
+ const usedRef = args?.some((arg) => /^@e\d+\b/.test(arg)) ?? false;
131
+ if (!usedRef || !/could not locate element|element not found|no element/i.test(message)) {
132
+ return message;
133
+ }
134
+ return [
135
+ message,
136
+ "This @ref may be stale after navigation, scrolling, or a DOM update. Run `agent_browser` with `{ \"args\": [\"snapshot\", \"-i\"] }` again and retry with a current ref, or use a stable `find` locator.",
137
+ ].join("\n");
138
+ }
139
+ export function getAgentBrowserErrorText(options) {
140
+ const { aborted, envelope, exitCode, parseError, plainTextInspection, spawnError, stderr, timedOut } = options;
141
+ if (plainTextInspection)
142
+ return undefined;
143
+ if (timedOut)
144
+ return buildWatchdogTimeoutMessage(options);
145
+ if (aborted)
146
+ return "agent-browser was aborted.";
147
+ if (spawnError)
148
+ return spawnError.message;
149
+ if (parseError)
150
+ return parseError;
151
+ if (envelope?.success === false) {
152
+ if ((hasStructuredBatchStepFailure(envelope.data) || detectConfirmationRequired(envelope.data)) && envelope.error === undefined) {
153
+ return undefined;
154
+ }
155
+ const envelopeErrorText = extractEnvelopeErrorText(envelope.error);
156
+ if (envelopeErrorText && isUpstreamIpcReadTimeoutMessage(envelopeErrorText)) {
157
+ return buildUpstreamIpcReadTimeoutMessage();
158
+ }
159
+ const fallback = envelopeErrorText ?? (stderr.trim() || buildFailureFallback(options));
160
+ return maybeAppendStaleRefHint(fallback, options.staleRefArgs ?? options.effectiveArgs);
161
+ }
162
+ if (exitCode !== 0) {
163
+ return stderr.trim() || buildExitCodeFallback(options);
164
+ }
165
+ return undefined;
166
+ }
@@ -0,0 +1,92 @@
1
+ import { isRecord } from "../parsing.js";
2
+ import { redactSensitiveText } from "../runtime.js";
3
+ import { getStringRecordField, isApiLikeNetworkRequest } from "./network.js";
4
+ function getArrayField(data, key) {
5
+ const value = data[key];
6
+ return Array.isArray(value) ? value : undefined;
7
+ }
8
+ function networkRoutePatternMatchesUrl(pattern, url) {
9
+ if (pattern === url)
10
+ return true;
11
+ if (pattern.includes("*")) {
12
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
13
+ return new RegExp(`^${escaped}$`).test(url);
14
+ }
15
+ return pattern.length >= 4 && url.includes(pattern);
16
+ }
17
+ function getSafeRequestId(item) {
18
+ const requestId = getStringRecordField(item, "requestId") ?? getStringRecordField(item, "id");
19
+ if (!requestId || redactSensitiveText(requestId) !== requestId)
20
+ return undefined;
21
+ return requestId;
22
+ }
23
+ function getRouteDiagnosticReason(item, route) {
24
+ const statusMissing = typeof item.status !== "number";
25
+ const error = getStringRecordField(item, "error") ?? getStringRecordField(item, "failureText") ?? getStringRecordField(item, "errorText");
26
+ if (error && /(?:cors|cross-origin|preflight|access-control-allow-origin)/i.test(error))
27
+ return "cors-likely-routed-request";
28
+ if (statusMissing && isApiLikeNetworkRequest(item))
29
+ return "pending-routed-request";
30
+ if (route.mode !== "abort" && ((typeof item.status === "number" && item.status >= 400) || item.failed === true || typeof error === "string"))
31
+ return "unfulfilled-routed-request";
32
+ return undefined;
33
+ }
34
+ export function getNetworkRouteMode(args) {
35
+ if (args.includes("--abort"))
36
+ return "abort";
37
+ if (args.includes("--body"))
38
+ return "body";
39
+ return "handler";
40
+ }
41
+ export function applyNetworkRouteRecords(routes, commandTokens, succeeded) {
42
+ if (!succeeded || commandTokens?.[0] !== "network")
43
+ return routes;
44
+ const subcommand = commandTokens[1];
45
+ if (subcommand !== "route" && subcommand !== "unroute")
46
+ return routes;
47
+ const existing = routes ?? [];
48
+ const pattern = commandTokens[2];
49
+ if (subcommand === "route" && pattern)
50
+ return [...existing.filter((route) => route.pattern !== pattern), { mode: getNetworkRouteMode(commandTokens), pattern }];
51
+ if (!pattern)
52
+ return undefined;
53
+ const next = existing.filter((route) => route.pattern !== pattern);
54
+ return next.length > 0 ? next : undefined;
55
+ }
56
+ export function buildNetworkRouteDiagnostics(data, routes) {
57
+ if (!routes || routes.length === 0 || !isRecord(data))
58
+ return undefined;
59
+ const requests = getArrayField(data, "requests");
60
+ if (!requests)
61
+ return undefined;
62
+ const diagnostics = [];
63
+ for (const item of requests) {
64
+ if (!isRecord(item))
65
+ continue;
66
+ const url = getStringRecordField(item, "url");
67
+ if (!url)
68
+ continue;
69
+ const route = routes.find((candidate) => networkRoutePatternMatchesUrl(candidate.pattern, url));
70
+ if (!route)
71
+ continue;
72
+ const reason = getRouteDiagnosticReason(item, route);
73
+ if (!reason)
74
+ continue;
75
+ const requestId = getSafeRequestId(item);
76
+ const requestUrl = redactSensitiveText(url);
77
+ const routePattern = redactSensitiveText(route.pattern);
78
+ diagnostics.push({
79
+ mode: route.mode,
80
+ reason,
81
+ ...(requestId ? { requestId } : {}),
82
+ requestUrl,
83
+ routePattern,
84
+ summary: reason === "cors-likely-routed-request"
85
+ ? `Routed request ${requestId ?? requestUrl} looks CORS/preflight-related for route ${routePattern}.`
86
+ : reason === "unfulfilled-routed-request"
87
+ ? `Routed request ${requestId ?? requestUrl} failed instead of returning the configured route ${routePattern}.`
88
+ : `Routed request ${requestId ?? requestUrl} is still pending/no-status for route ${routePattern}.`,
89
+ });
90
+ }
91
+ return diagnostics.length > 0 ? diagnostics.slice(0, 5) : undefined;
92
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Purpose: Classify failed network requests into actionable vs benign diagnostics.
3
+ * Responsibilities: Recognize failed request rows, de-prioritize browser icon misses, and summarize failure counts.
4
+ * Scope: Network diagnostic classification only.
5
+ * Usage: QA preset analysis and presentation network summaries.
6
+ * Invariants/Assumptions: Browser favicon/apple-touch icon misses are warnings; API/document/script failures are actionable.
7
+ */
8
+ import { isRecord } from "../parsing.js";
9
+ export function getStringRecordField(value, key) {
10
+ const field = value[key];
11
+ return typeof field === "string" && field.trim().length > 0 ? field.trim() : undefined;
12
+ }
13
+ export function getNetworkRequestUrlPath(url) {
14
+ if (!url)
15
+ return undefined;
16
+ try {
17
+ return new URL(url).pathname;
18
+ }
19
+ catch {
20
+ const withoutQuery = url.split(/[?#]/, 1)[0];
21
+ return withoutQuery.length > 0 ? withoutQuery : undefined;
22
+ }
23
+ }
24
+ function isFailedNetworkRequest(request) {
25
+ return (typeof request.status === "number" && request.status >= 400) || request.failed === true || typeof request.error === "string";
26
+ }
27
+ export function isNetworkArtifactNoiseRequest(request) {
28
+ const url = getStringRecordField(request, "url") ?? "";
29
+ const resourceType = (getStringRecordField(request, "resourceType") ?? getStringRecordField(request, "mimeType") ?? "").toLowerCase();
30
+ return /^data:image\//i.test(url) || (url.startsWith("data:") && resourceType.includes("image"));
31
+ }
32
+ function isBenignAssetFailure(request, url, resourceType) {
33
+ const path = getNetworkRequestUrlPath(url);
34
+ if (!path)
35
+ return false;
36
+ const normalizedResourceType = resourceType?.toLowerCase();
37
+ return /(?:^|\/)(?:favicon(?:[-.\w]*)?\.(?:ico|png|svg)|apple-touch-icon(?:[-.\w]*)?\.png)$/i.test(path)
38
+ && (request.status === 404 || request.failed === true || typeof request.error === "string")
39
+ && (!normalizedResourceType || ["image", "img", "other"].includes(normalizedResourceType) || normalizedResourceType.startsWith("image/"));
40
+ }
41
+ export function isApiLikeNetworkRequest(request) {
42
+ const method = (getStringRecordField(request, "method") ?? "GET").toUpperCase();
43
+ const resourceType = (getStringRecordField(request, "resourceType") ?? "").toLowerCase();
44
+ const mimeType = (getStringRecordField(request, "mimeType") ?? "").toLowerCase();
45
+ const path = getNetworkRequestUrlPath(getStringRecordField(request, "url")) ?? "";
46
+ return resourceType === "fetch" || resourceType === "xhr" || mimeType.includes("json") || /\/(?:api|graphql|rpc)(?:\/|$)/i.test(path) || !["GET", "HEAD"].includes(method);
47
+ }
48
+ export function classifyNetworkRequestFailure(request) {
49
+ if (!isFailedNetworkRequest(request))
50
+ return undefined;
51
+ const url = getStringRecordField(request, "url");
52
+ const resourceType = getStringRecordField(request, "resourceType") ?? getStringRecordField(request, "mimeType");
53
+ const status = typeof request.status === "number" ? request.status : undefined;
54
+ if (isBenignAssetFailure(request, url, resourceType)) {
55
+ return { impact: "benign", reason: "low-impact browser icon asset", resourceType, status, url };
56
+ }
57
+ return { impact: "actionable", reason: "document, script, API, or non-benign request failure", resourceType, status, url };
58
+ }
59
+ export function summarizeNetworkFailures(requests) {
60
+ const failures = requests.flatMap((request) => {
61
+ if (!isRecord(request) || isNetworkArtifactNoiseRequest(request))
62
+ return [];
63
+ const classification = classifyNetworkRequestFailure(request);
64
+ return classification ? [classification] : [];
65
+ });
66
+ const benignCount = failures.filter((failure) => failure.impact === "benign").length;
67
+ return {
68
+ actionableCount: failures.length - benignCount,
69
+ benignCount,
70
+ failures,
71
+ totalCount: failures.length,
72
+ };
73
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Purpose: Own machine-readable agent_browser next-action contracts and merge policy.
3
+ * Responsibilities: Define the stable nextAction shape, build basic argv follow-ups, and provide deterministic action-list collection helpers.
4
+ * Scope: Result follow-up action mechanics only; command-specific recovery and artifact policies live in neighboring modules.
5
+ * Usage: Imported by result presentation helpers and the extension entrypoint when attaching details.nextActions.
6
+ * Invariants/Assumptions: Action ids are stable machine-readable contracts; dedupe preserves first occurrence order.
7
+ */
8
+ export function withOptionalSessionArgs(sessionName, args) {
9
+ return sessionName && args[0] !== "--session" ? ["--session", sessionName, ...args] : args;
10
+ }
11
+ export function buildNextToolAction(options) {
12
+ return {
13
+ id: options.id,
14
+ params: {
15
+ args: options.args,
16
+ ...(options.sessionMode ? { sessionMode: options.sessionMode } : {}),
17
+ ...(options.stdin ? { stdin: options.stdin } : {}),
18
+ },
19
+ reason: options.reason,
20
+ ...(options.safety ? { safety: options.safety } : {}),
21
+ tool: "agent_browser",
22
+ };
23
+ }
24
+ export function appendUniqueAgentBrowserNextActions(target, additions) {
25
+ if (!additions || additions.length === 0)
26
+ return target;
27
+ const existingIds = new Set(target.map((action) => action.id));
28
+ for (const action of additions) {
29
+ if (existingIds.has(action.id))
30
+ continue;
31
+ target.push(action);
32
+ existingIds.add(action.id);
33
+ }
34
+ return target;
35
+ }
36
+ export function isStandaloneSnapshotNextAction(action) {
37
+ const args = action.params?.args;
38
+ if (!args || action.params?.stdin)
39
+ return false;
40
+ const commandIndex = args[0] === "--session" ? 2 : 0;
41
+ return args[commandIndex] === "snapshot";
42
+ }
43
+ export function alignPageChangeSummaryNextActionIds(summary, nextActions) {
44
+ if (!summary?.nextActionIds || !nextActions)
45
+ return summary;
46
+ const nextActionIds = new Set(nextActions.map((action) => action.id));
47
+ const alignedIds = summary.nextActionIds.filter((id) => nextActionIds.has(id));
48
+ return alignedIds.length > 0 ? { ...summary, nextActionIds: alignedIds } : { ...summary, nextActionIds: undefined };
49
+ }
50
+ export class AgentBrowserNextActionCollector {
51
+ actions;
52
+ constructor(initialActions = undefined) {
53
+ this.actions = initialActions ? [...initialActions] : [];
54
+ }
55
+ append(actions) {
56
+ if (!actions || actions.length === 0)
57
+ return;
58
+ this.actions.push(...actions);
59
+ }
60
+ appendUnique(actions) {
61
+ appendUniqueAgentBrowserNextActions(this.actions, actions);
62
+ }
63
+ replace(actions) {
64
+ this.actions = actions ? [...actions] : [];
65
+ }
66
+ removeWhere(predicate) {
67
+ this.actions = this.actions.filter((action) => !predicate(action));
68
+ }
69
+ toArray() {
70
+ return this.actions.length > 0 ? [...this.actions] : undefined;
71
+ }
72
+ }