vanilla-agent 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,19 @@
1
1
  import { AgentWidgetReasoning, AgentWidgetToolCall, AgentWidgetStreamParser, AgentWidgetStreamParserResult } from "../types";
2
2
  import { parse as parsePartialJson, STR, OBJ } from "partial-json";
3
3
 
4
+ /**
5
+ * Unescapes JSON string escape sequences that LLMs often double-escape.
6
+ * Converts literal \n, \r, \t sequences to actual control characters.
7
+ */
8
+ const unescapeJsonString = (str: string): string => {
9
+ return str
10
+ .replace(/\\n/g, '\n')
11
+ .replace(/\\r/g, '\r')
12
+ .replace(/\\t/g, '\t')
13
+ .replace(/\\"/g, '"')
14
+ .replace(/\\\\/g, '\\');
15
+ };
16
+
4
17
  export const formatUnknownValue = (value: unknown): string => {
5
18
  if (value === null) return "null";
6
19
  if (value === undefined) return "";
@@ -277,7 +290,7 @@ export const createJsonStreamParser = (): AgentWidgetStreamParser => {
277
290
  // Check for component directives - extract text if present for combined text+component
278
291
  if (parsed.component && typeof parsed.component === "string") {
279
292
  // For component directives, extract text if present, otherwise empty
280
- extractedText = typeof parsed.text === "string" ? parsed.text : "";
293
+ extractedText = typeof parsed.text === "string" ? unescapeJsonString(parsed.text) : "";
281
294
  }
282
295
  // Check for form directives - these also don't have text fields
283
296
  else if (parsed.type === "init" && parsed.form) {
@@ -286,7 +299,7 @@ export const createJsonStreamParser = (): AgentWidgetStreamParser => {
286
299
  }
287
300
  // Extract text field if available
288
301
  else if (typeof parsed.text === "string") {
289
- extractedText = parsed.text;
302
+ extractedText = unescapeJsonString(parsed.text);
290
303
  }
291
304
  }
292
305
  } catch (error) {
@@ -332,10 +345,15 @@ export const createFlexibleJsonStreamParser = (
332
345
  const defaultExtractor = (parsed: any): string | null => {
333
346
  if (!parsed || typeof parsed !== "object") return null;
334
347
 
348
+ // Helper to safely extract and unescape text
349
+ const getText = (value: any): string | null => {
350
+ return typeof value === "string" ? unescapeJsonString(value) : null;
351
+ };
352
+
335
353
  // Check for component directives - extract text if present for combined text+component
336
354
  if (parsed.component && typeof parsed.component === "string") {
337
355
  // For component directives, extract text if present, otherwise empty
338
- return typeof parsed.text === "string" ? parsed.text : "";
356
+ return typeof parsed.text === "string" ? unescapeJsonString(parsed.text) : "";
339
357
  }
340
358
 
341
359
  // Check for form directives - these also don't have text fields
@@ -348,18 +366,18 @@ export const createFlexibleJsonStreamParser = (
348
366
  if (parsed.action) {
349
367
  switch (parsed.action) {
350
368
  case 'nav_then_click':
351
- return parsed.on_load_text || parsed.text || null;
369
+ return getText(parsed.on_load_text) || getText(parsed.text) || null;
352
370
  case 'message':
353
371
  case 'message_and_click':
354
372
  case 'checkout':
355
- return parsed.text || null;
373
+ return getText(parsed.text) || null;
356
374
  default:
357
- return parsed.text || parsed.display_text || parsed.message || null;
375
+ return getText(parsed.text) || getText(parsed.display_text) || getText(parsed.message) || null;
358
376
  }
359
377
  }
360
378
 
361
379
  // Fallback to common text field names
362
- return parsed.text || parsed.display_text || parsed.message || parsed.content || null;
380
+ return getText(parsed.text) || getText(parsed.display_text) || getText(parsed.message) || getText(parsed.content) || null;
363
381
  };
364
382
 
365
383
  const extractText = textExtractor || defaultExtractor;