@aiscene/shared 1.6.0 → 1.7.2

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 (97) hide show
  1. package/dist/es/cli/cli-runner.mjs +13 -1
  2. package/dist/es/constants/example-code.mjs +25 -21
  3. package/dist/es/env/parse-model-config.mjs +11 -1
  4. package/dist/es/env/types.mjs +1 -0
  5. package/dist/es/extractor/dom-util.mjs +9 -5
  6. package/dist/es/extractor/locator.mjs +6 -6
  7. package/dist/es/extractor/util.mjs +6 -6
  8. package/dist/es/img/canvas-fallback.mjs +1 -1
  9. package/dist/es/index.mjs +1 -3
  10. package/dist/es/logger.mjs +1 -7
  11. package/dist/es/mcp/base-server.mjs +1 -1
  12. package/dist/es/mcp/base-tools.mjs +2 -2
  13. package/dist/es/mcp/tool-generator.mjs +131 -57
  14. package/dist/es/node/fs.mjs +1 -1
  15. package/dist/es/us-keyboard-layout.mjs +1 -1
  16. package/dist/es/utils.mjs +4 -9
  17. package/dist/lib/baseDB.js +1 -1
  18. package/dist/lib/cli/cli-runner.js +14 -2
  19. package/dist/lib/cli/index.js +1 -1
  20. package/dist/lib/common.js +1 -1
  21. package/dist/lib/constants/example-code.js +26 -22
  22. package/dist/lib/constants/index.js +1 -1
  23. package/dist/lib/env/basic.js +1 -1
  24. package/dist/lib/env/constants.js +1 -1
  25. package/dist/lib/env/global-config-manager.js +1 -1
  26. package/dist/lib/env/helper.js +1 -1
  27. package/dist/lib/env/index.js +1 -1
  28. package/dist/lib/env/init-debug.js +1 -1
  29. package/dist/lib/env/model-config-manager.js +1 -1
  30. package/dist/lib/env/parse-model-config.js +12 -2
  31. package/dist/lib/env/types.js +2 -1
  32. package/dist/lib/env/utils.js +1 -1
  33. package/dist/lib/extractor/constants.js +1 -1
  34. package/dist/lib/extractor/dom-util.js +10 -6
  35. package/dist/lib/extractor/index.js +1 -1
  36. package/dist/lib/extractor/locator.js +7 -7
  37. package/dist/lib/extractor/tree.js +1 -1
  38. package/dist/lib/extractor/util.js +7 -7
  39. package/dist/lib/extractor/web-extractor.js +1 -1
  40. package/dist/lib/img/box-select.js +1 -1
  41. package/dist/lib/img/canvas-fallback.js +2 -2
  42. package/dist/lib/img/get-photon.js +1 -1
  43. package/dist/lib/img/get-sharp.js +1 -1
  44. package/dist/lib/img/index.js +1 -1
  45. package/dist/lib/img/info.js +1 -1
  46. package/dist/lib/img/transform.js +1 -1
  47. package/dist/lib/index.js +3 -14
  48. package/dist/lib/logger.js +1 -10
  49. package/dist/lib/mcp/base-server.js +3 -3
  50. package/dist/lib/mcp/base-tools.js +5 -5
  51. package/dist/lib/mcp/chrome-path.js +1 -1
  52. package/dist/lib/mcp/index.js +1 -1
  53. package/dist/lib/mcp/inject-report-html-plugin.js +1 -1
  54. package/dist/lib/mcp/launcher-helper.js +1 -1
  55. package/dist/lib/mcp/tool-generator.js +134 -60
  56. package/dist/lib/mcp/types.js +1 -1
  57. package/dist/lib/node/fs.js +2 -2
  58. package/dist/lib/node/index.js +1 -1
  59. package/dist/lib/node/port.js +1 -1
  60. package/dist/lib/polyfills/async-hooks.js +1 -1
  61. package/dist/lib/polyfills/index.js +1 -1
  62. package/dist/lib/types/index.js +1 -1
  63. package/dist/lib/us-keyboard-layout.js +2 -2
  64. package/dist/lib/utils.js +4 -12
  65. package/dist/lib/zod-schema-utils.js +1 -1
  66. package/dist/types/cli/cli-runner.d.ts +8 -0
  67. package/dist/types/cli/index.d.ts +1 -1
  68. package/dist/types/constants/example-code.d.ts +1 -1
  69. package/dist/types/env/types.d.ts +1 -1
  70. package/dist/types/extractor/dom-util.d.ts +5 -4
  71. package/dist/types/index.d.ts +0 -2
  72. package/dist/types/logger.d.ts +0 -1
  73. package/dist/types/mcp/types.d.ts +1 -0
  74. package/dist/types/utils.d.ts +0 -3
  75. package/package.json +2 -2
  76. package/src/cli/cli-runner.ts +26 -1
  77. package/src/cli/index.ts +1 -1
  78. package/src/constants/example-code.ts +25 -21
  79. package/src/env/parse-model-config.ts +21 -1
  80. package/src/env/types.ts +2 -0
  81. package/src/extractor/dom-util.ts +10 -5
  82. package/src/img/canvas-fallback.ts +1 -1
  83. package/src/index.ts +0 -7
  84. package/src/logger.ts +0 -11
  85. package/src/mcp/base-server.ts +1 -1
  86. package/src/mcp/base-tools.ts +2 -2
  87. package/src/mcp/tool-generator.ts +218 -67
  88. package/src/mcp/types.ts +4 -0
  89. package/src/utils.ts +0 -17
  90. package/dist/es/build/copy-static.mjs +0 -31
  91. package/dist/es/build/rspack-config.mjs +0 -4
  92. package/dist/lib/build/copy-static.js +0 -79
  93. package/dist/lib/build/rspack-config.js +0 -38
  94. package/dist/types/build/copy-static.d.ts +0 -31
  95. package/dist/types/build/rspack-config.d.ts +0 -8
  96. package/src/build/copy-static.ts +0 -68
  97. package/src/build/rspack-config.ts +0 -12
