pi-agent-browser-native 0.2.48 → 0.2.50

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 (189) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +21 -11
  3. package/dist/extensions/agent-browser/index.js +808 -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 +669 -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 +363 -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 +956 -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 +855 -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 +5 -5
  95. package/docs/COMMAND_REFERENCE.md +4 -4
  96. package/docs/RELEASE.md +22 -11
  97. package/docs/REQUIREMENTS.md +1 -1
  98. package/docs/SUPPORT_MATRIX.md +5 -4
  99. package/docs/TOOL_CONTRACT.md +1 -1
  100. package/package.json +9 -5
  101. package/scripts/config.mjs +14 -20
  102. package/scripts/doctor.mjs +8 -7
  103. package/extensions/agent-browser/index.ts +0 -961
  104. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  105. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  106. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  107. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  108. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  109. package/extensions/agent-browser/lib/config-policy.js +0 -690
  110. package/extensions/agent-browser/lib/config.ts +0 -211
  111. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  112. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  113. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  114. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  115. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  116. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  117. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  118. package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
  119. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  120. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  121. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  122. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  123. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  124. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  125. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  126. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  127. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  128. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  129. package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
  130. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
  131. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
  132. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
  133. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  134. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
  135. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
  136. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
  137. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
  138. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
  139. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
  140. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
  141. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  142. package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
  143. package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
  144. package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
  145. package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
  146. package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
  147. package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
  148. package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -267
  149. package/extensions/agent-browser/lib/playbook.ts +0 -142
  150. package/extensions/agent-browser/lib/process.ts +0 -516
  151. package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
  152. package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
  153. package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
  154. package/extensions/agent-browser/lib/results/categories.ts +0 -106
  155. package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
  156. package/extensions/agent-browser/lib/results/contracts.ts +0 -241
  157. package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
  158. package/extensions/agent-browser/lib/results/envelope.ts +0 -195
  159. package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
  160. package/extensions/agent-browser/lib/results/network.ts +0 -78
  161. package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
  162. package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
  163. package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
  164. package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
  165. package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
  166. package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
  167. package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
  168. package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
  169. package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
  170. package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
  171. package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
  172. package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
  173. package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
  174. package/extensions/agent-browser/lib/results/presentation.ts +0 -257
  175. package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
  176. package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
  177. package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
  178. package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
  179. package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
  180. package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
  181. package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
  182. package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
  183. package/extensions/agent-browser/lib/results/text.ts +0 -40
  184. package/extensions/agent-browser/lib/runtime.ts +0 -988
  185. package/extensions/agent-browser/lib/session-page-state.ts +0 -512
  186. package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
  187. package/extensions/agent-browser/lib/temp.ts +0 -577
  188. package/extensions/agent-browser/lib/web-search.ts +0 -728
  189. /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
