@aiscene/shared 8.0.3 → 8.0.4

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 (99) 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 +3 -1
  22. package/dist/es/img/info.mjs +45 -1
  23. package/dist/es/img/photon-loader.mjs +4 -0
  24. package/dist/es/recorder.mjs +249 -0
  25. package/dist/lib/agent-tools/agent-behavior-init-args.js +87 -0
  26. package/dist/lib/agent-tools/base-tools.js +197 -0
  27. package/dist/lib/agent-tools/chrome-path.js +87 -0
  28. package/dist/lib/agent-tools/cli-report-session.js +121 -0
  29. package/dist/lib/agent-tools/error-formatter.js +149 -0
  30. package/dist/lib/agent-tools/index.js +114 -0
  31. package/dist/lib/agent-tools/init-arg-utils.js +78 -0
  32. package/dist/lib/agent-tools/observation-artifact.js +42 -0
  33. package/dist/lib/agent-tools/observation-record.js +297 -0
  34. package/dist/lib/agent-tools/tool-defaults.js +97 -0
  35. package/dist/lib/agent-tools/tool-generator.js +569 -0
  36. package/dist/lib/agent-tools/types.js +40 -0
  37. package/dist/lib/agent-tools/user-prompt.js +103 -0
  38. package/dist/lib/cli/interrupt.js +156 -0
  39. package/dist/lib/cli/record-command.js +164 -0
  40. package/dist/lib/cli/screenshot-file.js +58 -0
  41. package/dist/lib/cli/verbose-ai-act.js +273 -0
  42. package/dist/lib/cli/verbose-screenshot.js +177 -0
  43. package/dist/lib/cli/verbose.js +465 -0
  44. package/dist/lib/env/parse-model-config.js +1 -1
  45. package/dist/lib/env/types.js +5 -0
  46. package/dist/lib/img/info.js +48 -1
  47. package/dist/lib/img/photon-loader.js +38 -0
  48. package/dist/lib/recorder.js +307 -0
  49. package/dist/types/agent-tools/agent-behavior-init-args.d.ts +17 -0
  50. package/dist/types/agent-tools/base-tools.d.ts +158 -0
  51. package/dist/types/agent-tools/chrome-path.d.ts +2 -0
  52. package/dist/types/agent-tools/cli-report-session.d.ts +12 -0
  53. package/dist/types/agent-tools/error-formatter.d.ts +30 -0
  54. package/dist/types/agent-tools/index.d.ts +9 -0
  55. package/dist/types/agent-tools/init-arg-utils.d.ts +13 -0
  56. package/dist/types/agent-tools/observation-artifact.d.ts +10 -0
  57. package/dist/types/agent-tools/observation-record.d.ts +38 -0
  58. package/dist/types/agent-tools/tool-defaults.d.ts +63 -0
  59. package/dist/types/agent-tools/tool-generator.d.ts +13 -0
  60. package/dist/types/agent-tools/types.d.ts +213 -0
  61. package/dist/types/agent-tools/user-prompt.d.ts +13 -0
  62. package/dist/types/cli/interrupt.d.ts +49 -0
  63. package/dist/types/cli/record-command.d.ts +3 -0
  64. package/dist/types/cli/screenshot-file.d.ts +10 -0
  65. package/dist/types/cli/verbose-ai-act.d.ts +44 -0
  66. package/dist/types/cli/verbose-screenshot.d.ts +10 -0
  67. package/dist/types/cli/verbose.d.ts +40 -0
  68. package/dist/types/env/types.d.ts +4 -3
  69. package/dist/types/img/info.d.ts +2 -0
  70. package/dist/types/img/photon-loader.d.ts +2 -0
  71. package/dist/types/mcp/types.d.ts +1 -0
  72. package/dist/types/recorder.d.ts +113 -0
  73. package/package.json +1 -1
  74. package/src/agent-tools/agent-behavior-init-args.ts +109 -0
  75. package/src/agent-tools/base-tools.ts +399 -0
  76. package/src/agent-tools/chrome-path.ts +74 -0
  77. package/src/agent-tools/cli-report-session.ts +130 -0
  78. package/src/agent-tools/error-formatter.ts +177 -0
  79. package/src/agent-tools/index.ts +9 -0
  80. package/src/agent-tools/init-arg-utils.ts +105 -0
  81. package/src/agent-tools/observation-artifact.ts +29 -0
  82. package/src/agent-tools/observation-record.ts +331 -0
  83. package/src/agent-tools/tool-defaults.ts +119 -0
  84. package/src/agent-tools/tool-generator.ts +866 -0
  85. package/src/agent-tools/types.ts +250 -0
  86. package/src/agent-tools/user-prompt.ts +102 -0
  87. package/src/cli/interrupt.ts +207 -0
  88. package/src/cli/record-command.ts +177 -0
  89. package/src/cli/screenshot-file.ts +61 -0
  90. package/src/cli/verbose-ai-act.ts +387 -0
  91. package/src/cli/verbose-screenshot.ts +269 -0
  92. package/src/cli/verbose.ts +753 -0
  93. package/src/env/types.ts +2 -0
  94. package/src/img/index.ts +12 -0
  95. package/src/img/info.ts +61 -0
  96. package/src/img/photon-loader.ts +5 -0
  97. package/src/img/transform.ts +261 -2
  98. package/src/mcp/types.ts +2 -0
  99. package/src/recorder.ts +625 -0
