pi-agent-browser-native 0.2.48 → 0.2.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +16 -6
  3. package/dist/extensions/agent-browser/index.js +785 -0
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
  6. package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
  7. package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
  8. package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
  9. package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
  10. package/dist/extensions/agent-browser/lib/config.js +122 -0
  11. package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
  12. package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
  13. package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
  14. package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
  15. package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
  16. package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
  17. package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
  18. package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
  19. package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
  20. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
  21. package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
  22. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
  23. package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
  24. package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
  25. package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
  26. package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
  27. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
  28. package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
  29. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
  30. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
  31. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
  32. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
  40. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
  41. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
  42. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
  43. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
  44. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
  45. package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
  46. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
  47. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
  48. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
  49. package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
  50. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
  51. package/dist/extensions/agent-browser/lib/playbook.js +121 -0
  52. package/dist/extensions/agent-browser/lib/process.js +448 -0
  53. package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
  54. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
  55. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
  56. package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
  57. package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
  58. package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
  59. package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
  60. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
  61. package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
  62. package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
  63. package/dist/extensions/agent-browser/lib/results/network.js +73 -0
  64. package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
  65. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
  66. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
  67. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
  68. package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
  69. package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
  70. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
  71. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
  72. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
  73. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
  74. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
  75. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
  76. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
  77. package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
  78. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
  79. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
  80. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
  81. package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
  82. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
  83. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
  84. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
  85. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
  86. package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
  87. package/dist/extensions/agent-browser/lib/results/text.js +40 -0
  88. package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
  89. package/dist/extensions/agent-browser/lib/runtime.js +816 -0
  90. package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
  91. package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
  92. package/dist/extensions/agent-browser/lib/temp.js +498 -0
  93. package/dist/extensions/agent-browser/lib/web-search.js +562 -0
  94. package/docs/RELEASE.md +22 -11
  95. package/docs/SUPPORT_MATRIX.md +4 -3
  96. package/package.json +9 -5
  97. package/scripts/config.mjs +8 -2
  98. package/scripts/doctor.mjs +8 -7
  99. package/extensions/agent-browser/index.ts +0 -961
  100. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  101. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  102. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  103. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  104. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  105. package/extensions/agent-browser/lib/config-policy.js +0 -690
  106. package/extensions/agent-browser/lib/config.ts +0 -211
  107. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  108. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  109. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  110. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  111. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  112. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  113. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  114. package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
  115. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  116. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  117. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  118. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  119. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  120. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  121. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  122. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  123. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  124. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  125. package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
  126. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
  127. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
  128. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
  129. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  130. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
  131. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
  132. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
  133. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
  134. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
  135. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
  136. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
  137. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  138. package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
  139. package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
  140. package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
  141. package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
  142. package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
  143. package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
  144. package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -267
  145. package/extensions/agent-browser/lib/playbook.ts +0 -142
  146. package/extensions/agent-browser/lib/process.ts +0 -516
  147. package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
  148. package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
  149. package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
  150. package/extensions/agent-browser/lib/results/categories.ts +0 -106
  151. package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
  152. package/extensions/agent-browser/lib/results/contracts.ts +0 -241
  153. package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
  154. package/extensions/agent-browser/lib/results/envelope.ts +0 -195
  155. package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
  156. package/extensions/agent-browser/lib/results/network.ts +0 -78
  157. package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
  158. package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
  159. package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
  160. package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
  161. package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
  162. package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
  163. package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
  164. package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
  165. package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
  166. package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
  167. package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
  168. package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
  169. package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
  170. package/extensions/agent-browser/lib/results/presentation.ts +0 -257
  171. package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
  172. package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
  173. package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
  174. package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
  175. package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
  176. package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
  177. package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
  178. package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
  179. package/extensions/agent-browser/lib/results/text.ts +0 -40
  180. package/extensions/agent-browser/lib/runtime.ts +0 -988
  181. package/extensions/agent-browser/lib/session-page-state.ts +0 -512
  182. package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
  183. package/extensions/agent-browser/lib/temp.ts +0 -577
  184. package/extensions/agent-browser/lib/web-search.ts +0 -728
  185. /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