@@ -0,0 +1,956 @@
1
+ /**
2
+ * Purpose: Render diagnostic command families and safe redacted diagnostic data.
3
+ * Responsibilities: Format sessions, profiles, auth/cookies/storage, network diagnostics, console/errors, stream/dashboard/chat, and build network follow-up actions.
4
+ * Scope: Diagnostic/result-state command presentation only; core orchestration stays in presentation.ts.
5
+ */
6
+ import { isRecord } from "../../parsing.js";
7
+ import { isSensitiveFieldName, redactSensitiveText, redactSensitiveValue } from "../../runtime.js";
8
+ import { classifyNetworkRequestFailure, isApiLikeNetworkRequest, isNetworkArtifactNoiseRequest, summarizeNetworkFailures } from "../network.js";
9
+ import { withOptionalSessionArgs } from "../next-actions.js";
10
+ import { stringifyUnknown, truncateText } from "../text.js";
11
+ import { firstLine, formatCount, getArrayField, getStringField, parseJsonPreviewString, redactModelFacingText, redactModelFacingTextIfSensitive, stringifyModelFacing, } from "./common.js";
12
+ const DIAGNOSTIC_REQUEST_PREVIEW_LIMIT = 40;
13
+ const DIAGNOSTIC_LOG_PREVIEW_LIMIT = 80;
14
+ const NETWORK_BODY_PREVIEW_MAX_CHARS = 280;
15
+ const NETWORK_ERROR_PREVIEW_MAX_CHARS = 220;
16
+ const NETWORK_NEXT_ACTION_LIMIT = 6;
17
+ const NETWORK_FILTER_MAX_CHARS = 160;
18
+ const STORAGE_VALUE_PREVIEW_MAX_CHARS = 160;
19
+ const STORAGE_SECRET_KEY_PATTERN = /(?:access(?:_|-)?token|account|api(?:_|-)?key|auth(?:orization)?|bearer|client(?:_|-)?secret|cookie|credential|csrf|email|id(?:_|-)?token|jwt|pass(?:word)?|private(?:_|-)?key|profile|refresh(?:_|-)?token|secret|session|sid|sig(?:nature)?|token|user(?:name)?|x(?:_|-)?api(?:_|-)?key|xsrf)/i;
20
+ const STORAGE_BENIGN_KEY_PATTERN = /^(?:color(?:scheme)?|debug|dev|experiment|feature(?:flag)?|flag|language|layout|locale|mode|onboarding|sort|tab|theme|timezone|tour|variant|view)$/i;
21
+ const STORAGE_TOKEN_VALUE_PATTERN = /(?:\bBearer\s+[A-Za-z0-9._~-]+|\bBasic\s+[A-Za-z0-9+/=]+|^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$|(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9_~+/=-]{32,})/;
22
+ const STORAGE_SECRET_VALUE_WORD_PATTERN = /(?:secret|token|password|passwd|bearer|credential|authorization|cookie|session[-_ ]?id)/i;
23
+ const STORAGE_EMAIL_VALUE_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
24
+ const STORAGE_IDENTITY_VALUE_PATTERN = /(?:^|[\s:=/_-])(?:account|profile|session|sid|user(?:id|name)?)(?:[\s:=/_-]|$)/i;
25
+ const NETWORK_FILTER_SENSITIVE_SEGMENT_TERMS = [
26
+ "apikey",
27
+ "api-key",
28
+ "api_key",
29
+ "authentication",
30
+ "authorization",
31
+ "bearer",
32
+ "credential",
33
+ "credentials",
34
+ "jwt",
35
+ "passwd",
36
+ "password",
37
+ "reset",
38
+ "secret",
39
+ "session",
40
+ "token",
41
+ ];
42
+ const NETWORK_FILTER_OPAQUE_SEGMENT_PATTERN = /^(?:[A-Fa-f0-9]{16,}|(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9_-]{16,})$/;
43
+ const NETWORK_PREVIEW_FIELD_CANDIDATES = {
44
+ request: ["postData"],
45
+ response: ["responseBody"],
46
+ error: ["error", "failureText", "errorText"],
47
+ };
48
+ const AUTH_SHOW_SAFE_FIELDS = ["name", "profile", "url", "username", "createdAt", "updatedAt"];
49
+ export function getTabSummary(data) {
50
+ const tabs = Array.isArray(data.tabs) ? data.tabs : undefined;
51
+ if (!tabs)
52
+ return undefined;
53
+ const lines = tabs.map((tab, index) => {
54
+ if (!isRecord(tab))
55
+ return `${index}: <invalid tab>`;
56
+ const marker = tab.active === true ? "*" : "-";
57
+ const title = typeof tab.title === "string" ? tab.title : "(untitled)";
58
+ const url = typeof tab.url === "string" ? tab.url : "(no url)";
59
+ const label = typeof tab.label === "string" && tab.label.trim().length > 0 ? tab.label.trim() : undefined;
60
+ const tabSelector = typeof tab.tabId === "string" && tab.tabId.trim().length > 0
61
+ ? tab.tabId.trim()
62
+ : label
63
+ ? label
64
+ : typeof tab.index === "number"
65
+ ? String(tab.index)
66
+ : String(index);
67
+ const labelText = label && label !== tabSelector ? ` label=${redactModelFacingText(label)}` : "";
68
+ return `${marker} [${tabSelector}]${labelText} ${title} — ${url}`;
69
+ });
70
+ return lines.join("\n");
71
+ }
72
+ export function getStreamSummary(data) {
73
+ if (data.alreadyEnabled === true) {
74
+ const lines = ["Stream already enabled (idempotent no-op)."];
75
+ if (typeof data.port === "number") {
76
+ lines.push(`Port: ${data.port}`);
77
+ lines.push(`WebSocket URL: ${getStreamWebSocketUrl(data.port)}`);
78
+ }
79
+ lines.push("Run stream status for current connection details or stream disable when streaming is no longer needed.");
80
+ return lines.join("\n");
81
+ }
82
+ if (typeof data.enabled !== "boolean" || typeof data.connected !== "boolean") {
83
+ return undefined;
84
+ }
85
+ const lines = [
86
+ `Enabled: ${data.enabled}`,
87
+ `Connected: ${data.connected}`,
88
+ `Screencasting: ${data.screencasting === true}`,
89
+ ];
90
+ if (typeof data.port === "number") {
91
+ lines.push(`Port: ${data.port}`);
92
+ lines.push(`WebSocket URL: ${getStreamWebSocketUrl(data.port)}`);
93
+ lines.push(`Frame format: JSON messages with base64 JPEG frame data`);
94
+ }
95
+ return lines.join("\n");
96
+ }
97
+ function getStreamWebSocketUrl(port) {
98
+ return `ws://127.0.0.1:${port}`;
99
+ }
100
+ export function enrichStreamStatusData(commandInfo, data) {
101
+ if (commandInfo.command !== "stream" || commandInfo.subcommand !== "status" || !isRecord(data) || typeof data.port !== "number") {
102
+ return data;
103
+ }
104
+ return {
105
+ ...data,
106
+ frameFormat: "JSON messages with base64 JPEG frame data",
107
+ wsUrl: getStreamWebSocketUrl(data.port),
108
+ };
109
+ }
110
+ export function formatDiagnosticSummary(commandInfo, data) {
111
+ if (commandInfo.command === "session") {
112
+ const sessions = getArrayField(data, "sessions");
113
+ if (sessions)
114
+ return `Sessions: ${sessions.length}`;
115
+ const session = getStringField(data, "session");
116
+ if (session)
117
+ return `Session: ${session}`;
118
+ }
119
+ if (commandInfo.command === "profiles") {
120
+ const profiles = getArrayField(data, "profiles");
121
+ if (profiles)
122
+ return `Chrome profiles: ${profiles.length}`;
123
+ }
124
+ if (commandInfo.command === "auth") {
125
+ const profiles = getArrayField(data, "profiles");
126
+ if (profiles)
127
+ return `Auth profiles: ${profiles.length}`;
128
+ const name = getStringField(data, "name") ?? getStringField(data, "profile") ?? commandInfo.subcommand;
129
+ if (name && commandInfo.subcommand === "show")
130
+ return `Auth profile: ${name}`;
131
+ if (name && ["save", "login", "delete"].includes(commandInfo.subcommand ?? ""))
132
+ return `Auth ${commandInfo.subcommand}: ${name}`;
133
+ }
134
+ if (commandInfo.command === "cookies") {
135
+ const cookies = getArrayField(data, "cookies");
136
+ if (cookies)
137
+ return `Cookies: ${cookies.length}`;
138
+ const name = getStringField(data, "name");
139
+ if (name)
140
+ return name;
141
+ if (data.set === true)
142
+ return "Cookie set";
143
+ if (data.cleared === true || data.clear === true)
144
+ return "Cookies cleared";
145
+ }
146
+ if (commandInfo.command === "storage") {
147
+ const entries = getArrayField(data, "entries") ?? getArrayField(data, "items");
148
+ if (entries)
149
+ return `Storage entries: ${entries.length}`;
150
+ const key = getStringField(data, "key");
151
+ if (key && (commandInfo.subcommand === "set" || data.set === true || Object.hasOwn(data, "value")))
152
+ return `Storage set: ${key}`;
153
+ if (data.cleared === true || data.clear === true)
154
+ return "Storage cleared";
155
+ }
156
+ if (commandInfo.command === "dialog") {
157
+ const open = typeof data.open === "boolean" ? data.open : undefined;
158
+ if (open !== undefined)
159
+ return open ? "Dialog open" : "No dialog open";
160
+ if (data.accepted === true)
161
+ return "Dialog accepted";
162
+ if (data.dismissed === true)
163
+ return "Dialog dismissed";
164
+ }
165
+ if (commandInfo.command === "frame") {
166
+ const frame = getStringField(data, "frame") ?? getStringField(data, "name") ?? getStringField(data, "selector") ?? commandInfo.subcommand;
167
+ if (frame)
168
+ return `Frame: ${frame}`;
169
+ }
170
+ if (commandInfo.command === "state") {
171
+ const states = getArrayField(data, "states") ?? getArrayField(data, "files");
172
+ if (states)
173
+ return `States: ${states.length}`;
174
+ if (commandInfo.subcommand === "load")
175
+ return undefined;
176
+ const stateName = getStringField(data, "name") ?? getStringField(data, "file") ?? getStringField(data, "path") ?? commandInfo.subcommand;
177
+ if (stateName)
178
+ return `State ${commandInfo.subcommand ?? "result"}: ${stateName}`;
179
+ }
180
+ if (commandInfo.command === "network") {
181
+ if (commandInfo.subcommand === "requests") {
182
+ const requests = getArrayField(data, "requests");
183
+ if (requests)
184
+ return `Network requests: ${requests.length}`;
185
+ }
186
+ if (commandInfo.subcommand === "route") {
187
+ const routed = getStringField(data, "routed") ?? getStringField(data, "url") ?? getStringField(data, "pattern");
188
+ return routed ? `Network route: ${redactModelFacingTextIfSensitive(routed)}` : "Network route configured";
189
+ }
190
+ if (commandInfo.subcommand === "unroute") {
191
+ const unrouted = getStringField(data, "unrouted") ?? getStringField(data, "url") ?? getStringField(data, "pattern");
192
+ return unrouted ? `Network unroute: ${redactModelFacingTextIfSensitive(unrouted)}` : "Network route removed";
193
+ }
194
+ if (commandInfo.subcommand === "har") {
195
+ const state = getStringField(data, "state") ?? getStringField(data, "status") ?? commandInfo.subcommand;
196
+ return `Network HAR: ${state}`;
197
+ }
198
+ }
199
+ if (commandInfo.command === "diff") {
200
+ if (commandInfo.subcommand === "snapshot")
201
+ return "Snapshot diff completed";
202
+ if (commandInfo.subcommand === "url")
203
+ return "URL diff completed";
204
+ }
205
+ if (["trace", "profiler"].includes(commandInfo.command ?? "")) {
206
+ const state = getStringField(data, "state") ?? getStringField(data, "status") ?? commandInfo.subcommand;
207
+ if (state)
208
+ return `${commandInfo.command === "trace" ? "Trace" : "Profiler"}: ${state}`;
209
+ }
210
+ if (commandInfo.command === "highlight")
211
+ return "Element highlighted";
212
+ if (commandInfo.command === "inspect")
213
+ return "DevTools inspect opened";
214
+ if (commandInfo.command === "clipboard")
215
+ return `Clipboard ${commandInfo.subcommand ?? "completed"}`;
216
+ if (commandInfo.command === "stream") {
217
+ if (commandInfo.subcommand === "enable") {
218
+ if (data.alreadyEnabled === true)
219
+ return "Stream already enabled";
220
+ const port = typeof data.port === "number" ? ` on port ${data.port}` : "";
221
+ return `Stream enabled${port}`;
222
+ }
223
+ if (commandInfo.subcommand === "disable")
224
+ return "Stream disabled";
225
+ }
226
+ if (commandInfo.command === "chat")
227
+ return "Chat response";
228
+ if (commandInfo.command === "console") {
229
+ const messages = getArrayField(data, "messages");
230
+ if (messages)
231
+ return `Console messages: ${messages.length}`;
232
+ }
233
+ if (commandInfo.command === "errors") {
234
+ const errors = getArrayField(data, "errors");
235
+ if (errors)
236
+ return `Page errors: ${errors.length}`;
237
+ }
238
+ if (commandInfo.command === "dashboard") {
239
+ if (typeof data.port === "number")
240
+ return `Dashboard running on port ${data.port}`;
241
+ if (data.stopped === true)
242
+ return "Dashboard stopped";
243
+ if (data.stopped === false) {
244
+ const reason = getStringField(data, "reason");
245
+ return reason ? `Dashboard not stopped: ${reason}` : "Dashboard not stopped";
246
+ }
247
+ }
248
+ if (commandInfo.command === "doctor") {
249
+ const status = getStringField(data, "status") ?? getStringField(data, "result");
250
+ if (status)
251
+ return `Doctor: ${status}`;
252
+ const checks = getArrayField(data, "checks") ?? getArrayField(data, "issues") ?? getArrayField(data, "problems");
253
+ if (checks)
254
+ return `Doctor: ${formatCount(checks.length, "item")}`;
255
+ }
256
+ return undefined;
257
+ }
258
+ function formatSessionText(data) {
259
+ const sessions = getArrayField(data, "sessions");
260
+ if (sessions) {
261
+ if (sessions.length === 0)
262
+ return "No active sessions.";
263
+ return sessions
264
+ .map((item, index) => {
265
+ if (!isRecord(item))
266
+ return `${index + 1}. ${stringifyModelFacing(item)}`;
267
+ const name = redactModelFacingText(getStringField(item, "name") ?? getStringField(item, "session") ?? getStringField(item, "id") ?? `(session ${index + 1})`);
268
+ const active = item.active === true;
269
+ const url = getStringField(item, "url");
270
+ const title = getStringField(item, "title");
271
+ const label = getStringField(item, "label");
272
+ const tabCount = typeof item.tabCount === "number" ? `${item.tabCount} tab${item.tabCount === 1 ? "" : "s"}` : undefined;
273
+ const metadata = [
274
+ `active=${active ? "true" : "false"}`,
275
+ label ? `label=${redactModelFacingText(label)}` : undefined,
276
+ title ? `title=${redactModelFacingTextIfSensitive(title)}` : undefined,
277
+ url ? `url=${redactModelFacingTextIfSensitive(url)}` : undefined,
278
+ tabCount,
279
+ ].filter(Boolean).join("; ");
280
+ return `${index + 1}. name=${name}${active ? " *active*" : ""}; ${metadata}`;
281
+ })
282
+ .join("\n");
283
+ }
284
+ const session = getStringField(data, "session");
285
+ return session ? `Current session: ${redactModelFacingText(session)}` : undefined;
286
+ }
287
+ export function formatProfilesText(profiles, label) {
288
+ if (profiles.length === 0)
289
+ return `No ${label}.`;
290
+ return profiles
291
+ .map((item, index) => {
292
+ if (!isRecord(item))
293
+ return `${index + 1}. ${stringifyModelFacing(item)}`;
294
+ const name = redactModelFacingText(getStringField(item, "name") ?? getStringField(item, "profile") ?? `(unnamed ${index + 1})`);
295
+ const directory = getStringField(item, "directory") ?? getStringField(item, "path");
296
+ return directory ? `${index + 1}. ${name} (${redactModelFacingText(directory)})` : `${index + 1}. ${name}`;
297
+ })
298
+ .join("\n");
299
+ }
300
+ function formatAuthShowText(data) {
301
+ const lines = AUTH_SHOW_SAFE_FIELDS.flatMap((key) => {
302
+ const value = data[key];
303
+ return typeof value === "string" && value.trim().length > 0 ? [`${key}: ${redactModelFacingText(value.trim())}`] : [];
304
+ });
305
+ return lines.length > 0 ? lines.join("\n") : undefined;
306
+ }
307
+ function getPreviewCandidate(item, keys) {
308
+ for (const key of keys) {
309
+ const value = item[key];
310
+ if (value !== undefined && value !== null && value !== "")
311
+ return value;
312
+ }
313
+ return undefined;
314
+ }
315
+ function formatNetworkPreviewValue(value, maxChars) {
316
+ if (value === undefined || value === null)
317
+ return undefined;
318
+ const previewValue = typeof value === "string" ? parseJsonPreviewString(value) : value;
319
+ const redacted = redactSensitiveValue(previewValue);
320
+ const raw = typeof redacted === "string" ? redacted : stringifyUnknown(redacted);
321
+ const normalized = raw.replace(/\s+/g, " ").trim();
322
+ if (normalized.length === 0)
323
+ return undefined;
324
+ return truncateText(redactSensitiveText(normalized), maxChars);
325
+ }
326
+ function appendNetworkPreview(lines, label, value, maxChars) {
327
+ const preview = formatNetworkPreviewValue(value, maxChars);
328
+ if (!preview)
329
+ return;
330
+ lines.push(` ${label}: ${preview}`);
331
+ }
332
+ function formatNetworkRequestLine(item, index) {
333
+ const method = getStringField(item, "method") ?? "GET";
334
+ const status = typeof item.status === "number" ? String(item.status) : "pending";
335
+ const type = getStringField(item, "resourceType") ?? getStringField(item, "mimeType");
336
+ const url = getStringField(item, "url") ?? "(no url)";
337
+ const requestId = getStringField(item, "requestId") ?? getStringField(item, "id");
338
+ const idText = requestId ? ` [${redactSensitiveText(requestId)}]` : "";
339
+ const failureClassification = classifyNetworkRequestFailure(item);
340
+ const impactText = failureClassification ? ` [${failureClassification.impact}: ${failureClassification.reason}]` : "";
341
+ const lines = [`${index + 1}. ${status} ${method} ${truncateText(redactSensitiveText(url), 180)}${type ? ` (${type})` : ""}${idText}${impactText}`];
342
+ appendNetworkPreview(lines, "Payload", getPreviewCandidate(item, NETWORK_PREVIEW_FIELD_CANDIDATES.request), NETWORK_BODY_PREVIEW_MAX_CHARS);
343
+ appendNetworkPreview(lines, "Response", getPreviewCandidate(item, NETWORK_PREVIEW_FIELD_CANDIDATES.response), NETWORK_BODY_PREVIEW_MAX_CHARS);
344
+ appendNetworkPreview(lines, "Error", getPreviewCandidate(item, NETWORK_PREVIEW_FIELD_CANDIDATES.error), NETWORK_ERROR_PREVIEW_MAX_CHARS);
345
+ return lines;
346
+ }
347
+ function formatNetworkRequestsText(data) {
348
+ const requests = getArrayField(data, "requests");
349
+ if (!requests)
350
+ return undefined;
351
+ if (requests.length === 0)
352
+ return "No network requests captured. Scope: upstream session aggregate unless the upstream command output says it was cleared or filtered for this page.";
353
+ const shown = ["Scope: upstream session aggregate unless the upstream command output says it was cleared or filtered for this page; do not attribute old requests to the current page without URL/time evidence."];
354
+ const indexedRequests = requests.map((item, index) => ({ index, item }));
355
+ const artifactNoiseRequests = indexedRequests.filter((indexed) => isRecord(indexed.item) && isNetworkArtifactNoiseRequest(indexed.item));
356
+ const previewRequests = indexedRequests.filter((indexed) => !(isRecord(indexed.item) && isNetworkArtifactNoiseRequest(indexed.item)));
357
+ const networkFailureSummary = summarizeNetworkFailures(previewRequests.map((indexed) => indexed.item));
358
+ if (networkFailureSummary.totalCount > 0) {
359
+ shown.push(`Network failure summary: ${networkFailureSummary.actionableCount} actionable, ${networkFailureSummary.benignCount} benign low-impact (${networkFailureSummary.totalCount} total).`);
360
+ }
361
+ const failedRequests = [];
362
+ const normalRequests = [];
363
+ for (const indexed of previewRequests) {
364
+ if (isRecord(indexed.item) && classifyNetworkRequestFailure(indexed.item))
365
+ failedRequests.push(indexed);
366
+ else
367
+ normalRequests.push(indexed);
368
+ }
369
+ if (artifactNoiseRequests.length > 0) {
370
+ shown.push(`Diagnostic noise hidden from preview: ${artifactNoiseRequests.length} data:image/artifact request row${artifactNoiseRequests.length === 1 ? "" : "s"}; raw rows remain in details.data.requests.`);
371
+ }
372
+ failedRequests.sort((left, right) => {
373
+ const leftClassification = isRecord(left.item) ? classifyNetworkRequestFailure(left.item) : undefined;
374
+ const rightClassification = isRecord(right.item) ? classifyNetworkRequestFailure(right.item) : undefined;
375
+ const leftRank = leftClassification?.impact === "actionable" ? 0 : 1;
376
+ const rightRank = rightClassification?.impact === "actionable" ? 0 : 1;
377
+ return leftRank - rightRank || left.index - right.index;
378
+ });
379
+ const prioritizedRequests = [...failedRequests, ...normalRequests];
380
+ shown.push(...prioritizedRequests.slice(0, DIAGNOSTIC_REQUEST_PREVIEW_LIMIT).flatMap(({ item, index }) => {
381
+ if (!isRecord(item))
382
+ return [`${index + 1}. ${stringifyModelFacing(item)}`];
383
+ return formatNetworkRequestLine(item, index);
384
+ }));
385
+ const omittedPreviewCount = Math.max(0, prioritizedRequests.length - DIAGNOSTIC_REQUEST_PREVIEW_LIMIT);
386
+ if (omittedPreviewCount > 0) {
387
+ shown.push(`... (${omittedPreviewCount} additional non-noise requests omitted from preview; failed requests are shown first when present)`);
388
+ }
389
+ return shown.join("\n");
390
+ }
391
+ function formatNetworkRequestText(data) {
392
+ if (!getStringField(data, "url") && !getStringField(data, "requestId") && !getStringField(data, "id")) {
393
+ return undefined;
394
+ }
395
+ return formatNetworkRequestLine(data, 0).join("\n");
396
+ }
397
+ function getSafeNetworkActionValue(value) {
398
+ if (!value)
399
+ return undefined;
400
+ const trimmed = value.trim();
401
+ if (trimmed.length === 0 || redactSensitiveText(trimmed) !== trimmed)
402
+ return undefined;
403
+ return trimmed;
404
+ }
405
+ function getNetworkRequestId(item) {
406
+ return getSafeNetworkActionValue(getStringField(item, "requestId") ?? getStringField(item, "id"));
407
+ }
408
+ function isSensitiveNetworkPathSegment(segment) {
409
+ const normalized = segment.toLowerCase();
410
+ return normalized === "auth" || NETWORK_FILTER_SENSITIVE_SEGMENT_TERMS.some((term) => normalized.includes(term));
411
+ }
412
+ function pathFilterMayExposeSensitiveSegment(filter) {
413
+ const decoded = (() => {
414
+ try {
415
+ return decodeURIComponent(filter);
416
+ }
417
+ catch {
418
+ return filter;
419
+ }
420
+ })();
421
+ return decoded.split("/").some((segment) => isSensitiveNetworkPathSegment(segment) || NETWORK_FILTER_OPAQUE_SEGMENT_PATTERN.test(segment));
422
+ }
423
+ function getNetworkRequestPathFilter(item) {
424
+ const url = getStringField(item, "url");
425
+ if (!url)
426
+ return undefined;
427
+ let filter;
428
+ try {
429
+ filter = new URL(url).pathname;
430
+ }
431
+ catch {
432
+ filter = url.split(/[?#]/, 1)[0];
433
+ }
434
+ filter = filter?.trim();
435
+ if (!filter || filter === "/" || filter.length > NETWORK_FILTER_MAX_CHARS || pathFilterMayExposeSensitiveSegment(filter))
436
+ return undefined;
437
+ return getSafeNetworkActionValue(filter);
438
+ }
439
+ function getNetworkRequestActionCandidate(item) {
440
+ if (isNetworkArtifactNoiseRequest(item))
441
+ return undefined;
442
+ const requestId = getNetworkRequestId(item);
443
+ if (!requestId)
444
+ return undefined;
445
+ const classification = classifyNetworkRequestFailure(item);
446
+ const kind = classification?.impact === "actionable"
447
+ ? "actionable"
448
+ : classification?.impact === "benign"
449
+ ? "benign"
450
+ : isApiLikeNetworkRequest(item)
451
+ ? "api"
452
+ : "request";
453
+ return { filter: getNetworkRequestPathFilter(item), item, kind, requestId };
454
+ }
455
+ function chooseNetworkRequestActionCandidate(candidates) {
456
+ return candidates.find((candidate) => candidate.kind === "actionable")
457
+ ?? candidates.find((candidate) => candidate.kind === "api")
458
+ ?? candidates.find((candidate) => candidate.kind === "benign")
459
+ ?? candidates[0];
460
+ }
461
+ function formatNetworkRequestActionDescriptor(candidate) {
462
+ const method = getStringField(candidate.item, "method") ?? "GET";
463
+ const status = typeof candidate.item.status === "number" ? String(candidate.item.status) : "pending";
464
+ const target = candidate.filter ? ` ${candidate.filter}` : "";
465
+ return `${status} ${method}${target} [${candidate.requestId}]`;
466
+ }
467
+ function getNetworkRequestDetailActionId(candidate) {
468
+ if (candidate.kind === "actionable")
469
+ return "inspect-actionable-network-request";
470
+ if (candidate.kind === "benign")
471
+ return "inspect-benign-network-request";
472
+ return "inspect-network-request";
473
+ }
474
+ export function formatNetworkRouteDiagnosticsText(diagnostics) {
475
+ if (!diagnostics || diagnostics.length === 0)
476
+ return undefined;
477
+ const lines = ["Network route diagnostics:"];
478
+ for (const diagnostic of diagnostics) {
479
+ const target = diagnostic.requestId ? `[${diagnostic.requestId}] ${diagnostic.requestUrl ?? "request"}` : diagnostic.requestUrl ?? "request";
480
+ lines.push(`- ${diagnostic.reason}: ${target} matched route ${diagnostic.routePattern} (${diagnostic.mode}).`);
481
+ }
482
+ lines.push("If this route is intended as a mock, inspect the request/headers and treat failed, pending, or CORS-looking rows as unfulfilled until a mocked response is observed.");
483
+ return lines.join("\n");
484
+ }
485
+ export function buildNetworkRouteDiagnosticsNextActions(diagnostics, sessionName) {
486
+ const diagnostic = diagnostics?.find((item) => item.requestId) ?? diagnostics?.[0];
487
+ if (!diagnostic)
488
+ return undefined;
489
+ const actions = [];
490
+ if (diagnostic.requestId) {
491
+ actions.push({
492
+ id: "inspect-routed-network-request",
493
+ params: { args: withOptionalSessionArgs(sessionName, ["network", "request", diagnostic.requestId]) },
494
+ reason: `Inspect the routed request ${diagnostic.requestId} before assuming the route mock fulfilled normally.`,
495
+ safety: "Read-only request diagnostic; look for failed status, pending state, CORS/preflight errors, response body, and headers.",
496
+ tool: "agent_browser",
497
+ });
498
+ }
499
+ actions.push({
500
+ id: "start-network-har-capture-for-route-mock",
501
+ params: { args: withOptionalSessionArgs(sessionName, ["network", "har", "start"]) },
502
+ reason: "Capture a HAR before reproducing the route mock so pending/CORS behavior has request and response headers.",
503
+ safety: "HARs can contain URLs and headers; stop to an explicit path and avoid sharing sensitive captures.",
504
+ tool: "agent_browser",
505
+ });
506
+ return actions;
507
+ }
508
+ export function buildNetworkRequestsNextActions(data, sessionName, routeDiagnostics) {
509
+ if (!isRecord(data))
510
+ return undefined;
511
+ const requests = getArrayField(data, "requests");
512
+ if (!requests)
513
+ return undefined;
514
+ const candidates = requests.flatMap((item) => {
515
+ if (!isRecord(item))
516
+ return [];
517
+ const candidate = getNetworkRequestActionCandidate(item);
518
+ return candidate ? [candidate] : [];
519
+ });
520
+ const selected = chooseNetworkRequestActionCandidate(candidates);
521
+ if (!selected)
522
+ return undefined;
523
+ const descriptor = formatNetworkRequestActionDescriptor(selected);
524
+ const actions = [
525
+ {
526
+ id: getNetworkRequestDetailActionId(selected),
527
+ params: { args: withOptionalSessionArgs(sessionName, ["network", "request", selected.requestId]) },
528
+ reason: `Inspect full request details for ${descriptor}.`,
529
+ safety: "Read-only network diagnostic; request inspection must not replace the active page/ref context.",
530
+ tool: "agent_browser",
531
+ },
532
+ ];
533
+ if (selected.kind === "actionable") {
534
+ actions.push({
535
+ id: "trace-actionable-network-source",
536
+ params: { networkSourceLookup: { requestId: selected.requestId, ...(sessionName ? { session: sessionName } : {}) } },
537
+ reason: `Look for local source candidates related to ${descriptor}.`,
538
+ safety: "Read-only experimental helper; it reports bounded candidates and may miss bundled or dynamic call sites.",
539
+ tool: "agent_browser",
540
+ });
541
+ }
542
+ if (selected.filter) {
543
+ actions.push({
544
+ id: "filter-network-requests-by-path",
545
+ params: { args: withOptionalSessionArgs(sessionName, ["network", "requests", "--filter", selected.filter]) },
546
+ reason: `List captured requests matching ${selected.filter}.`,
547
+ safety: "Read-only request-list filter; absence from a compact preview is not proof the request did not happen.",
548
+ tool: "agent_browser",
549
+ });
550
+ }
551
+ actions.push({
552
+ id: "clear-network-requests-before-repro",
553
+ params: { args: withOptionalSessionArgs(sessionName, ["network", "requests", "--clear"]) },
554
+ reason: "Clear the aggregate request buffer before reproducing the current-page network behavior.",
555
+ safety: "This mutates only diagnostic buffers for the session; capture or inspect needed old rows first.",
556
+ tool: "agent_browser",
557
+ });
558
+ actions.push({
559
+ id: "start-network-har-capture",
560
+ params: { args: withOptionalSessionArgs(sessionName, ["network", "har", "start"]) },
561
+ reason: "Start HAR capture before reproducing the network behavior again.",
562
+ safety: "HARs can contain URLs and headers; stop to an explicit path, inspect metadata, and avoid sharing sensitive captures.",
563
+ tool: "agent_browser",
564
+ });
565
+ return [...(buildNetworkRouteDiagnosticsNextActions(routeDiagnostics, sessionName) ?? []), ...actions].slice(0, NETWORK_NEXT_ACTION_LIMIT);
566
+ }
567
+ export function buildStreamNextActions(commandInfo, data, sessionName) {
568
+ if (commandInfo.command !== "stream" || commandInfo.subcommand !== "enable" || !isRecord(data) || data.alreadyEnabled !== true)
569
+ return undefined;
570
+ return [
571
+ {
572
+ id: "check-stream-status-after-noop",
573
+ params: { args: withOptionalSessionArgs(sessionName, ["stream", "status"]) },
574
+ reason: "Read current stream port and connection details after the idempotent enable no-op.",
575
+ safety: "Read-only stream diagnostic.",
576
+ tool: "agent_browser",
577
+ },
578
+ {
579
+ id: "disable-existing-stream-when-done",
580
+ params: { args: withOptionalSessionArgs(sessionName, ["stream", "disable"]) },
581
+ reason: "Disable the existing stream when it is no longer needed.",
582
+ safety: "Only run when no other workflow is relying on the current stream.",
583
+ tool: "agent_browser",
584
+ },
585
+ ];
586
+ }
587
+ function formatConsoleText(data) {
588
+ const messages = getArrayField(data, "messages");
589
+ if (!messages)
590
+ return undefined;
591
+ if (messages.length === 0)
592
+ return "No console messages. Scope: upstream session aggregate unless the upstream command output says it was cleared or filtered for this page.";
593
+ const shown = ["Scope: upstream session aggregate unless the upstream command output says it was cleared or filtered for this page; do not attribute old messages to the current page without URL/time evidence."];
594
+ shown.push(...messages.slice(0, DIAGNOSTIC_LOG_PREVIEW_LIMIT).map((item, index) => {
595
+ if (!isRecord(item))
596
+ return `${index + 1}. ${stringifyModelFacing(item)}`;
597
+ const type = redactModelFacingText(getStringField(item, "type") ?? "message");
598
+ const text = getStringField(item, "text") ?? stringifyModelFacing(item);
599
+ return `${index + 1}. [${type}] ${firstLine(redactModelFacingText(text).replace(/\s+/g, " ").trim(), 220)}`;
600
+ }));
601
+ const previewedMessageCount = Math.min(messages.length, DIAGNOSTIC_LOG_PREVIEW_LIMIT);
602
+ if (messages.length > previewedMessageCount) {
603
+ shown.push(`... (${messages.length - previewedMessageCount} additional console messages omitted from preview)`);
604
+ }
605
+ return shown.join("\n");
606
+ }
607
+ function formatErrorsText(data) {
608
+ const errors = getArrayField(data, "errors");
609
+ if (!errors)
610
+ return undefined;
611
+ if (errors.length === 0)
612
+ return "No page errors.";
613
+ const shown = errors.slice(0, DIAGNOSTIC_LOG_PREVIEW_LIMIT).map((item, index) => {
614
+ if (!isRecord(item))
615
+ return `${index + 1}. ${stringifyModelFacing(item)}`;
616
+ const text = getStringField(item, "text") ?? stringifyModelFacing(item);
617
+ const location = [
618
+ getStringField(item, "url"),
619
+ typeof item.line === "number" ? `line ${item.line}` : undefined,
620
+ typeof item.column === "number" ? `column ${item.column}` : undefined,
621
+ ]
622
+ .filter(Boolean)
623
+ .map((item) => redactModelFacingText(String(item)))
624
+ .join(":");
625
+ const safeText = firstLine(redactModelFacingText(text), 220);
626
+ return location ? `${index + 1}. ${safeText} (${location})` : `${index + 1}. ${safeText}`;
627
+ });
628
+ if (errors.length > shown.length) {
629
+ shown.push(`... (${errors.length - shown.length} additional errors omitted from preview)`);
630
+ }
631
+ return shown.join("\n");
632
+ }
633
+ function formatDashboardText(data) {
634
+ const lines = [];
635
+ if (typeof data.port === "number")
636
+ lines.push(`Port: ${data.port}`);
637
+ if (typeof data.pid === "number")
638
+ lines.push(`PID: ${data.pid}`);
639
+ if (typeof data.stopped === "boolean")
640
+ lines.push(`Stopped: ${data.stopped}`);
641
+ const reason = getStringField(data, "reason");
642
+ if (reason)
643
+ lines.push(`Reason: ${redactModelFacingText(reason)}`);
644
+ return lines.length > 0 ? lines.join("\n") : undefined;
645
+ }
646
+ function formatChatText(data) {
647
+ const response = getStringField(data, "response") ?? getStringField(data, "message") ?? getStringField(data, "text") ?? getStringField(data, "result");
648
+ if (response)
649
+ return redactModelFacingText(response);
650
+ const model = getStringField(data, "model");
651
+ const provider = getStringField(data, "provider");
652
+ const lines = [model ? `Model: ${redactModelFacingText(model)}` : undefined, provider ? `Provider: ${redactModelFacingText(provider)}` : undefined].filter(Boolean);
653
+ return lines.length > 0 ? lines.join("\n") : undefined;
654
+ }
655
+ function formatDoctorText(data) {
656
+ const lines = [];
657
+ const status = getStringField(data, "status") ?? getStringField(data, "result");
658
+ if (status)
659
+ lines.push(`Status: ${redactModelFacingText(status)}`);
660
+ const summary = isRecord(data.summary) ? data.summary : undefined;
661
+ if (summary) {
662
+ const parts = ["pass", "warn", "fail"].flatMap((key) => typeof summary[key] === "number" ? [`${key}:${summary[key]}`] : []);
663
+ if (parts.length > 0)
664
+ lines.push(`Summary: ${parts.join(", ")}`);
665
+ }
666
+ const checks = getArrayField(data, "checks");
667
+ if (checks) {
668
+ lines.push(`Checks: ${checks.length}`);
669
+ for (const [index, item] of checks.slice(0, 30).entries()) {
670
+ if (!isRecord(item)) {
671
+ lines.push(`${index + 1}. ${stringifyModelFacing(item)}`);
672
+ continue;
673
+ }
674
+ const checkStatus = getStringField(item, "status") ?? "info";
675
+ const id = getStringField(item, "id");
676
+ const category = getStringField(item, "category");
677
+ const message = getStringField(item, "message") ?? getStringField(item, "name") ?? getStringField(item, "title") ?? getStringField(item, "check") ?? stringifyModelFacing(item);
678
+ const label = [category, id].filter(Boolean).join("/");
679
+ lines.push(`${index + 1}. [${redactModelFacingText(checkStatus)}]${label ? ` ${redactModelFacingText(label)}:` : ""} ${firstLine(redactModelFacingText(message), 220)}`);
680
+ const fix = getStringField(item, "fix");
681
+ if (fix)
682
+ lines.push(` fix: ${redactModelFacingText(fix)}`);
683
+ }
684
+ if (checks.length > 30)
685
+ lines.push(`... (${checks.length - 30} additional checks omitted from preview)`);
686
+ }
687
+ for (const key of ["issues", "problems"]) {
688
+ const items = getArrayField(data, key);
689
+ if (items)
690
+ lines.push(`${key}: ${items.length}`);
691
+ }
692
+ if (lines.length === 0) {
693
+ const keys = Object.keys(data).filter((key) => key !== "success");
694
+ if (keys.length > 0)
695
+ return `Doctor diagnostics returned unrecognized fields: ${keys.map(redactModelFacingText).join(", ")}. See details.data for structured diagnostics.`;
696
+ }
697
+ return lines.length > 0 ? lines.join("\n") : undefined;
698
+ }
699
+ function formatCookieRecordText(item, fallbackName) {
700
+ const name = redactModelFacingText(getStringField(item, "name") ?? fallbackName);
701
+ const domain = getStringField(item, "domain");
702
+ const path = getStringField(item, "path");
703
+ const flags = [item.httpOnly === true ? "httpOnly" : undefined, item.secure === true ? "secure" : undefined].filter(Boolean).join(", ");
704
+ const location = [domain, path].filter(Boolean).join("");
705
+ return [name, location ? `(${redactModelFacingText(location)})` : undefined, flags ? `[${flags}]` : undefined].filter(Boolean).join(" ");
706
+ }
707
+ function formatCookiesText(data) {
708
+ const cookies = getArrayField(data, "cookies");
709
+ if (cookies) {
710
+ if (cookies.length === 0)
711
+ return "No cookies.";
712
+ return cookies
713
+ .map((item, index) => (isRecord(item) ? formatCookieRecordText(item, `(cookie ${index + 1})`) : `${index + 1}. [REDACTED]`))
714
+ .join("\n");
715
+ }
716
+ if (getStringField(data, "name") || getStringField(data, "domain") || getStringField(data, "path") || Object.hasOwn(data, "value")) {
717
+ return formatCookieRecordText(data, "cookie");
718
+ }
719
+ if (data.set === true)
720
+ return "Cookie set.";
721
+ if (data.cleared === true || data.clear === true)
722
+ return "Cookies cleared.";
723
+ return undefined;
724
+ }
725
+ function valueContainsStorageSecret(value) {
726
+ if (typeof value === "string") {
727
+ const trimmed = value.trim();
728
+ if (trimmed.length === 0)
729
+ return false;
730
+ if (STORAGE_TOKEN_VALUE_PATTERN.test(trimmed) || STORAGE_SECRET_VALUE_WORD_PATTERN.test(trimmed) || STORAGE_EMAIL_VALUE_PATTERN.test(trimmed) || STORAGE_IDENTITY_VALUE_PATTERN.test(trimmed))
731
+ return true;
732
+ try {
733
+ const url = new URL(trimmed);
734
+ if (url.protocol === "http:" || url.protocol === "https:" || url.username || url.password || url.search)
735
+ return true;
736
+ }
737
+ catch { }
738
+ if (redactSensitiveText(trimmed) !== trimmed || redactModelFacingTextIfSensitive(trimmed) !== trimmed)
739
+ return true;
740
+ try {
741
+ return valueContainsStorageSecret(JSON.parse(trimmed));
742
+ }
743
+ catch {
744
+ return false;
745
+ }
746
+ }
747
+ if (Array.isArray(value))
748
+ return value.some((item) => valueContainsStorageSecret(item));
749
+ if (!isRecord(value))
750
+ return false;
751
+ return Object.entries(value).some(([key, entryValue]) => STORAGE_SECRET_KEY_PATTERN.test(key) || valueContainsStorageSecret(entryValue));
752
+ }
753
+ function shouldRevealStorageValue(key, value) {
754
+ if (!key || STORAGE_SECRET_KEY_PATTERN.test(key) || !STORAGE_BENIGN_KEY_PATTERN.test(key))
755
+ return false;
756
+ if (valueContainsStorageSecret(value))
757
+ return false;
758
+ if (typeof value === "string")
759
+ return value.length <= STORAGE_VALUE_PREVIEW_MAX_CHARS;
760
+ return value === null || typeof value === "number" || typeof value === "boolean";
761
+ }
762
+ function formatStorageValue(key, value) {
763
+ if (!shouldRevealStorageValue(key, value))
764
+ return "[REDACTED]";
765
+ if (typeof value === "string")
766
+ return redactModelFacingText(value);
767
+ return stringifyModelFacing(value);
768
+ }
769
+ function redactStorageEntryValue(item) {
770
+ if (!Object.hasOwn(item, "value"))
771
+ return redactStructuredPresentationValue(item);
772
+ const key = getStringField(item, "key") ?? getStringField(item, "name");
773
+ const value = item.value;
774
+ if (shouldRevealStorageValue(key, value))
775
+ return redactStructuredPresentationValue(item);
776
+ return {
777
+ ...redactStructuredPresentationValue({ ...item, value: undefined }),
778
+ value: "[REDACTED]",
779
+ valueRedacted: true,
780
+ valueRedactionReason: key && STORAGE_SECRET_KEY_PATTERN.test(key) ? "sensitive-key" : "sensitive-value",
781
+ };
782
+ }
783
+ function redactStorageData(value) {
784
+ if (Array.isArray(value))
785
+ return value.map((item) => redactStorageData(item));
786
+ if (!isRecord(value))
787
+ return redactStructuredPresentationValue(value);
788
+ const entries = Object.fromEntries(Object.entries(value).map(([key, entryValue]) => {
789
+ if ((key === "entries" || key === "items") && Array.isArray(entryValue))
790
+ return [key, entryValue.map((item) => isRecord(item) ? redactStorageEntryValue(item) : redactStructuredPresentationValue(item))];
791
+ if (key === "value") {
792
+ const itemKey = getStringField(value, "key") ?? getStringField(value, "name");
793
+ return [key, shouldRevealStorageValue(itemKey, entryValue) ? redactStructuredPresentationValue(entryValue) : "[REDACTED]"];
794
+ }
795
+ return [key, redactStructuredPresentationValue(entryValue)];
796
+ }));
797
+ if (Object.hasOwn(value, "value")) {
798
+ const key = getStringField(value, "key") ?? getStringField(value, "name");
799
+ if (!shouldRevealStorageValue(key, value.value)) {
800
+ entries.valueRedacted = true;
801
+ entries.valueRedactionReason = key && STORAGE_SECRET_KEY_PATTERN.test(key) ? "sensitive-key" : "sensitive-value";
802
+ }
803
+ }
804
+ return entries;
805
+ }
806
+ function formatStorageText(data) {
807
+ const type = getStringField(data, "type") ?? getStringField(data, "storage") ?? "storage";
808
+ const entries = getArrayField(data, "entries") ?? getArrayField(data, "items");
809
+ if (entries) {
810
+ if (entries.length === 0)
811
+ return `${type}: no entries.`;
812
+ return entries
813
+ .map((item, index) => {
814
+ if (!isRecord(item))
815
+ return `${index + 1}. [REDACTED]`;
816
+ const rawKey = getStringField(item, "key") ?? getStringField(item, "name") ?? `(entry ${index + 1})`;
817
+ const key = redactModelFacingText(rawKey);
818
+ return Object.hasOwn(item, "value") ? `${key}: ${formatStorageValue(rawKey, item.value)}` : key;
819
+ })
820
+ .join("\n");
821
+ }
822
+ const key = getStringField(data, "key");
823
+ if (key && Object.hasOwn(data, "value"))
824
+ return `${type} ${redactModelFacingText(key)}: ${formatStorageValue(key, data.value)}`;
825
+ if (key && data.set === true)
826
+ return `${type} set: ${redactModelFacingText(key)}`;
827
+ if (data.cleared === true || data.clear === true)
828
+ return `${type} cleared.`;
829
+ return undefined;
830
+ }
831
+ function formatDialogText(data) {
832
+ const lines = [];
833
+ if (typeof data.open === "boolean")
834
+ lines.push(data.open ? "Dialog open." : "No dialog open.");
835
+ const type = getStringField(data, "type");
836
+ if (type)
837
+ lines.push(`Type: ${redactModelFacingText(type)}`);
838
+ const message = getStringField(data, "message");
839
+ if (message)
840
+ lines.push(`Message: ${/(?:auth|authorization|bearer|cookie|pass(?:word)?|secret|session|token)/i.test(message) ? "[REDACTED]" : redactModelFacingText(message)}`);
841
+ if (data.accepted === true)
842
+ lines.push("Accepted.");
843
+ if (data.dismissed === true)
844
+ lines.push("Dismissed.");
845
+ return lines.length > 0 ? lines.join("\n") : undefined;
846
+ }
847
+ function formatFrameText(data) {
848
+ const frame = getStringField(data, "frame") ?? getStringField(data, "name") ?? getStringField(data, "selector");
849
+ const url = getStringField(data, "url");
850
+ const title = getStringField(data, "title");
851
+ const lines = [frame ? `Frame: ${redactModelFacingText(frame)}` : undefined, title ? `Title: ${redactModelFacingText(title)}` : undefined, url ? `URL: ${redactModelFacingTextIfSensitive(url)}` : undefined].filter(Boolean);
852
+ return lines.length > 0 ? lines.join("\n") : undefined;
853
+ }
854
+ function formatStateText(data) {
855
+ const states = getArrayField(data, "states") ?? getArrayField(data, "files");
856
+ if (states) {
857
+ if (states.length === 0)
858
+ return "No saved states.";
859
+ return states
860
+ .map((item, index) => {
861
+ if (!isRecord(item))
862
+ return `${index + 1}. ${redactModelFacingTextIfSensitive(stringifyModelFacing(item))}`;
863
+ const name = getStringField(item, "name") ?? getStringField(item, "file") ?? getStringField(item, "path") ?? `(state ${index + 1})`;
864
+ const url = getStringField(item, "url");
865
+ return url ? `${index + 1}. ${redactModelFacingText(name)} — ${redactModelFacingTextIfSensitive(url)}` : `${index + 1}. ${redactModelFacingText(name)}`;
866
+ })
867
+ .join("\n");
868
+ }
869
+ if (data.loaded === true)
870
+ return `State loaded: ${redactModelFacingText(getStringField(data, "path") ?? getStringField(data, "name") ?? "ok")}`;
871
+ if (data.cleared === true || data.clear === true)
872
+ return "State cleared.";
873
+ return undefined;
874
+ }
875
+ function redactStructuredPresentationValue(value) {
876
+ if (typeof value === "string")
877
+ return redactModelFacingTextIfSensitive(value);
878
+ if (Array.isArray(value))
879
+ return value.map((item) => redactStructuredPresentationValue(item));
880
+ if (!isRecord(value))
881
+ return value;
882
+ return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [
883
+ key,
884
+ isSensitiveFieldName(key) ? "[REDACTED]" : redactStructuredPresentationValue(entryValue),
885
+ ]));
886
+ }
887
+ function redactStatefulValues(value, sensitiveKeys) {
888
+ if (Array.isArray(value))
889
+ return value.map((item) => redactStatefulValues(item, sensitiveKeys));
890
+ if (!isRecord(value))
891
+ return redactStructuredPresentationValue(value);
892
+ return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [
893
+ key,
894
+ sensitiveKeys.has(key.toLowerCase()) ? "[REDACTED]" : redactStatefulValues(entryValue, sensitiveKeys),
895
+ ]));
896
+ }
897
+ export function redactPresentationData(commandInfo, data) {
898
+ if (commandInfo.command === "cookies")
899
+ return redactStatefulValues(data, new Set(["value"]));
900
+ if (commandInfo.command === "storage")
901
+ return redactStorageData(data);
902
+ return redactStructuredPresentationValue(data);
903
+ }
904
+ export function formatDiagnosticText(commandInfo, data) {
905
+ if (commandInfo.command === "session")
906
+ return formatSessionText(data);
907
+ if (commandInfo.command === "profiles") {
908
+ const profiles = getArrayField(data, "profiles");
909
+ if (profiles)
910
+ return formatProfilesText(profiles, "Chrome profiles");
911
+ }
912
+ if (commandInfo.command === "auth") {
913
+ const profiles = getArrayField(data, "profiles");
914
+ if (profiles)
915
+ return formatProfilesText(profiles, "auth profiles");
916
+ if (commandInfo.subcommand === "show")
917
+ return formatAuthShowText(data);
918
+ }
919
+ if (commandInfo.command === "cookies")
920
+ return formatCookiesText(data);
921
+ if (commandInfo.command === "storage")
922
+ return formatStorageText(data);
923
+ if (commandInfo.command === "dialog")
924
+ return formatDialogText(data);
925
+ if (commandInfo.command === "frame")
926
+ return formatFrameText(data);
927
+ if (commandInfo.command === "state")
928
+ return formatStateText(data);
929
+ if (commandInfo.command === "network" && commandInfo.subcommand === "requests")
930
+ return formatNetworkRequestsText(data);
931
+ if (commandInfo.command === "network" && commandInfo.subcommand === "request")
932
+ return formatNetworkRequestText(data);
933
+ if (commandInfo.command === "diff")
934
+ return stringifyModelFacing(data);
935
+ if (commandInfo.command === "clipboard") {
936
+ const text = getStringField(data, "text") ?? getStringField(data, "value") ?? getStringField(data, "result");
937
+ if (text)
938
+ return redactModelFacingText(text);
939
+ }
940
+ if (commandInfo.command === "stream") {
941
+ const streamSummary = getStreamSummary(data);
942
+ if (streamSummary)
943
+ return streamSummary;
944
+ }
945
+ if (commandInfo.command === "chat")
946
+ return formatChatText(data);
947
+ if (commandInfo.command === "console")
948
+ return formatConsoleText(data);
949
+ if (commandInfo.command === "errors")
950
+ return formatErrorsText(data);
951
+ if (commandInfo.command === "dashboard")
952
+ return formatDashboardText(data);
953
+ if (commandInfo.command === "doctor")
954
+ return formatDoctorText(data);
955
+ return undefined;
956
+ }