@midscene/test 1.12.4-beta-20260903111111.0 → 1.12.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 (40) hide show
  1. package/dist/es/android/index.mjs +50 -106
  2. package/dist/es/android/index.mjs.map +1 -1
  3. package/dist/es/cli/index.mjs +99 -149
  4. package/dist/es/cli/index.mjs.map +1 -1
  5. package/dist/es/harmony/index.mjs +119 -0
  6. package/dist/es/harmony/index.mjs.map +1 -0
  7. package/dist/es/index.mjs +36 -87
  8. package/dist/es/index.mjs.map +1 -1
  9. package/dist/es/ios/index.mjs +51 -101
  10. package/dist/es/ios/index.mjs.map +1 -1
  11. package/dist/es/midscene/index.mjs +37 -235
  12. package/dist/es/midscene/index.mjs.map +1 -1
  13. package/dist/es/playwright/index.mjs +14 -10
  14. package/dist/es/playwright/index.mjs.map +1 -1
  15. package/dist/es/test-cli.mjs +102 -150
  16. package/dist/es/test-cli.mjs.map +1 -1
  17. package/dist/lib/android/index.js +151 -178
  18. package/dist/lib/android/index.js.map +1 -1
  19. package/dist/lib/cli/index.js +99 -149
  20. package/dist/lib/cli/index.js.map +1 -1
  21. package/dist/lib/harmony/index.js +188 -0
  22. package/dist/lib/harmony/index.js.map +1 -0
  23. package/dist/lib/index.js +37 -100
  24. package/dist/lib/index.js.map +1 -1
  25. package/dist/lib/ios/index.js +152 -173
  26. package/dist/lib/ios/index.js.map +1 -1
  27. package/dist/lib/midscene/index.js +264 -412
  28. package/dist/lib/midscene/index.js.map +1 -1
  29. package/dist/lib/playwright/index.js +14 -10
  30. package/dist/lib/playwright/index.js.map +1 -1
  31. package/dist/lib/test-cli.js +102 -150
  32. package/dist/lib/test-cli.js.map +1 -1
  33. package/dist/types/android/index.d.ts +27 -101
  34. package/dist/types/cli/index.d.ts +9 -32
  35. package/dist/types/harmony/index.d.ts +124 -0
  36. package/dist/types/index.d.ts +18 -45
  37. package/dist/types/ios/index.d.ts +28 -107
  38. package/dist/types/midscene/index.d.ts +54 -153
  39. package/dist/types/playwright/index.d.ts +9 -63
  40. package/package.json +25 -6
@@ -1,3 +1,4 @@
1
+ import { aiActInputSchema, aiActOptionsInputSchema, aiAssertInputSchema, aiAssertOptionsInputSchema, aiTapInputSchema, insightInputSchema, insightOptionsInputSchema, locateOptionsInputSchema, promptImageInputSchema, recordToReportInputSchema, recordToReportOptionsInputSchema, reportScreenshotInputSchema, structuredUserPromptInputSchema, userPromptInputSchema } from "@midscene/core/agent/test-runner";
1
2
  import { z } from "zod/v4";
