@midscene/shared 1.10.5 → 1.10.6-beta-20260717061640.0

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.
@@ -41,8 +41,11 @@ __webpack_require__.d(__webpack_exports__, {
41
41
  const external_node_assert_namespaceObject = require("node:assert");
42
42
  var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
43
43
  const index_js_namespaceObject = require("../constants/index.js");
44
+ const external_utils_js_namespaceObject = require("../utils.js");
44
45
  const external_get_photon_js_namespaceObject = require("./get-photon.js");
45
46
  var external_get_photon_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_photon_js_namespaceObject);
47
+ const external_get_sharp_js_namespaceObject = require("./get-sharp.js");
48
+ var external_get_sharp_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_sharp_js_namespaceObject);
46
49
  const external_transform_js_namespaceObject = require("./transform.js");
47
50
  const DIGIT_FONT = {
48
51
  0: [
@@ -956,8 +959,45 @@ const createSvgOverlay = async (elements, imageWidth, imageHeight, boxPadding =
956
959
  }
957
960
  return overlayPixels;
958
961
  };
962
+ async function decodeImageWithSharp(inputImgBase64, size) {
963
+ const { body } = (0, external_transform_js_namespaceObject.parseBase64)(inputImgBase64);
964
+ const Sharp = await external_get_sharp_js_default()();
965
+ let pipeline = Sharp(Buffer.from(body, 'base64'));
966
+ if (size) pipeline = pipeline.resize(size.width, size.height, {
967
+ fit: 'fill',
968
+ kernel: 'nearest'
969
+ });
970
+ const { data, info } = await pipeline.ensureAlpha().raw().toBuffer({
971
+ resolveWithObject: true
972
+ });
973
+ if (!info.width || !info.height || 4 !== info.channels) throw new Error('Image processing failed to produce RGBA pixels');
974
+ return {
975
+ pixels: new Uint8Array(data),
976
+ width: info.width,
977
+ height: info.height
978
+ };
979
+ }
980
+ async function encodeRgbaWithSharp(pixels, width, height) {
981
+ const Sharp = await external_get_sharp_js_default()();
982
+ const output = await Sharp(Buffer.from(pixels), {
983
+ raw: {
984
+ width,
985
+ height,
986
+ channels: 4
987
+ }
988
+ }).jpeg({
989
+ quality: 90,
990
+ chromaSubsampling: '4:4:4'
991
+ }).toBuffer();
992
+ return (0, external_transform_js_namespaceObject.createImgBase64ByFormat)('jpeg', output.toString('base64'));
993
+ }
959
994
  const compositeElementInfoImg = async (options)=>{
960
995
  external_node_assert_default()(options.inputImgBase64, 'inputImgBase64 is required');
996
+ if (external_utils_js_namespaceObject.ifInNode) {
997
+ const { pixels, width, height } = await decodeImageWithSharp(options.inputImgBase64, options.size);
998
+ const overlayPixels = await createSvgOverlay(options.elementsPositionInfo, width, height, options.annotationPadding, options.borderThickness, options.prompt, options.centerPoint);
999
+ return encodeRgbaWithSharp(blendPixels(pixels, overlayPixels, width, height), width, height);
1000
+ }
961
1001
  const { PhotonImage, SamplingFilter, resize } = await external_get_photon_js_default()();
962
1002
  let width = 0;
963
1003
  let height = 0;
@@ -997,6 +1037,12 @@ const compositeElementInfoImg = async (options)=>{
997
1037
  };
998
1038
  const compositePointMarkerImg = async (options)=>{
999
1039
  external_node_assert_default()(options.inputImgBase64, 'inputImgBase64 is required');
1040
+ if (external_utils_js_namespaceObject.ifInNode) {
1041
+ const { pixels, width, height } = await decodeImageWithSharp(options.inputImgBase64, options.size);
1042
+ const overlayPixels = new Uint8Array(width * height * 4);
1043
+ drawPointMarker(overlayPixels, width, height, options.point, options.radius ?? 14, options.indexId ?? 1);
1044
+ return encodeRgbaWithSharp(blendPixels(pixels, overlayPixels, width, height), width, height);
1045
+ }
1000
1046
  const { PhotonImage, SamplingFilter, resize } = await external_get_photon_js_default()();
1001
1047
  let width = 0;
1002
1048
  let height = 0;
@@ -35,7 +35,7 @@ let isInitialized = false;
35
35
  let usingCanvasFallback = false;
36
36
  async function getPhoton() {
37
37
  if (photonModule && isInitialized) return photonModule;
38
- const env = external_utils_js_namespaceObject.ifInBrowser ? 'browser' : external_utils_js_namespaceObject.ifInWorker ? 'worker' : external_utils_js_namespaceObject.ifInNode ? 'node' : 'unknown';
38
+ const env = external_utils_js_namespaceObject.ifInBrowser ? 'browser' : external_utils_js_namespaceObject.ifInWorker ? 'worker' : 'unknown';
39
39
  debug(`Loading photon module in ${env} environment`);
40
40
  try {
41
41
  if (external_utils_js_namespaceObject.ifInBrowser || external_utils_js_namespaceObject.ifInWorker) {
@@ -43,10 +43,7 @@ async function getPhoton() {
43
43
  if ('function' == typeof photon.default) await photon.default();
44
44
  debug('Photon loaded: @silvia-odwyer/photon (browser/worker)');
45
45
  photonModule = photon;
46
- } else if (external_utils_js_namespaceObject.ifInNode) {
47
- photonModule = await import("@silvia-odwyer/photon-node");
48
- debug('Photon loaded: @silvia-odwyer/photon-node (node)');
49
- }
46
+ } else throw new Error('Photon is only available in browser environments');
50
47
  if (!photonModule?.PhotonImage) throw new Error('PhotonImage is not available');
51
48
  if (!photonModule.PhotonImage.new_from_byteslice && !photonModule.PhotonImage.new_from_base64) throw new Error('PhotonImage.new_from_byteslice or new_from_base64 is not available');
52
49
  isInitialized = true;
@@ -42,15 +42,34 @@ __webpack_require__.d(__webpack_exports__, {
42
42
  const external_node_assert_namespaceObject = require("node:assert");
43
43
  var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
44
44
  const external_node_buffer_namespaceObject = require("node:buffer");
45
+ const external_utils_js_namespaceObject = require("../utils.js");
45
46
  const external_get_photon_js_namespaceObject = require("./get-photon.js");
46
47
  var external_get_photon_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_photon_js_namespaceObject);
48
+ const external_get_sharp_js_namespaceObject = require("./get-sharp.js");
49
+ var external_get_sharp_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_sharp_js_namespaceObject);
47
50
  async function imageInfoOfBase64(imageBase64) {
48
- const { PhotonImage } = await external_get_photon_js_default()();
49
51
  const base64Data = imageBase64.replace(/^data:image\/\w+;base64,/, '').replace(/\s/g, '');
50
52
  external_node_assert_default()(base64Data, 'Invalid image: empty base64 data');
51
53
  external_node_assert_default()(/^[A-Za-z0-9+/]+={0,2}$/.test(base64Data) && base64Data.length % 4 !== 1, 'Invalid image: malformed base64 data');
52
54
  const imageBuffer = external_node_buffer_namespaceObject.Buffer.from(base64Data, 'base64');
53
55
  external_node_assert_default()(isValidImageBuffer(imageBuffer), 'Invalid image: unsupported format');
56
+ if (external_utils_js_namespaceObject.ifInNode) {
57
+ let metadata;
58
+ try {
59
+ const Sharp = await external_get_sharp_js_default()();
60
+ metadata = await Sharp(imageBuffer).metadata();
61
+ } catch (error) {
62
+ throw new Error(`Invalid image: failed to decode base64 data (${error instanceof Error ? error.message : String(error)})`, {
63
+ cause: error
64
+ });
65
+ }
66
+ external_node_assert_default()(metadata.width && metadata.height, 'Invalid image: cannot get width or height');
67
+ return {
68
+ width: metadata.width,
69
+ height: metadata.height
70
+ };
71
+ }
72
+ const { PhotonImage } = await external_get_photon_js_default()();
54
73
  let result;
55
74
  try {
56
75
  result = PhotonImage.new_from_base64(base64Data);
@@ -79,7 +79,7 @@ async function resizeAndConvertImgBuffer(inputFormat, inputData, newSize) {
79
79
  external_node_assert_default()(newSize && newSize.width > 0 && newSize.height > 0, 'newSize must be positive');
80
80
  const resizeStartTime = Date.now();
81
81
  imgDebug(`resizeImg start, target size: ${newSize.width}x${newSize.height}`);
82
- if (external_utils_js_namespaceObject.ifInNode) try {
82
+ if (external_utils_js_namespaceObject.ifInNode) {
83
83
  const Sharp = await external_get_sharp_js_default()();
84
84
  const metadata = await Sharp(inputData).metadata();
85
85
  const { width: originalWidth, height: originalHeight } = metadata;
@@ -97,8 +97,6 @@ async function resizeAndConvertImgBuffer(inputFormat, inputData, newSize) {
97
97
  buffer: resizedBuffer,
98
98
  format: 'jpeg'
99
99
  };
100
- } catch (error) {
101
- imgDebug('Sharp failed, falling back to Photon:', error);
102
100
  }
103
101
  const { PhotonImage, SamplingFilter, resize } = await external_get_photon_js_default()();
104
102
  const inputBytes = new Uint8Array(inputData);
@@ -246,6 +244,39 @@ async function paddingToMatchBlock(image, blockSize = 28) {
246
244
  };
247
245
  }
248
246
  async function paddingToMatchBlockByBase64(imageBase64, blockSize = 28) {
247
+ if (external_utils_js_namespaceObject.ifInNode) {
248
+ const { body } = parseBase64(imageBase64);
249
+ const inputBuffer = external_node_buffer_namespaceObject.Buffer.from(body, 'base64');
250
+ const Sharp = await external_get_sharp_js_default()();
251
+ const metadata = await Sharp(inputBuffer).metadata();
252
+ const width = metadata.width;
253
+ const height = metadata.height;
254
+ if (!width || !height) throw new Error('Failed to get image dimensions');
255
+ const targetWidth = Math.ceil(width / blockSize) * blockSize;
256
+ const targetHeight = Math.ceil(height / blockSize) * blockSize;
257
+ if (targetWidth === width && targetHeight === height) return {
258
+ width,
259
+ height,
260
+ imageBase64
261
+ };
262
+ const output = await Sharp(inputBuffer).extend({
263
+ right: targetWidth - width,
264
+ bottom: targetHeight - height,
265
+ background: {
266
+ r: 255,
267
+ g: 255,
268
+ b: 255,
269
+ alpha: 1
270
+ }
271
+ }).jpeg({
272
+ quality: 90
273
+ }).toBuffer();
274
+ return {
275
+ width: targetWidth,
276
+ height: targetHeight,
277
+ imageBase64: createImgBase64ByFormat('jpeg', output.toString('base64'))
278
+ };
279
+ }
249
280
  const photonImage = await photonFromBase64(imageBase64);
250
281
  try {
251
282
  const paddedResult = await paddingToMatchBlock(photonImage, blockSize);
@@ -261,6 +292,27 @@ async function paddingToMatchBlockByBase64(imageBase64, blockSize = 28) {
261
292
  }
262
293
  }
263
294
  async function cropByRect(imageBase64, rect) {
295
+ if (external_utils_js_namespaceObject.ifInNode) {
296
+ const { body } = parseBase64(imageBase64);
297
+ const Sharp = await external_get_sharp_js_default()();
298
+ const left = Math.trunc(rect.left);
299
+ const top = Math.trunc(rect.top);
300
+ const width = Math.trunc(rect.left + rect.width) - left;
301
+ const height = Math.trunc(rect.top + rect.height) - top;
302
+ const output = await Sharp(external_node_buffer_namespaceObject.Buffer.from(body, 'base64')).extract({
303
+ left,
304
+ top,
305
+ width,
306
+ height
307
+ }).jpeg({
308
+ quality: 90
309
+ }).toBuffer();
310
+ return {
311
+ width,
312
+ height,
313
+ imageBase64: createImgBase64ByFormat('jpeg', output.toString('base64'))
314
+ };
315
+ }
264
316
  const { crop } = await external_get_photon_js_default()();
265
317
  const photonImage = await photonFromBase64(imageBase64);
266
318
  const { left, top, width, height } = rect;
@@ -336,7 +388,7 @@ async function scaleImage(imageBase64, scale) {
336
388
  const buffer = external_node_buffer_namespaceObject.Buffer.from(body, 'base64');
337
389
  const scaleStartTime = Date.now();
338
390
  imgDebug(`scaleImage start, scale factor: ${scale}`);
339
- if (external_utils_js_namespaceObject.ifInNode) try {
391
+ if (external_utils_js_namespaceObject.ifInNode) {
340
392
  const Sharp = await external_get_sharp_js_default()();
341
393
  const metadata = await Sharp(buffer).metadata();
342
394
  const originalWidth = metadata.width || 0;
@@ -358,8 +410,6 @@ async function scaleImage(imageBase64, scale) {
358
410
  height: newHeight,
359
411
  imageBase64: base64
360
412
  };
361
- } catch (error) {
362
- imgDebug('Sharp failed, falling back to Photon:', error);
363
413
  }
364
414
  const { PhotonImage, SamplingFilter, resize } = await external_get_photon_js_default()();
365
415
  const inputBytes = new Uint8Array(buffer);
@@ -48,21 +48,32 @@ const external_common_js_namespaceObject = require("./common.js");
48
48
  const external_utils_js_namespaceObject = require("./utils.js");
49
49
  const topicPrefix = 'midscene';
50
50
  const logStreams = new Map();
51
+ const backpressuredLogStreams = new Set();
52
+ const unavailableLogStreams = new Set();
51
53
  const debugInstances = new Map();
52
54
  function getLogStream(topic) {
53
55
  const topicFileName = topic.replace(/:/g, '-');
56
+ if (unavailableLogStreams.has(topicFileName)) return null;
54
57
  if (!logStreams.has(topicFileName)) {
55
58
  const logFile = external_node_path_default().join((0, external_common_js_namespaceObject.getMidsceneRunSubDir)('log'), `${topicFileName}.log`);
56
59
  const stream = external_node_fs_default().createWriteStream(logFile, {
57
60
  flags: 'a'
58
61
  });
62
+ stream.on('error', ()=>{
63
+ unavailableLogStreams.add(topicFileName);
64
+ backpressuredLogStreams.delete(topicFileName);
65
+ if (logStreams.get(topicFileName) === stream) logStreams.delete(topicFileName);
66
+ });
59
67
  logStreams.set(topicFileName, stream);
60
68
  }
61
- return logStreams.get(topicFileName);
69
+ return logStreams.get(topicFileName) ?? null;
62
70
  }
63
71
  function writeLogToFile(topic, message) {
64
72
  if (!external_utils_js_namespaceObject.ifInNode) return;
73
+ const topicFileName = topic.replace(/:/g, '-');
74
+ if (backpressuredLogStreams.has(topicFileName)) return;
65
75
  const stream = getLogStream(topic);
76
+ if (!stream) return;
66
77
  const now = new Date();
67
78
  const isoDate = now.toLocaleDateString('sv-SE');
68
79
  const isoTime = now.toLocaleTimeString('sv-SE');
@@ -73,7 +84,17 @@ function writeLogToFile(topic, message) {
73
84
  const minutes = (Math.abs(timezoneOffsetMinutes) % 60).toString().padStart(2, '0');
74
85
  const timezoneString = `${sign}${hours}:${minutes}`;
75
86
  const localISOTime = `${isoDate}T${isoTime}.${milliseconds}${timezoneString}`;
76
- stream.write(`[${localISOTime}] ${message}\n`);
87
+ try {
88
+ if (!stream.write(`[${localISOTime}] ${message}\n`)) {
89
+ backpressuredLogStreams.add(topicFileName);
90
+ stream.once('drain', ()=>{
91
+ backpressuredLogStreams.delete(topicFileName);
92
+ });
93
+ }
94
+ } catch {
95
+ unavailableLogStreams.add(topicFileName);
96
+ backpressuredLogStreams.delete(topicFileName);
97
+ }
77
98
  }
78
99
  function getDebug(topic, options) {
79
100
  const fullTopic = `${topicPrefix}:${topic}`;
@@ -44,7 +44,6 @@ function isMidsceneLocatorField(field) {
44
44
  if (actualField._def?.typeName === 'ZodObject') {
45
45
  const shape = actualField._def.shape?.();
46
46
  if (shape) {
47
- if ('midscene_location_field_flag' in shape) return true;
48
47
  if ('prompt' in shape && shape.prompt) return true;
49
48
  }
50
49
  }
@@ -32,6 +32,7 @@ interface IModelConfigKeys {
32
32
  reasoningEffort: string;
33
33
  reasoningEnabled: string;
34
34
  reasoningBudget: string;
35
+ responseFormat: string;
35
36
  }
36
37
  export declare const INSIGHT_MODEL_CONFIG_KEYS: IModelConfigKeys;
37
38
  export declare const PLANNING_MODEL_CONFIG_KEYS: IModelConfigKeys;
@@ -24,7 +24,9 @@ export declare const MIDSCENE_MODEL_RETRY_INTERVAL = "MIDSCENE_MODEL_RETRY_INTER
24
24
  export declare const MIDSCENE_MODEL_REASONING_EFFORT = "MIDSCENE_MODEL_REASONING_EFFORT";
25
25
  export declare const MIDSCENE_MODEL_REASONING_ENABLED = "MIDSCENE_MODEL_REASONING_ENABLED";
26
26
  export declare const MIDSCENE_MODEL_REASONING_BUDGET = "MIDSCENE_MODEL_REASONING_BUDGET";
27
+ export declare const MIDSCENE_MODEL_RESPONSE_FORMAT = "MIDSCENE_MODEL_RESPONSE_FORMAT";
27
28
  export type TModelReasoningEnabled = boolean | 'default';
29
+ export type TModelResponseFormat = 'none' | 'auto';
28
30
  /**
29
31
  * @deprecated Use MIDSCENE_MODEL_API_KEY instead. This is kept for backward compatibility.
30
32
  */
@@ -81,6 +83,7 @@ export declare const MIDSCENE_INSIGHT_MODEL_FAMILY = "MIDSCENE_INSIGHT_MODEL_FAM
81
83
  export declare const MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT = "MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT";
82
84
  export declare const MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED = "MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED";
83
85
  export declare const MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET = "MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET";
86
+ export declare const MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT = "MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT";
84
87
  export declare const MIDSCENE_PLANNING_MODEL_NAME = "MIDSCENE_PLANNING_MODEL_NAME";
85
88
  export declare const MIDSCENE_PLANNING_MODEL_SOCKS_PROXY = "MIDSCENE_PLANNING_MODEL_SOCKS_PROXY";
86
89
  export declare const MIDSCENE_PLANNING_MODEL_HTTP_PROXY = "MIDSCENE_PLANNING_MODEL_HTTP_PROXY";
@@ -96,6 +99,7 @@ export declare const MIDSCENE_PLANNING_MODEL_FAMILY = "MIDSCENE_PLANNING_MODEL_F
96
99
  export declare const MIDSCENE_PLANNING_MODEL_REASONING_EFFORT = "MIDSCENE_PLANNING_MODEL_REASONING_EFFORT";
97
100
  export declare const MIDSCENE_PLANNING_MODEL_REASONING_ENABLED = "MIDSCENE_PLANNING_MODEL_REASONING_ENABLED";
98
101
  export declare const MIDSCENE_PLANNING_MODEL_REASONING_BUDGET = "MIDSCENE_PLANNING_MODEL_REASONING_BUDGET";
102
+ export declare const MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT = "MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT";
99
103
  export declare const MIDSCENE_MODEL_FAMILY = "MIDSCENE_MODEL_FAMILY";
100
104
  /**
101
105
  * env keys declared but unused
@@ -120,8 +124,8 @@ export declare const GLOBAL_ENV_KEYS: readonly ["MIDSCENE_CACHE", "MIDSCENE_LANG
120
124
  * Can be override by both agent.modelConfig and overrideAIConfig
121
125
  * Can only be access after agent.constructor
122
126
  */
123
- export declare const MODEL_ENV_KEYS: readonly ["MIDSCENE_MODEL_NAME", "MIDSCENE_MODEL_INIT_CONFIG_JSON", "MIDSCENE_MODEL_EXTRA_BODY_JSON", "MIDSCENE_MODEL_API_KEY", "MIDSCENE_MODEL_BASE_URL", "MIDSCENE_MODEL_SOCKS_PROXY", "MIDSCENE_MODEL_HTTP_PROXY", "MIDSCENE_MODEL_TIMEOUT", "MIDSCENE_MODEL_TEMPERATURE", "MIDSCENE_MODEL_RETRY_COUNT", "MIDSCENE_MODEL_RETRY_INTERVAL", "MIDSCENE_MODEL_REASONING_EFFORT", "MIDSCENE_MODEL_REASONING_ENABLED", "MIDSCENE_MODEL_REASONING_BUDGET", "MIDSCENE_USE_VLM_UI_TARS", "MIDSCENE_USE_QWEN_VL", "MIDSCENE_USE_QWEN3_VL", "MIDSCENE_USE_DOUBAO_VISION", "MIDSCENE_USE_GEMINI", "MIDSCENE_USE_VL_MODEL", "OPENAI_API_KEY", "OPENAI_BASE_URL", "MIDSCENE_OPENAI_INIT_CONFIG_JSON", "MIDSCENE_OPENAI_HTTP_PROXY", "MIDSCENE_OPENAI_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_NAME", "MIDSCENE_INSIGHT_MODEL_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_HTTP_PROXY", "MIDSCENE_INSIGHT_MODEL_BASE_URL", "MIDSCENE_INSIGHT_MODEL_API_KEY", "MIDSCENE_INSIGHT_MODEL_INIT_CONFIG_JSON", "MIDSCENE_INSIGHT_MODEL_EXTRA_BODY_JSON", "MIDSCENE_INSIGHT_MODEL_TIMEOUT", "MIDSCENE_INSIGHT_MODEL_TEMPERATURE", "MIDSCENE_INSIGHT_MODEL_RETRY_COUNT", "MIDSCENE_INSIGHT_MODEL_RETRY_INTERVAL", "MIDSCENE_INSIGHT_MODEL_FAMILY", "MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT", "MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED", "MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET", "MIDSCENE_PLANNING_MODEL_NAME", "MIDSCENE_PLANNING_MODEL_SOCKS_PROXY", "MIDSCENE_PLANNING_MODEL_HTTP_PROXY", "MIDSCENE_PLANNING_MODEL_BASE_URL", "MIDSCENE_PLANNING_MODEL_API_KEY", "MIDSCENE_PLANNING_MODEL_INIT_CONFIG_JSON", "MIDSCENE_PLANNING_MODEL_EXTRA_BODY_JSON", "MIDSCENE_PLANNING_MODEL_TIMEOUT", "MIDSCENE_PLANNING_MODEL_TEMPERATURE", "MIDSCENE_PLANNING_MODEL_RETRY_COUNT", "MIDSCENE_PLANNING_MODEL_RETRY_INTERVAL", "MIDSCENE_PLANNING_MODEL_FAMILY", "MIDSCENE_PLANNING_MODEL_REASONING_EFFORT", "MIDSCENE_PLANNING_MODEL_REASONING_ENABLED", "MIDSCENE_PLANNING_MODEL_REASONING_BUDGET", "MIDSCENE_MODEL_FAMILY"];
124
- export declare const ALL_ENV_KEYS: readonly [...string[], "MIDSCENE_DEBUG_MODE", "MIDSCENE_DEBUG_MODEL_PROFILE", "MIDSCENE_DEBUG_MODEL_RESPONSE", "MIDSCENE_RUN_DIR", "MIDSCENE_CACHE", "MIDSCENE_LANGSMITH_DEBUG", "MIDSCENE_LANGFUSE_DEBUG", "MIDSCENE_REPORT_QUIET", "MIDSCENE_CACHE_MAX_FILENAME_LENGTH", "MIDSCENE_REPLANNING_CYCLE_LIMIT", "MIDSCENE_ADB_PATH", "MIDSCENE_ADB_REMOTE_HOST", "MIDSCENE_ADB_REMOTE_PORT", "MIDSCENE_ANDROID_IME_STRATEGY", "MIDSCENE_IOS_DEVICE_UDID", "MIDSCENE_IOS_SIMULATOR_UDID", "MIDSCENE_REPORT_TAG_NAME", "MIDSCENE_PREFERRED_LANGUAGE", "MATCH_BY_POSITION", "MIDSCENE_CHROME_PATH", "MIDSCENE_MCP_CHROME_PATH", "DOCKER_CONTAINER", "MIDSCENE_MODEL_NAME", "MIDSCENE_MODEL_INIT_CONFIG_JSON", "MIDSCENE_MODEL_EXTRA_BODY_JSON", "MIDSCENE_MODEL_API_KEY", "MIDSCENE_MODEL_BASE_URL", "MIDSCENE_MODEL_SOCKS_PROXY", "MIDSCENE_MODEL_HTTP_PROXY", "MIDSCENE_MODEL_TIMEOUT", "MIDSCENE_MODEL_TEMPERATURE", "MIDSCENE_MODEL_RETRY_COUNT", "MIDSCENE_MODEL_RETRY_INTERVAL", "MIDSCENE_MODEL_REASONING_EFFORT", "MIDSCENE_MODEL_REASONING_ENABLED", "MIDSCENE_MODEL_REASONING_BUDGET", "MIDSCENE_USE_VLM_UI_TARS", "MIDSCENE_USE_QWEN_VL", "MIDSCENE_USE_QWEN3_VL", "MIDSCENE_USE_DOUBAO_VISION", "MIDSCENE_USE_GEMINI", "MIDSCENE_USE_VL_MODEL", "OPENAI_API_KEY", "OPENAI_BASE_URL", "MIDSCENE_OPENAI_INIT_CONFIG_JSON", "MIDSCENE_OPENAI_HTTP_PROXY", "MIDSCENE_OPENAI_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_NAME", "MIDSCENE_INSIGHT_MODEL_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_HTTP_PROXY", "MIDSCENE_INSIGHT_MODEL_BASE_URL", "MIDSCENE_INSIGHT_MODEL_API_KEY", "MIDSCENE_INSIGHT_MODEL_INIT_CONFIG_JSON", "MIDSCENE_INSIGHT_MODEL_EXTRA_BODY_JSON", "MIDSCENE_INSIGHT_MODEL_TIMEOUT", "MIDSCENE_INSIGHT_MODEL_TEMPERATURE", "MIDSCENE_INSIGHT_MODEL_RETRY_COUNT", "MIDSCENE_INSIGHT_MODEL_RETRY_INTERVAL", "MIDSCENE_INSIGHT_MODEL_FAMILY", "MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT", "MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED", "MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET", "MIDSCENE_PLANNING_MODEL_NAME", "MIDSCENE_PLANNING_MODEL_SOCKS_PROXY", "MIDSCENE_PLANNING_MODEL_HTTP_PROXY", "MIDSCENE_PLANNING_MODEL_BASE_URL", "MIDSCENE_PLANNING_MODEL_API_KEY", "MIDSCENE_PLANNING_MODEL_INIT_CONFIG_JSON", "MIDSCENE_PLANNING_MODEL_EXTRA_BODY_JSON", "MIDSCENE_PLANNING_MODEL_TIMEOUT", "MIDSCENE_PLANNING_MODEL_TEMPERATURE", "MIDSCENE_PLANNING_MODEL_RETRY_COUNT", "MIDSCENE_PLANNING_MODEL_RETRY_INTERVAL", "MIDSCENE_PLANNING_MODEL_FAMILY", "MIDSCENE_PLANNING_MODEL_REASONING_EFFORT", "MIDSCENE_PLANNING_MODEL_REASONING_ENABLED", "MIDSCENE_PLANNING_MODEL_REASONING_BUDGET", "MIDSCENE_MODEL_FAMILY"];
127
+ export declare const MODEL_ENV_KEYS: readonly ["MIDSCENE_MODEL_NAME", "MIDSCENE_MODEL_INIT_CONFIG_JSON", "MIDSCENE_MODEL_EXTRA_BODY_JSON", "MIDSCENE_MODEL_API_KEY", "MIDSCENE_MODEL_BASE_URL", "MIDSCENE_MODEL_SOCKS_PROXY", "MIDSCENE_MODEL_HTTP_PROXY", "MIDSCENE_MODEL_TIMEOUT", "MIDSCENE_MODEL_TEMPERATURE", "MIDSCENE_MODEL_RETRY_COUNT", "MIDSCENE_MODEL_RETRY_INTERVAL", "MIDSCENE_MODEL_REASONING_EFFORT", "MIDSCENE_MODEL_REASONING_ENABLED", "MIDSCENE_MODEL_REASONING_BUDGET", "MIDSCENE_MODEL_RESPONSE_FORMAT", "MIDSCENE_USE_VLM_UI_TARS", "MIDSCENE_USE_QWEN_VL", "MIDSCENE_USE_QWEN3_VL", "MIDSCENE_USE_DOUBAO_VISION", "MIDSCENE_USE_GEMINI", "MIDSCENE_USE_VL_MODEL", "OPENAI_API_KEY", "OPENAI_BASE_URL", "MIDSCENE_OPENAI_INIT_CONFIG_JSON", "MIDSCENE_OPENAI_HTTP_PROXY", "MIDSCENE_OPENAI_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_NAME", "MIDSCENE_INSIGHT_MODEL_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_HTTP_PROXY", "MIDSCENE_INSIGHT_MODEL_BASE_URL", "MIDSCENE_INSIGHT_MODEL_API_KEY", "MIDSCENE_INSIGHT_MODEL_INIT_CONFIG_JSON", "MIDSCENE_INSIGHT_MODEL_EXTRA_BODY_JSON", "MIDSCENE_INSIGHT_MODEL_TIMEOUT", "MIDSCENE_INSIGHT_MODEL_TEMPERATURE", "MIDSCENE_INSIGHT_MODEL_RETRY_COUNT", "MIDSCENE_INSIGHT_MODEL_RETRY_INTERVAL", "MIDSCENE_INSIGHT_MODEL_FAMILY", "MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT", "MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED", "MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET", "MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT", "MIDSCENE_PLANNING_MODEL_NAME", "MIDSCENE_PLANNING_MODEL_SOCKS_PROXY", "MIDSCENE_PLANNING_MODEL_HTTP_PROXY", "MIDSCENE_PLANNING_MODEL_BASE_URL", "MIDSCENE_PLANNING_MODEL_API_KEY", "MIDSCENE_PLANNING_MODEL_INIT_CONFIG_JSON", "MIDSCENE_PLANNING_MODEL_EXTRA_BODY_JSON", "MIDSCENE_PLANNING_MODEL_TIMEOUT", "MIDSCENE_PLANNING_MODEL_TEMPERATURE", "MIDSCENE_PLANNING_MODEL_RETRY_COUNT", "MIDSCENE_PLANNING_MODEL_RETRY_INTERVAL", "MIDSCENE_PLANNING_MODEL_FAMILY", "MIDSCENE_PLANNING_MODEL_REASONING_EFFORT", "MIDSCENE_PLANNING_MODEL_REASONING_ENABLED", "MIDSCENE_PLANNING_MODEL_REASONING_BUDGET", "MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT", "MIDSCENE_MODEL_FAMILY"];
128
+ export declare const ALL_ENV_KEYS: readonly [...string[], "MIDSCENE_DEBUG_MODE", "MIDSCENE_DEBUG_MODEL_PROFILE", "MIDSCENE_DEBUG_MODEL_RESPONSE", "MIDSCENE_RUN_DIR", "MIDSCENE_CACHE", "MIDSCENE_LANGSMITH_DEBUG", "MIDSCENE_LANGFUSE_DEBUG", "MIDSCENE_REPORT_QUIET", "MIDSCENE_CACHE_MAX_FILENAME_LENGTH", "MIDSCENE_REPLANNING_CYCLE_LIMIT", "MIDSCENE_ADB_PATH", "MIDSCENE_ADB_REMOTE_HOST", "MIDSCENE_ADB_REMOTE_PORT", "MIDSCENE_ANDROID_IME_STRATEGY", "MIDSCENE_IOS_DEVICE_UDID", "MIDSCENE_IOS_SIMULATOR_UDID", "MIDSCENE_REPORT_TAG_NAME", "MIDSCENE_PREFERRED_LANGUAGE", "MATCH_BY_POSITION", "MIDSCENE_CHROME_PATH", "MIDSCENE_MCP_CHROME_PATH", "DOCKER_CONTAINER", "MIDSCENE_MODEL_NAME", "MIDSCENE_MODEL_INIT_CONFIG_JSON", "MIDSCENE_MODEL_EXTRA_BODY_JSON", "MIDSCENE_MODEL_API_KEY", "MIDSCENE_MODEL_BASE_URL", "MIDSCENE_MODEL_SOCKS_PROXY", "MIDSCENE_MODEL_HTTP_PROXY", "MIDSCENE_MODEL_TIMEOUT", "MIDSCENE_MODEL_TEMPERATURE", "MIDSCENE_MODEL_RETRY_COUNT", "MIDSCENE_MODEL_RETRY_INTERVAL", "MIDSCENE_MODEL_REASONING_EFFORT", "MIDSCENE_MODEL_REASONING_ENABLED", "MIDSCENE_MODEL_REASONING_BUDGET", "MIDSCENE_MODEL_RESPONSE_FORMAT", "MIDSCENE_USE_VLM_UI_TARS", "MIDSCENE_USE_QWEN_VL", "MIDSCENE_USE_QWEN3_VL", "MIDSCENE_USE_DOUBAO_VISION", "MIDSCENE_USE_GEMINI", "MIDSCENE_USE_VL_MODEL", "OPENAI_API_KEY", "OPENAI_BASE_URL", "MIDSCENE_OPENAI_INIT_CONFIG_JSON", "MIDSCENE_OPENAI_HTTP_PROXY", "MIDSCENE_OPENAI_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_NAME", "MIDSCENE_INSIGHT_MODEL_SOCKS_PROXY", "MIDSCENE_INSIGHT_MODEL_HTTP_PROXY", "MIDSCENE_INSIGHT_MODEL_BASE_URL", "MIDSCENE_INSIGHT_MODEL_API_KEY", "MIDSCENE_INSIGHT_MODEL_INIT_CONFIG_JSON", "MIDSCENE_INSIGHT_MODEL_EXTRA_BODY_JSON", "MIDSCENE_INSIGHT_MODEL_TIMEOUT", "MIDSCENE_INSIGHT_MODEL_TEMPERATURE", "MIDSCENE_INSIGHT_MODEL_RETRY_COUNT", "MIDSCENE_INSIGHT_MODEL_RETRY_INTERVAL", "MIDSCENE_INSIGHT_MODEL_FAMILY", "MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT", "MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED", "MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET", "MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT", "MIDSCENE_PLANNING_MODEL_NAME", "MIDSCENE_PLANNING_MODEL_SOCKS_PROXY", "MIDSCENE_PLANNING_MODEL_HTTP_PROXY", "MIDSCENE_PLANNING_MODEL_BASE_URL", "MIDSCENE_PLANNING_MODEL_API_KEY", "MIDSCENE_PLANNING_MODEL_INIT_CONFIG_JSON", "MIDSCENE_PLANNING_MODEL_EXTRA_BODY_JSON", "MIDSCENE_PLANNING_MODEL_TIMEOUT", "MIDSCENE_PLANNING_MODEL_TEMPERATURE", "MIDSCENE_PLANNING_MODEL_RETRY_COUNT", "MIDSCENE_PLANNING_MODEL_RETRY_INTERVAL", "MIDSCENE_PLANNING_MODEL_FAMILY", "MIDSCENE_PLANNING_MODEL_REASONING_EFFORT", "MIDSCENE_PLANNING_MODEL_REASONING_ENABLED", "MIDSCENE_PLANNING_MODEL_REASONING_BUDGET", "MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT", "MIDSCENE_MODEL_FAMILY"];
125
129
  export type TEnvKeys = (typeof ALL_ENV_KEYS)[number];
126
130
  export type TGlobalConfig = Record<TEnvKeys, string | undefined>;
127
131
  /**
@@ -174,6 +178,7 @@ export interface IModelConfigForDefault {
174
178
  [MIDSCENE_MODEL_REASONING_EFFORT]?: string;
175
179
  [MIDSCENE_MODEL_REASONING_ENABLED]?: string;
176
180
  [MIDSCENE_MODEL_REASONING_BUDGET]?: string;
181
+ [MIDSCENE_MODEL_RESPONSE_FORMAT]?: TModelResponseFormat;
177
182
  }
178
183
  export interface IModelConfigForDefaultLegacy {
179
184
  [MIDSCENE_MODEL_NAME]: string;
@@ -262,6 +267,8 @@ export interface IModelConfig {
262
267
  /**
263
268
  * Number of retries when AI call fails.
264
269
  * Default is 1 (retry once after failure).
270
+ * Retries occur on HTTP errors or when the model response cannot be
271
+ * structurally parsed.
265
272
  */
266
273
  retryCount?: number;
267
274
  /**
@@ -285,6 +292,11 @@ export interface IModelConfig {
285
292
  * Passed through to model-family-specific parameters (e.g., thinking_budget for qwen).
286
293
  */
287
294
  reasoningBudget?: number;
295
+ /**
296
+ * Response format strategy. "auto" lets the model adapter enable a
297
+ * provider-supported structured response format for eligible intents.
298
+ */
299
+ responseFormat?: TModelResponseFormat;
288
300
  /**
289
301
  * Model family - unified model configuration
290
302
  * Maps directly to model families like 'qwen2.5-vl', 'qwen3-vl', 'doubao-vision', 'doubao-seed', etc.
@@ -1,17 +1,17 @@
1
1
  export default function getPhoton(): Promise<{
2
- PhotonImage: typeof import('@silvia-odwyer/photon-node').PhotonImage;
3
- SamplingFilter: typeof import('@silvia-odwyer/photon-node').SamplingFilter;
4
- resize: typeof import('@silvia-odwyer/photon-node').resize;
5
- crop: typeof import('@silvia-odwyer/photon-node').crop;
6
- open_image: typeof import('@silvia-odwyer/photon-node').open_image;
7
- base64_to_image: typeof import('@silvia-odwyer/photon-node').base64_to_image;
8
- padding_uniform: typeof import('@silvia-odwyer/photon-node').padding_uniform;
9
- padding_left: typeof import('@silvia-odwyer/photon-node').padding_left;
10
- padding_right: typeof import('@silvia-odwyer/photon-node').padding_right;
11
- padding_top: typeof import('@silvia-odwyer/photon-node').padding_top;
12
- padding_bottom: typeof import('@silvia-odwyer/photon-node').padding_bottom;
13
- watermark: typeof import('@silvia-odwyer/photon-node').watermark;
14
- Rgba: typeof import('@silvia-odwyer/photon-node').Rgba;
2
+ PhotonImage: typeof import('@silvia-odwyer/photon').PhotonImage;
3
+ SamplingFilter: typeof import('@silvia-odwyer/photon').SamplingFilter;
4
+ resize: typeof import('@silvia-odwyer/photon').resize;
5
+ crop: typeof import('@silvia-odwyer/photon').crop;
6
+ open_image: typeof import('@silvia-odwyer/photon').open_image;
7
+ base64_to_image: typeof import('@silvia-odwyer/photon').base64_to_image;
8
+ padding_uniform: typeof import('@silvia-odwyer/photon').padding_uniform;
9
+ padding_left: typeof import('@silvia-odwyer/photon').padding_left;
10
+ padding_right: typeof import('@silvia-odwyer/photon').padding_right;
11
+ padding_top: typeof import('@silvia-odwyer/photon').padding_top;
12
+ padding_bottom: typeof import('@silvia-odwyer/photon').padding_bottom;
13
+ watermark: typeof import('@silvia-odwyer/photon').watermark;
14
+ Rgba: typeof import('@silvia-odwyer/photon').Rgba;
15
15
  }>;
16
16
  /**
17
17
  * Check if we're using the Canvas fallback instead of Photon
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import type { PhotonImage as PhotonImageType } from '@silvia-odwyer/photon-node';
2
+ import type { PhotonImage as PhotonImageType } from '@silvia-odwyer/photon';
3
3
  import type { Rect } from '../types';
4
4
  /**
5
5
  * Saves a Base64-encoded image to a file
@@ -13,6 +13,18 @@ export interface MidsceneRecorderPageInfo {
13
13
  width: number;
14
14
  height: number;
15
15
  }
16
+ /**
17
+ * A screenshot stored outside the recording event payload.
18
+ *
19
+ * Recorder events are persisted in the Studio renderer. Keeping full data
20
+ * URLs there makes long recordings retain every screenshot in the renderer
21
+ * heap, so screenshot bytes live in the Playground run directory instead.
22
+ */
23
+ export interface MidsceneRecorderScreenshotAssetRef {
24
+ id: string;
25
+ mimeType: string;
26
+ bytes: number;
27
+ }
16
28
  export interface MidsceneRecorderEvent {
17
29
  type: MidsceneRecorderEventType;
18
30
  source?: MidsceneRecorderSourceKind;
@@ -25,6 +37,8 @@ export interface MidsceneRecorderEvent {
25
37
  pageInfo: MidsceneRecorderPageInfo;
26
38
  screenshotBefore?: string;
27
39
  screenshotAfter?: string;
40
+ /** The single screenshot retained for AI description and Markdown export. */
41
+ screenshotAsset?: MidsceneRecorderScreenshotAssetRef;
28
42
  semantic?: MidsceneRecorderSemantic;
29
43
  elementDescription?: string;
30
44
  descriptionLoading?: boolean;
@@ -6,9 +6,7 @@ import type { z } from 'zod';
6
6
  export declare function unwrapZodField(field: unknown): unknown;
7
7
  /**
8
8
  * Check if a field is a Midscene locator field
9
- * Checks for either:
10
- * 1. midscene_location_field_flag in shape (result schema)
11
- * 2. prompt field in shape (input schema)
9
+ * Locator input schemas are identified by their prompt field.
12
10
  */
13
11
  export declare function isMidsceneLocatorField(field: unknown): boolean;
14
12
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/shared",
3
- "version": "1.10.5",
3
+ "version": "1.10.6-beta-20260717061640.0",
4
4
  "repository": "https://github.com/web-infra-dev/midscene",
5
5
  "homepage": "https://midscenejs.com/",
6
6
  "types": "./dist/types/index.d.ts",
@@ -95,7 +95,6 @@
95
95
  ],
96
96
  "dependencies": {
97
97
  "@silvia-odwyer/photon": "0.3.3",
98
- "@silvia-odwyer/photon-node": "0.3.3",
99
98
  "debug": "4.4.0",
100
99
  "js-sha256": "0.11.0",
101
100
  "sharp": "^0.34.3",
@@ -9,6 +9,7 @@ import {
9
9
  MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET,
10
10
  MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT,
11
11
  MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED,
12
+ MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT,
12
13
  MIDSCENE_INSIGHT_MODEL_RETRY_COUNT,
13
14
  MIDSCENE_INSIGHT_MODEL_RETRY_INTERVAL,
14
15
  MIDSCENE_INSIGHT_MODEL_SOCKS_PROXY,
@@ -24,6 +25,7 @@ import {
24
25
  MIDSCENE_MODEL_REASONING_BUDGET,
25
26
  MIDSCENE_MODEL_REASONING_EFFORT,
26
27
  MIDSCENE_MODEL_REASONING_ENABLED,
28
+ MIDSCENE_MODEL_RESPONSE_FORMAT,
27
29
  MIDSCENE_MODEL_RETRY_COUNT,
28
30
  MIDSCENE_MODEL_RETRY_INTERVAL,
29
31
  MIDSCENE_MODEL_SOCKS_PROXY,
@@ -42,6 +44,7 @@ import {
42
44
  MIDSCENE_PLANNING_MODEL_REASONING_BUDGET,
43
45
  MIDSCENE_PLANNING_MODEL_REASONING_EFFORT,
44
46
  MIDSCENE_PLANNING_MODEL_REASONING_ENABLED,
47
+ MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT,
45
48
  MIDSCENE_PLANNING_MODEL_RETRY_COUNT,
46
49
  MIDSCENE_PLANNING_MODEL_RETRY_INTERVAL,
47
50
  MIDSCENE_PLANNING_MODEL_SOCKS_PROXY,
@@ -85,6 +88,7 @@ interface IModelConfigKeys {
85
88
  reasoningEffort: string;
86
89
  reasoningEnabled: string;
87
90
  reasoningBudget: string;
91
+ responseFormat: string;
88
92
  }
89
93
 
90
94
  export const INSIGHT_MODEL_CONFIG_KEYS: IModelConfigKeys = {
@@ -121,6 +125,7 @@ export const INSIGHT_MODEL_CONFIG_KEYS: IModelConfigKeys = {
121
125
  reasoningEffort: MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT,
122
126
  reasoningEnabled: MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED,
123
127
  reasoningBudget: MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET,
128
+ responseFormat: MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT,
124
129
  } as const;
125
130
 
126
131
  export const PLANNING_MODEL_CONFIG_KEYS: IModelConfigKeys = {
@@ -157,6 +162,7 @@ export const PLANNING_MODEL_CONFIG_KEYS: IModelConfigKeys = {
157
162
  reasoningEffort: MIDSCENE_PLANNING_MODEL_REASONING_EFFORT,
158
163
  reasoningEnabled: MIDSCENE_PLANNING_MODEL_REASONING_ENABLED,
159
164
  reasoningBudget: MIDSCENE_PLANNING_MODEL_REASONING_BUDGET,
165
+ responseFormat: MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT,
160
166
  } as const;
161
167
 
162
168
  // modelConfig return default
@@ -194,6 +200,7 @@ export const DEFAULT_MODEL_CONFIG_KEYS: IModelConfigKeys = {
194
200
  reasoningEffort: MIDSCENE_MODEL_REASONING_EFFORT,
195
201
  reasoningEnabled: MIDSCENE_MODEL_REASONING_ENABLED,
196
202
  reasoningBudget: MIDSCENE_MODEL_REASONING_BUDGET,
203
+ responseFormat: MIDSCENE_MODEL_RESPONSE_FORMAT,
197
204
  } as const;
198
205
 
199
206
  // read from process.env
@@ -231,4 +238,5 @@ export const DEFAULT_MODEL_CONFIG_KEYS_LEGACY: IModelConfigKeys = {
231
238
  reasoningEffort: MIDSCENE_MODEL_REASONING_EFFORT,
232
239
  reasoningEnabled: MIDSCENE_MODEL_REASONING_ENABLED,
233
240
  reasoningBudget: MIDSCENE_MODEL_REASONING_BUDGET,
241
+ responseFormat: MIDSCENE_MODEL_RESPONSE_FORMAT,
234
242
  } as const;
@@ -288,6 +288,14 @@ export const parseOpenaiSdkConfig = ({
288
288
  const num = Number(val);
289
289
  return Number.isFinite(num) ? num : undefined;
290
290
  })(),
291
+ responseFormat: (() => {
292
+ const val = provider[keys.responseFormat]?.trim()?.toLowerCase();
293
+ if (!val || val === 'auto') return 'auto';
294
+ if (val === 'none') return 'none';
295
+ throw new Error(
296
+ `${keys.responseFormat} must be one of: none, auto. Got: ${val}`,
297
+ );
298
+ })(),
291
299
  };
292
300
  };
293
301
 
package/src/env/types.ts CHANGED
@@ -35,8 +35,10 @@ export const MIDSCENE_MODEL_REASONING_ENABLED =
35
35
  'MIDSCENE_MODEL_REASONING_ENABLED';
36
36
  export const MIDSCENE_MODEL_REASONING_BUDGET =
37
37
  'MIDSCENE_MODEL_REASONING_BUDGET';
38
+ export const MIDSCENE_MODEL_RESPONSE_FORMAT = 'MIDSCENE_MODEL_RESPONSE_FORMAT';
38
39
 
39
40
  export type TModelReasoningEnabled = boolean | 'default';
41
+ export type TModelResponseFormat = 'none' | 'auto';
40
42
 
41
43
  /**
42
44
  * @deprecated Use MIDSCENE_MODEL_API_KEY instead. This is kept for backward compatibility.
@@ -117,6 +119,8 @@ export const MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED =
117
119
  'MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED';
118
120
  export const MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET =
119
121
  'MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET';
122
+ export const MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT =
123
+ 'MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT';
120
124
 
121
125
  // PLANNING
122
126
  export const MIDSCENE_PLANNING_MODEL_NAME = 'MIDSCENE_PLANNING_MODEL_NAME';
@@ -147,6 +151,8 @@ export const MIDSCENE_PLANNING_MODEL_REASONING_ENABLED =
147
151
  'MIDSCENE_PLANNING_MODEL_REASONING_ENABLED';
148
152
  export const MIDSCENE_PLANNING_MODEL_REASONING_BUDGET =
149
153
  'MIDSCENE_PLANNING_MODEL_REASONING_BUDGET';
154
+ export const MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT =
155
+ 'MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT';
150
156
  export const MIDSCENE_MODEL_FAMILY = 'MIDSCENE_MODEL_FAMILY';
151
157
 
152
158
  /**
@@ -224,6 +230,7 @@ export const MODEL_ENV_KEYS = [
224
230
  MIDSCENE_MODEL_REASONING_EFFORT,
225
231
  MIDSCENE_MODEL_REASONING_ENABLED,
226
232
  MIDSCENE_MODEL_REASONING_BUDGET,
233
+ MIDSCENE_MODEL_RESPONSE_FORMAT,
227
234
  MIDSCENE_USE_VLM_UI_TARS,
228
235
  MIDSCENE_USE_QWEN_VL,
229
236
  MIDSCENE_USE_QWEN3_VL,
@@ -252,6 +259,7 @@ export const MODEL_ENV_KEYS = [
252
259
  MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT,
253
260
  MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED,
254
261
  MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET,
262
+ MIDSCENE_INSIGHT_MODEL_RESPONSE_FORMAT,
255
263
  // PLANNING
256
264
  MIDSCENE_PLANNING_MODEL_NAME,
257
265
  MIDSCENE_PLANNING_MODEL_SOCKS_PROXY,
@@ -268,6 +276,7 @@ export const MODEL_ENV_KEYS = [
268
276
  MIDSCENE_PLANNING_MODEL_REASONING_EFFORT,
269
277
  MIDSCENE_PLANNING_MODEL_REASONING_ENABLED,
270
278
  MIDSCENE_PLANNING_MODEL_REASONING_BUDGET,
279
+ MIDSCENE_PLANNING_MODEL_RESPONSE_FORMAT,
271
280
  MIDSCENE_MODEL_FAMILY,
272
281
  ] as const;
273
282
 
@@ -390,6 +399,8 @@ export interface IModelConfigForDefault {
390
399
  [MIDSCENE_MODEL_REASONING_ENABLED]?: string;
391
400
  // reasoning budget (number as string)
392
401
  [MIDSCENE_MODEL_REASONING_BUDGET]?: string;
402
+ // Response format strategy (none/auto)
403
+ [MIDSCENE_MODEL_RESPONSE_FORMAT]?: TModelResponseFormat;
393
404
  }
394
405
 
395
406
  export interface IModelConfigForDefaultLegacy {
@@ -490,6 +501,8 @@ export interface IModelConfig {
490
501
  /**
491
502
  * Number of retries when AI call fails.
492
503
  * Default is 1 (retry once after failure).
504
+ * Retries occur on HTTP errors or when the model response cannot be
505
+ * structurally parsed.
493
506
  */
494
507
  retryCount?: number;
495
508
  /**
@@ -513,6 +526,11 @@ export interface IModelConfig {
513
526
  * Passed through to model-family-specific parameters (e.g., thinking_budget for qwen).
514
527
  */
515
528
  reasoningBudget?: number;
529
+ /**
530
+ * Response format strategy. "auto" lets the model adapter enable a
531
+ * provider-supported structured response format for eligible intents.
532
+ */
533
+ responseFormat?: TModelResponseFormat;
516
534
  /**
517
535
  * Model family - unified model configuration
518
536
  * Maps directly to model families like 'qwen2.5-vl', 'qwen3-vl', 'doubao-vision', 'doubao-seed', etc.