@aiscene/shared 8.0.3 → 8.0.5

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 (105) hide show
  1. package/dist/es/agent-tools/agent-behavior-init-args.mjs +44 -0
  2. package/dist/es/agent-tools/base-tools.mjs +163 -0
  3. package/dist/es/agent-tools/chrome-path.mjs +50 -0
  4. package/dist/es/agent-tools/cli-report-session.mjs +78 -0
  5. package/dist/es/agent-tools/error-formatter.mjs +106 -0
  6. package/dist/es/agent-tools/index.mjs +9 -0
  7. package/dist/es/agent-tools/init-arg-utils.mjs +38 -0
  8. package/dist/es/agent-tools/observation-artifact.mjs +5 -0
  9. package/dist/es/agent-tools/observation-record.mjs +248 -0
  10. package/dist/es/agent-tools/tool-defaults.mjs +54 -0
  11. package/dist/es/agent-tools/tool-generator.mjs +529 -0
  12. package/dist/es/agent-tools/types.mjs +3 -0
  13. package/dist/es/agent-tools/user-prompt.mjs +66 -0
  14. package/dist/es/cli/interrupt.mjs +116 -0
  15. package/dist/es/cli/record-command.mjs +130 -0
  16. package/dist/es/cli/screenshot-file.mjs +24 -0
  17. package/dist/es/cli/verbose-ai-act.mjs +230 -0
  18. package/dist/es/cli/verbose-screenshot.mjs +134 -0
  19. package/dist/es/cli/verbose.mjs +404 -0
  20. package/dist/es/env/parse-model-config.mjs +1 -1
  21. package/dist/es/env/types.mjs +18 -3
  22. package/dist/es/img/index.mjs +3 -3
  23. package/dist/es/img/info.mjs +45 -1
  24. package/dist/es/img/photon-loader.mjs +4 -0
  25. package/dist/es/img/transform.mjs +113 -3
  26. package/dist/es/recorder.mjs +249 -0
  27. package/dist/lib/agent-tools/agent-behavior-init-args.js +87 -0
  28. package/dist/lib/agent-tools/base-tools.js +197 -0
  29. package/dist/lib/agent-tools/chrome-path.js +87 -0
  30. package/dist/lib/agent-tools/cli-report-session.js +121 -0
  31. package/dist/lib/agent-tools/error-formatter.js +149 -0
  32. package/dist/lib/agent-tools/index.js +114 -0
  33. package/dist/lib/agent-tools/init-arg-utils.js +78 -0
  34. package/dist/lib/agent-tools/observation-artifact.js +42 -0
  35. package/dist/lib/agent-tools/observation-record.js +297 -0
  36. package/dist/lib/agent-tools/tool-defaults.js +97 -0
  37. package/dist/lib/agent-tools/tool-generator.js +569 -0
  38. package/dist/lib/agent-tools/types.js +40 -0
  39. package/dist/lib/agent-tools/user-prompt.js +103 -0
  40. package/dist/lib/cli/interrupt.js +156 -0
  41. package/dist/lib/cli/record-command.js +164 -0
  42. package/dist/lib/cli/screenshot-file.js +58 -0
  43. package/dist/lib/cli/verbose-ai-act.js +273 -0
  44. package/dist/lib/cli/verbose-screenshot.js +177 -0
  45. package/dist/lib/cli/verbose.js +465 -0
  46. package/dist/lib/env/parse-model-config.js +1 -1
  47. package/dist/lib/env/types.js +32 -2
  48. package/dist/lib/img/index.js +29 -5
  49. package/dist/lib/img/info.js +48 -1
  50. package/dist/lib/img/photon-loader.js +38 -0
  51. package/dist/lib/img/transform.js +135 -4
  52. package/dist/lib/recorder.js +307 -0
  53. package/dist/types/agent-tools/agent-behavior-init-args.d.ts +17 -0
  54. package/dist/types/agent-tools/base-tools.d.ts +158 -0
  55. package/dist/types/agent-tools/chrome-path.d.ts +2 -0
  56. package/dist/types/agent-tools/cli-report-session.d.ts +12 -0
  57. package/dist/types/agent-tools/error-formatter.d.ts +30 -0
  58. package/dist/types/agent-tools/index.d.ts +9 -0
  59. package/dist/types/agent-tools/init-arg-utils.d.ts +13 -0
  60. package/dist/types/agent-tools/observation-artifact.d.ts +10 -0
  61. package/dist/types/agent-tools/observation-record.d.ts +38 -0
  62. package/dist/types/agent-tools/tool-defaults.d.ts +63 -0
  63. package/dist/types/agent-tools/tool-generator.d.ts +13 -0
  64. package/dist/types/agent-tools/types.d.ts +213 -0
  65. package/dist/types/agent-tools/user-prompt.d.ts +13 -0
  66. package/dist/types/cli/interrupt.d.ts +49 -0
  67. package/dist/types/cli/record-command.d.ts +3 -0
  68. package/dist/types/cli/screenshot-file.d.ts +10 -0
  69. package/dist/types/cli/verbose-ai-act.d.ts +44 -0
  70. package/dist/types/cli/verbose-screenshot.d.ts +10 -0
  71. package/dist/types/cli/verbose.d.ts +40 -0
  72. package/dist/types/env/types.d.ts +16 -7
  73. package/dist/types/img/index.d.ts +2 -2
  74. package/dist/types/img/info.d.ts +2 -0
  75. package/dist/types/img/photon-loader.d.ts +2 -0
  76. package/dist/types/img/transform.d.ts +22 -2
  77. package/dist/types/mcp/types.d.ts +1 -0
  78. package/dist/types/recorder.d.ts +113 -0
  79. package/package.json +1 -1
  80. package/src/agent-tools/agent-behavior-init-args.ts +109 -0
  81. package/src/agent-tools/base-tools.ts +399 -0
  82. package/src/agent-tools/chrome-path.ts +74 -0
  83. package/src/agent-tools/cli-report-session.ts +130 -0
  84. package/src/agent-tools/error-formatter.ts +177 -0
  85. package/src/agent-tools/index.ts +9 -0
  86. package/src/agent-tools/init-arg-utils.ts +105 -0
  87. package/src/agent-tools/observation-artifact.ts +29 -0
  88. package/src/agent-tools/observation-record.ts +331 -0
  89. package/src/agent-tools/tool-defaults.ts +119 -0
  90. package/src/agent-tools/tool-generator.ts +866 -0
  91. package/src/agent-tools/types.ts +250 -0
  92. package/src/agent-tools/user-prompt.ts +102 -0
  93. package/src/cli/interrupt.ts +207 -0
  94. package/src/cli/record-command.ts +177 -0
  95. package/src/cli/screenshot-file.ts +61 -0
  96. package/src/cli/verbose-ai-act.ts +387 -0
  97. package/src/cli/verbose-screenshot.ts +269 -0
  98. package/src/cli/verbose.ts +753 -0
  99. package/src/env/types.ts +30 -2
  100. package/src/img/index.ts +12 -0
  101. package/src/img/info.ts +61 -0
  102. package/src/img/photon-loader.ts +5 -0
  103. package/src/img/transform.ts +262 -3
  104. package/src/mcp/types.ts +2 -0
  105. package/src/recorder.ts +625 -0
