@midscene/core 1.17.6 → 1.17.7

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 (72) hide show
  1. package/dist/es/agent/agent-report-manager.mjs +1 -1
  2. package/dist/es/agent/agent-report-manager.mjs.map +1 -1
  3. package/dist/es/agent/utils.mjs +1 -1
  4. package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs +80 -0
  5. package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs.map +1 -0
  6. package/dist/es/ai-model/model-adapter/locate.mjs +2 -1
  7. package/dist/es/ai-model/model-adapter/locate.mjs.map +1 -1
  8. package/dist/es/ai-model/workflows/grounding/locate.mjs +1 -1
  9. package/dist/es/ai-model/workflows/grounding/locate.mjs.map +1 -1
  10. package/dist/es/ai-model/workflows/grounding/section-locate.mjs +1 -1
  11. package/dist/es/ai-model/workflows/grounding/section-locate.mjs.map +1 -1
  12. package/dist/es/ai-model/workflows/grounding/utils.mjs +8 -26
  13. package/dist/es/ai-model/workflows/grounding/utils.mjs.map +1 -1
  14. package/dist/es/dump/report-image-export.mjs +58 -0
  15. package/dist/es/dump/report-image-export.mjs.map +1 -0
  16. package/dist/es/dump/screenshot-store.mjs +5 -0
  17. package/dist/es/dump/screenshot-store.mjs.map +1 -1
  18. package/dist/es/report-cli.mjs +13 -29
  19. package/dist/es/report-cli.mjs.map +1 -1
  20. package/dist/es/report-generator.mjs +22 -8
  21. package/dist/es/report-generator.mjs.map +1 -1
  22. package/dist/es/report-html-template.mjs +1 -1
  23. package/dist/es/report-markdown.mjs +50 -53
  24. package/dist/es/report-markdown.mjs.map +1 -1
  25. package/dist/es/report.mjs +8 -24
  26. package/dist/es/report.mjs.map +1 -1
  27. package/dist/es/utils.mjs +1 -1
  28. package/dist/lib/agent/agent-report-manager.js +1 -1
  29. package/dist/lib/agent/agent-report-manager.js.map +1 -1
  30. package/dist/lib/agent/utils.js +1 -1
  31. package/dist/lib/ai-model/model-adapter/locate-prompt-order.js +117 -0
  32. package/dist/lib/ai-model/model-adapter/locate-prompt-order.js.map +1 -0
  33. package/dist/lib/ai-model/model-adapter/locate.js +2 -1
  34. package/dist/lib/ai-model/model-adapter/locate.js.map +1 -1
  35. package/dist/lib/ai-model/workflows/grounding/locate.js +1 -1
  36. package/dist/lib/ai-model/workflows/grounding/locate.js.map +1 -1
  37. package/dist/lib/ai-model/workflows/grounding/section-locate.js +1 -1
  38. package/dist/lib/ai-model/workflows/grounding/section-locate.js.map +1 -1
  39. package/dist/lib/ai-model/workflows/grounding/utils.js +8 -26
  40. package/dist/lib/ai-model/workflows/grounding/utils.js.map +1 -1
  41. package/dist/lib/dump/report-image-export.js +92 -0
  42. package/dist/lib/dump/report-image-export.js.map +1 -0
  43. package/dist/lib/dump/screenshot-store.js +5 -0
  44. package/dist/lib/dump/screenshot-store.js.map +1 -1
  45. package/dist/lib/report-cli.js +12 -28
  46. package/dist/lib/report-cli.js.map +1 -1
  47. package/dist/lib/report-generator.js +22 -8
  48. package/dist/lib/report-generator.js.map +1 -1
  49. package/dist/lib/report-html-template.js +1 -1
  50. package/dist/lib/report-markdown.js +50 -53
  51. package/dist/lib/report-markdown.js.map +1 -1
  52. package/dist/lib/report.js +6 -22
  53. package/dist/lib/report.js.map +1 -1
  54. package/dist/lib/utils.js +1 -1
  55. package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts +24 -0
  56. package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts.map +1 -0
  57. package/dist/types/ai-model/model-adapter/locate.d.ts.map +1 -1
  58. package/dist/types/ai-model/model-adapter/types.d.ts +9 -3
  59. package/dist/types/ai-model/model-adapter/types.d.ts.map +1 -1
  60. package/dist/types/ai-model/workflows/grounding/utils.d.ts +4 -9
  61. package/dist/types/ai-model/workflows/grounding/utils.d.ts.map +1 -1
  62. package/dist/types/dump/report-image-export.d.ts +12 -0
  63. package/dist/types/dump/report-image-export.d.ts.map +1 -0
  64. package/dist/types/dump/screenshot-store.d.ts +2 -0
  65. package/dist/types/dump/screenshot-store.d.ts.map +1 -1
  66. package/dist/types/report-cli.d.ts.map +1 -1
  67. package/dist/types/report-generator.d.ts +4 -2
  68. package/dist/types/report-generator.d.ts.map +1 -1
  69. package/dist/types/report-markdown.d.ts +4 -2
  70. package/dist/types/report-markdown.d.ts.map +1 -1
  71. package/dist/types/report.d.ts.map +1 -1
  72. package/package.json +2 -2
@@ -211,54 +211,52 @@ function restoredSourceRef(screenshot) {
211
211
  }
212
212
  function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, taskIndex, options) {
213
213
  const markdownLabel = options?.label || `task-${taskIndex + 1}`;
214
+ const result = (attachment)=>{
215
+ attachment.suggestedFileName = options?.resolveScreenshotFileName?.(attachment) ?? attachment.suggestedFileName;
216
+ return {
217
+ markdown: `\n![${markdownLabel}](${screenshotBaseDir}/${attachment.suggestedFileName})`,
218
+ attachment
219
+ };
220
+ };
214
221
  if (ScreenshotItem.isScreenshotItem(screenshot)) {
215
222
  const ext = screenshot.extension;
216
223
  const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${screenshot.id}.${ext}`;
217
- return {
218
- markdown: `\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,
219
- attachment: {
220
- id: screenshot.id,
221
- suggestedFileName,
222
- mimeType: `image/${'jpeg' === ext ? 'jpeg' : 'png'}`,
223
- executionIndex,
224
- taskIndex,
225
- base64Data: tryExtractBase64(screenshot)
226
- }
227
- };
224
+ return result({
225
+ id: screenshot.id,
226
+ suggestedFileName,
227
+ mimeType: `image/${'jpeg' === ext ? 'jpeg' : 'png'}`,
228
+ executionIndex,
229
+ taskIndex,
230
+ base64Data: tryExtractBase64(screenshot)
231
+ });
228
232
  }
229
233
  const ref = normalizeScreenshotRef(screenshot);
230
234
  if (ref) {
231
235
  const ext = imageRefFileExtension(ref);
232
236
  const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${ref.id}.${ext}`;
233
- return {
234
- markdown: `\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,
235
- attachment: {
236
- id: ref.id,
237
- suggestedFileName,
238
- sourceRef: ref,
239
- mimeType: ref.mimeType,
240
- executionIndex,
241
- taskIndex,
242
- base64Data: tryExtractBase64(screenshot)
243
- }
244
- };
237
+ return result({
238
+ id: ref.id,
239
+ suggestedFileName,
240
+ sourceRef: ref,
241
+ mimeType: ref.mimeType,
242
+ executionIndex,
243
+ taskIndex,
244
+ base64Data: tryExtractBase64(screenshot)
245
+ });
245
246
  }
246
247
  const sourceRef = restoredSourceRef(screenshot);
247
248
  if (sourceRef) {
248
249
  const ext = imageRefFileExtension(sourceRef);
249
250
  const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${sourceRef.id}.${ext}`;
250
- return {
251
- markdown: `\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,
252
- attachment: {
253
- id: sourceRef.id,
254
- suggestedFileName,
255
- sourceRef,
256
- mimeType: sourceRef.mimeType,
257
- executionIndex,
258
- taskIndex,
259
- base64Data: tryExtractBase64(screenshot)
260
- }
261
- };
251
+ return result({
252
+ id: sourceRef.id,
253
+ suggestedFileName,
254
+ sourceRef,
255
+ mimeType: sourceRef.mimeType,
256
+ executionIndex,
257
+ taskIndex,
258
+ base64Data: tryExtractBase64(screenshot)
259
+ });
262
260
  }
263
261
  const base64 = tryExtractBase64(screenshot);