package/src/env/types.ts CHANGED
@@ -10,6 +10,7 @@ export const MIDSCENE_DANGEROUSLY_PRINT_ALL_CONFIG =
10
10
  export const MIDSCENE_DEBUG_MODE = 'MIDSCENE_DEBUG_MODE';
11
11
  export const MIDSCENE_MCP_USE_PUPPETEER_MODE =
12
12
  'MIDSCENE_MCP_USE_PUPPETEER_MODE';
13
+ export const MIDSCENE_CHROME_PATH = 'MIDSCENE_CHROME_PATH';
13
14
  export const MIDSCENE_MCP_CHROME_PATH = 'MIDSCENE_MCP_CHROME_PATH';
14
15
  export const MIDSCENE_MCP_ANDROID_MODE = 'MIDSCENE_MCP_ANDROID_MODE';
15
16
  export const DOCKER_CONTAINER = 'DOCKER_CONTAINER';
@@ -195,6 +196,7 @@ export const STRING_ENV_KEYS = [
195
196
  MIDSCENE_REPORT_TAG_NAME,
196
197
  MIDSCENE_PREFERRED_LANGUAGE,
197
198
  MATCH_BY_POSITION,
199
+ MIDSCENE_CHROME_PATH,
198
200
  MIDSCENE_MCP_CHROME_PATH,
199
201
  DOCKER_CONTAINER,
200
202
  ] as const;