@@ -89,7 +89,7 @@ function printHelp(scriptName, commands, version) {
89
89
  }
90
90
  console.log(`\nUsage: ${scriptName} <command> [options]\n`);
91
91
  console.log('Commands:');
92
- for (const { name, def } of commands)console.log(` ${name.padEnd(30)} ${def.description}`);
92
+ for (const { name, def } of commands.filter((command)=>!command.hidden))console.log(` ${name.padEnd(30)} ${def.description}`);
93
93
  console.log(` ${'version'.padEnd(30)} Show CLI version`);
94
94
  console.log(`\nRun "${scriptName} <command> --help" for more info.`);
95
95
  }
@@ -105,6 +105,18 @@ async function runToolsCLI(tools, scriptName, options) {
105
105
  name: removePrefix(def.name, options?.stripPrefix).toLowerCase(),
106
106
  def
107
107
  }));
108
+ if (options?.extraCommands?.length) commands.push(...options.extraCommands.flatMap((cmd)=>[
109
+ {
110
+ name: cmd.name.toLowerCase(),
111
+ def: cmd.def,
112
+ hidden: cmd.hidden
113
+ },
114
+ ...(cmd.aliases ?? []).map((alias)=>({
115
+ name: alias.toLowerCase(),
116
+ def: cmd.def,
117
+ hidden: true
118
+ }))
119
+ ]));
108
120
  const cliVersion = options?.version;
109
121
  const [commandName, ...restArgs] = rawArgs;