264
262
  if (base64) {
@@ -266,24 +264,21 @@ function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, tas
266
264
  const idSuffix = options?.fallbackIdSuffix ? `-${options.fallbackIdSuffix}` : '';
267
265
  const id = `restored-${executionIndex + 1}-${taskIndex + 1}${idSuffix}`;
268
266
  const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${id}.${ext}`;
269
- return {
270
- markdown: `\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,
271
- attachment: {
272
- id,
273
- suggestedFileName,
274
- mimeType: `image/${ext}`,
275
- executionIndex,
276
- taskIndex,
277
- base64Data: base64
278
- }
279
- };
267
+ return result({
268
+ id,
269
+ suggestedFileName,
270
+ mimeType: `image/${ext}`,
271
+ executionIndex,
272
+ taskIndex,
273
+ base64Data: base64
274
+ });
280
275
  }
281
276
  throw new Error(`executionToMarkdown: missing screenshot for execution #${executionIndex + 1} task #${taskIndex + 1}`);
282
277
  }
283
278
  function imageLabelSuffix(value) {
284
279
  return value.toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'screenshot';
285
280
  }
286
- function screenshotsMarkdownSection(task, screenshotBaseDir, executionIndex, taskIndex) {
281
+ function screenshotsMarkdownSection(task, screenshotBaseDir, executionIndex, taskIndex, resolveScreenshotFileName) {
287
282
  const lines = [
288
283
  '',
289
284
  '### Screenshots'
@@ -293,11 +288,12 @@ function screenshotsMarkdownSection(task, screenshotBaseDir, executionIndex, tas
293
288
  if (task.uiContext?.screenshot) {
294
289
  const imageResult = screenshotAttachment(task.uiContext.screenshot, screenshotBaseDir, executionIndex, taskIndex, {
295
290
  fallbackIdSuffix: 'ui-context',
296
- label: `task-${taskIndex + 1}-ui-context`
291
+ label: `task-${taskIndex + 1}-ui-context`,
292
+ resolveScreenshotFileName
297
293
  });
298
294
  const time = resolveTaskTiming(task);
299
295
  screenshotIndex += 1;
300
- lines.push(`- #${screenshotIndex} type=ui-context, ts=${formatTime(time.start)}, timing=ui-context`);
296
+ lines.push(`- #${screenshotIndex} type=ui-context, ts=${formatTime(time.start)}, timing=ui-context, id=${imageResult.attachment.id}`);
301
297
  lines.push(imageResult.markdown);
302
298
  attachments.push(imageResult.attachment);
303
299
  }
@@ -306,11 +302,12 @@ function screenshotsMarkdownSection(task, screenshotBaseDir, executionIndex, tas
306
302
  const descriptionText = item.description ? `, description=${item.description}` : '';
307
303
  const timing = item.timing || 'N/A';
308
304
  screenshotIndex += 1;
309
- lines.push(`- #${screenshotIndex} type=${item.type}, ts=${formatTime(item.ts)}, timing=${timing}${descriptionText}`);
310
305
  const imageResult = screenshotAttachment(item.screenshot, screenshotBaseDir, executionIndex, taskIndex, {
311
306
  fallbackIdSuffix: `recorder-${recorderIndex + 1}`,
312
- label: `task-${taskIndex + 1}-${imageLabelSuffix(timing)}`
307
+ label: `task-${taskIndex + 1}-${imageLabelSuffix(timing)}`,
308
+ resolveScreenshotFileName
313
309
  });
310
+ lines.push(`- #${screenshotIndex} type=${item.type}, ts=${formatTime(item.ts)}, timing=${timing}${descriptionText}, id=${imageResult.attachment.id}`);
314
311
  lines.push(imageResult.markdown);
315
312
  attachments.push(imageResult.attachment);
316
313
  });
@@ -371,7 +368,7 @@ function renderExecution(executionRaw, executionIndex, options) {
371
368
  appendJsonSection(lines, 'Log', task.log);
372
369
  appendTextSection(lines, 'Thought', task.thought);
373
370
  appendTextSection(lines, 'Reasoning Content', task.reasoning_content);
374
- const screenshotsSection = screenshotsMarkdownSection(task, screenshotBaseDir, executionIndex, taskIndex);
371
+ const screenshotsSection = screenshotsMarkdownSection(task, screenshotBaseDir, executionIndex, taskIndex, options?.resolveScreenshotFileName);
375
372
  if (screenshotsSection.lines.length) {
376
373
  lines.push(...screenshotsSection.lines);
377
374
  attachments.push(...screenshotsSection.attachments);
@@ -388,7 +385,7 @@ function executionToMarkdown(execution, options) {
388
385
  function reportToMarkdown(report, options) {
389
386
  const reportDump = toReportDump(report);
390
387
  const executionResults = reportDump.executions.map((execution, index)=>{
391
- const rendered = renderExecution(execution, index);
388
+ const rendered = renderExecution(execution, index, options);
392
389
  return {
393
390
  markdown: rendered.markdown,
394
391
  attachments: rendered.attachments
@@ -1 +1 @@
1
- {"version":3,"file":"report-markdown.mjs","sources":["../../src/report-markdown.ts"],"sourcesContent":["import { extractInsightParam, paramStr, typeStr } from '@/agent/ui-utils';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n AIUsageInfo,\n ExecutionDump,\n ExecutionTask,\n IExecutionDump,\n IReportActionDump,\n ModelBrief,\n ReportActionDump,\n} from '@/types';\nimport type { ScreenshotRef } from './dump/screenshot-store';\nimport {\n imageRefFileExtension,\n normalizeScreenshotRef,\n} from './dump/screenshot-store';\nimport { collectReportSummary } from './report-stats';\n\nconst screenshotDataUrlPattern =\n /^data:image\\/(png|jpeg|jpg);base64,([\\s\\S]*)$/i;\nconst rawBase64BodyPattern = /^[a-zA-Z0-9+/=\\s]+$/;\nconst jsonContextMaxStringLength = 12_000;\n\ntype ExecutionTaskWithExtraUsage = ExecutionTask & {\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n};\n\nexport interface MarkdownAttachment {\n id: string;\n /**\n * Stable, prefixed file name for the exported copy. The markdown image link\n * always points at `${screenshotBaseDir}/${suggestedFileName}`, so consumers\n * write the screenshot under this name to keep links in sync. See #2392.\n */\n suggestedFileName: string;\n mimeType?: string;\n /**\n * Reference to the screenshot in the source report, used to locate the\n * original bytes when copying them to the exported name. Absent for in-memory\n * screenshots, which carry their data in `base64Data` instead.\n */\n sourceRef?: ScreenshotRef;\n executionIndex: number;\n taskIndex: number;\n /** Populated when screenshot data is available in memory (e.g. browser context). */\n base64Data?: string;\n}\n\nexport interface ExecutionMarkdownOptions {\n screenshotBaseDir?: string;\n}\n\nexport interface ReportMarkdownOptions {\n /** Used only when the report has no recorded task timestamps. */\n wallTimeFallbackMs?: number;\n}\n\nexport interface ExecutionMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nexport interface ReportMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nfunction toExecutionDump(\n execution: ExecutionDump | IExecutionDump,\n): IExecutionDump {\n if (!execution || typeof execution !== 'object') {\n throw new Error('executionToMarkdown: execution is required');\n }\n\n if (!Array.isArray(execution.tasks)) {\n throw new Error('executionToMarkdown: execution.tasks must be an array');\n }\n\n if (!execution.name) {\n throw new Error('executionToMarkdown: execution.name is required');\n }\n\n return execution;\n}\n\nfunction toReportDump(\n report: ReportActionDump | IReportActionDump,\n): IReportActionDump {\n if (!report || typeof report !== 'object') {\n throw new Error('reportToMarkdown: report is required');\n }\n\n if (!Array.isArray(report.executions)) {\n throw new Error('reportToMarkdown: report.executions must be an array');\n }\n\n return report;\n}\n\nfunction formatTime(ts?: number): string {\n if (typeof ts !== 'number' || Number.isNaN(ts)) {\n return 'N/A';\n }\n return new Date(ts).toISOString();\n}\n\nfunction formatValue(value: unknown): string {\n if (value === undefined || value === null || value === '') {\n return 'N/A';\n }\n if (typeof value === 'number') {\n return Number.isFinite(value) ? String(value) : 'N/A';\n }\n return String(value);\n}\n\nfunction escapeTableCell(value: unknown): string {\n return formatValue(value).replace(/\\|/g, '\\\\|').replace(/\\n/g, '<br>');\n}\n\nfunction markdownTable(headers: string[], rows: unknown[][]): string[] {\n return [\n `| ${headers.map(escapeTableCell).join(' | ')} |`,\n `| ${headers.map(() => '---').join(' | ')} |`,\n ...rows.map((row) => `| ${row.map(escapeTableCell).join(' | ')} |`),\n ];\n}\n\nfunction modelBriefRows(modelBriefs: ModelBrief[]): unknown[][] {\n return modelBriefs.map((brief) => [\n brief.intent || 'default',\n brief.name || 'N/A',\n brief.modelDescription || 'N/A',\n ]);\n}\n\nfunction modelRowsFromUsage(report: IReportActionDump): unknown[][] {\n const rows = new Map<string, unknown[]>();\n const addUsage = (source: string, usage?: AIUsageInfo) => {\n if (!hasUsage(usage)) return;\n const intent = usage.intent || source;\n const model = usage.model_name || usage.response_model_name;\n const description = usage.model_description;\n const key = `${intent}\\0${model || ''}\\0${description || ''}`;\n if (rows.has(key)) {\n return;\n }\n rows.set(key, [intent, model || 'N/A', description || 'N/A']);\n };\n\n for (const execution of report.executions) {\n for (const task of execution.tasks) {\n const taskWithUsage = task as ExecutionTaskWithExtraUsage;\n addUsage('main', taskWithUsage.usage);\n addUsage('searchArea', taskWithUsage.searchAreaUsage);\n }\n }\n\n return Array.from(rows.values());\n}\n\nfunction usageValue(usage: AIUsageInfo | undefined, key: string): number {\n const value = usage?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n\nfunction hasUsage(usage: AIUsageInfo | undefined): usage is AIUsageInfo {\n if (!usage) return false;\n return [\n 'intent',\n 'model_name',\n 'model_description',\n 'prompt_tokens',\n 'cached_input',\n 'completion_tokens',\n 'total_tokens',\n 'time_cost',\n 'request_id',\n ].some((key) => usage[key] !== undefined && usage[key] !== null);\n}\n\nfunction usageTotalTokens(usage: AIUsageInfo): number {\n const explicitTotal = usageValue(usage, 'total_tokens');\n if (explicitTotal > 0) {\n return explicitTotal;\n }\n return (\n usageValue(usage, 'prompt_tokens') + usageValue(usage, 'completion_tokens')\n );\n}\n\nfunction usageRowsForTask(task: ExecutionTask): unknown[][] {\n const taskWithUsage = task as ExecutionTaskWithExtraUsage;\n const rows: unknown[][] = [];\n const appendUsage = (source: string, usage?: AIUsageInfo) => {\n if (!hasUsage(usage)) return;\n rows.push([\n source,\n usage.intent,\n usage.model_name || usage.response_model_name,\n usage.model_description,\n usageValue(usage, 'prompt_tokens'),\n usageValue(usage, 'cached_input'),\n usageValue(usage, 'completion_tokens'),\n usageTotalTokens(usage),\n usageValue(usage, 'time_cost'),\n usage.request_id,\n ]);\n };\n\n appendUsage('main', taskWithUsage.usage);\n appendUsage('searchArea', taskWithUsage.searchAreaUsage);\n return rows;\n}\n\nfunction sanitizeJsonForMarkdown(\n value: unknown,\n seen = new WeakSet<object>(),\n): unknown {\n if (typeof value === 'string') {\n if (\n screenshotDataUrlPattern.test(value) ||\n value.length > 1_000_000 ||\n /^[a-zA-Z0-9+/=\\s]{100000,}$/.test(value)\n ) {\n return `[omitted image/base64 string, length=${value.length}]`;\n }\n if (value.length > jsonContextMaxStringLength) {\n return `${value.slice(0, jsonContextMaxStringLength)}\\n[truncated, original length=${value.length}]`;\n }\n return value;\n }\n\n if (\n value === null ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n\n if (typeof value === 'function') {\n return '[omitted function]';\n }\n\n if (typeof value !== 'object' || value === undefined) {\n return undefined;\n }\n\n if (seen.has(value)) {\n return '[omitted circular reference]';\n }\n seen.add(value);\n\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeJsonForMarkdown(item, seen));\n }\n\n const input = value as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(input)) {\n if (entry === undefined || typeof entry === 'function') {\n continue;\n }\n if (\n typeof entry === 'string' &&\n /(^|_|-)(base64|screenshotBase64|imageBase64)$/i.test(key)\n ) {\n output[key] = `[omitted base64 string, length=${entry.length}]`;\n continue;\n }\n output[key] = sanitizeJsonForMarkdown(entry, seen);\n }\n seen.delete(value);\n return output;\n}\n\nfunction appendJsonSection(\n lines: string[],\n title: string,\n value: unknown,\n): void {\n if (value === undefined || value === null || value === '') {\n return;\n }\n const sanitized = sanitizeJsonForMarkdown(value);\n if (sanitized === undefined) {\n return;\n }\n\n lines.push('', `### ${title}`, '', '```json');\n lines.push(JSON.stringify(sanitized, null, 2));\n lines.push('```');\n}\n\nfunction appendTextSection(\n lines: string[],\n title: string,\n value: unknown,\n): void {\n if (typeof value !== 'string' || !value.trim()) {\n return;\n }\n lines.push('', `### ${title}`, '', '```text');\n lines.push(value.replace(/```/g, '` ` `'));\n lines.push('```');\n}\n\nfunction resolveTaskTiming(task: ExecutionTask): {\n start?: number;\n end?: number;\n cost?: number;\n} {\n const timing = task.timing;\n if (!timing) {\n return {};\n }\n\n const start = timing.start ?? timing.callAiStart ?? timing.callActionStart;\n const end =\n timing.end ??\n timing.callAiEnd ??\n timing.callActionEnd ??\n timing.captureAfterCallingSnapshotEnd;\n const cost =\n timing.cost ??\n (typeof start === 'number' && typeof end === 'number'\n ? end - start\n : undefined);\n\n return { start, end, cost };\n}\n\nfunction safeTaskParam(task: ExecutionTask): string {\n const readable = paramStr(task);\n if (readable) {\n return readable;\n }\n\n if (task.type === 'Insight') {\n return extractInsightParam((task as any).param).content;\n }\n\n return '';\n}\n\nfunction formatSize(\n size?: { width?: number; height?: number } | null,\n): string | undefined {\n if (\n !size ||\n typeof size.width !== 'number' ||\n typeof size.height !== 'number' ||\n Number.isNaN(size.width) ||\n Number.isNaN(size.height)\n ) {\n return undefined;\n }\n\n return `${size.width} x ${size.height}`;\n}\n\nfunction extractLocateCenter(\n task: ExecutionTask,\n): [number, number] | undefined {\n const outputCenter = (task.output as { element?: { center?: unknown } })\n ?.element?.center;\n if (\n Array.isArray(outputCenter) &&\n outputCenter.length >= 2 &&\n typeof outputCenter[0] === 'number' &&\n typeof outputCenter[1] === 'number'\n ) {\n return [outputCenter[0], outputCenter[1]];\n }\n\n const paramLocateCenter = (task.param as { locate?: { center?: unknown } })\n ?.locate?.center;\n if (\n Array.isArray(paramLocateCenter) &&\n paramLocateCenter.length >= 2 &&\n typeof paramLocateCenter[0] === 'number' &&\n typeof paramLocateCenter[1] === 'number'\n ) {\n return [paramLocateCenter[0], paramLocateCenter[1]];\n }\n\n const paramCenter = (task.param as { center?: unknown })?.center;\n if (\n Array.isArray(paramCenter) &&\n paramCenter.length >= 2 &&\n typeof paramCenter[0] === 'number' &&\n typeof paramCenter[1] === 'number'\n ) {\n return [paramCenter[0], paramCenter[1]];\n }\n\n return undefined;\n}\n\nfunction tryExtractBase64(screenshot: unknown): string | undefined {\n if (typeof screenshot === 'string') {\n const trimmedScreenshot = screenshot.trim();\n const dataUrlMatch = trimmedScreenshot.match(screenshotDataUrlPattern);\n if (dataUrlMatch) {\n const format = dataUrlMatch[1].toLowerCase() === 'jpg' ? 'jpeg' : 'png';\n const base64Body = dataUrlMatch[2].replace(/\\s/g, '');\n if (!base64Body) {\n return undefined;\n }\n return `data:image/${format};base64,${base64Body}`;\n }\n\n if (\n trimmedScreenshot.startsWith('data:') ||\n !rawBase64BodyPattern.test(trimmedScreenshot)\n ) {\n return undefined;\n }\n\n const base64Body = trimmedScreenshot.replace(/\\s/g, '');\n return base64Body ? `data:image/png;base64,${base64Body}` : undefined;\n }\n\n if (!screenshot || typeof screenshot !== 'object') return undefined;\n const s = screenshot as Record<string, unknown>;\n if (typeof s.base64 === 'string' && s.base64.length > 0) {\n return s.base64;\n }\n return undefined;\n}\n\nfunction restoredSourceRef(screenshot: unknown): ScreenshotRef | undefined {\n if (!screenshot || typeof screenshot !== 'object') {\n return undefined;\n }\n\n const sourceRef = (screenshot as Record<string, unknown>).sourceRef;\n return normalizeScreenshotRef(sourceRef) ?? undefined;\n}\n\nfunction screenshotAttachment(\n screenshot: unknown,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n options?: {\n fallbackIdSuffix?: string;\n label?: string;\n },\n): { markdown: string; attachment: MarkdownAttachment } {\n const markdownLabel = options?.label || `task-${taskIndex + 1}`;\n\n if (ScreenshotItem.isScreenshotItem(screenshot)) {\n const ext = screenshot.extension;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${screenshot.id}.${ext}`;\n return {\n markdown: `\\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,\n attachment: {\n id: screenshot.id,\n suggestedFileName,\n mimeType: `image/${ext === 'jpeg' ? 'jpeg' : 'png'}`,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n },\n };\n }\n\n const ref = normalizeScreenshotRef(screenshot);\n if (ref) {\n const ext = imageRefFileExtension(ref);\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${ref.id}.${ext}`;\n return {\n markdown: `\\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,\n attachment: {\n id: ref.id,\n suggestedFileName,\n sourceRef: ref,\n mimeType: ref.mimeType,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n },\n };\n }\n\n const sourceRef = restoredSourceRef(screenshot);\n if (sourceRef) {\n const ext = imageRefFileExtension(sourceRef);\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${sourceRef.id}.${ext}`;\n return {\n markdown: `\\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,\n attachment: {\n id: sourceRef.id,\n suggestedFileName,\n sourceRef,\n mimeType: sourceRef.mimeType,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n },\n };\n }\n\n const base64 = tryExtractBase64(screenshot);\n if (base64) {\n const ext = base64.startsWith('data:image/jpeg') ? 'jpeg' : 'png';\n const idSuffix = options?.fallbackIdSuffix\n ? `-${options.fallbackIdSuffix}`\n : '';\n const id = `restored-${executionIndex + 1}-${taskIndex + 1}${idSuffix}`;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${id}.${ext}`;\n return {\n markdown: `\\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,\n attachment: {\n id,\n suggestedFileName,\n mimeType: `image/${ext}`,\n executionIndex,\n taskIndex,\n base64Data: base64,\n },\n };\n }\n\n throw new Error(\n `executionToMarkdown: missing screenshot for execution #${executionIndex + 1} task #${taskIndex + 1}`,\n );\n}\n\nfunction imageLabelSuffix(value: string): string {\n return (\n value\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '') || 'screenshot'\n );\n}\n\nfunction screenshotsMarkdownSection(\n task: ExecutionTask,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n): { lines: string[]; attachments: MarkdownAttachment[] } {\n const lines: string[] = ['', '### Screenshots'];\n const attachments: MarkdownAttachment[] = [];\n let screenshotIndex = 0;\n\n if (task.uiContext?.screenshot) {\n const imageResult = screenshotAttachment(\n task.uiContext.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n {\n fallbackIdSuffix: 'ui-context',\n label: `task-${taskIndex + 1}-ui-context`,\n },\n );\n const time = resolveTaskTiming(task);\n screenshotIndex += 1;\n lines.push(\n `- #${screenshotIndex} type=ui-context, ts=${formatTime(time.start)}, timing=ui-context`,\n );\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n }\n\n task.recorder?.forEach((item, recorderIndex) => {\n if (!item.screenshot) {\n return;\n }\n\n const descriptionText = item.description\n ? `, description=${item.description}`\n : '';\n const timing = item.timing || 'N/A';\n screenshotIndex += 1;\n lines.push(\n `- #${screenshotIndex} type=${item.type}, ts=${formatTime(item.ts)}, timing=${timing}${descriptionText}`,\n );\n\n const imageResult = screenshotAttachment(\n item.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n {\n fallbackIdSuffix: `recorder-${recorderIndex + 1}`,\n label: `task-${taskIndex + 1}-${imageLabelSuffix(timing)}`,\n },\n );\n\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n });\n\n return screenshotIndex > 0\n ? { lines, attachments }\n : { lines: [], attachments };\n}\n\nfunction renderExecution(\n executionRaw: ExecutionDump | IExecutionDump,\n executionIndex: number,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n const execution = toExecutionDump(executionRaw);\n const screenshotBaseDir = options?.screenshotBaseDir ?? './screenshots';\n\n const lines: string[] = [];\n const attachments: MarkdownAttachment[] = [];\n\n lines.push(`# ${execution.name}`);\n if (execution.description) {\n lines.push('', execution.description);\n }\n\n lines.push('', `- Execution start: ${formatTime(execution.logTime)}`);\n lines.push(`- Task count: ${execution.tasks.length}`);\n if (execution.aiActContext) {\n appendTextSection(lines, 'AI Action Context', execution.aiActContext);\n }\n\n execution.tasks.forEach((task, taskIndex) => {\n const title = typeStr(task);\n const detail = safeTaskParam(task);\n const time = resolveTaskTiming(task);\n\n lines.push(\n '',\n `## ${taskIndex + 1}. ${title}${detail ? ` - ${detail}` : ''}`,\n );\n lines.push(`- Task ID: ${task.taskId || 'N/A'}`);\n lines.push(`- Type: ${task.type || 'N/A'}`);\n lines.push(`- SubType: ${task.subType || 'N/A'}`);\n lines.push(`- Status: ${task.status || 'unknown'}`);\n lines.push(`- Start: ${formatTime(time.start)}`);\n lines.push(`- End: ${formatTime(time.end)}`);\n lines.push(\n `- Cost(ms): ${typeof time.cost === 'number' ? time.cost : 'N/A'}`,\n );\n lines.push(\n `- Screen size: ${formatSize(task.uiContext?.shotSize) || 'N/A'}`,\n );\n\n if (task.subType === 'Locate') {\n const locateCenter = extractLocateCenter(task);\n if (locateCenter) {\n lines.push(`- Locate center: (${locateCenter[0]}, ${locateCenter[1]})`);\n }\n }\n\n if (task.errorMessage) {\n lines.push(`- Error: ${task.errorMessage}`);\n }\n\n const usageRows = usageRowsForTask(task);\n if (usageRows.length) {\n lines.push('', '### Model Usage');\n lines.push(\n ...markdownTable(\n [\n 'Source',\n 'Intent',\n 'Model',\n 'Description',\n 'Prompt Tokens',\n 'Cached Input',\n 'Completion Tokens',\n 'Total Tokens',\n 'Model Call Time(ms)',\n 'Request ID',\n ],\n usageRows,\n ),\n );\n }\n\n appendJsonSection(lines, 'Param', task.param);\n appendJsonSection(lines, 'Output', task.output);\n appendJsonSection(lines, 'Log', task.log);\n appendTextSection(lines, 'Thought', task.thought);\n appendTextSection(\n lines,\n 'Reasoning Content',\n (task as ExecutionTaskWithExtraUsage).reasoning_content,\n );\n\n const screenshotsSection = screenshotsMarkdownSection(\n task,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n );\n if (screenshotsSection.lines.length) {\n lines.push(...screenshotsSection.lines);\n attachments.push(...screenshotsSection.attachments);\n }\n });\n\n return {\n markdown: lines.join('\\n'),\n attachments,\n };\n}\n\nexport function executionToMarkdown(\n execution: ExecutionDump | IExecutionDump,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n return renderExecution(execution, 0, options);\n}\n\nexport function reportToMarkdown(\n report: ReportActionDump | IReportActionDump,\n options?: ReportMarkdownOptions,\n): ReportMarkdownResult {\n const reportDump = toReportDump(report);\n\n const executionResults = reportDump.executions.map((execution, index) => {\n const rendered = renderExecution(execution, index);\n return {\n markdown: rendered.markdown,\n attachments: rendered.attachments,\n };\n });\n\n const attachments = executionResults.flatMap((item) => item.attachments);\n const reportSummary = collectReportSummary(reportDump, options);\n const modelRows = reportDump.modelBriefs?.length\n ? modelBriefRows(reportDump.modelBriefs)\n : modelRowsFromUsage(reportDump);\n\n const modelInfoLines = [\n '\\n## Model Info',\n ...(modelRows.length\n ? markdownTable(['Intent', 'Model', 'Description'], modelRows)\n : ['- No model metadata recorded.']),\n ];\n\n const elapsedTimeDescription =\n reportSummary.timing.wallTimeSource === 'task-timestamps'\n ? 'Total span from the first recorded task start to the last recorded task end, including model calls, actions, waits, and gaps.'\n : reportSummary.timing.wallTimeSource === 'fallback'\n ? 'Total elapsed duration reported by the enclosing test runner because task timestamps were unavailable.'\n : 'The total elapsed span is unavailable because the report has no recorded task timestamps.';\n const timingSummaryLines = [\n '\\n## Timing Summary',\n ...markdownTable(\n ['Metric', 'Duration(ms)', 'Definition'],\n [\n [\n 'Elapsed Time',\n reportSummary.timing.wallTimeMs ?? 'N/A',\n elapsedTimeDescription,\n ],\n [\n 'Model Call Time',\n reportSummary.timing.modelCallTimeMs,\n 'Total duration of all recorded model calls.',\n ],\n ],\n ),\n ];\n\n const tokenSummaryLines = [\n '\\n## Token Usage Summary',\n ...(reportSummary.models.length\n ? markdownTable(\n [\n 'Model',\n 'Calls',\n 'Prompt Tokens',\n 'Cached Input',\n 'Completion Tokens',\n 'Total Tokens',\n 'Model Call Time(ms)',\n ],\n reportSummary.models.map((model) => [\n model.modelName,\n model.callCount,\n model.promptTokens,\n model.cachedInputTokens,\n model.completionTokens,\n model.totalTokens,\n model.modelCallTimeMs,\n ]),\n )\n : ['- No token usage recorded.']),\n ];\n\n const header = [\n `# ${reportDump.groupName}`,\n reportDump.groupDescription ? `\\n${reportDump.groupDescription}` : '',\n `\\n- SDK Version: ${reportDump.sdkVersion}`,\n reportDump.deviceType ? `- Device Type: ${reportDump.deviceType}` : '',\n `- Execution count: ${reportDump.executions.length}`,\n ...modelInfoLines,\n ...timingSummaryLines,\n ...tokenSummaryLines,\n ]\n .filter(Boolean)\n .join('\\n');\n\n return {\n markdown: `${header}\\n\\n${executionResults.map((item) => item.markdown).join('\\n\\n---\\n\\n')}`,\n attachments,\n };\n}\n"],"names":["screenshotDataUrlPattern","rawBase64BodyPattern","jsonContextMaxStringLength","toExecutionDump","execution","Error","Array","toReportDump","report","formatTime","ts","Number","Date","formatValue","value","String","escapeTableCell","markdownTable","headers","rows","row","modelBriefRows","modelBriefs","brief","modelRowsFromUsage","Map","addUsage","source","usage","hasUsage","intent","model","description","key","task","taskWithUsage","usageValue","undefined","usageTotalTokens","explicitTotal","usageRowsForTask","appendUsage","sanitizeJsonForMarkdown","seen","WeakSet","item","input","output","entry","Object","appendJsonSection","lines","title","sanitized","JSON","appendTextSection","resolveTaskTiming","timing","start","end","cost","safeTaskParam","readable","paramStr","extractInsightParam","formatSize","size","extractLocateCenter","outputCenter","paramLocateCenter","paramCenter","tryExtractBase64","screenshot","trimmedScreenshot","dataUrlMatch","format","base64Body","s","restoredSourceRef","sourceRef","normalizeScreenshotRef","screenshotAttachment","screenshotBaseDir","executionIndex","taskIndex","options","markdownLabel","ScreenshotItem","ext","suggestedFileName","ref","imageRefFileExtension","base64","idSuffix","id","imageLabelSuffix","screenshotsMarkdownSection","attachments","screenshotIndex","imageResult","time","recorderIndex","descriptionText","renderExecution","executionRaw","typeStr","detail","locateCenter","usageRows","screenshotsSection","executionToMarkdown","reportToMarkdown","reportDump","executionResults","index","rendered","reportSummary","collectReportSummary","modelRows","modelInfoLines","elapsedTimeDescription","timingSummaryLines","tokenSummaryLines","header","Boolean"],"mappings":"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"}
1
+ {"version":3,"file":"report-markdown.mjs","sources":["../../src/report-markdown.ts"],"sourcesContent":["import { extractInsightParam, paramStr, typeStr } from '@/agent/ui-utils';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n AIUsageInfo,\n ExecutionDump,\n ExecutionTask,\n IExecutionDump,\n IReportActionDump,\n ModelBrief,\n ReportActionDump,\n} from '@/types';\nimport type { ScreenshotRef } from './dump/screenshot-store';\nimport {\n imageRefFileExtension,\n normalizeScreenshotRef,\n} from './dump/screenshot-store';\nimport { collectReportSummary } from './report-stats';\n\nconst screenshotDataUrlPattern =\n /^data:image\\/(png|jpeg|jpg);base64,([\\s\\S]*)$/i;\nconst rawBase64BodyPattern = /^[a-zA-Z0-9+/=\\s]+$/;\nconst jsonContextMaxStringLength = 12_000;\n\ntype ExecutionTaskWithExtraUsage = ExecutionTask & {\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n};\n\nexport interface MarkdownAttachment {\n id: string;\n /**\n * File name for the exported copy. The markdown image link\n * always points at `${screenshotBaseDir}/${suggestedFileName}`, so consumers\n * write the screenshot under this name to keep links in sync. See #2392.\n */\n suggestedFileName: string;\n mimeType?: string;\n /**\n * Reference to the screenshot in the source report, used to locate the\n * original bytes when copying them to the exported name. Absent for in-memory\n * screenshots, which carry their data in `base64Data` instead.\n */\n sourceRef?: ScreenshotRef;\n executionIndex: number;\n taskIndex: number;\n /** Populated when screenshot data is available in memory (e.g. browser context). */\n base64Data?: string;\n}\n\nexport interface ExecutionMarkdownOptions {\n screenshotBaseDir?: string;\n /** Resolve the exported asset name without changing observation labels. */\n resolveScreenshotFileName?: (attachment: MarkdownAttachment) => string;\n}\n\nexport interface ReportMarkdownOptions\n extends Pick<ExecutionMarkdownOptions, 'resolveScreenshotFileName'> {\n /** Used only when the report has no recorded task timestamps. */\n wallTimeFallbackMs?: number;\n}\n\nexport interface ExecutionMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nexport interface ReportMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nfunction toExecutionDump(\n execution: ExecutionDump | IExecutionDump,\n): IExecutionDump {\n if (!execution || typeof execution !== 'object') {\n throw new Error('executionToMarkdown: execution is required');\n }\n\n if (!Array.isArray(execution.tasks)) {\n throw new Error('executionToMarkdown: execution.tasks must be an array');\n }\n\n if (!execution.name) {\n throw new Error('executionToMarkdown: execution.name is required');\n }\n\n return execution;\n}\n\nfunction toReportDump(\n report: ReportActionDump | IReportActionDump,\n): IReportActionDump {\n if (!report || typeof report !== 'object') {\n throw new Error('reportToMarkdown: report is required');\n }\n\n if (!Array.isArray(report.executions)) {\n throw new Error('reportToMarkdown: report.executions must be an array');\n }\n\n return report;\n}\n\nfunction formatTime(ts?: number): string {\n if (typeof ts !== 'number' || Number.isNaN(ts)) {\n return 'N/A';\n }\n return new Date(ts).toISOString();\n}\n\nfunction formatValue(value: unknown): string {\n if (value === undefined || value === null || value === '') {\n return 'N/A';\n }\n if (typeof value === 'number') {\n return Number.isFinite(value) ? String(value) : 'N/A';\n }\n return String(value);\n}\n\nfunction escapeTableCell(value: unknown): string {\n return formatValue(value).replace(/\\|/g, '\\\\|').replace(/\\n/g, '<br>');\n}\n\nfunction markdownTable(headers: string[], rows: unknown[][]): string[] {\n return [\n `| ${headers.map(escapeTableCell).join(' | ')} |`,\n `| ${headers.map(() => '---').join(' | ')} |`,\n ...rows.map((row) => `| ${row.map(escapeTableCell).join(' | ')} |`),\n ];\n}\n\nfunction modelBriefRows(modelBriefs: ModelBrief[]): unknown[][] {\n return modelBriefs.map((brief) => [\n brief.intent || 'default',\n brief.name || 'N/A',\n brief.modelDescription || 'N/A',\n ]);\n}\n\nfunction modelRowsFromUsage(report: IReportActionDump): unknown[][] {\n const rows = new Map<string, unknown[]>();\n const addUsage = (source: string, usage?: AIUsageInfo) => {\n if (!hasUsage(usage)) return;\n const intent = usage.intent || source;\n const model = usage.model_name || usage.response_model_name;\n const description = usage.model_description;\n const key = `${intent}\\0${model || ''}\\0${description || ''}`;\n if (rows.has(key)) {\n return;\n }\n rows.set(key, [intent, model || 'N/A', description || 'N/A']);\n };\n\n for (const execution of report.executions) {\n for (const task of execution.tasks) {\n const taskWithUsage = task as ExecutionTaskWithExtraUsage;\n addUsage('main', taskWithUsage.usage);\n addUsage('searchArea', taskWithUsage.searchAreaUsage);\n }\n }\n\n return Array.from(rows.values());\n}\n\nfunction usageValue(usage: AIUsageInfo | undefined, key: string): number {\n const value = usage?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n\nfunction hasUsage(usage: AIUsageInfo | undefined): usage is AIUsageInfo {\n if (!usage) return false;\n return [\n 'intent',\n 'model_name',\n 'model_description',\n 'prompt_tokens',\n 'cached_input',\n 'completion_tokens',\n 'total_tokens',\n 'time_cost',\n 'request_id',\n ].some((key) => usage[key] !== undefined && usage[key] !== null);\n}\n\nfunction usageTotalTokens(usage: AIUsageInfo): number {\n const explicitTotal = usageValue(usage, 'total_tokens');\n if (explicitTotal > 0) {\n return explicitTotal;\n }\n return (\n usageValue(usage, 'prompt_tokens') + usageValue(usage, 'completion_tokens')\n );\n}\n\nfunction usageRowsForTask(task: ExecutionTask): unknown[][] {\n const taskWithUsage = task as ExecutionTaskWithExtraUsage;\n const rows: unknown[][] = [];\n const appendUsage = (source: string, usage?: AIUsageInfo) => {\n if (!hasUsage(usage)) return;\n rows.push([\n source,\n usage.intent,\n usage.model_name || usage.response_model_name,\n usage.model_description,\n usageValue(usage, 'prompt_tokens'),\n usageValue(usage, 'cached_input'),\n usageValue(usage, 'completion_tokens'),\n usageTotalTokens(usage),\n usageValue(usage, 'time_cost'),\n usage.request_id,\n ]);\n };\n\n appendUsage('main', taskWithUsage.usage);\n appendUsage('searchArea', taskWithUsage.searchAreaUsage);\n return rows;\n}\n\nfunction sanitizeJsonForMarkdown(\n value: unknown,\n seen = new WeakSet<object>(),\n): unknown {\n if (typeof value === 'string') {\n if (\n screenshotDataUrlPattern.test(value) ||\n value.length > 1_000_000 ||\n /^[a-zA-Z0-9+/=\\s]{100000,}$/.test(value)\n ) {\n return `[omitted image/base64 string, length=${value.length}]`;\n }\n if (value.length > jsonContextMaxStringLength) {\n return `${value.slice(0, jsonContextMaxStringLength)}\\n[truncated, original length=${value.length}]`;\n }\n return value;\n }\n\n if (\n value === null ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n\n if (typeof value === 'function') {\n return '[omitted function]';\n }\n\n if (typeof value !== 'object' || value === undefined) {\n return undefined;\n }\n\n if (seen.has(value)) {\n return '[omitted circular reference]';\n }\n seen.add(value);\n\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeJsonForMarkdown(item, seen));\n }\n\n const input = value as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(input)) {\n if (entry === undefined || typeof entry === 'function') {\n continue;\n }\n if (\n typeof entry === 'string' &&\n /(^|_|-)(base64|screenshotBase64|imageBase64)$/i.test(key)\n ) {\n output[key] = `[omitted base64 string, length=${entry.length}]`;\n continue;\n }\n output[key] = sanitizeJsonForMarkdown(entry, seen);\n }\n seen.delete(value);\n return output;\n}\n\nfunction appendJsonSection(\n lines: string[],\n title: string,\n value: unknown,\n): void {\n if (value === undefined || value === null || value === '') {\n return;\n }\n const sanitized = sanitizeJsonForMarkdown(value);\n if (sanitized === undefined) {\n return;\n }\n\n lines.push('', `### ${title}`, '', '```json');\n lines.push(JSON.stringify(sanitized, null, 2));\n lines.push('```');\n}\n\nfunction appendTextSection(\n lines: string[],\n title: string,\n value: unknown,\n): void {\n if (typeof value !== 'string' || !value.trim()) {\n return;\n }\n lines.push('', `### ${title}`, '', '```text');\n lines.push(value.replace(/```/g, '` ` `'));\n lines.push('```');\n}\n\nfunction resolveTaskTiming(task: ExecutionTask): {\n start?: number;\n end?: number;\n cost?: number;\n} {\n const timing = task.timing;\n if (!timing) {\n return {};\n }\n\n const start = timing.start ?? timing.callAiStart ?? timing.callActionStart;\n const end =\n timing.end ??\n timing.callAiEnd ??\n timing.callActionEnd ??\n timing.captureAfterCallingSnapshotEnd;\n const cost =\n timing.cost ??\n (typeof start === 'number' && typeof end === 'number'\n ? end - start\n : undefined);\n\n return { start, end, cost };\n}\n\nfunction safeTaskParam(task: ExecutionTask): string {\n const readable = paramStr(task);\n if (readable) {\n return readable;\n }\n\n if (task.type === 'Insight') {\n return extractInsightParam((task as any).param).content;\n }\n\n return '';\n}\n\nfunction formatSize(\n size?: { width?: number; height?: number } | null,\n): string | undefined {\n if (\n !size ||\n typeof size.width !== 'number' ||\n typeof size.height !== 'number' ||\n Number.isNaN(size.width) ||\n Number.isNaN(size.height)\n ) {\n return undefined;\n }\n\n return `${size.width} x ${size.height}`;\n}\n\nfunction extractLocateCenter(\n task: ExecutionTask,\n): [number, number] | undefined {\n const outputCenter = (task.output as { element?: { center?: unknown } })\n ?.element?.center;\n if (\n Array.isArray(outputCenter) &&\n outputCenter.length >= 2 &&\n typeof outputCenter[0] === 'number' &&\n typeof outputCenter[1] === 'number'\n ) {\n return [outputCenter[0], outputCenter[1]];\n }\n\n const paramLocateCenter = (task.param as { locate?: { center?: unknown } })\n ?.locate?.center;\n if (\n Array.isArray(paramLocateCenter) &&\n paramLocateCenter.length >= 2 &&\n typeof paramLocateCenter[0] === 'number' &&\n typeof paramLocateCenter[1] === 'number'\n ) {\n return [paramLocateCenter[0], paramLocateCenter[1]];\n }\n\n const paramCenter = (task.param as { center?: unknown })?.center;\n if (\n Array.isArray(paramCenter) &&\n paramCenter.length >= 2 &&\n typeof paramCenter[0] === 'number' &&\n typeof paramCenter[1] === 'number'\n ) {\n return [paramCenter[0], paramCenter[1]];\n }\n\n return undefined;\n}\n\nfunction tryExtractBase64(screenshot: unknown): string | undefined {\n if (typeof screenshot === 'string') {\n const trimmedScreenshot = screenshot.trim();\n const dataUrlMatch = trimmedScreenshot.match(screenshotDataUrlPattern);\n if (dataUrlMatch) {\n const format = dataUrlMatch[1].toLowerCase() === 'jpg' ? 'jpeg' : 'png';\n const base64Body = dataUrlMatch[2].replace(/\\s/g, '');\n if (!base64Body) {\n return undefined;\n }\n return `data:image/${format};base64,${base64Body}`;\n }\n\n if (\n trimmedScreenshot.startsWith('data:') ||\n !rawBase64BodyPattern.test(trimmedScreenshot)\n ) {\n return undefined;\n }\n\n const base64Body = trimmedScreenshot.replace(/\\s/g, '');\n return base64Body ? `data:image/png;base64,${base64Body}` : undefined;\n }\n\n if (!screenshot || typeof screenshot !== 'object') return undefined;\n const s = screenshot as Record<string, unknown>;\n if (typeof s.base64 === 'string' && s.base64.length > 0) {\n return s.base64;\n }\n return undefined;\n}\n\nfunction restoredSourceRef(screenshot: unknown): ScreenshotRef | undefined {\n if (!screenshot || typeof screenshot !== 'object') {\n return undefined;\n }\n\n const sourceRef = (screenshot as Record<string, unknown>).sourceRef;\n return normalizeScreenshotRef(sourceRef) ?? undefined;\n}\n\nfunction screenshotAttachment(\n screenshot: unknown,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n options?: {\n fallbackIdSuffix?: string;\n label?: string;\n resolveScreenshotFileName?: ExecutionMarkdownOptions['resolveScreenshotFileName'];\n },\n): { markdown: string; attachment: MarkdownAttachment } {\n const markdownLabel = options?.label || `task-${taskIndex + 1}`;\n const result = (attachment: MarkdownAttachment) => {\n attachment.suggestedFileName =\n options?.resolveScreenshotFileName?.(attachment) ??\n attachment.suggestedFileName;\n return {\n markdown: `\\n![${markdownLabel}](${screenshotBaseDir}/${attachment.suggestedFileName})`,\n attachment,\n };\n };\n\n if (ScreenshotItem.isScreenshotItem(screenshot)) {\n const ext = screenshot.extension;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${screenshot.id}.${ext}`;\n return result({\n id: screenshot.id,\n suggestedFileName,\n mimeType: `image/${ext === 'jpeg' ? 'jpeg' : 'png'}`,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n });\n }\n\n const ref = normalizeScreenshotRef(screenshot);\n if (ref) {\n const ext = imageRefFileExtension(ref);\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${ref.id}.${ext}`;\n return result({\n id: ref.id,\n suggestedFileName,\n sourceRef: ref,\n mimeType: ref.mimeType,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n });\n }\n\n const sourceRef = restoredSourceRef(screenshot);\n if (sourceRef) {\n const ext = imageRefFileExtension(sourceRef);\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${sourceRef.id}.${ext}`;\n return result({\n id: sourceRef.id,\n suggestedFileName,\n sourceRef,\n mimeType: sourceRef.mimeType,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n });\n }\n\n const base64 = tryExtractBase64(screenshot);\n if (base64) {\n const ext = base64.startsWith('data:image/jpeg') ? 'jpeg' : 'png';\n const idSuffix = options?.fallbackIdSuffix\n ? `-${options.fallbackIdSuffix}`\n : '';\n const id = `restored-${executionIndex + 1}-${taskIndex + 1}${idSuffix}`;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${id}.${ext}`;\n return result({\n id,\n suggestedFileName,\n mimeType: `image/${ext}`,\n executionIndex,\n taskIndex,\n base64Data: base64,\n });\n }\n\n throw new Error(\n `executionToMarkdown: missing screenshot for execution #${executionIndex + 1} task #${taskIndex + 1}`,\n );\n}\n\nfunction imageLabelSuffix(value: string): string {\n return (\n value\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '') || 'screenshot'\n );\n}\n\nfunction screenshotsMarkdownSection(\n task: ExecutionTask,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n resolveScreenshotFileName?: ExecutionMarkdownOptions['resolveScreenshotFileName'],\n): { lines: string[]; attachments: MarkdownAttachment[] } {\n const lines: string[] = ['', '### Screenshots'];\n const attachments: MarkdownAttachment[] = [];\n let screenshotIndex = 0;\n\n if (task.uiContext?.screenshot) {\n const imageResult = screenshotAttachment(\n task.uiContext.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n {\n fallbackIdSuffix: 'ui-context',\n label: `task-${taskIndex + 1}-ui-context`,\n resolveScreenshotFileName,\n },\n );\n const time = resolveTaskTiming(task);\n screenshotIndex += 1;\n lines.push(\n `- #${screenshotIndex} type=ui-context, ts=${formatTime(time.start)}, timing=ui-context, id=${imageResult.attachment.id}`,\n );\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n }\n\n task.recorder?.forEach((item, recorderIndex) => {\n if (!item.screenshot) {\n return;\n }\n\n const descriptionText = item.description\n ? `, description=${item.description}`\n : '';\n const timing = item.timing || 'N/A';\n screenshotIndex += 1;\n const imageResult = screenshotAttachment(\n item.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n {\n fallbackIdSuffix: `recorder-${recorderIndex + 1}`,\n label: `task-${taskIndex + 1}-${imageLabelSuffix(timing)}`,\n resolveScreenshotFileName,\n },\n );\n\n lines.push(\n `- #${screenshotIndex} type=${item.type}, ts=${formatTime(item.ts)}, timing=${timing}${descriptionText}, id=${imageResult.attachment.id}`,\n );\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n });\n\n return screenshotIndex > 0\n ? { lines, attachments }\n : { lines: [], attachments };\n}\n\nfunction renderExecution(\n executionRaw: ExecutionDump | IExecutionDump,\n executionIndex: number,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n const execution = toExecutionDump(executionRaw);\n const screenshotBaseDir = options?.screenshotBaseDir ?? './screenshots';\n\n const lines: string[] = [];\n const attachments: MarkdownAttachment[] = [];\n\n lines.push(`# ${execution.name}`);\n if (execution.description) {\n lines.push('', execution.description);\n }\n\n lines.push('', `- Execution start: ${formatTime(execution.logTime)}`);\n lines.push(`- Task count: ${execution.tasks.length}`);\n if (execution.aiActContext) {\n appendTextSection(lines, 'AI Action Context', execution.aiActContext);\n }\n\n execution.tasks.forEach((task, taskIndex) => {\n const title = typeStr(task);\n const detail = safeTaskParam(task);\n const time = resolveTaskTiming(task);\n\n lines.push(\n '',\n `## ${taskIndex + 1}. ${title}${detail ? ` - ${detail}` : ''}`,\n );\n lines.push(`- Task ID: ${task.taskId || 'N/A'}`);\n lines.push(`- Type: ${task.type || 'N/A'}`);\n lines.push(`- SubType: ${task.subType || 'N/A'}`);\n lines.push(`- Status: ${task.status || 'unknown'}`);\n lines.push(`- Start: ${formatTime(time.start)}`);\n lines.push(`- End: ${formatTime(time.end)}`);\n lines.push(\n `- Cost(ms): ${typeof time.cost === 'number' ? time.cost : 'N/A'}`,\n );\n lines.push(\n `- Screen size: ${formatSize(task.uiContext?.shotSize) || 'N/A'}`,\n );\n\n if (task.subType === 'Locate') {\n const locateCenter = extractLocateCenter(task);\n if (locateCenter) {\n lines.push(`- Locate center: (${locateCenter[0]}, ${locateCenter[1]})`);\n }\n }\n\n if (task.errorMessage) {\n lines.push(`- Error: ${task.errorMessage}`);\n }\n\n const usageRows = usageRowsForTask(task);\n if (usageRows.length) {\n lines.push('', '### Model Usage');\n lines.push(\n ...markdownTable(\n [\n 'Source',\n 'Intent',\n 'Model',\n 'Description',\n 'Prompt Tokens',\n 'Cached Input',\n 'Completion Tokens',\n 'Total Tokens',\n 'Model Call Time(ms)',\n 'Request ID',\n ],\n usageRows,\n ),\n );\n }\n\n appendJsonSection(lines, 'Param', task.param);\n appendJsonSection(lines, 'Output', task.output);\n appendJsonSection(lines, 'Log', task.log);\n appendTextSection(lines, 'Thought', task.thought);\n appendTextSection(\n lines,\n 'Reasoning Content',\n (task as ExecutionTaskWithExtraUsage).reasoning_content,\n );\n\n const screenshotsSection = screenshotsMarkdownSection(\n task,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n options?.resolveScreenshotFileName,\n );\n if (screenshotsSection.lines.length) {\n lines.push(...screenshotsSection.lines);\n attachments.push(...screenshotsSection.attachments);\n }\n });\n\n return {\n markdown: lines.join('\\n'),\n attachments,\n };\n}\n\nexport function executionToMarkdown(\n execution: ExecutionDump | IExecutionDump,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n return renderExecution(execution, 0, options);\n}\n\nexport function reportToMarkdown(\n report: ReportActionDump | IReportActionDump,\n options?: ReportMarkdownOptions,\n): ReportMarkdownResult {\n const reportDump = toReportDump(report);\n\n const executionResults = reportDump.executions.map((execution, index) => {\n const rendered = renderExecution(execution, index, options);\n return {\n markdown: rendered.markdown,\n attachments: rendered.attachments,\n };\n });\n\n const attachments = executionResults.flatMap((item) => item.attachments);\n const reportSummary = collectReportSummary(reportDump, options);\n const modelRows = reportDump.modelBriefs?.length\n ? modelBriefRows(reportDump.modelBriefs)\n : modelRowsFromUsage(reportDump);\n\n const modelInfoLines = [\n '\\n## Model Info',\n ...(modelRows.length\n ? markdownTable(['Intent', 'Model', 'Description'], modelRows)\n : ['- No model metadata recorded.']),\n ];\n\n const elapsedTimeDescription =\n reportSummary.timing.wallTimeSource === 'task-timestamps'\n ? 'Total span from the first recorded task start to the last recorded task end, including model calls, actions, waits, and gaps.'\n : reportSummary.timing.wallTimeSource === 'fallback'\n ? 'Total elapsed duration reported by the enclosing test runner because task timestamps were unavailable.'\n : 'The total elapsed span is unavailable because the report has no recorded task timestamps.';\n const timingSummaryLines = [\n '\\n## Timing Summary',\n ...markdownTable(\n ['Metric', 'Duration(ms)', 'Definition'],\n [\n [\n 'Elapsed Time',\n reportSummary.timing.wallTimeMs ?? 'N/A',\n elapsedTimeDescription,\n ],\n [\n 'Model Call Time',\n reportSummary.timing.modelCallTimeMs,\n 'Total duration of all recorded model calls.',\n ],\n ],\n ),\n ];\n\n const tokenSummaryLines = [\n '\\n## Token Usage Summary',\n ...(reportSummary.models.length\n ? markdownTable(\n [\n 'Model',\n 'Calls',\n 'Prompt Tokens',\n 'Cached Input',\n 'Completion Tokens',\n 'Total Tokens',\n 'Model Call Time(ms)',\n ],\n reportSummary.models.map((model) => [\n model.modelName,\n model.callCount,\n model.promptTokens,\n model.cachedInputTokens,\n model.completionTokens,\n model.totalTokens,\n model.modelCallTimeMs,\n ]),\n )\n : ['- No token usage recorded.']),\n ];\n\n const header = [\n `# ${reportDump.groupName}`,\n reportDump.groupDescription ? `\\n${reportDump.groupDescription}` : '',\n `\\n- SDK Version: ${reportDump.sdkVersion}`,\n reportDump.deviceType ? `- Device Type: ${reportDump.deviceType}` : '',\n `- Execution count: ${reportDump.executions.length}`,\n ...modelInfoLines,\n ...timingSummaryLines,\n ...tokenSummaryLines,\n ]\n .filter(Boolean)\n .join('\\n');\n\n return {\n markdown: `${header}\\n\\n${executionResults.map((item) => item.markdown).join('\\n\\n---\\n\\n')}`,\n attachments,\n };\n}\n"],"names":["screenshotDataUrlPattern","rawBase64BodyPattern","jsonContextMaxStringLength","toExecutionDump","execution","Error","Array","toReportDump","report","formatTime","ts","Number","Date","formatValue","value","String","escapeTableCell","markdownTable","headers","rows","row","modelBriefRows","modelBriefs","brief","modelRowsFromUsage","Map","addUsage","source","usage","hasUsage","intent","model","description","key","task","taskWithUsage","usageValue","undefined","usageTotalTokens","explicitTotal","usageRowsForTask","appendUsage","sanitizeJsonForMarkdown","seen","WeakSet","item","input","output","entry","Object","appendJsonSection","lines","title","sanitized","JSON","appendTextSection","resolveTaskTiming","timing","start","end","cost","safeTaskParam","readable","paramStr","extractInsightParam","formatSize","size","extractLocateCenter","outputCenter","paramLocateCenter","paramCenter","tryExtractBase64","screenshot","trimmedScreenshot","dataUrlMatch","format","base64Body","s","restoredSourceRef","sourceRef","normalizeScreenshotRef","screenshotAttachment","screenshotBaseDir","executionIndex","taskIndex","options","markdownLabel","result","attachment","ScreenshotItem","ext","suggestedFileName","ref","imageRefFileExtension","base64","idSuffix","id","imageLabelSuffix","screenshotsMarkdownSection","resolveScreenshotFileName","attachments","screenshotIndex","imageResult","time","recorderIndex","descriptionText","renderExecution","executionRaw","typeStr","detail","locateCenter","usageRows","screenshotsSection","executionToMarkdown","reportToMarkdown","reportDump","executionResults","index","rendered","reportSummary","collectReportSummary","modelRows","modelInfoLines","elapsedTimeDescription","timingSummaryLines","tokenSummaryLines","header","Boolean"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
- import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
3
3
  import { basename, dirname, join, resolve, sep } from "node:path";
4
4
  import { getMidsceneRunSubDir } from "@midscene/shared/common";
5
5
  import { antiEscapeScriptTag, logMsg, uuid } from "@midscene/shared/utils";
6
6
  import { getReportFileName } from "./agent/report-file-name.mjs";
7
7
  import { DATA_SCREENSHOT_MODE_ATTR, extractLastDumpScriptSync, generateAgentReportComment, streamDumpScriptsSync } from "./dump/html-utils.mjs";
8
- import { imageRefFileExtension, normalizeStoredImageRef } from "./dump/image-reference.mjs";
8
+ import { normalizeStoredImageRef } from "./dump/image-reference.mjs";
9
9
  import { readReportActionDump, readReportArtifactManifest, readReportArtifactManifestAsync, readReportDumpScripts, readReportRunnerDump, streamReportDumpScripts } from "./dump/report-artifact-reader.mjs";
10
- import { parseBase64ImageDataUrl, resolveImageSource } from "./dump/screenshot-store.mjs";
10
+ import { ReportImageExporter } from "./dump/report-image-export.mjs";
11
11
  import { ExecutionRecorder } from "./dump/execution-recorder.mjs";
12
12
  import { ReportArtifactStore } from "./dump/report-artifact-store.mjs";
13
13
  import { ReportSession } from "./dump/report-session.mjs";
@@ -640,7 +640,7 @@ function collectDedupedExecutions(htmlPath) {
640
640
  executions
641
641
  };
642
642
  }
643
- function externalizeImagesInExecution(execution, opts) {
643
+ function externalizeImagesInExecution(execution, images) {
644
644
  const visit = (node)=>{
645
645
  if (Array.isArray(node)) {
646
646
  for (const item of node)visit(item);
@@ -649,20 +649,8 @@ function externalizeImagesInExecution(execution, opts) {
649
649
  if ('object' != typeof node || null === node) return;
650
650
  const ref = normalizeStoredImageRef(node);
651
651
  if (ref) {
652
- const ext = imageRefFileExtension(ref);
653
- const fileName = `${ref.id}.${ext}`;
652
+ const fileName = images.write(ref);
654
653
  const relativePath = `./screenshots/${fileName}`;
655
- const absolutePath = join(opts.screenshotsDir, fileName);
656
- if (!opts.writtenFiles.has(fileName)) {
657
- const resolved = resolveImageSource(ref, {
658
- reportPath: opts.htmlPath
659
- });
660
- if ('data-uri' === resolved.type) {
661
- const { rawBase64 } = parseBase64ImageDataUrl(resolved.dataUri);
662
- writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));
663
- } else copyFileSync(resolved.filePath, absolutePath);
664
- opts.writtenFiles.add(fileName);
665
- }
666
654
  ref.storage = 'file';
667
655
  ref.path = relativePath;
668
656
  return;
@@ -681,16 +669,12 @@ function splitReportHtmlByExecution(options) {
681
669
  recursive: true
682
670
  });
683
671
  const executionJsonFiles = [];
684
- const writtenScreenshotFiles = new Set();
672
+ const images = new ReportImageExporter(htmlPath, screenshotsDir);
685
673
  const { baseDump, executions } = collectDedupedExecutions(htmlPath);
686
674
  let fileIndex = 0;
687
675
  for (const execution of executions){
688
676
  fileIndex += 1;
689
- externalizeImagesInExecution(execution, {
690
- htmlPath,
691
- screenshotsDir,
692
- writtenFiles: writtenScreenshotFiles
693
- });
677
+ externalizeImagesInExecution(execution, images);
694
678
  const singleExecutionDump = new ReportActionDump({
695
679
  sdkVersion: baseDump.sdkVersion,
696
680
  groupName: baseDump.groupName,
@@ -707,7 +691,7 @@ function splitReportHtmlByExecution(options) {
707
691
  }
708
692
  return {
709
693
  executionJsonFiles,
710
- screenshotFiles: Array.from(writtenScreenshotFiles).sort().map((fileName)=>join(screenshotsDir, fileName))
694
+ screenshotFiles: images.files
711
695
  };
712
696
  }
713
697
  export { ExecutionRecorder, ReportArtifactStore, ReportMergingTool, ReportSession, TestRunReportAssembler, calculateTestRunHealth, classifyTestRunCase, collectDedupedExecutions, dedupeExecutionsKeepLatest, exportReportArtifact, isDirectoryModeReport, readReportActionDump, readReportArtifactManifest, readReportArtifactManifestAsync, readReportDumpScripts, readReportRunnerDump, removeReportArtifact, splitReportHtmlByExecution, streamReportDumpScripts };