@@ -182,14 +182,25 @@ function stripThinkingTags(content: string): string {
182182function repairJson ( text : string ) : string {
183183 let repaired = text ;
184184
185+ // Fix backslash-escaped quotes that make JSON unparseable.
186+ // Some LLMs output: {"action":"CLICK",...,\"reasoning\":\"text\"}
187+ // where the outer quotes are real but inner key/value quotes are escaped.
188+ // Heuristic: if the text has unescaped quotes AND backslash-escaped quotes
189+ // mixed together, unescape the backslash-escaped ones.
190+ const hasUnescapedQuotes = / [ ^ \\ ] " / . test ( repaired ) ;
191+ const hasEscapedQuotes = / \\ " / . test ( repaired ) ;
192+ if ( hasUnescapedQuotes && hasEscapedQuotes ) {
193+ repaired = repaired . replace ( / \\ " / g, '"' ) ;
194+ }
195+
185196 // Add double quotes around unquoted object keys
186197 // Matches: word-characters followed by colon (not already inside a string)
187198 // Pattern: start of object `{` or comma `,`, optional whitespace, then unquoted key, then `:`
188199 repaired = repaired . replace ( / ( [ { , ] \s * ) ( [ \w $ ] + ) \s * : / g, '$1"$2":' ) ;
189200
190201 // Replace single-quoted strings with double-quoted strings
191202 // This is a simple heuristic — it won't handle escaped single quotes inside strings,
192- // but it handles the common case of LLMs outputting ` 'text'` instead of ` "text"`
203+ // but it handles the common case of LLMs outputting 'text' instead of "text"
193204 repaired = repaired . replace ( / ' ( [ ^ ' ] * ) ' / g, '"$1"' ) ;
194205
195206 // Remove trailing commas before } or ]
@@ -338,6 +349,8 @@ const ACTION_ALIASES: Record<string, string> = {
338349 CLICK_ELEMENT : 'CLICK' ,
339350 CLICK_BUTTON : 'CLICK' ,
340351 CLICK_LINK : 'CLICK' ,
352+ CLICK_XY : 'CLICK' ,
353+ TYPE_AT : 'TYPE' ,
341354 INPUT : 'TYPE_AND_SUBMIT' ,
342355 TYPE_TEXT : 'TYPE_AND_SUBMIT' ,
343356 ENTER_TEXT : 'TYPE_AND_SUBMIT' ,
@@ -347,6 +360,8 @@ const ACTION_ALIASES: Record<string, string> = {
347360 OPEN : 'NAVIGATE' ,
348361 SCROLL_DOWN : 'SCROLL' ,
349362 SCROLL_UP : 'SCROLL' ,
363+ SCROLL_TO : 'SCROLL' ,
364+ SCROLL_INTO_VIEW : 'SCROLL' ,
350365} ;
351366
352367/**
@@ -517,6 +532,20 @@ function normalizeStep(step: Record<string, unknown>): Record<string, unknown> {
517532 if ( 'target' in normalizedStep && normalizedStep . target === null ) {
518533 delete normalizedStep . target ;
519534 }
535+ if ( 'input' in normalizedStep && typeof normalizedStep . input !== 'string' ) {
536+ const inputValue = normalizedStep . input ;
537+ if ( inputValue === null || inputValue === undefined ) {
538+ delete normalizedStep . input ;
539+ } else if (
540+ typeof inputValue === 'number' ||
541+ typeof inputValue === 'boolean' ||
542+ typeof inputValue === 'bigint'
543+ ) {
544+ normalizedStep . input = String ( inputValue ) ;
545+ } else {
546+ normalizedStep . input = JSON . stringify ( inputValue ) ;
547+ }
548+ }
520549
521550 if ( 'id' in normalizedStep && typeof normalizedStep . id === 'string' ) {
522551 const parsed = parseInt ( normalizedStep . id , 10 ) ;
0 commit comments