@@ -0,0 +1,515 @@
1
+ /**
2
+ * Purpose: Own file artifact detection, verification, manifest merging, and inline image attachment for tool presentation.
3
+ * Responsibilities: Build artifact metadata, verification summaries, saved-file details, artifact retention notices, and safe image content.
4
+ * Scope: Artifact and image presentation only.
5
+ */
6
+ import { readFile, stat } from "node:fs/promises";
7
+ import { extname, resolve } from "node:path";
8
+ import { isRecord, parsePositiveInteger } from "../../parsing.js";
9
+ import { formatSessionArtifactRetentionSummary, mergeSessionArtifactManifest, } from "../artifact-manifest.js";
10
+ import { isPendingRecordingArtifact, isPendingRecordingCommand } from "../artifact-state.js";
11
+ import { classifyAgentBrowserSuccessCategory } from "../categories.js";
12
+ const IMAGE_EXTENSION_TO_MIME_TYPE = {
13
+ ".gif": "image/gif",
14
+ ".jpeg": "image/jpeg",
15
+ ".jpg": "image/jpeg",
16
+ ".png": "image/png",
17
+ ".webp": "image/webp",
18
+ };
19
+ const INLINE_IMAGE_MAX_BYTES_ENV = "PI_AGENT_BROWSER_INLINE_IMAGE_MAX_BYTES";
20
+ const DEFAULT_INLINE_IMAGE_MAX_BYTES = 5 * 1_024 * 1_024;
21
+ function getImageMimeType(filePath) {
22
+ const extension = extname(filePath).toLowerCase();
23
+ return IMAGE_EXTENSION_TO_MIME_TYPE[extension];
24
+ }
25
+ function getInlineImageMaxBytes(env = process.env) {
26
+ return parsePositiveInteger(env[INLINE_IMAGE_MAX_BYTES_ENV]) ?? DEFAULT_INLINE_IMAGE_MAX_BYTES;
27
+ }
28
+ function formatByteCount(bytes) {
29
+ if (bytes < 1_024)
30
+ return `${bytes} B`;
31
+ if (bytes < 1_024 * 1_024)
32
+ return `${(bytes / 1_024).toFixed(1)} KiB`;
33
+ return `${(bytes / (1_024 * 1_024)).toFixed(1)} MiB`;
34
+ }
35
+ function appendPresentationNotice(presentation, message) {
36
+ const existingText = presentation.content[0]?.type === "text" ? presentation.content[0].text : "";
37
+ presentation.content[0] = {
38
+ type: "text",
39
+ text: existingText.length > 0 ? `${existingText}\n\n${message}` : message,
40
+ };
41
+ }
42
+ function shouldAppendArtifactRetentionNotice(entries) {
43
+ return entries.some((entry) => entry.retentionState === "evicted" || entry.storageScope !== "explicit-path");
44
+ }
45
+ function getManifestEntryKey(entry) {
46
+ return entry.storageScope === "explicit-path" && entry.absolutePath ? `${entry.storageScope}:${entry.absolutePath}` : `${entry.storageScope}:${entry.path}`;
47
+ }
48
+ export function manifestHasNewNoticeWorthyEntries(base, current) {
49
+ if (!current)
50
+ return false;
51
+ const baseKeys = new Set((base?.entries ?? []).map(getManifestEntryKey));
52
+ return current.entries.some((entry) => !baseKeys.has(getManifestEntryKey(entry)) && (entry.retentionState === "evicted" || entry.storageScope !== "explicit-path"));
53
+ }
54
+ export function applyArtifactManifest(presentation, baseManifest, entries) {
55
+ if (entries.length === 0)
56
+ return presentation;
57
+ const artifactManifest = mergeSessionArtifactManifest({ base: baseManifest, entries });
58
+ if (!artifactManifest)
59
+ return presentation;
60
+ presentation.artifactManifest = artifactManifest;
61
+ presentation.artifactRetentionSummary = formatSessionArtifactRetentionSummary(artifactManifest);
62
+ if (shouldAppendArtifactRetentionNotice(entries)) {
63
+ appendPresentationNotice(presentation, presentation.artifactRetentionSummary);
64
+ }
65
+ return presentation;
66
+ }
67
+ export function getScreenshotSummary(data) {
68
+ return typeof data.path === "string" ? `Saved image: ${data.path}` : undefined;
69
+ }
70
+ const PATH_FIELD_CANDIDATES = [
71
+ "path",
72
+ "file",
73
+ "filePath",
74
+ "outputPath",
75
+ "downloadPath",
76
+ "diffPath",
77
+ "harPath",
78
+ "savedPath",
79
+ "statePath",
80
+ "tracePath",
81
+ "profilePath",
82
+ "videoPath",
83
+ ];
84
+ const ARTIFACT_EXTENSION_TO_MEDIA_TYPE = {
85
+ ".cpuprofile": "application/json",
86
+ ".har": "application/json",
87
+ ".html": "text/html",
88
+ ".json": "application/json",
89
+ ".pdf": "application/pdf",
90
+ ".txt": "text/plain",
91
+ ".webm": "video/webm",
92
+ ".zip": "application/zip",
93
+ ...IMAGE_EXTENSION_TO_MIME_TYPE,
94
+ };
95
+ function getArtifactKind(commandInfo) {
96
+ if (commandInfo.command === "screenshot")
97
+ return "image";
98
+ if (commandInfo.command === "diff" && commandInfo.subcommand === "screenshot")
99
+ return "image";
100
+ if (commandInfo.command === "pdf")
101
+ return "pdf";
102
+ if (commandInfo.command === "download")
103
+ return "download";
104
+ if (commandInfo.command === "wait" && commandInfo.subcommand === "--download")
105
+ return "download";
106
+ if (commandInfo.command === "state" && commandInfo.subcommand === "save")
107
+ return "file";
108
+ if (commandInfo.command === "trace")
109
+ return "trace";
110
+ if (commandInfo.command === "profiler")
111
+ return "profile";
112
+ if (commandInfo.command === "record")
113
+ return "video";
114
+ if (commandInfo.command === "network" && commandInfo.subcommand === "har")
115
+ return "har";
116
+ return undefined;
117
+ }
118
+ function isNonFileArtifactPathCandidate(path) {
119
+ return /^(?:data|blob|https?|javascript|mailto):/i.test(path.trim());
120
+ }
121
+ function extractPathStrings(data) {
122
+ if (typeof data === "string") {
123
+ return data.trim().length > 0 && !isNonFileArtifactPathCandidate(data) ? [data] : [];
124
+ }
125
+ if (!isRecord(data)) {
126
+ return [];
127
+ }
128
+ const paths = [];
129
+ for (const key of PATH_FIELD_CANDIDATES) {
130
+ const value = data[key];
131
+ if (typeof value === "string" && value.trim().length > 0 && !isNonFileArtifactPathCandidate(value)) {
132
+ paths.push(value);
133
+ }
134
+ if (Array.isArray(value)) {
135
+ for (const item of value) {
136
+ if (typeof item === "string" && item.trim().length > 0 && !isNonFileArtifactPathCandidate(item)) {
137
+ paths.push(item);
138
+ }
139
+ }
140
+ }
141
+ }
142
+ return [...new Set(paths)];
143
+ }
144
+ async function buildFileArtifactMetadata(options) {
145
+ const kind = getArtifactKind(options.commandInfo);
146
+ if (!kind) {
147
+ return undefined;
148
+ }
149
+ const absolutePath = options.artifactRequest?.absolutePath ?? resolve(options.cwd, options.path);
150
+ const displayPath = options.artifactRequest?.path ?? options.path;
151
+ const extension = extname(absolutePath || options.path).toLowerCase() || undefined;
152
+ const pendingRecording = isPendingRecordingCommand(options.commandInfo.command, options.commandInfo.subcommand, kind);
153
+ let exists;
154
+ let sizeBytes;
155
+ if (!pendingRecording) {
156
+ try {
157
+ const fileStats = await stat(absolutePath);
158
+ exists = true;
159
+ sizeBytes = fileStats.size;
160
+ }
161
+ catch {
162
+ exists = false;
163
+ }
164
+ }
165
+ return {
166
+ absolutePath,
167
+ artifactType: kind,
168
+ command: options.commandInfo.command,
169
+ cwd: options.cwd,
170
+ exists,
171
+ extension,
172
+ kind,
173
+ mediaType: extension ? ARTIFACT_EXTENSION_TO_MEDIA_TYPE[extension] : undefined,
174
+ path: displayPath,
175
+ recordingState: pendingRecording ? "openRecording" : undefined,
176
+ requestedPath: options.artifactRequest?.path,
177
+ session: options.sessionName,
178
+ sizeBytes,
179
+ status: options.artifactRequest?.status ?? (pendingRecording ? "pending" : exists === false ? "missing" : "saved"),
180
+ subcommand: options.commandInfo.subcommand,
181
+ tempPath: options.artifactRequest?.tempPath,
182
+ willExistOnStop: pendingRecording ? true : undefined,
183
+ };
184
+ }
185
+ async function buildPreviousRestartRecordingArtifact(options) {
186
+ if (options.commandInfo.command !== "record" || options.commandInfo.subcommand !== "restart")
187
+ return undefined;
188
+ const previousRecording = options.artifactManifest?.entries.find((entry) => (entry.command === "record" &&
189
+ (entry.subcommand === "start" || entry.subcommand === "restart") &&
190
+ entry.kind === "video" &&
191
+ (!options.sessionName || !entry.session || entry.session === options.sessionName) &&
192
+ !options.currentPaths.has(entry.path) &&
193
+ (!entry.absolutePath || !options.currentPaths.has(entry.absolutePath))));
194
+ if (!previousRecording)
195
+ return undefined;
196
+ const absolutePath = previousRecording.absolutePath ?? resolve(options.cwd, previousRecording.path);
197
+ try {
198
+ const fileStats = await stat(absolutePath);
199
+ return {
200
+ absolutePath,
201
+ artifactType: "video",
202
+ command: "record",
203
+ cwd: previousRecording.cwd ?? options.cwd,
204
+ exists: true,
205
+ extension: previousRecording.extension ?? (extname(absolutePath).toLowerCase() || undefined),
206
+ kind: "video",
207
+ mediaType: previousRecording.mediaType,
208
+ path: previousRecording.path,
209
+ requestedPath: previousRecording.requestedPath,
210
+ session: previousRecording.session ?? options.sessionName,
211
+ sizeBytes: fileStats.size,
212
+ status: "saved",
213
+ subcommand: "restart-previous",
214
+ };
215
+ }
216
+ catch {
217
+ return undefined;
218
+ }
219
+ }
220
+ export async function extractFileArtifacts(options) {
221
+ const candidates = extractPathStrings(options.data);
222
+ const currentArtifacts = (await Promise.all(candidates.map((path) => buildFileArtifactMetadata({ ...options, path })))).filter((artifact) => artifact !== undefined);
223
+ const currentPaths = new Set(currentArtifacts.flatMap((artifact) => [artifact.path, artifact.absolutePath]));
224
+ const previousRestartRecordingArtifact = await buildPreviousRestartRecordingArtifact({ artifactManifest: options.artifactManifest, commandInfo: options.commandInfo, currentPaths, cwd: options.cwd, sessionName: options.sessionName });
225
+ return previousRestartRecordingArtifact ? [previousRestartRecordingArtifact, ...currentArtifacts] : currentArtifacts;
226
+ }
227
+ export function buildManifestEntriesForFileArtifacts(artifacts, nowMs = Date.now()) {
228
+ return artifacts.map((artifact) => ({
229
+ absolutePath: artifact.absolutePath,
230
+ command: artifact.command,
231
+ createdAtMs: nowMs,
232
+ cwd: artifact.cwd,
233
+ exists: artifact.exists,
234
+ extension: artifact.extension,
235
+ kind: artifact.kind,
236
+ mediaType: artifact.mediaType,
237
+ path: artifact.path,
238
+ requestedPath: artifact.requestedPath,
239
+ retentionState: artifact.exists === false ? "missing" : "live",
240
+ session: artifact.session,
241
+ sizeBytes: artifact.sizeBytes,
242
+ storageScope: "explicit-path",
243
+ subcommand: artifact.subcommand,
244
+ }));
245
+ }
246
+ export function isManifestFileArtifact(artifact) {
247
+ return artifact.kind === "video" && artifact.command === "record" ? true : !isPendingRecordingArtifact(artifact);
248
+ }
249
+ function getArtifactVerificationEntry(artifact) {
250
+ if (isPendingRecordingArtifact(artifact)) {
251
+ return {
252
+ absolutePath: artifact.absolutePath,
253
+ exists: artifact.exists,
254
+ kind: artifact.kind,
255
+ limitation: "Recording output is pending until record stop completes.",
256
+ mediaType: artifact.mediaType,
257
+ path: artifact.path,
258
+ recordingState: artifact.recordingState ?? "openRecording",
259
+ requestedPath: artifact.requestedPath,
260
+ retentionState: undefined,
261
+ sizeBytes: artifact.sizeBytes,
262
+ state: "pending",
263
+ status: artifact.status ?? "pending",
264
+ storageScope: undefined,
265
+ willExistOnStop: artifact.willExistOnStop ?? true,
266
+ };
267
+ }
268
+ const state = artifact.exists === true
269
+ ? "verified"
270
+ : artifact.exists === false
271
+ ? "missing"
272
+ : "unverified";
273
+ return {
274
+ absolutePath: artifact.absolutePath,
275
+ exists: artifact.exists,
276
+ kind: artifact.kind,
277
+ limitation: state === "missing"
278
+ ? "The wrapper did not find the reported artifact at absolutePath. Treat the path as unverified until recovered or regenerated."
279
+ : state === "unverified"
280
+ ? "The wrapper could not prove local filesystem existence for this artifact."
281
+ : undefined,
282
+ mediaType: artifact.mediaType,
283
+ path: artifact.path,
284
+ requestedPath: artifact.requestedPath,
285
+ retentionState: artifact.exists === false ? "missing" : "live",
286
+ sizeBytes: artifact.sizeBytes,
287
+ state,
288
+ status: artifact.status,
289
+ storageScope: "explicit-path",
290
+ };
291
+ }
292
+ function getManifestVerificationEntry(entry) {
293
+ if (entry.storageScope === "explicit-path")
294
+ return undefined;
295
+ const state = entry.retentionState === "live"
296
+ ? "verified"
297
+ : entry.retentionState === "missing" || entry.retentionState === "evicted"
298
+ ? "missing"
299
+ : "unverified";
300
+ return {
301
+ absolutePath: entry.absolutePath,
302
+ exists: entry.exists,
303
+ kind: entry.kind,
304
+ limitation: entry.retentionState === "ephemeral"
305
+ ? "This spill file is process-temporary and may not survive reload or restart."
306
+ : entry.retentionState === "evicted"
307
+ ? "This persisted spill file was evicted from the bounded session artifact store."
308
+ : undefined,
309
+ mediaType: entry.mediaType,
310
+ path: entry.path,
311
+ requestedPath: entry.requestedPath,
312
+ retentionState: entry.retentionState,
313
+ sizeBytes: entry.sizeBytes,
314
+ state,
315
+ storageScope: entry.storageScope,
316
+ };
317
+ }
318
+ export function buildArtifactVerificationSummary(artifacts, manifest, manifestPaths) {
319
+ const entries = [
320
+ ...artifacts.map(getArtifactVerificationEntry),
321
+ ...(manifest?.entries.flatMap((entry) => {
322
+ if (manifestPaths && !manifestPaths.has(entry.path))
323
+ return [];
324
+ const verificationEntry = getManifestVerificationEntry(entry);
325
+ return verificationEntry ? [verificationEntry] : [];
326
+ }) ?? []),
327
+ ];
328
+ if (entries.length === 0)
329
+ return undefined;
330
+ const verifiedCount = entries.filter((entry) => entry.state === "verified").length;
331
+ const missingCount = entries.filter((entry) => entry.state === "missing").length;
332
+ const pendingCount = entries.filter((entry) => entry.state === "pending").length;
333
+ const unverifiedCount = entries.filter((entry) => entry.state === "unverified").length;
334
+ return {
335
+ artifacts: entries,
336
+ missingCount,
337
+ pendingCount,
338
+ unverifiedCount,
339
+ verified: entries.length > 0 && verifiedCount === entries.length,
340
+ verifiedCount,
341
+ };
342
+ }
343
+ export function hasMissingFileArtifact(artifacts) {
344
+ return (artifacts ?? []).some((artifact) => !isPendingRecordingArtifact(artifact) && artifact.exists === false);
345
+ }
346
+ export function formatMissingArtifactFailureText(artifacts) {
347
+ const missingArtifacts = (artifacts ?? []).filter((artifact) => !isPendingRecordingArtifact(artifact) && artifact.exists === false);
348
+ if (missingArtifacts.length === 0)
349
+ return undefined;
350
+ if (missingArtifacts.length === 1) {
351
+ const artifact = missingArtifacts[0];
352
+ return `Artifact verification failed: requested ${artifact.kind} was not found at ${artifact.absolutePath}.`;
353
+ }
354
+ return `Artifact verification failed: ${missingArtifacts.length} requested artifacts were not found on disk.`;
355
+ }
356
+ export function classifyPresentationSuccessCategory(options) {
357
+ if ((options.artifactVerification?.missingCount ?? 0) > 0 || (options.artifactVerification?.unverifiedCount ?? 0) > 0) {
358
+ return "artifact-unverified";
359
+ }
360
+ return classifyAgentBrowserSuccessCategory(options);
361
+ }
362
+ function formatArtifactLabel(artifact) {
363
+ switch (artifact.kind) {
364
+ case "download":
365
+ if (artifact.exists !== true) {
366
+ return artifact.command === "wait" && artifact.subcommand === "--download" ? "Download event reported; file not verified" : "Download reported; file not verified";
367
+ }
368
+ return artifact.command === "wait" && artifact.subcommand === "--download" ? "Download saved and verified" : "Downloaded file verified";
369
+ case "file":
370
+ return artifact.command === "state" ? "State file" : "Saved file";
371
+ case "har":
372
+ return "Saved HAR";
373
+ case "image":
374
+ if (artifact.exists !== true)
375
+ return artifact.command === "diff" && artifact.subcommand === "screenshot" ? "Diff image reported; file not verified" : "Image reported; file not verified";
376
+ return artifact.command === "diff" && artifact.subcommand === "screenshot" ? "Saved diff image" : "Saved image";
377
+ case "pdf":
378
+ return "Saved PDF";
379
+ case "profile":
380
+ return "Saved profile";
381
+ case "trace":
382
+ return "Saved trace";
383
+ case "video":
384
+ if (artifact.command === "record" && artifact.subcommand === "restart-previous")
385
+ return "Previous recording saved";
386
+ if (!isPendingRecordingArtifact(artifact))
387
+ return "Saved recording";
388
+ return artifact.subcommand === "restart" ? "Recording restarted; output will be written on stop" : "Recording started; output will be written on stop";
389
+ }
390
+ }
391
+ export function formatArtifactSummary(artifacts) {
392
+ if (artifacts.length === 0) {
393
+ return undefined;
394
+ }
395
+ if (artifacts.length === 1) {
396
+ const artifact = artifacts[0];
397
+ return `${formatArtifactLabel(artifact)}: ${artifact.path}`;
398
+ }
399
+ const restartArtifact = artifacts.find((artifact) => isPendingRecordingArtifact(artifact) && artifact.subcommand === "restart");
400
+ const previousRecordingArtifacts = artifacts.filter((artifact) => artifact.command === "record" && artifact.subcommand === "restart-previous");
401
+ if (restartArtifact && previousRecordingArtifacts.length > 0) {
402
+ return [...previousRecordingArtifacts, restartArtifact].map((artifact) => `${formatArtifactLabel(artifact)}: ${artifact.path}`).join("\n");
403
+ }
404
+ return `Saved ${artifacts.length} artifacts: ${artifacts.map((artifact) => `${artifact.kind} ${artifact.path}`).join(", ")}`;
405
+ }
406
+ export function formatArtifactMetadataLines(artifacts) {
407
+ return artifacts.map((artifact, index) => {
408
+ if (isPendingRecordingArtifact(artifact)) {
409
+ return [
410
+ `${formatArtifactLabel(artifact)}: ${artifact.path}`,
411
+ `Artifact type: ${artifact.kind}`,
412
+ `Requested path: ${artifact.requestedPath ?? artifact.path}`,
413
+ `Absolute path: ${artifact.absolutePath}`,
414
+ "Exists: pending until record stop",
415
+ `Status: ${artifact.status ?? "pending"}`,
416
+ `Recording state: ${artifact.recordingState ?? "openRecording"}`,
417
+ `Will exist on stop: ${artifact.willExistOnStop !== false}`,
418
+ artifact.session ? `Session: ${artifact.session}` : undefined,
419
+ artifact.cwd ? `CWD: ${artifact.cwd}` : undefined,
420
+ `Machine data: details.artifacts[${index}]`,
421
+ ].filter((item) => item !== undefined).join("\n");
422
+ }
423
+ return [
424
+ `${formatArtifactLabel(artifact)}: ${artifact.path}`,
425
+ `Artifact type: ${artifact.kind}`,
426
+ `Requested path: ${artifact.requestedPath ?? artifact.path}`,
427
+ `Absolute path: ${artifact.absolutePath}`,
428
+ `Exists: ${artifact.exists === true}`,
429
+ artifact.exists === false ? "not found on disk" : undefined,
430
+ typeof artifact.sizeBytes === "number" ? `Size: ${formatByteCount(artifact.sizeBytes)}` : undefined,
431
+ typeof artifact.sizeBytes === "number" ? `Size bytes: ${artifact.sizeBytes}` : undefined,
432
+ `Status: ${artifact.status ?? (artifact.exists === false ? "missing" : "saved")}`,
433
+ artifact.tempPath ? `Temp path: ${artifact.tempPath}` : undefined,
434
+ artifact.mediaType ? `Media type: ${artifact.mediaType}` : undefined,
435
+ artifact.session ? `Session: ${artifact.session}` : undefined,
436
+ artifact.cwd ? `CWD: ${artifact.cwd}` : undefined,
437
+ `Machine data: details.artifacts[${index}]`,
438
+ ].filter((item) => item !== undefined).join("\n");
439
+ });
440
+ }
441
+ function isDownloadWaitCommand(commandInfo) {
442
+ return commandInfo.command === "wait" && commandInfo.subcommand === "--download";
443
+ }
444
+ function extractSavedFilePath(data) {
445
+ return typeof data.path === "string" && data.path.trim().length > 0 ? data.path : undefined;
446
+ }
447
+ export function getSavedFileDetails(commandInfo, data) {
448
+ const path = extractSavedFilePath(data);
449
+ if (!path || isNonFileArtifactPathCandidate(path)) {
450
+ return undefined;
451
+ }
452
+ const savedFileCommand = isDownloadWaitCommand(commandInfo)
453
+ ? "wait"
454
+ : commandInfo.command === "download" || commandInfo.command === "pdf"
455
+ ? commandInfo.command
456
+ : undefined;
457
+ if (!savedFileCommand) {
458
+ return undefined;
459
+ }
460
+ const { path: _path, ...metadata } = data;
461
+ const details = {
462
+ command: savedFileCommand,
463
+ kind: savedFileCommand === "pdf" ? "pdf" : "download",
464
+ path,
465
+ };
466
+ if (Object.keys(metadata).length > 0) {
467
+ details.metadata = metadata;
468
+ }
469
+ if (commandInfo.subcommand) {
470
+ details.subcommand = commandInfo.subcommand;
471
+ }
472
+ return details;
473
+ }
474
+ function isTrustedScreenshotOutput(commandInfo) {
475
+ return commandInfo.command === "screenshot";
476
+ }
477
+ export function extractImagePath(commandInfo, cwd, data) {
478
+ if (!isTrustedScreenshotOutput(commandInfo)) {
479
+ return undefined;
480
+ }
481
+ if (typeof data === "string") {
482
+ const mimeType = getImageMimeType(data);
483
+ return mimeType ? resolve(cwd, data) : undefined;
484
+ }
485
+ if (!isRecord(data) || typeof data.path !== "string") {
486
+ return undefined;
487
+ }
488
+ const mimeType = getImageMimeType(data.path);
489
+ return mimeType ? resolve(cwd, data.path) : undefined;
490
+ }
491
+ export async function attachInlineImage(presentation, imagePath) {
492
+ const mimeType = getImageMimeType(imagePath);
493
+ if (!mimeType) {
494
+ return presentation;
495
+ }
496
+ try {
497
+ const fileStats = await stat(imagePath);
498
+ const inlineImageMaxBytes = getInlineImageMaxBytes();
499
+ if (fileStats.size > inlineImageMaxBytes) {
500
+ appendPresentationNotice(presentation, `Image attachment skipped: ${formatByteCount(fileStats.size)} exceeds the inline limit of ${formatByteCount(inlineImageMaxBytes)}.`);
501
+ presentation.imagePath = imagePath;
502
+ return presentation;
503
+ }
504
+ const file = await readFile(imagePath);
505
+ presentation.content.push({ type: "image", data: file.toString("base64"), mimeType });
506
+ presentation.imagePath = imagePath;
507
+ return presentation;
508
+ }
509
+ catch (error) {
510
+ const message = error instanceof Error ? error.message : String(error);
511
+ appendPresentationNotice(presentation, `Image attachment failed: ${message}`);
512
+ presentation.imagePath = imagePath;
513
+ return presentation;
514
+ }
515
+ }