110
122
  if (!commandName || '--help' === commandName || '-h' === commandName) {
@@ -80,6 +80,23 @@ test('ai shop', async ({
80
80
  });
81
81
  `;
82
82
  const YAML_EXAMPLE_CODE = `
83
+ CRITICAL - YAML Indentation Rules:
84
+ For actions with additional parameters (aiScroll, aiInput, aiKeyboardPress), the parameters must be SIBLING keys at the SAME indentation level as the action key, NOT nested children indented further.
85
+ CORRECT (parameters align with the action key):
86
+ - aiScroll:
87
+ direction: 'down'
88
+ scrollType: 'singleAction'
89
+ distance: 500
90
+ locate: "main content area"
91
+ - aiInput: 'text value'
92
+ locate: 'input field description'
93
+ WRONG (parameters are indented further than the action key, DO NOT do this):
94
+ - aiScroll:
95
+ direction: 'down'
96
+ scrollType: 'singleAction'
97
+ - aiInput: 'text value'
98
+ locate: 'input field description'
99
+
83
100
  1. Format:
84
101
 
85
102
  web:
@@ -91,13 +108,18 @@ tasks:
91
108
  - name: "descriptive task name"
92
109
  flow:
93
110
  - aiTap: "element description"
111
+ xpath: '/html/body/div[1]/button[1]'
94
112
  - aiInput: 'text value'
95
113
  locate: 'input field description'
114
+ xpath: '/html/body/div[1]/input[1]'
96
115
  - aiScroll:
97
- direction: down/up
98
- scrollType: scrollToBottom/scrollToTop/singleAction
116
+ direction: 'down'
117
+ scrollType: 'singleAction'
118
+ distance: 500
119
+ locate: "scrollable area description"
120
+ xpath: '/html/body/div[1]/main[1]'
99
121
  - aiAssert: "expected state"
100
- - sleep: milliseconds
122
+ - sleep: 1000
101
123
 
102
124
  2. Action Types:
103
125
  - aiTap: for clicks (natural language targeting)
@@ -106,24 +128,6 @@ tasks:
106
128
  - aiAssert: for validations
107
129
  - sleep: for delays (milliseconds)
108
130
 
109
- 3. Best Practices:
110
- - Group related actions into logical tasks
111
- - Use natural language descriptions
112
- - Add deepLocate: true for complex interactions
113
- - Keep task names concise but descriptive
114
-
115
- 4. CRITICAL - YAML Indentation Rules:
116
- - For actions with additional parameters (aiScroll, aiInput, aiKeyboardPress), the parameters must be SIBLING keys, NOT nested children
117
- - Parameters like direction, scrollType, locate must align with the action key, not indented further
118
- - CORRECT indentation example:
119
- - aiScroll:
120
- direction: down
121
- scrollType: singleAction
122
- - WRONG indentation (DO NOT do this):
123
- - aiScroll:
124
- direction: down
125
- scrollType: singleAction
126
-
127
131
 
128
132
 
129
133
  YAML type
@@ -43,6 +43,16 @@ const getModelDescription = (modelFamily, uiTarsModelVersion)=>{
43
43
  if (modelFamily) return `${modelFamily} mode`;
44
44
  return '';
45
45
  };
46
+ const normalizeOpenaiExtraConfig = (config)=>{
47
+ if (!config || 'object' != typeof config || Array.isArray(config)) return;
48
+ const { defaultHeaders, extra_headers, extraHeaders, ...rest } = config;
49
+ const headers = defaultHeaders ?? extra_headers ?? extraHeaders;
50
+ if (void 0 !== headers) return {
51
+ ...rest,
52
+ defaultHeaders: headers
53
+ };
54
+ return rest;
55
+ };
46
56
  const parseOpenaiSdkConfig = ({ keys, provider, useLegacyLogic = false })=>{
47
57
  initDebugConfig();
48
58
  const debugLog = getDebug('ai:config');
@@ -73,7 +83,7 @@ const parseOpenaiSdkConfig = ({ keys, provider, useLegacyLogic = false })=>{
73
83
  httpProxy,
74
84
  openaiBaseURL,
75
85
  openaiApiKey,
76
- openaiExtraConfig,
86
+ openaiExtraConfig: normalizeOpenaiExtraConfig(openaiExtraConfig),
77
87
  extraBody,
78
88
  modelFamily,
79
89
  uiTarsModelVersion,
@@ -193,6 +193,7 @@ const MODEL_FAMILY_VALUES = [
193
193
  'qwen2.5-vl',
194
194
  'qwen3-vl',
195
195
  'qwen3.5',
196
+ 'qwen3.6',
196
197
  'vlm-ui-tars',
197
198
  'vlm-ui-tars-doubao',
198
199
  'vlm-ui-tars-doubao-1.5',
@@ -81,12 +81,16 @@ function generateElementByPoint(center, description, edgeSize = 8) {
81
81
  description: description || ''
82
82
  };
83
83
  }
84
- function generateElementByRect(sourceRect, description, edgeSize = 8) {
84
+ function generateElementByRect(sourceRect, description, _edgeSize = 8) {
85
85
  const centerX = sourceRect.left + Math.floor((sourceRect.width - 1) / 2);
86
86
  const centerY = sourceRect.top + Math.floor((sourceRect.height - 1) / 2);
87
- return generateElementByPoint([
88
- centerX,
89
- centerY
90
- ], description, edgeSize);
87
+ return {
88
+ rect: sourceRect,
89
+ center: [
90
+ centerX,
91
+ centerY
92
+ ],
93
+ description: description || ''
94
+ };
91
95
  }
92
96
  export { generateElementByPoint, generateElementByRect, isAElement, isButtonElement, isContainerElement, isFormElement, isImgElement, isNotContainerElement, isSvgElement, isTextElement };
@@ -143,8 +143,8 @@ function getXpathsById(id) {
143
143
  ];
144
144
  }
145
145
  function getXpathsByPoint(point, isOrderSensitive) {
146
- let currentWindow = 'undefined' != typeof window ? window : void 0;
147
- let currentDocument = 'undefined' != typeof document ? document : void 0;
146
+ let currentWindow = "u" > typeof window ? window : void 0;
147
+ let currentDocument = "u" > typeof document ? document : void 0;
148
148
  let { left, top } = point;
149
149
  let depth = 0;
150
150
  const MAX_DEPTH = 10;
@@ -199,7 +199,7 @@ function getXpathsByPoint(point, isOrderSensitive) {
199
199
  function getNodeInfoByXpath(xpath) {
200
200
  const parts = xpath.split(SUB_XPATH_SEPARATOR).map((p)=>p.trim()).filter(Boolean);
201
201
  if (0 === parts.length) return null;
202
- let currentDocument = 'undefined' != typeof document ? document : void 0;
202
+ let currentDocument = "u" > typeof document ? document : void 0;
203
203
  let node = null;
204
204
  for(let i = 0; i < parts.length; i++){
205
205
  const currentXpath = parts[i];
@@ -227,13 +227,13 @@ function getNodeInfoByXpath(xpath) {
227
227
  function getElementInfoByXpath(xpath) {
228
228
  const node = getNodeInfoByXpath(xpath);
229
229
  if (!node) return null;
230
- let targetWindow = 'undefined' != typeof window ? window : void 0;
231
- let targetDocument = 'undefined' != typeof document ? document : void 0;
230
+ let targetWindow = "u" > typeof window ? window : void 0;
231
+ let targetDocument = "u" > typeof document ? document : void 0;
232
232
  if (node.ownerDocument?.defaultView) {
233
233
  targetWindow = node.ownerDocument.defaultView;
234
234
  targetDocument = node.ownerDocument;
235
235
  }
236
- const rootDoc = 'undefined' != typeof document ? document : null;
236
+ const rootDoc = "u" > typeof document ? document : null;
237
237
  const iframeOffset = calculateIframeOffset(node.ownerDocument ?? null, rootDoc);
238
238
  const targetWin = targetWindow;
239
239
  const targetDoc = targetDocument;
@@ -199,10 +199,10 @@ function getNodeAttributes(node, currentWindow) {
199
199
  }
200
200
  const NODE_CACHE_MAX_SIZE = 2000;
201
201
  function setNodeHashCacheListOnWindow() {
202
- if ('undefined' != typeof window) window.midsceneNodeHashCache = new Map();
202
+ if ("u" > typeof window) window.midsceneNodeHashCache = new Map();
203
203
  }
204
204
  function getNodeCacheMap() {
205
- if ('undefined' == typeof window) return;
205
+ if ("u" < typeof window) return;
206
206
  return window.midsceneNodeHashCache;
207
207
  }
208
208
  function setNodeToCacheList(node, id) {
@@ -221,7 +221,7 @@ function getNodeFromCacheList(id) {
221
221
  function midsceneGenerateHash(node, content, rect) {
222
222
  const slicedHash = generateHashId(rect, content);
223
223
  if (node) {
224
- if ('undefined' != typeof window && !getNodeCacheMap()) setNodeHashCacheListOnWindow();
224
+ if ("u" > typeof window && !getNodeCacheMap()) setNodeHashCacheListOnWindow();
225
225
  setNodeToCacheList(node, slicedHash);
226
226
  }
227
227
  return slicedHash;
@@ -230,13 +230,13 @@ function generateId(numberId) {
230
230
  return `${numberId}`;
231
231
  }
232
232
  function setGenerateHashOnWindow() {
233
- if ('undefined' != typeof window) window.midsceneGenerateHash = midsceneGenerateHash;
233
+ if ("u" > typeof window) window.midsceneGenerateHash = midsceneGenerateHash;
234
234
  }
235
235
  function setMidsceneVisibleRectOnWindow() {
236
- if ('undefined' != typeof window) window.midsceneVisibleRect = elementRect;
236
+ if ("u" > typeof window) window.midsceneVisibleRect = elementRect;
237
237
  }
238
238
  function setExtractTextWithPositionOnWindow() {
239
- if ('undefined' != typeof window) window.extractTextWithPosition = extractTextWithPosition;
239
+ if ("u" > typeof window) window.extractTextWithPosition = extractTextWithPosition;
240
240
  }
241
241
  function getTopDocument() {
242
242
  const container = document.body || document;
@@ -58,7 +58,7 @@ class CanvasImage {
58
58
  static async new_from_byteslice(bytes) {
59
59
  return new Promise((resolve, reject)=>{
60
60
  const blob = new Blob([
61
- bytes
61
+ bytes.buffer
62
62
  ], {
63
63
  type: 'image/png'
64
64
  });
package/dist/es/index.mjs CHANGED
@@ -1,4 +1,2 @@
1
- import { createCopyStaticPlugin, createPlaygroundCopyPlugin } from "./build/copy-static.mjs";
2
- import { commonIgnoreWarnings } from "./build/rspack-config.mjs";
3
1
  const src = {};
4
- export { commonIgnoreWarnings, createCopyStaticPlugin, createPlaygroundCopyPlugin, src as default };
2
+ export { src as default };
@@ -61,10 +61,4 @@ function enableDebug(topic) {
61
61
  if (ifInNode) return;
62
62
  debug.enable(`${topicPrefix}:${topic}`);
63
63
  }
64
- function cleanupLogStreams() {
65
- if (!ifInNode) return;
66
- for (const stream of logStreams.values())stream.end();
67
- logStreams.clear();
68
- debugInstances.clear();
69
- }
70
- export { cleanupLogStreams, enableDebug, getDebug };
64
+ export { enableDebug, getDebug };
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { setIsMcp } from "../utils.mjs";
2
+ import { setIsMcp } from "@aiscene/shared/utils";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -1,5 +1,5 @@
1
- import { parseBase64 } from "../img/index.mjs";
2
- import { getDebug } from "../logger.mjs";
1
+ import { parseBase64 } from "@aiscene/shared/img";
2
+ import { getDebug } from "@aiscene/shared/logger";
3
3
  import { generateCommonTools, generateToolsFromActionSpace } from "./tool-generator.mjs";
4
4
  function _define_property(obj, key, value) {
5
5
  if (key in obj) Object.defineProperty(obj, key, {
@@ -1,22 +1,21 @@
1
- import { parseBase64 } from "../img/index.mjs";
1
+ import { parseBase64 } from "@aiscene/shared/img";
2
2
  import { z } from "zod";
3
- import { getZodDescription, getZodTypeName } from "../zod-schema-utils.mjs";
3
+ import { getZodDescription, getZodTypeName, isMidsceneLocatorField, unwrapZodField } from "../zod-schema-utils.mjs";
4
4
  function getErrorMessage(error) {
5
5
  return error instanceof Error ? error.message : String(error);
6
6
  }
7
7
  function describeActionForMCP(action) {
8
8
  const actionDesc = action.description || `Execute ${action.name} action`;
9
9
  if (!action.paramSchema) return `${action.name} action, ${actionDesc}`;
10
- const schema = action.paramSchema;
11
- const isZodObjectType = schema._def?.typeName === 'ZodObject';
12
- if (!isZodObjectType || !schema.shape) {
13
- const typeName = getZodTypeName(schema);
14
- const description = getZodDescription(schema);
10
+ const shape = getZodObjectShape(action.paramSchema);
11
+ if (!shape) {
12
+ const typeName = getZodTypeName(action.paramSchema);
13
+ const description = getZodDescription(action.paramSchema);
15
14
  const paramDesc = description ? `${typeName} - ${description}` : typeName;
16
15
  return `${action.name} action, ${actionDesc}. Parameter: ${paramDesc}`;
17
16
  }
18
17
  const paramDescriptions = [];
19
- for (const [key, field] of Object.entries(schema.shape))if (field && 'object' == typeof field) {
18
+ for (const [key, field] of Object.entries(shape))if (field && 'object' == typeof field) {
20
19
  const isFieldOptional = 'function' == typeof field.isOptional && field.isOptional();
21
20
  const typeName = getZodTypeName(field);
22
21
  const description = getZodDescription(field);
@@ -30,9 +29,6 @@ function describeActionForMCP(action) {
30
29
  function isZodOptional(value) {
31
30
  return '_def' in value && value._def?.typeName === 'ZodOptional';
32
31
  }
33
- function isZodObject(value) {
34
- return '_def' in value && value._def?.typeName === 'ZodObject' && 'shape' in value;
35
- }
36
32
  function unwrapOptional(value) {
37
33
  if (isZodOptional(value)) return {
38
34
  innerValue: value._def.innerType,
@@ -43,24 +39,31 @@ function unwrapOptional(value) {
43
39
  isOptional: false
44
40
  };
45
41
  }
46
- function isLocateField(value) {
47
- if (!isZodObject(value)) return false;
48
- return 'prompt' in value.shape;
42
+ function getZodObjectShape(value) {
43
+ if (!value) return;
44
+ const actualValue = unwrapZodField(value);
45
+ if (actualValue._def?.typeName !== 'ZodObject') return;
46
+ if ('function' == typeof actualValue._def.shape) return actualValue._def.shape();
47
+ return actualValue.shape;
48
+ }
49
+ function isRecord(value) {
50
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
49
51
  }
50
- function makePromptOptional(value, wrapInOptional) {
52
+ function makePromptOptional(shape, wrapInOptional) {
51
53
  const newShape = {
52
- ...value.shape
54
+ ...shape
53
55
  };
54
- newShape.prompt = value.shape.prompt.optional();
56
+ newShape.prompt = shape.prompt.optional();
55
57
  let newSchema = z.object(newShape).passthrough();
56
58
  if (wrapInOptional) newSchema = newSchema.optional();
57
59
  return newSchema;
58
60
  }
59
61
  function transformSchemaField(key, value) {
60
62
  const { innerValue, isOptional } = unwrapOptional(value);
61
- if (isZodObject(innerValue) && isLocateField(innerValue)) return [
63
+ const shape = getZodObjectShape(innerValue);
64
+ if (shape && isMidsceneLocatorField(innerValue)) return [
62
65
  key,
63
- makePromptOptional(innerValue, isOptional)
66
+ makePromptOptional(shape, isOptional)
64
67
  ];
65
68
  return [
66
69
  key,
@@ -69,9 +72,66 @@ function transformSchemaField(key, value) {
69
72
  }
70
73
  function extractActionSchema(paramSchema) {
71
74
  if (!paramSchema) return {};
72
- const schema = paramSchema;
73
- if (!isZodObject(schema)) return schema;
74
- return Object.fromEntries(Object.entries(schema.shape).map(([key, value])=>transformSchemaField(key, value)));
75
+ const shape = getZodObjectShape(paramSchema);
76
+ if (!shape) return paramSchema;
77
+ return Object.fromEntries(Object.entries(shape).map(([key, value])=>transformSchemaField(key, value)));
78
+ }
79
+ function getPromptText(prompt) {
80
+ if ('string' == typeof prompt) return prompt;
81
+ if (isRecord(prompt) && 'string' == typeof prompt.prompt) return prompt.prompt;
82
+ }
83
+ function moveLocateExtrasIntoPrompt(value, locateFieldKeys) {
84
+ const promptText = getPromptText(value.prompt);
85
+ if (!promptText) return value;
86
+ const normalizedPrompt = isRecord(value.prompt) ? {
87
+ ...value.prompt
88
+ } : {
89
+ prompt: promptText
90
+ };
91
+ const normalizedLocate = {};
92
+ let movedExtraField = false;
93
+ for (const [key, fieldValue] of Object.entries(value))if ('prompt' !== key) {
94
+ if (locateFieldKeys.has(key)) {
95
+ normalizedLocate[key] = fieldValue;
96
+ continue;
97
+ }
98
+ movedExtraField = true;
99
+ if (!(key in normalizedPrompt)) normalizedPrompt[key] = fieldValue;
100
+ }
101
+ if (!movedExtraField) return value;
102
+ return {
103
+ ...normalizedLocate,
104
+ prompt: normalizedPrompt
105
+ };
106
+ }
107
+ function normalizeLocateLikeArg(value, fieldSchema) {
108
+ if ('string' == typeof value) return {
109
+ prompt: value
110
+ };
111
+ if (!isRecord(value)) return value;
112
+ const shape = getZodObjectShape(fieldSchema);
113
+ if (!shape) return value;
114
+ return moveLocateExtrasIntoPrompt(value, new Set(Object.keys(shape)));
115
+ }
116
+ function normalizeActionArgs(args, paramSchema) {
117
+ if (!paramSchema) return args;
118
+ const shape = getZodObjectShape(paramSchema);
119
+ if (!shape) return args;
120
+ return Object.fromEntries(Object.entries(args).map(([key, value])=>{
121
+ const fieldSchema = shape[key];
122
+ if (!fieldSchema) return [
123
+ key,
124
+ value
125
+ ];
126
+ if (isMidsceneLocatorField(fieldSchema)) return [
127
+ key,
128
+ normalizeLocateLikeArg(value, fieldSchema)
129
+ ];
130
+ return [
131
+ key,
132
+ value
133
+ ];
134
+ }));
75
135
  }
76
136
  function serializeArgsToDescription(args) {
77
137
  try {
@@ -90,7 +150,7 @@ function serializeArgsToDescription(args) {
90
150
  }
91
151
  }
92
152
  function buildActionInstruction(actionName, args) {
93
- const locatePrompt = args.locate && 'object' == typeof args.locate ? args.locate.prompt : void 0;
153
+ const locatePrompt = isRecord(args.locate) ? getPromptText(args.locate.prompt) : void 0;
94
154
  switch(actionName){
95
155
  case 'Tap':
96
156
  return locatePrompt ? `Tap on "${locatePrompt}"` : 'Tap';
@@ -118,44 +178,59 @@ function buildActionInstruction(actionName, args) {
118
178
  }
119
179
  }
120
180
  }
121
- async function captureScreenshotResult(agent, actionName) {
181
+ async function executeAction(agent, actionName, args) {
182
+ if (agent.callActionInActionSpace) return agent.callActionInActionSpace(actionName, args);
183
+ if (agent.aiAction) {
184
+ const instruction = buildActionInstruction(actionName, args);
185
+ return agent.aiAction(instruction);
186
+ }
187
+ throw new Error(`Action "${actionName}" is not supported by this agent`);
188
+ }
189
+ async function captureScreenshotResult(agent, actionName, actionResult) {
190
+ const content = [
191
+ {
192
+ type: 'text',
193
+ text: `Action "${actionName}" completed.`
194
+ }
195
+ ];
196
+ if (void 0 !== actionResult) content.push({
197
+ type: 'text',
198
+ text: `Result: ${serializeActionResult(actionResult)}`
199
+ });
122
200
  try {
123
201
  const screenshot = await agent.page?.screenshotBase64();
124
202
  if (!screenshot) return {
125
- content: [
126
- {
127
- type: 'text',
128
- text: `Action "${actionName}" completed.`
129
- }
130
- ]
203
+ content
131
204
  };
132
205
  const { mimeType, body } = parseBase64(screenshot);
206
+ content.push({
207
+ type: 'image',
208
+ data: body,
209
+ mimeType
210
+ });
133
211
  return {
134
- content: [
135
- {
136
- type: 'text',
137
- text: `Action "${actionName}" completed.`
138
- },
139
- {
140
- type: 'image',
141
- data: body,
142
- mimeType
143
- }
144
- ]
212
+ content
145
213
  };
146
214
  } catch (error) {
147
215
  const errorMessage = getErrorMessage(error);
148
216
  console.error('Error capturing screenshot:', errorMessage);
217
+ content[0] = {
218
+ type: 'text',
219
+ text: `Action "${actionName}" completed (screenshot unavailable: ${errorMessage})`
220
+ };
149
221
  return {
150
- content: [
151
- {
152
- type: 'text',
153
- text: `Action "${actionName}" completed (screenshot unavailable: ${errorMessage})`
154
- }
155
- ]
222
+ content
156
223
  };
157
224
  }
158
225
  }
226
+ function serializeActionResult(actionResult) {
227
+ if ('string' == typeof actionResult) return actionResult;
228
+ try {
229
+ return JSON.stringify(actionResult);
230
+ } catch {
231
+ return String(actionResult);
232
+ }
233
+ }
159
234
  function createErrorResult(message) {
160
235
  return {
161
236
  content: [
@@ -214,17 +289,16 @@ function generateToolsFromActionSpace(actionSpace, getAgent) {
214
289
  handler: async (args)=>{
215
290
  try {
216
291
  const agent = await getAgent();
217
- if (agent.aiAction) {
218
- const instruction = buildActionInstruction(action.name, args);
219
- try {
220
- await agent.aiAction(instruction);
221
- } catch (error) {
222
- const errorMessage = getErrorMessage(error);
223
- console.error(`Error executing action "${action.name}":`, errorMessage);
224
- return await captureFailureResult(agent, action.name, errorMessage);
225
- }
292
+ const normalizedArgs = normalizeActionArgs(args, action.paramSchema);
293
+ let actionResult;
294
+ try {
295
+ actionResult = await executeAction(agent, action.name, normalizedArgs);
296
+ } catch (error) {
297
+ const errorMessage = getErrorMessage(error);
298
+ console.error(`Error executing action "${action.name}":`, errorMessage);
299
+ return await captureFailureResult(agent, action.name, errorMessage);
226
300
  }
227
- return await captureScreenshotResult(agent, action.name);
301
+ return await captureScreenshotResult(agent, action.name, actionResult);
228
302
  } catch (error) {
229
303
  const errorMessage = getErrorMessage(error);
230
304
  console.error(`Error in handler for "${action.name}":`, errorMessage);