@@ -7,6 +7,7 @@ import { getDebug } from "../logger.mjs";
7
7
  import { ifInNode } from "../utils.mjs";
8
8
  import get_photon from "./get-photon.mjs";
9
9
  import get_sharp from "./get-sharp.mjs";
10
+ import { encodedImageInfoOfBuffer } from "./info.mjs";
10
11
  const imgDebug = getDebug('img');
11
12
  async function saveBase64Image(options) {
12
13
  const { base64Data, outputPath } = options;
@@ -69,7 +70,116 @@ async function resizeAndConvertImgBuffer(inputFormat, inputData, newSize) {
69
70
  format: 'jpeg'
70
71
  };
71
72
  }
72
- const createImgBase64ByFormat = (format, body)=>`data:image/${format};base64,${body}`;
73
+ const rawBase64BodyPattern = /^[A-Za-z0-9+/]+={0,2}$/;
74
+ const base64ImageDataUrlPattern = /^data:image\/(?:png|jpe?g);base64,[A-Za-z0-9+/=\s]+$/i;
75
+ const supportedScreenshotDataUriPattern = /^data:image\/(png|jpe?g);base64,([A-Za-z0-9+/=\s]+)$/i;
76
+ function normalizeBase64Body(body) {
77
+ return body.replace(/\s/g, '');
78
+ }
79
+ function detectImageMimeTypeFromBuffer(buffer) {
80
+ if (buffer.length >= 8 && 0x89 === buffer[0] && 0x50 === buffer[1] && 0x4e === buffer[2] && 0x47 === buffer[3] && 0x0d === buffer[4] && 0x0a === buffer[5] && 0x1a === buffer[6] && 0x0a === buffer[7]) return 'image/png';
81
+ if (buffer.length >= 3 && 0xff === buffer[0] && 0xd8 === buffer[1] && 0xff === buffer[2]) return 'image/jpeg';
82
+ }
83
+ function inferBase64ImageFormat(base64Body) {
84
+ const buffer = Buffer.from(normalizeBase64Body(base64Body), 'base64');
85
+ const mimeType = detectImageMimeTypeFromBuffer(buffer);
86
+ if ('image/jpeg' === mimeType) return 'jpeg';
87
+ return 'png';
88
+ }
89
+ const createImgBase64ByFormat = (format, body)=>`data:image/${format};base64,${normalizeBase64Body(body)}`;
90
+ const normalizeScreenshotBase64 = (base64, options)=>{
91
+ const label = options?.label ?? 'screenshot base64';
92
+ const trimmedBase64 = base64.trim();
93
+ if (!trimmedBase64) throw new Error(`${label} cannot be empty`);
94
+ const dataUriMatch = trimmedBase64.match(supportedScreenshotDataUriPattern);
95
+ if (dataUriMatch) {
96
+ const imageFormat = 'jpg' === dataUriMatch[1].toLowerCase() ? 'jpeg' : dataUriMatch[1].toLowerCase();
97
+ const body = normalizeBase64Body(dataUriMatch[2]);
98
+ if (!body) throw new Error(`${label} cannot be empty`);
99
+ return createImgBase64ByFormat(imageFormat, body);
100
+ }
101
+ if (trimmedBase64.startsWith('data:')) throw new Error(`${label} must be a PNG/JPEG data URI or raw PNG base64 string`);
102
+ if (!rawBase64BodyPattern.test(normalizeBase64Body(trimmedBase64))) throw new Error(`${label} must be a PNG/JPEG data URI or raw PNG base64 string`);
103
+ return createImgBase64ByFormat('png', trimmedBase64);
104
+ };
105
+ const normalizeBase64Image = (base64)=>{
106
+ const trimmedBase64 = base64.trim();
107
+ if (base64ImageDataUrlPattern.test(trimmedBase64)) return trimmedBase64.replace(/\s/g, '');
108
+ const base64Body = normalizeBase64Body(trimmedBase64);
109
+ node_assert(base64Body, 'base64 image must include image data');
110
+ return createImgBase64ByFormat(inferBase64ImageFormat(base64Body), base64Body);
111
+ };
112
+ function assertValidJpegQuality(jpegQuality) {
113
+ if (!Number.isInteger(jpegQuality) || jpegQuality < 1 || jpegQuality > 100) throw new Error(`jpegQuality must be an integer between 1 and 100. Received: ${jpegQuality}`);
114
+ }
115
+ function assertValidImageSize(size, label) {
116
+ if (!Number.isInteger(size.width) || !Number.isInteger(size.height) || size.width <= 0 || size.height <= 0) throw new Error(`${label} width and height must be positive integers. Received width: ${size.width}, height: ${size.height}`);
117
+ }
118
+ async function convertImgBufferToJpeg(inputData, jpegQuality = 90) {
119
+ assertValidJpegQuality(jpegQuality);
120
+ if (ifInNode) try {
121
+ const Sharp = await get_sharp();
122
+ return await Sharp(inputData).jpeg({
123
+ quality: jpegQuality
124
+ }).toBuffer();
125
+ } catch (error) {
126
+ imgDebug('Sharp failed, falling back to Photon:', error);
127
+ }
128
+ const { PhotonImage } = await get_photon();
129
+ const inputBytes = new Uint8Array(inputData);
130
+ const bytesliceResult = PhotonImage.new_from_byteslice(inputBytes);
131
+ const image = bytesliceResult instanceof Promise ? await bytesliceResult : bytesliceResult;
132
+ try {
133
+ return Buffer.from(image.get_bytes_jpeg(jpegQuality));
134
+ } finally{
135
+ image.free();
136
+ }
137
+ }
138
+ async function convertBase64ImageToJpeg(inputBase64, jpegQuality = 90) {
139
+ assertValidJpegQuality(jpegQuality);
140
+ const normalized = normalizeBase64Image(inputBase64);
141
+ const { body } = parseBase64(normalized);
142
+ const imageBuffer = Buffer.from(body, 'base64');
143
+ const detectedMimeType = detectImageMimeTypeFromBuffer(imageBuffer);
144
+ if ('image/jpeg' === detectedMimeType) return createImgBase64ByFormat('jpeg', body);
145
+ if ('image/png' !== detectedMimeType) throw new Error(`inputBase64 must contain a PNG or JPEG image. Detected: ${detectedMimeType ?? 'unsupported format'}`);
146
+ const jpegBuffer = await convertImgBufferToJpeg(imageBuffer, jpegQuality);
147
+ return createImgBase64ByFormat('jpeg', jpegBuffer.toString('base64'));
148
+ }
149
+ async function resizeBase64ImageToJpeg(inputBase64, options) {
150
+ const jpegQuality = options.jpegQuality ?? 90;
151
+ assertValidJpegQuality(jpegQuality);
152
+ assertValidImageSize(options.targetSize, 'targetSize');
153
+ const normalized = normalizeBase64Image(inputBase64);
154
+ const { body, mimeType } = parseBase64(normalized);
155
+ const imageBuffer = Buffer.from(body, 'base64');
156
+ const sourceSize = encodedImageInfoOfBuffer(imageBuffer);
157
+ if (options.sourceSize) {
158
+ assertValidImageSize(options.sourceSize, 'sourceSize');
159
+ if (options.sourceSize.width !== sourceSize.width || options.sourceSize.height !== sourceSize.height) throw new Error(`sourceSize ${options.sourceSize.width}x${options.sourceSize.height} does not match encoded image dimensions ${sourceSize.width}x${sourceSize.height}`);
160
+ }
161
+ const dimensionsUnchanged = sourceSize.width === options.targetSize.width && sourceSize.height === options.targetSize.height;
162
+ if (dimensionsUnchanged) return convertBase64ImageToJpeg(normalized, jpegQuality);
163
+ const { buffer } = await resizeAndConvertImgBuffer(mimeType.split('/')[1], imageBuffer, options.targetSize);
164
+ return createImgBase64ByFormat('jpeg', buffer.toString('base64'));
165
+ }
166
+ async function constrainBase64ImageToMaxSize(inputBase64, options) {
167
+ if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) throw new Error(`maxSize must be a positive integer. Received: ${options.maxSize}`);
168
+ const normalized = normalizeBase64Image(inputBase64);
169
+ const { body } = parseBase64(normalized);
170
+ const sourceSize = encodedImageInfoOfBuffer(Buffer.from(body, 'base64'));
171
+ const largestDimension = Math.max(sourceSize.width, sourceSize.height);
172
+ if (largestDimension <= options.maxSize) return normalized;
173
+ const scale = options.maxSize / largestDimension;
174
+ return resizeBase64ImageToJpeg(normalized, {
175
+ targetSize: {
176
+ width: Math.max(1, Math.round(sourceSize.width * scale)),
177
+ height: Math.max(1, Math.round(sourceSize.height * scale))
178
+ },
179
+ sourceSize,
180
+ jpegQuality: options.jpegQuality
181
+ });
182
+ }
73
183
  async function resizeImgBase64(inputBase64, newSize) {
74
184
  const { body, mimeType } = parseBase64(inputBase64);
75
185
  const imageBuffer = Buffer.from(body, 'base64');
@@ -146,7 +256,7 @@ async function paddingToMatchBlockByBase64(imageBase64, blockSize = 28) {
146
256
  photonImage.free();
147
257
  }
148
258
  }