2
3
  function _define_property(obj, key, value) {
3
4
  if (key in obj) Object.defineProperty(obj, key, {
@@ -72,104 +73,24 @@ const validateDefinition = (options)=>{
72
73
  if ('string' != typeof options.name || 0 === options.name.trim().length) throw new NodeDefinitionError('Node name must be a non-empty string.');
73
74
  validateOptionalText(options.title, 'title', options.name);
74
75
  validateOptionalText(options.description, "description", options.name);
76
+ if (void 0 !== options.stringInputKey && false !== options.stringInputKey && ('string' != typeof options.stringInputKey || 0 === options.stringInputKey.trim().length)) throw new NodeDefinitionError(`Node "${options.name}" stringInputKey must be a non-empty string or false.`, {
77
+ node: options.name,
78
+ field: 'stringInputKey'
79
+ });
75
80
  validateInputSchema(options.inputSchema, options.name);
81
+ if ('string' == typeof options.stringInputKey && options.inputSchema instanceof z.ZodObject && !(options.stringInputKey in options.inputSchema.shape)) throw new NodeDefinitionError(`Node "${options.name}" stringInputKey must reference a field in inputSchema.`, {
82
+ node: options.name,
83
+ field: 'stringInputKey'
84
+ });
76
85
  if ('function' != typeof options.execute) throw new NodeDefinitionError(`Node "${options.name}" must provide an execute function.`, {
77
86
  node: options.name
78
87
  });
79
88
  };
80
- function define_node_defineNode(options) {
89
+ function defineNode(options) {
81
90
  validateDefinition(options);
82
91
  return options;
83
92
  }
84
- const lifecycleInputSchema = (operation, description)=>z.strictObject({
85
- prompt: z.string().regex(/\S/, 'prompt must contain a non-whitespace character').optional().describe(`String shorthand for the app to ${operation}.`),
86
- uri: z.string().regex(/\S/, 'uri must contain a non-whitespace character').optional().describe(description)
87
- }).superRefine((input, ctx)=>{
88
- if (void 0 === input.prompt === (void 0 === input.uri)) ctx.addIssue({
89
- code: 'custom',
90
- message: 'exactly one of prompt and uri is required'
91
- });
92
- });
93
- const launchInputSchema = lifecycleInputSchema('launch', 'The app, URL, URI, package name, or bundle identifier to launch.');
94
- lifecycleInputSchema('terminate', 'The package name, bundle identifier, or app name to terminate.');
95
- const requireLifecycleMethod = (agent, method, agentName)=>{
96
- if ('object' != typeof agent || null === agent || 'function' != typeof agent[method]) throw new NodeExecutionError(method, new TypeError(`getAgent() must return ${agentName} with ${method}().`));
97
- return agent[method];
98
- };
99
- const createLaunchNode = (getAgent, agentName = 'an Agent')=>define_node_defineNode({
100
- name: 'launch',
101
- description: 'Launch an app, URL, or URI through the current Midscene Agent. This Node does not install or manage applications.',
102
- inputSchema: launchInputSchema,
103
- async execute (ctx) {
104
- const uri = ctx.input.uri ?? ctx.input.prompt;
105
- const agent = await getAgent(ctx);
106
- const launch = requireLifecycleMethod(agent, 'launch', agentName);
107
- await launch.call(agent, uri);
108
- return {
109
- summary: `Launched ${uri}`
110
- };
111
- }
112
- });
113
93
  const nonBlankPrompt = (description)=>z.string().regex(/\S/, 'prompt must contain a non-whitespace character').describe(description);
114
- const promptImagesInputSchema = z.array(z.strictObject({
115
- name: nonBlankPrompt('The name used to identify this reference image.'),
116
- url: nonBlankPrompt('The URL, data URL, or file path of this reference image.')
117
- })).min(1).optional();
118
- const promptImageConversionInputSchema = z.boolean().optional().describe('Whether HTTP reference images are converted to base64 first.');
119
- const aiActOptionsInputSchema = z.strictObject({
120
- cacheable: z.boolean().optional().describe('Whether this action may use the Midscene cache.'),
121
- fileChooserAccept: z.union([
122
- z.string(),
123
- z.array(z.string())
124
- ]).optional().describe('Accepted file types for a file chooser.'),
125
- deepThink: z.union([
126
- z.literal('unset'),
127
- z.boolean()
128
- ]).optional().describe('Whether to enable deep thinking for this action.'),
129
- deepLocate: z.boolean().optional().describe('Whether to use deep element location.'),
130
- context: z.string().optional().describe('Additional context supplied to the UI Agent.')
131
- });
132
- const aiActInputSchema = z.strictObject({
133
- prompt: nonBlankPrompt('The natural-language UI task to perform.'),
134
- images: promptImagesInputSchema,
135
- convertHttpImage2Base64: promptImageConversionInputSchema,
136
- options: aiActOptionsInputSchema.optional()
137
- });
138
- const aiAssertOptionsInputSchema = z.strictObject({
139
- domIncluded: z.union([
140
- z.boolean(),
141
- z.literal('visible-only')
142
- ]).optional().describe('How DOM information is included in the assertion.'),
143
- screenshotIncluded: z.boolean().optional().describe('Whether the assertion includes a screenshot.'),
144
- context: z.string().optional().describe('Additional context supplied to the UI Agent.')
145
- });
146
- const aiAssertInputSchema = z.strictObject({
147
- prompt: nonBlankPrompt('The natural-language condition that must be true.'),
148
- images: promptImagesInputSchema,
149
- convertHttpImage2Base64: promptImageConversionInputSchema,
150
- message: z.string().optional().describe('The assertion failure message.'),
151
- options: aiAssertOptionsInputSchema.optional()
152
- });
153
- const reportScreenshotInputSchema = z.strictObject({
154
- base64: z.string().min(1).describe('A base64-encoded screenshot.'),
155
- description: z.string().optional().describe('What the screenshot shows.')
156
- });
157
- const recordToReportInputSchema = z.strictObject({
158
- prompt: z.string().optional().describe('String shorthand for the title.'),
159
- title: z.string().optional().describe('The report section title.'),
160
- content: z.string().optional().describe('The report text content.'),
161
- screenshotBase64: z.string().optional().describe('One base64-encoded screenshot.'),
162
- screenshots: z.array(reportScreenshotInputSchema).min(1).optional().describe('Screenshots attached to the report section.')
163
- }).superRefine((input, ctx)=>{
164
- if (void 0 !== input.prompt && void 0 !== input.title) ctx.addIssue({
165
- code: 'custom',
166
- message: 'prompt and title are mutually exclusive'
167
- });
168
- if (void 0 !== input.screenshotBase64 && void 0 !== input.screenshots) ctx.addIssue({
169
- code: 'custom',
170
- message: 'screenshotBase64 and screenshots are mutually exclusive'
171
- });
172
- });
173
94
  const waitInputSchema = z.strictObject({
174
95
  duration: z.number().positive().describe('How long to wait.'),
175
96
  unit: z["enum"]([
@@ -181,86 +102,6 @@ const waitInputSchema = z.strictObject({
181
102
  const agentInputSchema = z.strictObject({
182
103
  prompt: nonBlankPrompt('A self-contained task, including allowed tools and success conditions.')
183
104
  });
184
- const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
185
- const requireAgentMethod = (agent, method, node)=>{
186
- if (!isRecord(agent) || 'function' != typeof agent[method]) throw new NodeExecutionError(node, new TypeError(`getAgent() must return an Agent with ${method}().`));
187
- return agent[method];
188
- };
189
- const maxHistoryContextCharacters = 64000;
190
- const maxHistoryValuePreviewCharacters = 8000;
191
- const maxHistoryEntryCharacters = 24000;
192
- const historyOmissionNoticeReserve = 256;
193
- const compactHistoryContextValue = (value)=>{
194
- const serialized = JSON.stringify(value);
195
- if (void 0 === serialized || serialized.length <= maxHistoryValuePreviewCharacters) return value;
196
- return {
197
- omittedFromContext: true,
198
- originalCharacters: serialized.length,
199
- preview: 'string' == typeof value ? value.slice(0, maxHistoryValuePreviewCharacters) : serialized.slice(0, maxHistoryValuePreviewCharacters)
200
- };
201
- };
202
- const serializeHistoryEntryForContext = (entry, index)=>{
203
- const compacted = Object.fromEntries(Object.entries({
204
- index,
205
- ...entry
206
- }).map(([key, value])=>[
207
- key,
208
- compactHistoryContextValue(value)
209
- ]));
210
- const serialized = JSON.stringify(compacted);
211
- if (serialized.length <= maxHistoryEntryCharacters) return serialized;
212
- return JSON.stringify({
213
- index,
214
- scope: entry.scope,
215
- phase: entry.phase,
216
- stepIndex: entry.stepIndex,
217
- node: entry.node,
218
- status: entry.status,
219
- ...void 0 === entry.summary ? {} : {
220
- summary: compactHistoryContextValue(entry.summary)
221
- },
222
- omittedFromContext: true,
223
- compactedCharacters: serialized.length
224
- });
225
- };
226
- const renderNodeHistory = (history)=>{
227
- if (0 === history.length) return;
228
- const heading = 'Previous workflow results (read-only):';
229
- const availableCharacters = maxHistoryContextCharacters - heading.length - historyOmissionNoticeReserve;
230
- const renderedEntries = [];
231
- let renderedCharacters = 0;
232
- for(let index = history.length - 1; index >= 0; index -= 1){
233
- const rendered = serializeHistoryEntryForContext(history[index], index + 1);
234
- const separatorCharacters = 0 === renderedEntries.length ? 0 : 1;
235
- if (renderedCharacters + separatorCharacters + rendered.length > availableCharacters) break;
236
- renderedEntries.unshift(rendered);
237
- renderedCharacters += separatorCharacters + rendered.length;
238
- }
239
- const omittedEntries = history.length - renderedEntries.length;
240
- return [
241
- heading,
242
- ...0 === omittedEntries ? [] : [
243
- `${omittedEntries} earlier history entr${1 === omittedEntries ? 'y was' : 'ies were'} omitted from Agent context to stay within the size limit. Complete results remain available in the Test Runner output.`
244
- ],
245
- ...renderedEntries
246
- ].join('\n');
247
- };
248
- const mergeContext = (explicit, history)=>[
249
- explicit,
250
- renderNodeHistory(history)
251
- ].filter(Boolean).join('\n\n') || void 0;
252
- const toAgentPrompt = (input)=>{
253
- if (void 0 === input.images && void 0 === input.convertHttpImage2Base64) return input.prompt;
254
- return {
255
- prompt: input.prompt,
256
- ...void 0 === input.images ? {} : {
257
- images: input.images
258
- },
259
- ...void 0 === input.convertHttpImage2Base64 ? {} : {
260
- convertHttpImage2Base64: input.convertHttpImage2Base64
261
- }
262
- };
263
- };
264
105
  const waitFor = async (durationMs, signal)=>{
265
106
  if (signal.aborted) throw signal.reason ?? new Error('Wait aborted.');
266
107
  await new Promise((resolve, reject)=>{
@@ -277,9 +118,29 @@ const waitFor = async (durationMs, signal)=>{
277
118
  });
278
119
  });
279
120
  };
121
+ const createAgentTestRunnerNodes = (definitions, getAgent)=>definitions.map((definition)=>defineNode({
122
+ name: definition.name,
123
+ ...void 0 === definition.title ? {} : {
124
+ title: definition.title
125
+ },
126
+ ...void 0 === definition.description ? {} : {
127
+ description: definition.description
128
+ },
129
+ ...void 0 === definition.stringInputKey ? {} : {
130
+ stringInputKey: definition.stringInputKey
131
+ },
132
+ inputSchema: definition.inputSchema,
133
+ async execute (ctx) {
134
+ const agent = await getAgent(ctx);
135
+ return definition.execute(agent, ctx.input, {
136
+ signal: ctx.signal
137
+ });
138
+ }
139
+ }));
280
140
  function createMidsceneNodes(options) {
281
141
  if (!options || 'object' != typeof options) throw new NodeDefinitionError('createMidsceneNodes() options must be an object.');
282
142
  if ('function' != typeof options.agentProvider?.getAgent && 'function' != typeof options.getAgent) throw new NodeDefinitionError('createMidsceneNodes() requires getAgent or agentProvider.getAgent.');
143
+ if (!options.agentClass || 'function' != typeof options.agentClass.getTestRunnerNodeDefinitions) throw new NodeDefinitionError('createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().');
283
144
  const registeredAgentScopes = new Set();
284
145
  const getExecutionId = (ctx)=>'case' === ctx.scope ? ctx.case.runId : ctx.document.documentRunId;
285
146
  const getAgent = async (ctx)=>{
@@ -302,72 +163,13 @@ function createMidsceneNodes(options) {
302
163
  }
303
164
  return options.agentProvider.getAgent(runId, ctx);
304
165
  };
166
+ const agentDefinitions = options.agentClass.getTestRunnerNodeDefinitions();
305
167
  return [
306
- define_node_defineNode({
307
- name: 'aiAct',
308
- description: 'Perform a natural-language task with a Midscene UI Agent.',
309
- inputSchema: aiActInputSchema,
310
- async execute (ctx) {
311
- const agent = await getAgent(ctx);
312
- const aiAct = requireAgentMethod(agent, 'aiAct', 'aiAct');
313
- const output = await aiAct.call(agent, toAgentPrompt(ctx.input), {
314
- ...ctx.input.options,
315
- context: mergeContext(ctx.input.options?.context, ctx.history),
316
- abortSignal: ctx.signal
317
- });
318
- return void 0 === output ? void 0 : {
319
- summary: output
320
- };
321
- }
322
- }),
323
- define_node_defineNode({
324
- name: 'aiAssert',
325
- description: 'Assert a natural-language condition with a Midscene UI Agent.',
326
- inputSchema: aiAssertInputSchema,
327
- async execute (ctx) {
328
- const agent = await getAgent(ctx);
329
- const aiAssert = requireAgentMethod(agent, 'aiAssert', 'aiAssert');
330
- await aiAssert.call(agent, toAgentPrompt(ctx.input), ctx.input.message, {
331
- ...ctx.input.options,
332
- context: mergeContext(ctx.input.options?.context, ctx.history),
333
- abortSignal: ctx.signal
334
- });
335
- return {
336
- summary: `Assertion passed: ${ctx.input.prompt}`
337
- };
338
- }
339
- }),
340
- define_node_defineNode({
341
- name: 'recordToReport',
342
- description: 'Add text or screenshots to the current Midscene report.',
343
- inputSchema: recordToReportInputSchema,
344
- async execute (ctx) {
345
- const title = ctx.input.title ?? ctx.input.prompt;
346
- const reportOptions = {
347
- ...void 0 === ctx.input.content ? {} : {
348
- content: ctx.input.content
349
- },
350
- ...void 0 === ctx.input.screenshotBase64 ? {} : {
351
- screenshotBase64: ctx.input.screenshotBase64
352
- },
353
- ...void 0 === ctx.input.screenshots ? {} : {
354
- screenshots: ctx.input.screenshots
355
- }
356
- };
357
- const agent = await getAgent(ctx);
358
- const recordToReport = requireAgentMethod(agent, 'recordToReport', 'recordToReport');
359
- await recordToReport.call(agent, title, reportOptions);
360
- return {
361
- summary: `Recorded to report: ${title ?? 'untitled'}`
362
- };
363
- }
364
- }),
365
- ...false === options.includeLaunch ? [] : [
366
- createLaunchNode(getAgent)
367
- ],
368
- define_node_defineNode({
168
+ ...createAgentTestRunnerNodes(agentDefinitions, getAgent),
169
+ defineNode({
369
170
  name: 'wait',
370
171
  description: 'Wait for a fixed duration while honoring cancellation.',
172
+ stringInputKey: false,
371
173
  inputSchema: waitInputSchema,
372
174
  async execute (ctx) {
373
175
  const multiplier = 'min' === ctx.input.unit ? 60000 : 's' === ctx.input.unit ? 1000 : 1;
@@ -378,9 +180,10 @@ function createMidsceneNodes(options) {
378
180
  };
379
181
  }
380
182
  }),
381
- define_node_defineNode({
183
+ defineNode({
382
184
  name: 'agent',
383
185
  description: 'Execute one self-contained natural-language task with an injected Agent executor.',
186
+ stringInputKey: 'prompt',
384
187
  inputSchema: agentInputSchema,
385
188
  async execute (ctx) {
386
189
  if (!options.agentExecutor) throw new NodeExecutionError('agent', new TypeError('createMidsceneNodes() requires an agentExecutor.'));
@@ -393,7 +196,6 @@ function createMidsceneNodes(options) {
393
196
  };
394
197
  const result = await options.agentExecutor.execute({
395
198
  prompt: ctx.input.prompt,
396
- history: ctx.history,
397
199
  context: ctx.context,
398
200
  signal: ctx.signal,
399
201
  execution
@@ -405,6 +207,6 @@ function createMidsceneNodes(options) {
405
207
  })
406
208
  ];
407
209
  }
408
- export { agentInputSchema, aiActInputSchema, aiAssertInputSchema, createMidsceneNodes, launchInputSchema, recordToReportInputSchema, renderNodeHistory, waitInputSchema };
210
+ export { agentInputSchema, aiActInputSchema, aiActOptionsInputSchema, aiAssertInputSchema, aiAssertOptionsInputSchema, aiTapInputSchema, createAgentTestRunnerNodes, createMidsceneNodes, insightInputSchema, insightOptionsInputSchema, locateOptionsInputSchema, promptImageInputSchema, recordToReportInputSchema, recordToReportOptionsInputSchema, reportScreenshotInputSchema, structuredUserPromptInputSchema, userPromptInputSchema, waitInputSchema };
409
211
 
410
212
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"midscene/index.mjs","sources":["../../../src/errors.ts","../../../src/node/define-node.ts","../../../src/device/lifecycle.ts","../../../src/midscene/index.ts"],"sourcesContent":["import type { z } from 'zod/v4';\n\nexport interface WorkflowErrorOptions {\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class WorkflowError extends Error {\n readonly code: string;\n readonly details?: unknown;\n\n constructor(message: string, options: WorkflowErrorOptions = {}) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.code = options.code ?? 'WORKFLOW_ERROR';\n this.details = options.details;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n ...(this.details === undefined ? {} : { details: this.details }),\n };\n }\n}\n\nexport class WorkflowParseError extends WorkflowError {\n constructor(message: string, details?: unknown, cause?: unknown) {\n super(message, { code: 'WORKFLOW_PARSE_ERROR', details, cause });\n }\n}\n\nexport class NodeDefinitionError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_DEFINITION_ERROR', details });\n }\n}\n\nexport class DuplicateNodeError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is already registered.`, {\n code: 'DUPLICATE_NODE',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeNotFoundError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is not registered.`, {\n code: 'NODE_NOT_FOUND',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeInputValidationError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_INPUT_VALIDATION_ERROR', details });\n }\n\n static fromZod(node: string, error: z.ZodError): NodeInputValidationError {\n const issues = error.issues.map((issue) => ({\n code: issue.code,\n path: issue.path.map(String).join('.'),\n message: issue.message,\n }));\n const firstIssue = issues[0];\n const path = firstIssue?.path || '<root>';\n const message = firstIssue?.message ?? 'invalid input';\n return new NodeInputValidationError(\n `Node \"${node}\" input validation failed at \"${path}\": ${message}`,\n { node, issues },\n );\n }\n}\n\nexport class StepTimeoutError extends WorkflowError {\n readonly timeoutMs: number;\n readonly node?: string;\n\n constructor(timeoutMs: number, node?: string) {\n super(\n node\n ? `Node \"${node}\" timed out after ${timeoutMs}ms.`\n : `Step timed out after ${timeoutMs}ms.`,\n {\n code: 'STEP_TIMEOUT',\n details: { timeoutMs, ...(node === undefined ? {} : { node }) },\n },\n );\n this.timeoutMs = timeoutMs;\n this.node = node;\n }\n}\n\nexport class NodeExecutionError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string, cause: unknown) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Node \"${node}\" failed: ${causeMessage}`, {\n code: 'NODE_EXECUTION_ERROR',\n details: { node },\n cause,\n });\n this.node = node;\n }\n}\n\nexport class WorkflowLifecycleError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'WORKFLOW_LIFECYCLE_ERROR', details });\n }\n}\n\nexport class ProjectSetupError extends WorkflowError {\n constructor(cause: unknown, details: { projectName: string }) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" setup failed: ${causeMessage}`, {\n code: 'PROJECT_SETUP_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class ProjectTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: { projectName: string; registrationIndex: number },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" teardown failed: ${causeMessage}`, {\n code: 'PROJECT_TEARDOWN_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class NodeScopeTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: {\n scope: 'case' | 'document';\n scopeId: string;\n node: string;\n registrationIndex: number;\n },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(\n `${details.scope === 'case' ? 'Case attempt' : 'Workflow document'} node teardown failed for \"${details.node}\": ${causeMessage}`,\n { code: 'NODE_SCOPE_TEARDOWN_ERROR', details, cause },\n );\n }\n}\n\nexport class FatalDeviceError extends WorkflowError {\n constructor(message: string, cause?: unknown) {\n super(message, { code: 'FATAL_DEVICE_ERROR', cause });\n }\n}\n\nexport const isFatalDeviceError = (error: unknown): boolean => {\n if (error instanceof FatalDeviceError) return true;\n if (error instanceof WorkflowError && error.code === 'FATAL_DEVICE_ERROR') {\n return true;\n }\n if (\n error instanceof Error &&\n /device offline|device not found|(?:adb|bdc|device) connection (?:was )?closed/i.test(\n error.message,\n )\n ) {\n return true;\n }\n return error instanceof Error && error.cause !== undefined\n ? isFatalDeviceError(error.cause)\n : false;\n};\n\nexport class CaseExecutionError extends WorkflowError {\n readonly result: import('./engine/types').CaseRunResult;\n\n constructor(result: import('./engine/types').CaseRunResult) {\n super(`Case \"${result.name}\" failed.`, {\n code: 'CASE_EXECUTION_FAILED',\n details: { caseId: result.caseId, runId: result.runId },\n });\n this.result = result;\n }\n}\n\nexport class WorkflowDocumentExecutionError extends WorkflowError {\n readonly result: import('./engine/types').WorkflowDocumentRunResult;\n\n constructor(result: import('./engine/types').WorkflowDocumentRunResult) {\n super(`Workflow document \"${result.sourcePath}\" failed.`, {\n code: 'WORKFLOW_DOCUMENT_EXECUTION_FAILED',\n details: {\n documentId: result.documentId,\n documentRunId: result.documentRunId,\n },\n });\n this.result = result;\n }\n}\n\nexport function normalizeNodeExecutionError(\n error: unknown,\n node: string,\n): WorkflowError {\n return error instanceof WorkflowError\n ? error\n : new NodeExecutionError(node, error);\n}\n","import { z } from 'zod/v4';\nimport { NodeDefinitionError } from '../errors';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\nconst validateOptionalText = (\n value: unknown,\n field: 'title' | 'description',\n node: string,\n): void => {\n if (\n value !== undefined &&\n (typeof value !== 'string' || value.trim().length === 0)\n ) {\n throw new NodeDefinitionError(\n `Node \"${node}\" ${field} must be a non-empty string.`,\n { node, field },\n );\n }\n};\n\nconst validateInputSchema = (schema: unknown, node: string): void => {\n if (schema === undefined) return;\n if (!(schema instanceof z.ZodObject)) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must be a Zod object schema.`,\n { node, field: 'inputSchema' },\n );\n }\n if ('$' in schema.shape) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must not declare \"$\" as an input property.`,\n { node, field: 'inputSchema.$' },\n );\n }\n};\n\nconst validateDefinition = (options: {\n name: string;\n title?: unknown;\n description?: unknown;\n inputSchema?: unknown;\n execute: unknown;\n}): void => {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError('Node definition must be an object.');\n }\n\n if (typeof options.name !== 'string' || options.name.trim().length === 0) {\n throw new NodeDefinitionError('Node name must be a non-empty string.');\n }\n\n validateOptionalText(options.title, 'title', options.name);\n validateOptionalText(options.description, 'description', options.name);\n validateInputSchema(options.inputSchema, options.name);\n\n if (typeof options.execute !== 'function') {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" must provide an execute function.`,\n { node: options.name },\n );\n }\n};\n\nexport function defineNode<\n TSchema extends NodeInputSchema,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n\nexport function defineNode<\n TInput = unknown,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n): NodeDefinition<TInput, TData, TContext>;\n\nexport function defineNode(\n options: DefineNodeOptions<any, any, any>,\n): NodeDefinition<any, any, any> {\n validateDefinition(options);\n return options;\n}\n","import { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeExecutionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type { NodeDefinition, NodeExecutionContext } from '../node/types';\n\ntype NodeContext<TContext> = NodeExecutionContext<unknown, TContext>;\ntype AgentGetter<TContext> = (ctx: NodeContext<TContext>) => Awaitable<unknown>;\ntype LifecycleMethod = 'launch' | 'terminate';\n\n/** Device capabilities required by Android and iOS lifecycle Nodes. */\nexport interface DeviceLifecycleAgent {\n launch(uri: string): Promise<void>;\n terminate(uri: string): Promise<void>;\n}\n\nconst lifecycleInputSchema = (\n operation: LifecycleMethod,\n description: string,\n) =>\n z\n .strictObject({\n prompt: z\n .string()\n .regex(/\\S/, 'prompt must contain a non-whitespace character')\n .optional()\n .describe(`String shorthand for the app to ${operation}.`),\n uri: z\n .string()\n .regex(/\\S/, 'uri must contain a non-whitespace character')\n .optional()\n .describe(description),\n })\n .superRefine((input, ctx) => {\n if ((input.prompt === undefined) === (input.uri === undefined)) {\n ctx.addIssue({\n code: 'custom',\n message: 'exactly one of prompt and uri is required',\n });\n }\n });\n\n/** Input schema for the device launch Node. */\nexport const launchInputSchema = lifecycleInputSchema(\n 'launch',\n 'The app, URL, URI, package name, or bundle identifier to launch.',\n);\n\n/** Input schema for the device terminate Node. */\nexport const terminateInputSchema = lifecycleInputSchema(\n 'terminate',\n 'The package name, bundle identifier, or app name to terminate.',\n);\n\nexport type LaunchNodeInput = z.infer<typeof launchInputSchema>;\nexport type TerminateNodeInput = z.infer<typeof terminateInputSchema>;\n\nconst requireLifecycleMethod = (\n agent: unknown,\n method: LifecycleMethod,\n agentName: string,\n): DeviceLifecycleAgent[LifecycleMethod] => {\n if (\n typeof agent !== 'object' ||\n agent === null ||\n typeof (agent as Record<LifecycleMethod, unknown>)[method] !== 'function'\n ) {\n throw new NodeExecutionError(\n method,\n new TypeError(`getAgent() must return ${agentName} with ${method}().`),\n );\n }\n return (agent as DeviceLifecycleAgent)[method];\n};\n\nexport const createLaunchNode = <TContext>(\n getAgent: AgentGetter<TContext>,\n agentName = 'an Agent',\n): NodeDefinition<any, any, TContext> =>\n defineNode<typeof launchInputSchema, unknown, TContext>({\n name: 'launch',\n description:\n 'Launch an app, URL, or URI through the current Midscene Agent. This Node does not install or manage applications.',\n inputSchema: launchInputSchema,\n async execute(ctx) {\n const uri = ctx.input.uri ?? ctx.input.prompt!;\n const agent = await getAgent(ctx);\n const launch = requireLifecycleMethod(agent, 'launch', agentName);\n await launch.call(agent, uri);\n return { summary: `Launched ${uri}` };\n },\n });\n\nconst createTerminateNode = <TContext>(\n getAgent: AgentGetter<TContext>,\n agentName: string,\n): NodeDefinition<any, any, TContext> =>\n defineNode<typeof terminateInputSchema, unknown, TContext>({\n name: 'terminate',\n description:\n 'Terminate an application through the current Midscene Agent. This Node does not uninstall the application or clear its data.',\n inputSchema: terminateInputSchema,\n async execute(ctx) {\n const uri = ctx.input.uri ?? ctx.input.prompt!;\n const agent = await getAgent(ctx);\n const terminate = requireLifecycleMethod(agent, 'terminate', agentName);\n await terminate.call(agent, uri);\n return { summary: `Terminated ${uri}` };\n },\n });\n\nexport const createDeviceLifecycleNodes = <TContext>(\n getAgent: (ctx: NodeContext<TContext>) => Awaitable<DeviceLifecycleAgent>,\n agentName: string,\n): readonly NodeDefinition<any, any, TContext>[] => [\n createLaunchNode(getAgent, agentName),\n createTerminateNode(getAgent, agentName),\n];\n","import { z } from 'zod/v4';\nimport { createLaunchNode } from '../device/lifecycle';\nexport type { LaunchNodeInput } from '../device/lifecycle';\nexport { launchInputSchema } from '../device/lifecycle';\nimport type { Awaitable, NodeHistoryEntry } from '../engine/types';\nimport { NodeDefinitionError, NodeExecutionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type {\n NodeDefinition,\n NodeExecutionContext,\n NodeResult,\n} from '../node/types';\n\nexport interface MidsceneAiActOptions {\n cacheable?: boolean;\n fileChooserAccept?: string | string[];\n deepThink?: 'unset' | boolean;\n deepLocate?: boolean;\n context?: string;\n abortSignal?: AbortSignal;\n}\n\nexport interface MidsceneAiAssertOptions {\n domIncluded?: boolean | 'visible-only';\n screenshotIncluded?: boolean;\n context?: string;\n abortSignal?: AbortSignal;\n keepRawResponse?: boolean;\n}\n\nexport interface MidscenePromptImage {\n name: string;\n url: string;\n}\n\nexport type MidsceneUserPrompt =\n | string\n | {\n prompt: string;\n images?: MidscenePromptImage[];\n convertHttpImage2Base64?: boolean;\n };\n\nexport interface MidsceneReportScreenshot {\n base64: string;\n description?: string;\n}\n\nexport interface MidsceneRecordToReportOptions {\n content?: string;\n screenshotBase64?: string;\n screenshots?: MidsceneReportScreenshot[];\n}\n\nexport interface MidsceneUIAgent {\n aiAct(\n prompt: MidsceneUserPrompt,\n options?: MidsceneAiActOptions,\n ): Promise<string | undefined>;\n aiAssert(\n prompt: MidsceneUserPrompt,\n message?: string,\n options?: MidsceneAiAssertOptions,\n ): Promise<unknown>;\n recordToReport(\n title?: string,\n options?: MidsceneRecordToReportOptions,\n ): Promise<unknown>;\n /** Available on device Agents that support launching an app, URL, or URI. */\n launch?(uri: string): Promise<void>;\n}\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport interface AgentExecutorInput<TContext> {\n prompt: string;\n history: readonly NodeHistoryEntry[];\n context: TContext;\n signal: AbortSignal;\n execution:\n | { scope: 'case'; runId: string }\n | { scope: 'document'; runId: string };\n}\n\nexport interface AgentExecutor<TContext> {\n // biome-ignore lint/suspicious/noConfusingVoidType: executors may perform side effects without returning a summary.\n execute(input: AgentExecutorInput<TContext>): Awaitable<NodeResult | void>;\n}\n\nconst nonBlankPrompt = (description: string) =>\n z\n .string()\n .regex(/\\S/, 'prompt must contain a non-whitespace character')\n .describe(description);\n\nconst promptImagesInputSchema = z\n .array(\n z.strictObject({\n name: nonBlankPrompt('The name used to identify this reference image.'),\n url: nonBlankPrompt(\n 'The URL, data URL, or file path of this reference image.',\n ),\n }),\n )\n .min(1)\n .optional();\n\nconst promptImageConversionInputSchema = z\n .boolean()\n .optional()\n .describe('Whether HTTP reference images are converted to base64 first.');\n\nconst aiActOptionsInputSchema = z.strictObject({\n cacheable: z\n .boolean()\n .optional()\n .describe('Whether this action may use the Midscene cache.'),\n fileChooserAccept: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe('Accepted file types for a file chooser.'),\n deepThink: z\n .union([z.literal('unset'), z.boolean()])\n .optional()\n .describe('Whether to enable deep thinking for this action.'),\n deepLocate: z\n .boolean()\n .optional()\n .describe('Whether to use deep element location.'),\n context: z\n .string()\n .optional()\n .describe('Additional context supplied to the UI Agent.'),\n});\n\nexport const aiActInputSchema = z.strictObject({\n prompt: nonBlankPrompt('The natural-language UI task to perform.'),\n images: promptImagesInputSchema,\n convertHttpImage2Base64: promptImageConversionInputSchema,\n options: aiActOptionsInputSchema.optional(),\n});\n\nconst aiAssertOptionsInputSchema = z.strictObject({\n domIncluded: z\n .union([z.boolean(), z.literal('visible-only')])\n .optional()\n .describe('How DOM information is included in the assertion.'),\n screenshotIncluded: z\n .boolean()\n .optional()\n .describe('Whether the assertion includes a screenshot.'),\n context: z\n .string()\n .optional()\n .describe('Additional context supplied to the UI Agent.'),\n});\n\nexport const aiAssertInputSchema = z.strictObject({\n prompt: nonBlankPrompt('The natural-language condition that must be true.'),\n images: promptImagesInputSchema,\n convertHttpImage2Base64: promptImageConversionInputSchema,\n message: z.string().optional().describe('The assertion failure message.'),\n options: aiAssertOptionsInputSchema.optional(),\n});\n\nconst reportScreenshotInputSchema = z.strictObject({\n base64: z.string().min(1).describe('A base64-encoded screenshot.'),\n description: z.string().optional().describe('What the screenshot shows.'),\n});\n\nexport const recordToReportInputSchema = z\n .strictObject({\n prompt: z.string().optional().describe('String shorthand for the title.'),\n title: z.string().optional().describe('The report section title.'),\n content: z.string().optional().describe('The report text content.'),\n screenshotBase64: z\n .string()\n .optional()\n .describe('One base64-encoded screenshot.'),\n screenshots: z\n .array(reportScreenshotInputSchema)\n .min(1)\n .optional()\n .describe('Screenshots attached to the report section.'),\n })\n .superRefine((input, ctx) => {\n if (input.prompt !== undefined && input.title !== undefined) {\n ctx.addIssue({\n code: 'custom',\n message: 'prompt and title are mutually exclusive',\n });\n }\n if (\n input.screenshotBase64 !== undefined &&\n input.screenshots !== undefined\n ) {\n ctx.addIssue({\n code: 'custom',\n message: 'screenshotBase64 and screenshots are mutually exclusive',\n });\n }\n });\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport const agentInputSchema = z.strictObject({\n prompt: nonBlankPrompt(\n 'A self-contained task, including allowed tools and success conditions.',\n ),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\nexport type AgentNodeInput = z.infer<typeof agentInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n getAgent?(\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Disable when a project registers its own platform-specific launch Node. */\n includeLaunch?: boolean;\n agentExecutor?: AgentExecutor<TContext>;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst requireAgentMethod = <TMethod extends keyof MidsceneUIAgent>(\n agent: MidsceneUIAgent,\n method: TMethod,\n node: string,\n): NonNullable<MidsceneUIAgent[TMethod]> => {\n if (!isRecord(agent) || typeof agent[method] !== 'function') {\n throw new NodeExecutionError(\n node,\n new TypeError(`getAgent() must return an Agent with ${method}().`),\n );\n }\n return agent[method] as NonNullable<MidsceneUIAgent[TMethod]>;\n};\n\nconst maxHistoryContextCharacters = 64_000;\nconst maxHistoryValuePreviewCharacters = 8_000;\nconst maxHistoryEntryCharacters = 24_000;\nconst historyOmissionNoticeReserve = 256;\n\nconst compactHistoryContextValue = (value: unknown): unknown => {\n const serialized = JSON.stringify(value);\n if (\n serialized === undefined ||\n serialized.length <= maxHistoryValuePreviewCharacters\n ) {\n return value;\n }\n return {\n omittedFromContext: true,\n originalCharacters: serialized.length,\n preview:\n typeof value === 'string'\n ? value.slice(0, maxHistoryValuePreviewCharacters)\n : serialized.slice(0, maxHistoryValuePreviewCharacters),\n };\n};\n\nconst serializeHistoryEntryForContext = (\n entry: NodeHistoryEntry,\n index: number,\n): string => {\n const compacted = Object.fromEntries(\n Object.entries({ index, ...entry }).map(([key, value]) => [\n key,\n compactHistoryContextValue(value),\n ]),\n );\n const serialized = JSON.stringify(compacted);\n if (serialized.length <= maxHistoryEntryCharacters) return serialized;\n\n return JSON.stringify({\n index,\n scope: entry.scope,\n phase: entry.phase,\n stepIndex: entry.stepIndex,\n node: entry.node,\n status: entry.status,\n ...(entry.summary === undefined\n ? {}\n : { summary: compactHistoryContextValue(entry.summary) }),\n omittedFromContext: true,\n compactedCharacters: serialized.length,\n });\n};\n\nexport const renderNodeHistory = (\n history: readonly NodeHistoryEntry[],\n): string | undefined => {\n if (history.length === 0) return undefined;\n\n const heading = 'Previous workflow results (read-only):';\n const availableCharacters =\n maxHistoryContextCharacters - heading.length - historyOmissionNoticeReserve;\n const renderedEntries: string[] = [];\n let renderedCharacters = 0;\n\n for (let index = history.length - 1; index >= 0; index -= 1) {\n const rendered = serializeHistoryEntryForContext(history[index], index + 1);\n const separatorCharacters = renderedEntries.length === 0 ? 0 : 1;\n if (\n renderedCharacters + separatorCharacters + rendered.length >\n availableCharacters\n ) {\n break;\n }\n renderedEntries.unshift(rendered);\n renderedCharacters += separatorCharacters + rendered.length;\n }\n\n const omittedEntries = history.length - renderedEntries.length;\n return [\n heading,\n ...(omittedEntries === 0\n ? []\n : [\n `${omittedEntries} earlier history entr${omittedEntries === 1 ? 'y was' : 'ies were'} omitted from Agent context to stay within the size limit. Complete results remain available in the Test Runner output.`,\n ]),\n ...renderedEntries,\n ].join('\\n');\n};\n\nconst mergeContext = (\n explicit: string | undefined,\n history: readonly NodeHistoryEntry[],\n): string | undefined =>\n [explicit, renderNodeHistory(history)].filter(Boolean).join('\\n\\n') ||\n undefined;\n\nconst toAgentPrompt = (input: {\n prompt: string;\n images?: MidscenePromptImage[];\n convertHttpImage2Base64?: boolean;\n}): MidsceneUserPrompt => {\n if (\n input.images === undefined &&\n input.convertHttpImage2Base64 === undefined\n ) {\n return input.prompt;\n }\n return {\n prompt: input.prompt,\n ...(input.images === undefined ? {} : { images: input.images }),\n ...(input.convertHttpImage2Base64 === undefined\n ? {}\n : { convertHttpImage2Base64: input.convertHttpImage2Base64 }),\n };\n};\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n\n const registeredAgentScopes = new Set<string>();\n const getExecutionId = (ctx: NodeExecutionContext<unknown, TContext>) =>\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const getAgent = async (\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n if (!options.agentProvider) return options.getAgent!(ctx);\n const runId = getExecutionId(ctx);\n if (\n options.agentProvider.releaseAgent &&\n !registeredAgentScopes.has(runId)\n ) {\n registeredAgentScopes.add(runId);\n ctx.onTeardown(async () => {\n try {\n const released = await options.agentProvider!.releaseAgent!(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n registeredAgentScopes.delete(runId);\n }\n });\n }\n return options.agentProvider.getAgent(runId, ctx);\n };\n\n return [\n defineNode<typeof aiActInputSchema, unknown, TContext>({\n name: 'aiAct',\n description: 'Perform a natural-language task with a Midscene UI Agent.',\n inputSchema: aiActInputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n const aiAct = requireAgentMethod(agent, 'aiAct', 'aiAct');\n const output = await aiAct.call(agent, toAgentPrompt(ctx.input), {\n ...ctx.input.options,\n context: mergeContext(ctx.input.options?.context, ctx.history),\n abortSignal: ctx.signal,\n });\n return output === undefined ? undefined : { summary: output };\n },\n }),\n defineNode<typeof aiAssertInputSchema, unknown, TContext>({\n name: 'aiAssert',\n description:\n 'Assert a natural-language condition with a Midscene UI Agent.',\n inputSchema: aiAssertInputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n const aiAssert = requireAgentMethod(agent, 'aiAssert', 'aiAssert');\n await aiAssert.call(\n agent,\n toAgentPrompt(ctx.input),\n ctx.input.message,\n {\n ...ctx.input.options,\n context: mergeContext(ctx.input.options?.context, ctx.history),\n abortSignal: ctx.signal,\n },\n );\n return { summary: `Assertion passed: ${ctx.input.prompt}` };\n },\n }),\n defineNode<typeof recordToReportInputSchema, unknown, TContext>({\n name: 'recordToReport',\n description: 'Add text or screenshots to the current Midscene report.',\n inputSchema: recordToReportInputSchema,\n async execute(ctx) {\n const title = ctx.input.title ?? ctx.input.prompt;\n const reportOptions: MidsceneRecordToReportOptions = {\n ...(ctx.input.content === undefined\n ? {}\n : { content: ctx.input.content }),\n ...(ctx.input.screenshotBase64 === undefined\n ? {}\n : { screenshotBase64: ctx.input.screenshotBase64 }),\n ...(ctx.input.screenshots === undefined\n ? {}\n : { screenshots: ctx.input.screenshots }),\n };\n const agent = await getAgent(ctx);\n const recordToReport = requireAgentMethod(\n agent,\n 'recordToReport',\n 'recordToReport',\n );\n await recordToReport.call(agent, title, reportOptions);\n return { summary: `Recorded to report: ${title ?? 'untitled'}` };\n },\n }),\n ...(options.includeLaunch === false ? [] : [createLaunchNode(getAgent)]),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: 'wait',\n description: 'Wait for a fixed duration while honoring cancellation.',\n inputSchema: waitInputSchema,\n async execute(ctx) {\n const multiplier =\n ctx.input.unit === 'min'\n ? 60_000\n : ctx.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = ctx.input.duration * multiplier;\n await waitFor(durationMs, ctx.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n defineNode<typeof agentInputSchema, unknown, TContext>({\n name: 'agent',\n description:\n 'Execute one self-contained natural-language task with an injected Agent executor.',\n inputSchema: agentInputSchema,\n async execute(ctx) {\n if (!options.agentExecutor) {\n throw new NodeExecutionError(\n 'agent',\n new TypeError('createMidsceneNodes() requires an agentExecutor.'),\n );\n }\n const execution =\n ctx.scope === 'case'\n ? { scope: 'case' as const, runId: ctx.case.runId }\n : {\n scope: 'document' as const,\n runId: ctx.document.documentRunId,\n };\n const result = await options.agentExecutor.execute({\n prompt: ctx.input.prompt,\n history: ctx.history,\n context: ctx.context,\n signal: ctx.signal,\n execution,\n });\n return result ?? { summary: 'Agent task completed.' };\n },\n }),\n ];\n}\n"],"names":["WorkflowError","Error","undefined","message","options","NodeDefinitionError","details","NodeExecutionError","node","cause","causeMessage","String","validateOptionalText","value","field","validateInputSchema","schema","z","validateDefinition","defineNode","lifecycleInputSchema","operation","description","input","ctx","launchInputSchema","requireLifecycleMethod","agent","method","agentName","TypeError","createLaunchNode","getAgent","uri","launch","nonBlankPrompt","promptImagesInputSchema","promptImageConversionInputSchema","aiActOptionsInputSchema","aiActInputSchema","aiAssertOptionsInputSchema","aiAssertInputSchema","reportScreenshotInputSchema","recordToReportInputSchema","waitInputSchema","agentInputSchema","isRecord","Array","requireAgentMethod","maxHistoryContextCharacters","maxHistoryValuePreviewCharacters","maxHistoryEntryCharacters","historyOmissionNoticeReserve","compactHistoryContextValue","serialized","JSON","serializeHistoryEntryForContext","entry","index","compacted","Object","key","renderNodeHistory","history","heading","availableCharacters","renderedEntries","renderedCharacters","rendered","separatorCharacters","omittedEntries","mergeContext","explicit","Boolean","toAgentPrompt","waitFor","durationMs","signal","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createMidsceneNodes","registeredAgentScopes","Set","getExecutionId","runId","released","aiAct","output","aiAssert","title","reportOptions","recordToReport","multiplier","execution","result"],"mappings":"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"}
1
+ {"version":3,"file":"midscene/index.mjs","sources":["../../../src/errors.ts","../../../src/node/define-node.ts","../../../src/midscene/index.ts"],"sourcesContent":["import type { z } from 'zod/v4';\n\nexport interface WorkflowErrorOptions {\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class WorkflowError extends Error {\n readonly code: string;\n readonly details?: unknown;\n\n constructor(message: string, options: WorkflowErrorOptions = {}) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.code = options.code ?? 'WORKFLOW_ERROR';\n this.details = options.details;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n ...(this.details === undefined ? {} : { details: this.details }),\n };\n }\n}\n\nexport class WorkflowParseError extends WorkflowError {\n constructor(message: string, details?: unknown, cause?: unknown) {\n super(message, { code: 'WORKFLOW_PARSE_ERROR', details, cause });\n }\n}\n\nexport class NodeDefinitionError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_DEFINITION_ERROR', details });\n }\n}\n\nexport class DuplicateNodeError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is already registered.`, {\n code: 'DUPLICATE_NODE',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeNotFoundError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is not registered.`, {\n code: 'NODE_NOT_FOUND',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeInputValidationError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_INPUT_VALIDATION_ERROR', details });\n }\n\n static fromZod(node: string, error: z.ZodError): NodeInputValidationError {\n const issues = error.issues.map((issue) => ({\n code: issue.code,\n path: issue.path.map(String).join('.'),\n message: issue.message,\n }));\n const firstIssue = issues[0];\n const path = firstIssue?.path || '<root>';\n const message = firstIssue?.message ?? 'invalid input';\n return new NodeInputValidationError(\n `Node \"${node}\" input validation failed at \"${path}\": ${message}`,\n { node, issues },\n );\n }\n}\n\nexport class StepTimeoutError extends WorkflowError {\n readonly timeoutMs: number;\n readonly node?: string;\n\n constructor(timeoutMs: number, node?: string) {\n super(\n node\n ? `Node \"${node}\" timed out after ${timeoutMs}ms.`\n : `Step timed out after ${timeoutMs}ms.`,\n {\n code: 'STEP_TIMEOUT',\n details: { timeoutMs, ...(node === undefined ? {} : { node }) },\n },\n );\n this.timeoutMs = timeoutMs;\n this.node = node;\n }\n}\n\nexport class NodeExecutionError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string, cause: unknown) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Node \"${node}\" failed: ${causeMessage}`, {\n code: 'NODE_EXECUTION_ERROR',\n details: { node },\n cause,\n });\n this.node = node;\n }\n}\n\nexport class WorkflowLifecycleError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'WORKFLOW_LIFECYCLE_ERROR', details });\n }\n}\n\nexport class ProjectSetupError extends WorkflowError {\n constructor(cause: unknown, details: { projectName: string }) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" setup failed: ${causeMessage}`, {\n code: 'PROJECT_SETUP_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class ProjectTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: { projectName: string; registrationIndex: number },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" teardown failed: ${causeMessage}`, {\n code: 'PROJECT_TEARDOWN_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class NodeScopeTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: {\n scope: 'case' | 'document';\n scopeId: string;\n node: string;\n registrationIndex: number;\n },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(\n `${details.scope === 'case' ? 'Case attempt' : 'Workflow document'} node teardown failed for \"${details.node}\": ${causeMessage}`,\n { code: 'NODE_SCOPE_TEARDOWN_ERROR', details, cause },\n );\n }\n}\n\nexport class FatalDeviceError extends WorkflowError {\n constructor(message: string, cause?: unknown) {\n super(message, { code: 'FATAL_DEVICE_ERROR', cause });\n }\n}\n\nexport const isFatalDeviceError = (error: unknown): boolean => {\n if (error instanceof FatalDeviceError) return true;\n if (error instanceof WorkflowError && error.code === 'FATAL_DEVICE_ERROR') {\n return true;\n }\n if (\n error instanceof Error &&\n /device offline|device not found|(?:adb|bdc|device) connection (?:was )?closed/i.test(\n error.message,\n )\n ) {\n return true;\n }\n return error instanceof Error && error.cause !== undefined\n ? isFatalDeviceError(error.cause)\n : false;\n};\n\nexport class CaseExecutionError extends WorkflowError {\n readonly result: import('./engine/types').CaseRunResult;\n\n constructor(result: import('./engine/types').CaseRunResult) {\n super(`Case \"${result.name}\" failed.`, {\n code: 'CASE_EXECUTION_FAILED',\n details: { caseId: result.caseId, runId: result.runId },\n });\n this.result = result;\n }\n}\n\nexport class WorkflowDocumentExecutionError extends WorkflowError {\n readonly result: import('./engine/types').WorkflowDocumentRunResult;\n\n constructor(result: import('./engine/types').WorkflowDocumentRunResult) {\n super(`Workflow document \"${result.sourcePath}\" failed.`, {\n code: 'WORKFLOW_DOCUMENT_EXECUTION_FAILED',\n details: {\n documentId: result.documentId,\n documentRunId: result.documentRunId,\n },\n });\n this.result = result;\n }\n}\n\nexport function normalizeNodeExecutionError(\n error: unknown,\n node: string,\n): WorkflowError {\n return error instanceof WorkflowError\n ? error\n : new NodeExecutionError(node, error);\n}\n","import { z } from 'zod/v4';\nimport { NodeDefinitionError } from '../errors';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\nconst validateOptionalText = (\n value: unknown,\n field: 'title' | 'description',\n node: string,\n): void => {\n if (\n value !== undefined &&\n (typeof value !== 'string' || value.trim().length === 0)\n ) {\n throw new NodeDefinitionError(\n `Node \"${node}\" ${field} must be a non-empty string.`,\n { node, field },\n );\n }\n};\n\nconst validateInputSchema = (schema: unknown, node: string): void => {\n if (schema === undefined) return;\n if (!(schema instanceof z.ZodObject)) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must be a Zod object schema.`,\n { node, field: 'inputSchema' },\n );\n }\n if ('$' in schema.shape) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must not declare \"$\" as an input property.`,\n { node, field: 'inputSchema.$' },\n );\n }\n};\n\nconst validateDefinition = (options: {\n name: string;\n title?: unknown;\n description?: unknown;\n stringInputKey?: unknown;\n inputSchema?: unknown;\n execute: unknown;\n}): void => {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError('Node definition must be an object.');\n }\n\n if (typeof options.name !== 'string' || options.name.trim().length === 0) {\n throw new NodeDefinitionError('Node name must be a non-empty string.');\n }\n\n validateOptionalText(options.title, 'title', options.name);\n validateOptionalText(options.description, 'description', options.name);\n if (\n options.stringInputKey !== undefined &&\n options.stringInputKey !== false &&\n (typeof options.stringInputKey !== 'string' ||\n options.stringInputKey.trim().length === 0)\n ) {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" stringInputKey must be a non-empty string or false.`,\n { node: options.name, field: 'stringInputKey' },\n );\n }\n validateInputSchema(options.inputSchema, options.name);\n\n if (\n typeof options.stringInputKey === 'string' &&\n options.inputSchema instanceof z.ZodObject &&\n !(options.stringInputKey in options.inputSchema.shape)\n ) {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" stringInputKey must reference a field in inputSchema.`,\n { node: options.name, field: 'stringInputKey' },\n );\n }\n\n if (typeof options.execute !== 'function') {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" must provide an execute function.`,\n { node: options.name },\n );\n }\n};\n\nexport function defineNode<\n TSchema extends NodeInputSchema,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n\nexport function defineNode<\n TInput = unknown,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n): NodeDefinition<TInput, TData, TContext>;\n\nexport function defineNode(\n options: DefineNodeOptions<any, any, any>,\n): NodeDefinition<any, any, any> {\n validateDefinition(options);\n return options;\n}\n","import {\n type AgentTestRunnerNodeDefinition,\n type AgentTestRunnerNodeProvider,\n type CommonAgentTestRunnerApi,\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n} from '@midscene/core/agent/test-runner';\nimport { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeDefinitionError, NodeExecutionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type {\n NodeDefinition,\n NodeExecutionContext,\n NodeResult,\n} from '../node/types';\n\nexport type MidsceneUIAgent = CommonAgentTestRunnerApi;\n\nexport {\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n};\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport interface AgentExecutorInput<TContext> {\n prompt: string;\n context: TContext;\n signal: AbortSignal;\n execution:\n | { scope: 'case'; runId: string }\n | { scope: 'document'; runId: string };\n}\n\nexport interface AgentExecutor<TContext> {\n // biome-ignore lint/suspicious/noConfusingVoidType: executors may perform side effects without returning a summary.\n execute(input: AgentExecutorInput<TContext>): Awaitable<NodeResult | void>;\n}\n\nconst nonBlankPrompt = (description: string) =>\n z\n .string()\n .regex(/\\S/, 'prompt must contain a non-whitespace character')\n .describe(description);\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport const agentInputSchema = z.strictObject({\n prompt: nonBlankPrompt(\n 'A self-contained task, including allowed tools and success conditions.',\n ),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\nexport type AgentNodeInput = z.infer<typeof agentInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n getAgent?(\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Agent class that declares the Agent-backed Nodes to register. */\n agentClass: AgentTestRunnerNodeProvider;\n agentExecutor?: AgentExecutor<TContext>;\n}\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\n/** Adapt Agent-owned descriptions to executable Test Runner Nodes. */\nexport const createAgentTestRunnerNodes = <TContext>(\n definitions: readonly AgentTestRunnerNodeDefinition[],\n getAgent: (\n ctx: NodeExecutionContext<unknown, TContext>,\n ) => Awaitable<unknown>,\n): readonly NodeDefinition<any, any, TContext>[] =>\n definitions.map((definition) =>\n defineNode({\n name: definition.name,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n return definition.execute(agent, ctx.input, {\n signal: ctx.signal,\n });\n },\n }),\n );\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n if (\n !options.agentClass ||\n typeof options.agentClass.getTestRunnerNodeDefinitions !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().',\n );\n }\n\n const registeredAgentScopes = new Set<string>();\n const getExecutionId = (ctx: NodeExecutionContext<unknown, TContext>) =>\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const getAgent = async (\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n if (!options.agentProvider) return options.getAgent!(ctx);\n const runId = getExecutionId(ctx);\n if (\n options.agentProvider.releaseAgent &&\n !registeredAgentScopes.has(runId)\n ) {\n registeredAgentScopes.add(runId);\n ctx.onTeardown(async () => {\n try {\n const released = await options.agentProvider!.releaseAgent!(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n registeredAgentScopes.delete(runId);\n }\n });\n }\n return options.agentProvider.getAgent(runId, ctx);\n };\n\n const agentDefinitions = options.agentClass.getTestRunnerNodeDefinitions();\n\n return [\n ...createAgentTestRunnerNodes(agentDefinitions, getAgent),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: 'wait',\n description: 'Wait for a fixed duration while honoring cancellation.',\n stringInputKey: false,\n inputSchema: waitInputSchema,\n async execute(ctx) {\n const multiplier =\n ctx.input.unit === 'min'\n ? 60_000\n : ctx.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = ctx.input.duration * multiplier;\n await waitFor(durationMs, ctx.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n defineNode<typeof agentInputSchema, unknown, TContext>({\n name: 'agent',\n description:\n 'Execute one self-contained natural-language task with an injected Agent executor.',\n stringInputKey: 'prompt',\n inputSchema: agentInputSchema,\n async execute(ctx) {\n if (!options.agentExecutor) {\n throw new NodeExecutionError(\n 'agent',\n new TypeError('createMidsceneNodes() requires an agentExecutor.'),\n );\n }\n const execution =\n ctx.scope === 'case'\n ? { scope: 'case' as const, runId: ctx.case.runId }\n : {\n scope: 'document' as const,\n runId: ctx.document.documentRunId,\n };\n const result = await options.agentExecutor.execute({\n prompt: ctx.input.prompt,\n context: ctx.context,\n signal: ctx.signal,\n execution,\n });\n return result ?? { summary: 'Agent task completed.' };\n },\n }),\n ];\n}\n"],"names":["WorkflowError","Error","undefined","message","options","NodeDefinitionError","details","NodeExecutionError","node","cause","causeMessage","String","validateOptionalText","value","field","validateInputSchema","schema","z","validateDefinition","defineNode","nonBlankPrompt","description","waitInputSchema","agentInputSchema","waitFor","durationMs","signal","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createAgentTestRunnerNodes","definitions","getAgent","definition","ctx","agent","createMidsceneNodes","registeredAgentScopes","Set","getExecutionId","runId","released","agentDefinitions","multiplier","TypeError","execution","result"],"mappings":"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"}
@@ -61,7 +61,15 @@ const validateDefinition = (options)=>{
61
61
  if ('string' != typeof options.name || 0 === options.name.trim().length) throw new NodeDefinitionError('Node name must be a non-empty string.');
62
62
  validateOptionalText(options.title, 'title', options.name);
63
63
  validateOptionalText(options.description, "description", options.name);
64
+ if (void 0 !== options.stringInputKey && false !== options.stringInputKey && ('string' != typeof options.stringInputKey || 0 === options.stringInputKey.trim().length)) throw new NodeDefinitionError(`Node "${options.name}" stringInputKey must be a non-empty string or false.`, {
65
+ node: options.name,
66
+ field: 'stringInputKey'
67
+ });
64
68
  validateInputSchema(options.inputSchema, options.name);
69
+ if ('string' == typeof options.stringInputKey && options.inputSchema instanceof z.ZodObject && !(options.stringInputKey in options.inputSchema.shape)) throw new NodeDefinitionError(`Node "${options.name}" stringInputKey must reference a field in inputSchema.`, {
70
+ node: options.name,
71
+ field: 'stringInputKey'
72
+ });
65
73
  if ('function' != typeof options.execute) throw new NodeDefinitionError(`Node "${options.name}" must provide an execute function.`, {
66
74
  node: options.name
67
75
  });
@@ -100,6 +108,7 @@ const createClearCookiesNode = (options)=>defineNode({
100
108
  name: 'clearCookies',
101
109
  title: 'Clear browser cookies',
102
110
  description: 'Clear all cookies from the current Playwright BrowserContext, or only cookies matching name, domain, or path.',
111
+ stringInputKey: false,
103
112
  inputSchema: clearCookiesInputSchema,
104
113
  async execute (ctx) {
105
114
  throwIfAborted(ctx.signal, 'clearCookies');
@@ -249,6 +258,7 @@ const createSetCookiesNode = (options)=>defineNode({
249
258
  name: 'setCookies',
250
259
  title: 'Set browser cookies',
251
260
  description: 'Load cookies from an environment variable, configured profile, or Playwright storage-state file without persisting cookie values in workflow input or output.',
261
+ stringInputKey: false,
252
262
  inputSchema: setCookiesInputSchema,
253
263
  async execute (ctx) {
254
264
  throwIfAborted(ctx.signal, 'setCookies');
@@ -277,8 +287,7 @@ const createSetCookiesNode = (options)=>defineNode({
277
287
  const DEFAULT_NAVIGATION_TIMEOUT_MS = 60000;
278
288
  const DEFAULT_WAIT_UNTIL = 'domcontentloaded';
279
289
  const gotoUrlInputSchema = z.strictObject({
280
- prompt: z.string().min(1).optional().describe('String shorthand for the URL to open.'),
281
- url: z.string().min(1).optional().describe('An absolute HTTP(S) URL or a path relative to baseUrl.'),
290
+ url: z.string().min(1).describe('An absolute HTTP(S) URL or a path relative to baseUrl.'),
282
291
  waitUntil: z["enum"]([
283
292
  'commit',
284
293
  'domcontentloaded',
@@ -286,24 +295,18 @@ const gotoUrlInputSchema = z.strictObject({
286
295
  'networkidle'
287
296
  ]).default(DEFAULT_WAIT_UNTIL).describe('The Playwright navigation lifecycle event to wait for.'),
288
297
  timeoutMs: z.number().positive().default(DEFAULT_NAVIGATION_TIMEOUT_MS).describe('The navigation timeout in milliseconds.')
289
- }).superRefine((input, ctx)=>{
290
- if (void 0 === input.prompt === (void 0 === input.url)) ctx.addIssue({
291
- code: 'custom',
292
- message: 'exactly one of prompt and url is required'
293
- });
294
298
  });
295
299
  const createGotoUrlNode = (options)=>defineNode({
296
300
  name: 'gotoUrl',
297
301
  title: 'Open a Web URL',
298
302
  description: 'Navigate the current Playwright Page to an absolute HTTP(S) URL or a path relative to the configured baseUrl.',
303
+ stringInputKey: 'url',
299
304
  inputSchema: gotoUrlInputSchema,
300
305
  async execute (ctx) {
301
306
  throwIfAborted(ctx.signal, 'gotoUrl');
302
307
  const page = await options.getPage(ctx);
303
308
  const baseUrl = await options.getBaseUrl?.(ctx);
304
- const target = ctx.input.url ?? ctx.input.prompt;
305
- if (void 0 === target) throw new TypeError('gotoUrl requires a URL.');
306
- const url = resolveWebUrl(target, baseUrl, 'gotoUrl.url');
309
+ const url = resolveWebUrl(ctx.input.url, baseUrl, 'gotoUrl.url');
307
310
  const response = await page.goto(url, {
308
311
  waitUntil: ctx.input.waitUntil,
309
312
  timeout: ctx.input.timeoutMs
@@ -328,6 +331,7 @@ const createSetViewportSizeNode = (options)=>defineNode({
328
331
  name: 'setViewportSize',
329
332
  title: 'Set the browser viewport size',
330
333
  description: 'Set the current Playwright Page viewport size in CSS pixels and return the effective size.',
334
+ stringInputKey: false,
331
335
  inputSchema: setViewportSizeInputSchema,
332
336
  async execute (ctx) {
333
337
  throwIfAborted(ctx.signal, 'setViewportSize');