package/src/img/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export {
2
+ encodedImageInfoOfBuffer,
2
3
  imageInfoOfBase64,
3
4
  isValidPNGImageBuffer,
4
5
  isValidJPEGImageBuffer,
@@ -8,6 +9,10 @@ export {
8
9
  } from './info';
9
10
  export {
10
11
  resizeAndConvertImgBuffer,
12
+ convertImgBufferToJpeg,
13
+ convertBase64ImageToJpeg,
14
+ constrainBase64ImageToMaxSize,
15
+ resizeBase64ImageToJpeg,
11
16
  resizeImgBase64,
12
17
  zoomForGPT4o,
13
18
  saveBase64Image,
@@ -19,6 +24,13 @@ export {
19
24
  preProcessImageUrl,
20
25
  parseBase64,
21
26
  createImgBase64ByFormat,
27
+ inferBase64ImageFormat,
28
+ normalizeBase64Image,
29
+ normalizeScreenshotBase64,
30
+ type NormalizeScreenshotBase64Options,
31
+ type ConstrainBase64ImageToMaxSizeOptions,
32
+ type JpegBase64DataUrl,
33
+ type ResizeBase64ImageToJpegOptions,
22
34
  } from './transform';
23
35
  export {
24
36
  processImageElementInfo,
package/src/img/info.ts CHANGED
@@ -1,9 +1,70 @@
1
1
  import assert from 'node:assert';
2
+ import { Buffer } from 'node:buffer';
2
3
  import type { Size } from '../types';
3
4
  import getPhoton from './get-photon';
4
5
 
5
6
  export interface ImageInfo extends Size {}
6
7
 
8
+ const isJpegStartOfFrameMarker = (marker: number): boolean =>
9
+ marker >= 0xc0 &&
10
+ marker <= 0xcf &&
11
+ marker !== 0xc4 &&
12
+ marker !== 0xc8 &&
13
+ marker !== 0xcc;
14
+
15
+ function jpegInfoFromBuffer(imageBuffer: Buffer): ImageInfo {
16
+ let offset = 2;
17
+ while (offset < imageBuffer.length) {
18
+ if (imageBuffer[offset] !== 0xff) {
19
+ offset += 1;
20
+ continue;
21
+ }
22
+
23
+ while (offset < imageBuffer.length && imageBuffer[offset] === 0xff) {
24
+ offset += 1;
25
+ }
26
+ if (offset >= imageBuffer.length) break;
27
+
28
+ const marker = imageBuffer[offset];
29
+ offset += 1;
30
+ if (marker === 0xd9 || marker === 0xda) break;
31
+ if (
32
+ marker === 0x01 ||
33
+ marker === 0xd8 ||
34
+ (marker >= 0xd0 && marker <= 0xd7)
35
+ ) {
36
+ continue;
37
+ }
38
+ if (offset + 2 > imageBuffer.length) break;
39
+
40
+ const segmentLength = imageBuffer.readUInt16BE(offset);
41
+ if (segmentLength < 2 || offset + segmentLength > imageBuffer.length) break;
42
+ if (isJpegStartOfFrameMarker(marker)) {
43
+ if (segmentLength < 7) break;
44
+ const height = imageBuffer.readUInt16BE(offset + 3);
45
+ const width = imageBuffer.readUInt16BE(offset + 5);
46
+ assert(width && height, 'Invalid image: cannot get width or height');
47
+ return { width, height };
48
+ }
49
+ offset += segmentLength;
50
+ }
51
+
52
+ throw new Error('Invalid image: cannot get JPEG width or height');
53
+ }
54
+
55
+ export function encodedImageInfoOfBuffer(imageBuffer: Buffer): ImageInfo {
56
+ if (isValidPNGImageBuffer(imageBuffer)) {
57
+ const width = imageBuffer.readUInt32BE(16);
58
+ const height = imageBuffer.readUInt32BE(20);
59
+ assert(width && height, 'Invalid image: cannot get width or height');
60
+ return { width, height };
61
+ }
62
+ if (isValidJPEGImageBuffer(imageBuffer)) {
63
+ return jpegInfoFromBuffer(imageBuffer);
64
+ }
65
+ throw new Error('Invalid image: unsupported format');
66
+ }
67
+
7
68
  /**
8
69
  * Retrieves the dimensions of an image from a base64-encoded string
9
70
  *
@@ -0,0 +1,5 @@
1
+ export type PhotonModule = typeof import('@silvia-odwyer/photon');
2
+
3
+ export async function loadPhotonModule(): Promise<PhotonModule> {
4
+ return import('@silvia-odwyer/photon');
5
+ }
@@ -4,11 +4,12 @@ import { readFileSync } from 'node:fs';
4
4
  import { writeFile } from 'node:fs/promises';
5
5
  import path from 'node:path';
6
6
  import type { PhotonImage as PhotonImageType } from '@silvia-odwyer/photon-node';
7
- import type { Rect } from 'src/types';
7
+ import type { Rect, Size } from '../types';
8
8
  import { getDebug } from '../logger';
9
9
  import { ifInNode } from '../utils';
10
10
  import getPhoton from './get-photon';
11
11
  import getSharp from './get-sharp';
12
+ import { encodedImageInfoOfBuffer } from './info';
12
13
 
13
14
  const imgDebug = getDebug('img');
14
15
 
@@ -154,10 +155,268 @@ export async function resizeAndConvertImgBuffer(
154
155
  };
155
156
  }
156
157
 
158
+ const rawBase64BodyPattern = /^[A-Za-z0-9+/]+={0,2}$/;
159
+ const base64ImageDataUrlPattern =
160
+ /^data:image\/(?:png|jpe?g);base64,[A-Za-z0-9+/=\s]+$/i;
161
+ const supportedScreenshotDataUriPattern =
162
+ /^data:image\/(png|jpe?g);base64,([A-Za-z0-9+/=\s]+)$/i;
163
+
164
+ function normalizeBase64Body(body: string): string {
165
+ return body.replace(/\s/g, '');
166
+ }
167
+
168
+ function detectImageMimeTypeFromBuffer(buffer: Buffer): string | undefined {
169
+ if (
170
+ buffer.length >= 8 &&
171
+ buffer[0] === 0x89 &&
172
+ buffer[1] === 0x50 &&
173
+ buffer[2] === 0x4e &&
174
+ buffer[3] === 0x47 &&
175
+ buffer[4] === 0x0d &&
176
+ buffer[5] === 0x0a &&
177
+ buffer[6] === 0x1a &&
178
+ buffer[7] === 0x0a
179
+ ) {
180
+ return 'image/png';
181
+ }
182
+ if (
183
+ buffer.length >= 3 &&
184
+ buffer[0] === 0xff &&
185
+ buffer[1] === 0xd8 &&
186
+ buffer[2] === 0xff
187
+ ) {
188
+ return 'image/jpeg';
189
+ }
190
+ return undefined;
191
+ }
192
+
193
+ export function inferBase64ImageFormat(base64Body: string): string {
194
+ const buffer = Buffer.from(normalizeBase64Body(base64Body), 'base64');
195
+ const mimeType = detectImageMimeTypeFromBuffer(buffer);
196
+ if (mimeType === 'image/jpeg') return 'jpeg';
197
+ if (mimeType === 'image/png') return 'png';
198
+ return 'png';
199
+ }
200
+
157
201
  export const createImgBase64ByFormat = (format: string, body: string) => {
158
- return `data:image/${format};base64,${body}`;
202
+ return `data:image/${format};base64,${normalizeBase64Body(body)}`;
159
203
  };
160
204
 
205
+ export interface NormalizeScreenshotBase64Options {
206
+ label?: string;
207
+ }
208
+
209
+ export const normalizeScreenshotBase64 = (
210
+ base64: string,
211
+ options?: NormalizeScreenshotBase64Options,
212
+ ) => {
213
+ const label = options?.label ?? 'screenshot base64';
214
+ const trimmedBase64 = base64.trim();
215
+ if (!trimmedBase64) {
216
+ throw new Error(`${label} cannot be empty`);
217
+ }
218
+
219
+ const dataUriMatch = trimmedBase64.match(supportedScreenshotDataUriPattern);
220
+ if (dataUriMatch) {
221
+ const imageFormat =
222
+ dataUriMatch[1].toLowerCase() === 'jpg'
223
+ ? 'jpeg'
224
+ : dataUriMatch[1].toLowerCase();
225
+ const body = normalizeBase64Body(dataUriMatch[2]);
226
+ if (!body) {
227
+ throw new Error(`${label} cannot be empty`);
228
+ }
229
+ return createImgBase64ByFormat(imageFormat, body);
230
+ }
231
+
232
+ if (trimmedBase64.startsWith('data:')) {
233
+ throw new Error(
234
+ `${label} must be a PNG/JPEG data URI or raw PNG base64 string`,
235
+ );
236
+ }
237
+
238
+ if (!rawBase64BodyPattern.test(normalizeBase64Body(trimmedBase64))) {
239
+ throw new Error(
240
+ `${label} must be a PNG/JPEG data URI or raw PNG base64 string`,
241
+ );
242
+ }
243
+
244
+ return createImgBase64ByFormat('png', trimmedBase64);
245
+ };
246
+
247
+ export const normalizeBase64Image = (base64: string) => {
248
+ const trimmedBase64 = base64.trim();
249
+ if (base64ImageDataUrlPattern.test(trimmedBase64)) {
250
+ return trimmedBase64.replace(/\s/g, '');
251
+ }
252
+
253
+ const base64Body = normalizeBase64Body(trimmedBase64);
254
+ assert(base64Body, 'base64 image must include image data');
255
+ return createImgBase64ByFormat(
256
+ inferBase64ImageFormat(base64Body),
257
+ base64Body,
258
+ );
259
+ };
260
+
261
+ export type JpegBase64DataUrl = `data:image/jpeg;base64,${string}`;
262
+
263
+ export interface ResizeBase64ImageToJpegOptions {
264
+ sourceSize?: Size;
265
+ targetSize: Size;
266
+ jpegQuality?: number;
267
+ }
268
+
269
+ export interface ConstrainBase64ImageToMaxSizeOptions {
270
+ maxSize: number;
271
+ jpegQuality?: number;
272
+ }
273
+
274
+ function assertValidJpegQuality(jpegQuality: number): void {
275
+ if (!Number.isInteger(jpegQuality) || jpegQuality < 1 || jpegQuality > 100) {
276
+ throw new Error(
277
+ `jpegQuality must be an integer between 1 and 100. Received: ${jpegQuality}`,
278
+ );
279
+ }
280
+ }
281
+
282
+ function assertValidImageSize(size: Size, label: string): void {
283
+ if (
284
+ !Number.isInteger(size.width) ||
285
+ !Number.isInteger(size.height) ||
286
+ size.width <= 0 ||
287
+ size.height <= 0
288
+ ) {
289
+ throw new Error(
290
+ `${label} width and height must be positive integers. Received width: ${size.width}, height: ${size.height}`,
291
+ );
292
+ }
293
+ }
294
+
295
+ export async function convertImgBufferToJpeg(
296
+ inputData: Buffer,
297
+ jpegQuality = 90,
298
+ ): Promise<Buffer> {
299
+ assertValidJpegQuality(jpegQuality);
300
+
301
+ if (ifInNode) {
302
+ try {
303
+ const Sharp = await getSharp();
304
+ return await Sharp(inputData).jpeg({ quality: jpegQuality }).toBuffer();
305
+ } catch (error) {
306
+ imgDebug('Sharp failed, falling back to Photon:', error);
307
+ }
308
+ }
309
+
310
+ const { PhotonImage } = await getPhoton();
311
+ const inputBytes = new Uint8Array(inputData);
312
+ const bytesliceResult = PhotonImage.new_from_byteslice(inputBytes);
313
+ const image =
314
+ bytesliceResult instanceof Promise
315
+ ? await bytesliceResult
316
+ : bytesliceResult;
317
+ try {
318
+ return Buffer.from(image.get_bytes_jpeg(jpegQuality));
319
+ } finally {
320
+ image.free();
321
+ }
322
+ }
323
+
324
+ export async function convertBase64ImageToJpeg(
325
+ inputBase64: string,
326
+ jpegQuality = 90,
327
+ ): Promise<JpegBase64DataUrl> {
328
+ assertValidJpegQuality(jpegQuality);
329
+ const normalized = normalizeBase64Image(inputBase64);
330
+ const { body } = parseBase64(normalized);
331
+ const imageBuffer = Buffer.from(body, 'base64');
332
+ const detectedMimeType = detectImageMimeTypeFromBuffer(imageBuffer);
333
+ if (detectedMimeType === 'image/jpeg') {
334
+ return createImgBase64ByFormat('jpeg', body) as JpegBase64DataUrl;
335
+ }
336
+ if (detectedMimeType !== 'image/png') {
337
+ throw new Error(
338
+ `inputBase64 must contain a PNG or JPEG image. Detected: ${detectedMimeType ?? 'unsupported format'}`,
339
+ );
340
+ }
341
+
342
+ const jpegBuffer = await convertImgBufferToJpeg(imageBuffer, jpegQuality);
343
+ return createImgBase64ByFormat(
344
+ 'jpeg',
345
+ jpegBuffer.toString('base64'),
346
+ ) as JpegBase64DataUrl;
347
+ }
348
+
349
+ export async function resizeBase64ImageToJpeg(
350
+ inputBase64: string,
351
+ options: ResizeBase64ImageToJpegOptions,
352
+ ): Promise<JpegBase64DataUrl> {
353
+ const jpegQuality = options.jpegQuality ?? 90;
354
+ assertValidJpegQuality(jpegQuality);
355
+ assertValidImageSize(options.targetSize, 'targetSize');
356
+
357
+ const normalized = normalizeBase64Image(inputBase64);
358
+ const { body, mimeType } = parseBase64(normalized);
359
+ const imageBuffer = Buffer.from(body, 'base64');
360
+ const sourceSize = encodedImageInfoOfBuffer(imageBuffer);
361
+ if (options.sourceSize) {
362
+ assertValidImageSize(options.sourceSize, 'sourceSize');
363
+ if (
364
+ options.sourceSize.width !== sourceSize.width ||
365
+ options.sourceSize.height !== sourceSize.height
366
+ ) {
367
+ throw new Error(
368
+ `sourceSize ${options.sourceSize.width}x${options.sourceSize.height} does not match encoded image dimensions ${sourceSize.width}x${sourceSize.height}`,
369
+ );
370
+ }
371
+ }
372
+
373
+ const dimensionsUnchanged =
374
+ sourceSize.width === options.targetSize.width &&
375
+ sourceSize.height === options.targetSize.height;
376
+ if (dimensionsUnchanged) {
377
+ return convertBase64ImageToJpeg(normalized, jpegQuality);
378
+ }
379
+
380
+ const { buffer } = await resizeAndConvertImgBuffer(
381
+ mimeType.split('/')[1],
382
+ imageBuffer,
383
+ options.targetSize,
384
+ );
385
+ return createImgBase64ByFormat(
386
+ 'jpeg',
387
+ buffer.toString('base64'),
388
+ ) as JpegBase64DataUrl;
389
+ }
390
+
391
+ export async function constrainBase64ImageToMaxSize(
392
+ inputBase64: string,
393
+ options: ConstrainBase64ImageToMaxSizeOptions,
394
+ ): Promise<string> {
395
+ if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) {
396
+ throw new Error(
397
+ `maxSize must be a positive integer. Received: ${options.maxSize}`,
398
+ );
399
+ }
400
+
401
+ const normalized = normalizeBase64Image(inputBase64);
402
+ const { body } = parseBase64(normalized);
403
+ const sourceSize = encodedImageInfoOfBuffer(Buffer.from(body, 'base64'));
404
+ const largestDimension = Math.max(sourceSize.width, sourceSize.height);
405
+ if (largestDimension <= options.maxSize) {
406
+ return normalized;
407
+ }
408
+
409
+ const scale = options.maxSize / largestDimension;
410
+ return resizeBase64ImageToJpeg(normalized, {
411
+ targetSize: {
412
+ width: Math.max(1, Math.round(sourceSize.width * scale)),
413
+ height: Math.max(1, Math.round(sourceSize.height * scale)),
414
+ },
415
+ sourceSize,
416
+ jpegQuality: options.jpegQuality,
417
+ });
418
+ }
419
+
161
420
  export async function resizeImgBase64(
162
421
  inputBase64: string,
163
422
  newSize: {
package/src/mcp/types.ts CHANGED
@@ -111,6 +111,8 @@ export interface BaseAgent {
111
111
  msg?: string,
112
112
  options?: Record<string, unknown>,
113
113
  ) => Promise<unknown>;
114
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
+ aiQuery?: (demand: any, opt?: any) => Promise<any>;
114
116
  }
115
117
 
116
118
  /**