149
- async function cropByRect(imageBase64, rect, paddingImage) {
259
+ async function cropByRect(imageBase64, rect, paddingImage = false) {
150
260
  const { crop } = await get_photon();
151
261
  const photonImage = await photonFromBase64(imageBase64);
152
262
  const { left, top, width, height } = rect;
@@ -272,4 +382,4 @@ async function scaleImage(imageBase64, scale) {
272
382
  imageBase64: base64
273
383
  };
274
384
  }
275
- export { createImgBase64ByFormat, cropByRect, httpImg2Base64, localImg2Base64, paddingToMatchBlock, paddingToMatchBlockByBase64, parseBase64, photonFromBase64, photonToBase64, preProcessImageUrl, resizeAndConvertImgBuffer, resizeImgBase64, saveBase64Image, scaleImage, zoomForGPT4o };
385
+ export { constrainBase64ImageToMaxSize, convertBase64ImageToJpeg, convertImgBufferToJpeg, createImgBase64ByFormat, cropByRect, httpImg2Base64, inferBase64ImageFormat, localImg2Base64, normalizeBase64Image, normalizeScreenshotBase64, paddingToMatchBlock, paddingToMatchBlockByBase64, parseBase64, photonFromBase64, photonToBase64, preProcessImageUrl, resizeAndConvertImgBuffer, resizeBase64ImageToJpeg, resizeImgBase64, saveBase64Image, scaleImage, zoomForGPT4o };
@@ -0,0 +1,249 @@
1
+ const DEFAULT_MIDSCENE_RECORDER_MARKDOWN_MAX_SCREENSHOTS = 20;
2
+ function isMidsceneRecorderPendingDescription(value) {
3
+ return value?.trim() === 'AI is analyzing element...';
4
+ }
5
+ function getMidsceneRecorderSemantic(event) {
6
+ return event.semantic;
7
+ }
8
+ function getRecorderPointerActionVerb(actionType) {
9
+ switch(actionType){
10
+ case 'Tap':
11
+ return 'Tap';
12
+ case 'DoubleClick':
13
+ return 'Double click';
14
+ case 'LongPress':
15
+ return 'Long press';
16
+ case 'RightClick':
17
+ return 'Right click';
18
+ default:
19
+ return 'Click';
20
+ }
21
+ }
22
+ function getRecorderDragActionVerb(actionType) {
23
+ switch(actionType){
24
+ case 'Swipe':
25
+ return 'Swipe';
26
+ case 'DragAndDrop':
27
+ return 'Drag';
28
+ default:
29
+ return 'Drag';
30
+ }
31
+ }
32
+ function buildMidsceneRecorderReplayInstruction(event, elementDescription) {
33
+ switch(event.type){
34
+ case 'navigation':
35
+ if ('Stop' === event.actionType) return 'Stop loading the current page.';
36
+ if ('GoBack' === event.actionType) return 'Go back in the browser.';
37
+ if ('GoForward' === event.actionType) return 'Go forward in the browser.';
38
+ if ('Reload' === event.actionType) return 'Reload the current page.';
39
+ if ('NavigationChanged' === event.actionType && event.url) return `Wait for navigation to complete at \`${event.url}\`.`;
40
+ return event.url ? `Navigate to \`${event.url}\`.` : `Navigate using ${elementDescription}.`;
41
+ case 'scroll':
42
+ return event.scrollDestinationDescription ? `Scroll the page/region with description "${elementDescription}" by value "${event.value || 'down'}" until "${event.scrollDestinationDescription}" is visible.` : `Scroll the page/region with description "${elementDescription}" by value "${event.value || 'down'}".`;
43
+ case 'drag':
44
+ {
45
+ const verb = getRecorderDragActionVerb(event.actionType);
46
+ return `${verb} through the area described as "${elementDescription}".`;
47
+ }
48
+ case 'input':
49
+ return `Input "${event.value || ''}" into the element described as "${elementDescription}".`;
50
+ case 'keydown':
51
+ return `Press "${event.value || 'the recorded key'}" on the element described as "${elementDescription}".`;
52
+ default:
53
+ {
54
+ const verb = getRecorderPointerActionVerb(event.actionType);
55
+ if ('Long press' === verb) return `${verb} the element described as "${elementDescription}".`;
56
+ return `${verb} on the element described as "${elementDescription}".`;
57
+ }
58
+ }
59
+ }
60
+ function buildMidsceneRecorderActionSummary(event, elementDescription) {
61
+ switch(event.type){
62
+ case 'navigation':
63
+ if ('Stop' === event.actionType) return 'Stop page loading';
64
+ if ('GoBack' === event.actionType) return 'Go back';
65
+ if ('GoForward' === event.actionType) return 'Go forward';
66
+ if ('Reload' === event.actionType) return 'Reload page';
67
+ if ('NavigationChanged' === event.actionType && event.url) return `Wait for navigation to complete at ${event.url}`;
68
+ return event.url ? `Navigate to ${event.url}` : 'Navigate';
69
+ case 'scroll':
70
+ return event.scrollDestinationDescription ? `Scroll ${elementDescription} toward ${event.scrollDestinationDescription}` : `Scroll ${elementDescription}`;
71
+ case 'drag':
72
+ return `${getRecorderDragActionVerb(event.actionType)} ${elementDescription}`;
73
+ case 'input':
74
+ return `Input into ${elementDescription}`;
75
+ case 'keydown':
76
+ return `Press ${event.value || 'key'} on ${elementDescription}`;
77
+ default:
78
+ return `${getRecorderPointerActionVerb(event.actionType)} ${elementDescription}`;
79
+ }
80
+ }
81
+ function getMidsceneRecorderEventDescription(event) {
82
+ const semantic = getMidsceneRecorderSemantic(event);
83
+ if (semantic?.actionSummary && !isMidsceneRecorderPendingDescription(semantic.actionSummary)) return semantic.actionSummary;
84
+ if (semantic?.elementDescription && !isMidsceneRecorderPendingDescription(semantic.elementDescription)) return semantic.elementDescription;
85
+ if (semantic?.replayInstruction && !isMidsceneRecorderPendingDescription(semantic.replayInstruction)) return semantic.replayInstruction;
86
+ if ('navigation' === event.type && event.url) return `Navigate to ${event.url}`;
87
+ if (event.value) return event.actionType ? `${event.actionType} ${event.value}` : event.value;
88
+ if (event.elementRect?.x !== void 0 && event.elementRect?.y !== void 0) {
89
+ const prefix = event.actionType || event.type;
90
+ return `${prefix} (${Math.round(event.elementRect.x)}, ${Math.round(event.elementRect.y)})`;
91
+ }
92
+ return event.actionType || event.type;
93
+ }
94
+ function getMidsceneRecorderScreenshotsForLLM(events, maxScreenshots = 1) {
95
+ return selectRecorderScreenshotCandidates(getRecorderScreenshotCandidates(events), maxScreenshots).map((candidate)=>candidate.screenshot);
96
+ }
97
+ function sanitizeMidsceneRecorderFileName(value) {
98
+ return value.trim().replace(/[\\/:*?"<>|]+/g, '-').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '').toLowerCase() || 'midscene-recording';
99
+ }
100
+ function normalizeMarkdownAssetBaseDir(baseDir) {
101
+ const value = (baseDir || './screenshots').replace(/\/+$/g, '');
102
+ if (value.startsWith('./') || value.startsWith('../')) return value;
103
+ return `./${value}`;
104
+ }
105
+ function padEventIndex(index) {
106
+ return String(index + 1).padStart(3, '0');
107
+ }
108
+ function parseScreenshotDataUrl(value) {
109
+ const dataUrlMatch = value.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/);
110
+ if (dataUrlMatch) {
111
+ const mimeType = dataUrlMatch[1];
112
+ const extension = mimeType.includes('jpeg') ? 'jpg' : mimeType.split('/')[1]?.replace(/[^a-zA-Z0-9]/g, '') || 'png';
113
+ return {
114
+ dataUrl: value,
115
+ base64Data: dataUrlMatch[2],
116
+ mimeType,
117
+ extension
118
+ };
119
+ }
120
+ if (/^[a-zA-Z0-9+/=\s]+$/.test(value) && value.trim().length > 0) {
121
+ const base64Data = value.replace(/\s+/g, '');
122
+ return {
123
+ dataUrl: `data:image/png;base64,${base64Data}`,
124
+ base64Data,
125
+ mimeType: 'image/png',
126
+ extension: 'png'
127
+ };
128
+ }
129
+ }
130
+ function getRecorderEventScreenshot(event) {
131
+ return event.screenshotWithBox || event.screenshotAfter || event.screenshotBefore;
132
+ }
133
+ function hasCoordinateFallback(event) {
134
+ const semantic = getMidsceneRecorderSemantic(event);
135
+ return !semantic?.elementDescription && event.elementRect?.x !== void 0 && event.elementRect?.y !== void 0;
136
+ }
137
+ function shouldIncludeMarkdownScreenshot(event, eventIndex, lastEventIndex) {
138
+ const semantic = getMidsceneRecorderSemantic(event);
139
+ return 0 === eventIndex || eventIndex === lastEventIndex || 'navigation' === event.type || 'scroll' === event.type || 'input' === event.type || Boolean(event.screenshotWithBox) || !semantic?.elementDescription || hasCoordinateFallback(event);
140
+ }
141
+ function getRecorderScreenshotCandidatePriority(candidate, firstEventIndex, lastEventIndex) {
142
+ const event = candidate.event;
143
+ let priority = 0;
144
+ if (candidate.eventIndex === firstEventIndex) priority += 100;
145
+ if (candidate.eventIndex === lastEventIndex) priority += 95;
146
+ if ('navigation' === event.type) priority += 80;
147
+ if (event.screenshotWithBox) priority += 70;
148
+ const semantic = getMidsceneRecorderSemantic(event);
149
+ if (semantic?.source === 'heuristic' || semantic?.confidence === 'low' || semantic?.error) priority += 60;
150
+ if ('input' === event.type || 'scroll' === event.type) priority += 40;
151
+ if (!semantic?.elementDescription || hasCoordinateFallback(event)) priority += 30;
152
+ return priority;
153
+ }
154
+ function selectEvenlyDistributedCandidates(candidates, count) {
155
+ if (count <= 0) return [];
156
+ if (candidates.length <= count) return candidates;
157
+ if (1 === count) return [
158
+ candidates[Math.floor((candidates.length - 1) / 2)]
159
+ ];
160
+ return Array.from({
161
+ length: count
162
+ }, (_, index)=>{
163
+ const candidateIndex = Math.round(index * (candidates.length - 1) / (count - 1));
164
+ return candidates[candidateIndex];
165
+ });
166
+ }
167
+ function selectRecorderScreenshotCandidates(candidates, maxScreenshots) {
168
+ if (maxScreenshots <= 0 || 0 === candidates.length) return [];
169
+ if (candidates.length <= maxScreenshots) return candidates;
170
+ const selected = new Map();
171
+ const firstEventIndex = candidates[0].eventIndex;
172
+ const lastEventIndex = candidates[candidates.length - 1].eventIndex;
173
+ const addCandidate = (candidate)=>{
174
+ if (!candidate || selected.size >= maxScreenshots) return;
175
+ selected.set(candidate.eventIndex, candidate);
176
+ };
177
+ const addEvenly = (pool)=>{
178
+ const remaining = maxScreenshots - selected.size;
179
+ if (remaining <= 0) return;
180
+ const unselected = pool.filter((candidate)=>!selected.has(candidate.eventIndex));
181
+ for (const candidate of selectEvenlyDistributedCandidates(unselected, remaining))addCandidate(candidate);
182
+ };
183
+ addCandidate(candidates[0]);
184
+ addCandidate(candidates[candidates.length - 1]);
185
+ addEvenly(candidates.filter((candidate)=>getRecorderScreenshotCandidatePriority(candidate, firstEventIndex, lastEventIndex) >= 60));
186
+ addEvenly(candidates.filter((candidate)=>getRecorderScreenshotCandidatePriority(candidate, firstEventIndex, lastEventIndex) >= 40));
187
+ addEvenly(candidates);
188
+ return Array.from(selected.values()).sort((left, right)=>left.eventIndex - right.eventIndex);
189
+ }
190
+ function getRecorderScreenshotCandidates(events) {
191
+ const candidates = [];
192
+ const seenScreenshots = new Set();
193
+ const lastEventIndex = events.length - 1;
194
+ for(let eventIndex = 0; eventIndex < events.length; eventIndex += 1){
195
+ const event = events[eventIndex];
196
+ if (!shouldIncludeMarkdownScreenshot(event, eventIndex, lastEventIndex)) continue;
197
+ const screenshot = getRecorderEventScreenshot(event);
198
+ if (!(!screenshot || seenScreenshots.has(screenshot))) {
199
+ seenScreenshots.add(screenshot);
200
+ candidates.push({
201
+ event,
202
+ eventIndex,
203
+ screenshot
204
+ });
205
+ }
206
+ }
207
+ return candidates;
208
+ }
209
+ function createMidsceneRecorderMarkdownScreenshotAssets(events, options = {}) {
210
+ const baseDir = normalizeMarkdownAssetBaseDir(options.baseDir);
211
+ const maxScreenshots = options.maxScreenshots ?? DEFAULT_MIDSCENE_RECORDER_MARKDOWN_MAX_SCREENSHOTS;
212
+ const candidates = [];
213
+ for (const candidate of getRecorderScreenshotCandidates(events)){
214
+ const parsedScreenshot = parseScreenshotDataUrl(candidate.screenshot);
215
+ if (parsedScreenshot) candidates.push({
216
+ ...candidate,
217
+ parsedScreenshot
218
+ });
219
+ }
220
+ return selectRecorderScreenshotCandidates(candidates, maxScreenshots).map(({ event, eventIndex, parsedScreenshot })=>{
221
+ const safeType = event.type.replace(/[^a-zA-Z0-9-]/g, '-');
222
+ const fileName = `event-${padEventIndex(eventIndex)}-${safeType}.${parsedScreenshot.extension}`;
223
+ return {
224
+ eventIndex,
225
+ eventHashId: event.hashId,
226
+ eventType: event.type,
227
+ relativePath: `${baseDir}/${fileName}`,
228
+ dataUrl: parsedScreenshot.dataUrl,
229
+ base64Data: parsedScreenshot.base64Data,
230
+ mimeType: parsedScreenshot.mimeType
231
+ };
232
+ });
233
+ }
234
+ function scalarToYaml(value) {
235
+ return JSON.stringify(value);
236
+ }
237
+ function stringifyMidsceneRecorderTargetBlock(target) {
238
+ const lines = [
239
+ `${target.platformId}:`
240
+ ];
241
+ const values = Object.entries(target.values);
242
+ if (0 === values.length) {
243
+ lines.push(' {}');
244
+ return lines.join('\n');
245
+ }
246
+ for (const [key, value] of values)lines.push(` ${key}: ${scalarToYaml(value)}`);
247
+ return lines.join('\n');
248
+ }
249
+ export { DEFAULT_MIDSCENE_RECORDER_MARKDOWN_MAX_SCREENSHOTS, buildMidsceneRecorderActionSummary, buildMidsceneRecorderReplayInstruction, createMidsceneRecorderMarkdownScreenshotAssets, getMidsceneRecorderEventDescription, getMidsceneRecorderScreenshotsForLLM, getMidsceneRecorderSemantic, sanitizeMidsceneRecorderFileName, stringifyMidsceneRecorderTargetBlock };
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ extractAgentBehaviorInitArgs: ()=>extractAgentBehaviorInitArgs,
28
+ agentBehaviorInitArgShape: ()=>agentBehaviorInitArgShape,
29
+ getAgentInitArgsSignature: ()=>getAgentInitArgsSignature,
30
+ shouldRebuildAgentForInitArgs: ()=>shouldRebuildAgentForInitArgs
31
+ });
32
+ const external_zod_namespaceObject = require("zod");
33
+ const agentBehaviorInitArgShape = {
34
+ aiActContext: external_zod_namespaceObject.z.string().optional().describe('Background knowledge passed to aiAct. Default: no extra context.'),
35
+ replanningCycleLimit: external_zod_namespaceObject.z.number().int().nonnegative().optional().describe('Maximum number of replanning cycles for aiAct. Default: model adapter default.'),
36
+ waitAfterAction: external_zod_namespaceObject.z.number().nonnegative().optional().describe('Wait time in milliseconds after each action execution. Default: 300ms.'),
37
+ screenshotShrinkFactor: external_zod_namespaceObject.z.number().min(1).optional().describe('Screenshot shrink factor before sending images to AI. Default: 1; high values may reduce recognition quality, especially on mobile.')
38
+ };
39
+ function extractAgentBehaviorInitArgs(extracted) {
40
+ if (!extracted) return;
41
+ const agentOptions = {
42
+ ...'string' == typeof extracted.aiActContext ? {
43
+ aiActContext: extracted.aiActContext
44
+ } : {},
45
+ ...'string' == typeof extracted.aiActionContext ? {
46
+ aiActionContext: extracted.aiActionContext
47
+ } : {},
48
+ ...'number' == typeof extracted.replanningCycleLimit ? {
49
+ replanningCycleLimit: extracted.replanningCycleLimit
50
+ } : {},
51
+ ...'number' == typeof extracted.waitAfterAction ? {
52
+ waitAfterAction: extracted.waitAfterAction
53
+ } : {},
54
+ ...'number' == typeof extracted.screenshotShrinkFactor ? {
55
+ screenshotShrinkFactor: extracted.screenshotShrinkFactor
56
+ } : {}
57
+ };
58
+ return Object.keys(agentOptions).length > 0 ? agentOptions : void 0;
59
+ }
60
+ function stableJsonValue(value) {
61
+ if (Array.isArray(value)) return value.map(stableJsonValue);
62
+ if (value && 'object' == typeof value) return Object.fromEntries(Object.entries(value).sort(([left], [right])=>left.localeCompare(right)).map(([key, nestedValue])=>[
63
+ key,
64
+ stableJsonValue(nestedValue)
65
+ ]));
66
+ return value;
67
+ }
68
+ function getAgentInitArgsSignature(initArgs) {
69
+ if (!initArgs || 0 === Object.keys(initArgs).length) return;
70
+ return JSON.stringify(stableJsonValue(initArgs));
71
+ }
72
+ function shouldRebuildAgentForInitArgs(currentSignature, nextSignature) {
73
+ return currentSignature !== nextSignature && (void 0 !== currentSignature || void 0 !== nextSignature);
74
+ }
75
+ exports.agentBehaviorInitArgShape = __webpack_exports__.agentBehaviorInitArgShape;
76
+ exports.extractAgentBehaviorInitArgs = __webpack_exports__.extractAgentBehaviorInitArgs;
77
+ exports.getAgentInitArgsSignature = __webpack_exports__.getAgentInitArgsSignature;
78
+ exports.shouldRebuildAgentForInitArgs = __webpack_exports__.shouldRebuildAgentForInitArgs;
79
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
80
+ "agentBehaviorInitArgShape",
81
+ "extractAgentBehaviorInitArgs",
82
+ "getAgentInitArgsSignature",
83
+ "shouldRebuildAgentForInitArgs"
84
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
85
+ Object.defineProperty(exports, '__esModule', {
86
+ value: true
87
+ });
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ BaseMidsceneTools: ()=>BaseMidsceneTools
28
+ });
29
+ const img_namespaceObject = require("@aiscene/shared/img");
30
+ const logger_namespaceObject = require("@aiscene/shared/logger");
31
+ const record_command_js_namespaceObject = require("../cli/record-command.js");
32
+ const external_key_alias_utils_js_namespaceObject = require("../key-alias-utils.js");
33
+ const external_cli_report_session_js_namespaceObject = require("./cli-report-session.js");
34
+ const external_init_arg_utils_js_namespaceObject = require("./init-arg-utils.js");
35
+ const external_tool_defaults_js_namespaceObject = require("./tool-defaults.js");
36
+ const external_tool_generator_js_namespaceObject = require("./tool-generator.js");
37
+ function _define_property(obj, key, value) {
38
+ if (key in obj) Object.defineProperty(obj, key, {
39
+ value: value,
40
+ enumerable: true,
41
+ configurable: true,
42
+ writable: true
43
+ });
44
+ else obj[key] = value;
45
+ return obj;
46
+ }
47
+ const debug = (0, logger_namespaceObject.getDebug)('agent-tools:base-tools');
48
+ class BaseMidsceneTools {
49
+ getInitArgKeys() {
50
+ return this.initArgSpec ? Object.keys(this.initArgSpec.shape) : [];
51
+ }
52
+ extractAgentInitParam(args) {
53
+ if (!this.initArgSpec) return;
54
+ const extracted = (0, external_init_arg_utils_js_namespaceObject.extractNamespacedArgs)(args, this.initArgSpec.namespace, this.getInitArgKeys());
55
+ if (this.initArgSpec.adapt) return this.initArgSpec.adapt(extracted);
56
+ return extracted;
57
+ }
58
+ sanitizeToolArgs(args) {
59
+ if (!this.initArgSpec) return args;
60
+ return (0, external_init_arg_utils_js_namespaceObject.sanitizeNamespacedArgs)(args, this.initArgSpec.namespace, this.getInitArgKeys());
61
+ }
62
+ getAgentInitArgSchema() {
63
+ if (!this.initArgSpec) return {};
64
+ return (0, external_init_arg_utils_js_namespaceObject.createNamespacedInitArgSchema)(this.initArgSpec.namespace, this.initArgSpec.shape);
65
+ }
66
+ getAgentInitArgCliMetadata() {
67
+ if (!this.initArgSpec?.cli) return;
68
+ const options = Object.fromEntries(this.getInitArgKeys().map((key)=>{
69
+ const canonicalKey = `${this.initArgSpec.namespace}.${key}`;
70
+ const preferredName = this.initArgSpec.cli?.preferredNames?.[key] ?? (this.initArgSpec.cli?.preferBareKeys ? (0, external_key_alias_utils_js_namespaceObject.camelToKebab)(key) : canonicalKey);
71
+ const acceptedNames = new Set([
72
+ preferredName,
73
+ ...this.initArgSpec.cli?.preferBareKeys ? (0, external_key_alias_utils_js_namespaceObject.getKeyAliases)(key) : (0, external_key_alias_utils_js_namespaceObject.getKeyAliases)(canonicalKey)
74
+ ]);
75
+ acceptedNames.delete(preferredName);
76
+ return [
77
+ canonicalKey,
78
+ {
79
+ preferredName,
80
+ aliases: [
81
+ ...acceptedNames
82
+ ]
83
+ }
84
+ ];
85
+ }));
86
+ return {
87
+ options
88
+ };
89
+ }
90
+ preparePlatformTools() {
91
+ return [];
92
+ }
93
+ getCliReportSessionName() {}
94
+ createNewCliReportSession(targetIdentity) {
95
+ const sessionName = this.getCliReportSessionName();
96
+ if (!sessionName) return;
97
+ return (0, external_cli_report_session_js_namespaceObject.generateCliReportSession)(sessionName, targetIdentity);
98
+ }
99
+ commitCliReportSession(session) {
100
+ if (session) (0, external_cli_report_session_js_namespaceObject.writeCliReportSession)(session);
101
+ }
102
+ readCliReportFileName() {
103
+ const sessionName = this.getCliReportSessionName();
104
+ if (!sessionName) return;
105
+ return (0, external_cli_report_session_js_namespaceObject.readCliReportSession)(sessionName)?.reportFileName;
106
+ }
107
+ readCliReportAgentOptions() {
108
+ const reportFileName = this.readCliReportFileName();
109
+ if (!reportFileName) return;
110
+ return {
111
+ reportFileName,
112
+ reportAttributes: {
113
+ 'data-group-id': reportFileName
114
+ }
115
+ };
116
+ }
117
+ async initTools() {
118
+ this.toolDefinitions = [];
119
+ const platformTools = this.preparePlatformTools();
120
+ this.toolDefinitions.push(...platformTools);
121
+ let actionSpace;
122
+ if (this.agent) {
123
+ actionSpace = await this.agent.getActionSpace();
124
+ debug('Action space from agent:', actionSpace.map((a)=>a.name).join(', '));
125
+ } else {
126
+ const tempDevice = this.createTemporaryDevice();
127
+ actionSpace = tempDevice.actionSpace();
128
+ await tempDevice.destroy?.();
129
+ debug('Action space from temporary device:', actionSpace.map((a)=>a.name).join(', '));
130
+ }
131
+ const actionTools = (0, external_tool_generator_js_namespaceObject.generateToolsFromActionSpace)(actionSpace, (args = {})=>this.ensureAgent(this.extractAgentInitParam(args)), (args = {})=>this.sanitizeToolArgs(args), this.getAgentInitArgSchema(), this.getAgentInitArgCliMetadata(), this.toolDefaults);
132
+ const commonTools = (0, external_tool_generator_js_namespaceObject.generateCommonTools)((args = {})=>this.ensureAgent(this.extractAgentInitParam(args)), this.getAgentInitArgSchema(), this.getAgentInitArgCliMetadata(), this.toolDefaults);
133
+ this.toolDefinitions.push(...actionTools, ...commonTools);
134
+ debug('Total tools prepared:', this.toolDefinitions.length);
135
+ }
136
+ async destroy() {
137
+ await this.agent?.destroy?.();
138
+ }
139
+ getToolDefinitions() {
140
+ return this.toolDefinitions;
141
+ }
142
+ getCliToolDefinitions() {
143
+ return [
144
+ (0, record_command_js_namespaceObject.createRecordCliCommand)((args = {})=>this.ensureAgent(this.extractAgentInitParam(args)), this.getAgentInitArgSchema(), this.getAgentInitArgCliMetadata())
145
+ ];
146
+ }
147
+ setAgent(agent) {
148
+ this.agent = agent;
149
+ }
150
+ setToolDefaults(toolDefaults) {
151
+ this.toolDefaults = (0, external_tool_defaults_js_namespaceObject.mergeToolDefaults)(this.toolDefaults, toolDefaults);
152
+ }
153
+ buildScreenshotContent(screenshot) {
154
+ const { mimeType, body } = (0, img_namespaceObject.parseBase64)(screenshot);
155
+ return [
156
+ {
157
+ type: 'image',
158
+ data: body,
159
+ mimeType
160
+ }
161
+ ];
162
+ }
163
+ buildTextResult(text) {
164
+ return {
165
+ content: [
166
+ {
167
+ type: 'text',
168
+ text
169
+ }
170
+ ]
171
+ };
172
+ }
173
+ createDisconnectHandler(platformName) {
174
+ return async ()=>{
175
+ if (!this.agent) return this.buildTextResult('No active connection to disconnect');
176
+ try {
177
+ await this.agent.destroy?.();
178
+ } catch (error) {
179
+ debug('Failed to destroy agent during disconnect:', error);
180
+ }
181
+ this.agent = void 0;
182
+ return this.buildTextResult(`Disconnected from ${platformName}`);
183
+ };
184
+ }
185
+ constructor(){
186
+ _define_property(this, "agent", void 0);
187
+ _define_property(this, "toolDefinitions", []);
188
+ _define_property(this, "toolDefaults", {});
189
+ }
190
+ }
191
+ exports.BaseMidsceneTools = __webpack_exports__.BaseMidsceneTools;
192
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
193
+ "BaseMidsceneTools"
194
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
195
+ Object.defineProperty(exports, '__esModule', {
196
+ value: true
197
+ });