vanilla-agent 1.6.0 → 1.7.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.
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +16 -16
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
- package/src/client.test.ts +1 -1
- package/src/client.ts +18 -9
- package/src/utils/formatting.test.ts +10 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanilla-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Themeable, plugable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -30,13 +30,15 @@
|
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^20.12.7",
|
|
33
|
+
"@vitest/ui": "^4.0.9",
|
|
33
34
|
"eslint": "^8.57.0",
|
|
34
35
|
"eslint-config-prettier": "^9.1.0",
|
|
35
36
|
"postcss": "^8.4.38",
|
|
36
37
|
"rimraf": "^5.0.5",
|
|
37
38
|
"tailwindcss": "^3.4.10",
|
|
38
39
|
"tsup": "^8.0.1",
|
|
39
|
-
"typescript": "^5.4.5"
|
|
40
|
+
"typescript": "^5.4.5",
|
|
41
|
+
"vitest": "^4.0.9"
|
|
40
42
|
},
|
|
41
43
|
"engines": {
|
|
42
44
|
"node": ">=18.17.0"
|
|
@@ -58,6 +60,9 @@
|
|
|
58
60
|
"build:client": "tsup src/index.ts --format esm,cjs,iife --global-name AgentWidget --minify --sourcemap --splitting false --dts --loader \".css=text\"",
|
|
59
61
|
"build:installer": "tsup src/install.ts --format iife --global-name SiteAgentInstaller --out-dir dist --minify --sourcemap --no-splitting",
|
|
60
62
|
"lint": "eslint . --ext .ts",
|
|
61
|
-
"typecheck": "tsc --noEmit"
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"test": "vitest",
|
|
65
|
+
"test:ui": "vitest --ui",
|
|
66
|
+
"test:run": "vitest run"
|
|
62
67
|
}
|
|
63
68
|
}
|
package/src/client.test.ts
CHANGED
|
@@ -22,7 +22,7 @@ describe('AgentWidgetClient - JSON Streaming', () => {
|
|
|
22
22
|
'',
|
|
23
23
|
'data: {"type":"step_start","id":"step_01k9x5db72fzwvmdenryn0qm48","name":"Prompt 1","stepType":"prompt","index":1,"totalSteps":1,"startedAt":"2025-11-12T23:47:39.565Z"}',
|
|
24
24
|
'',
|
|
25
|
-
'data: {"type":"step_chunk","id":"step_01k9x5db72fzwvmdenryn0qm48","name":"Prompt 1","executionType":"prompt","index":2,"text":"{
|
|
25
|
+
'data: {"type":"step_chunk","id":"step_01k9x5db72fzwvmdenryn0qm48","name":"Prompt 1","executionType":"prompt","index":2,"text":"{\\n"}',
|
|
26
26
|
'',
|
|
27
27
|
'data: {"type":"step_chunk","id":"step_01k9x5db72fzwvmdenryn0qm48","name":"Prompt 1","executionType":"prompt","index":2,"text":" "}',
|
|
28
28
|
'',
|
package/src/client.ts
CHANGED
|
@@ -640,6 +640,7 @@ export class AgentWidgetClient {
|
|
|
640
640
|
// Accumulate raw content for structured format parsing
|
|
641
641
|
const rawBuffer = rawContentBuffers.get(assistant.id) ?? "";
|
|
642
642
|
const accumulatedRaw = rawBuffer + chunk;
|
|
643
|
+
// Store raw content for action parsing, but NEVER set assistant.content to raw JSON
|
|
643
644
|
assistant.rawContent = accumulatedRaw;
|
|
644
645
|
|
|
645
646
|
// Use stream parser to parse
|
|
@@ -699,6 +700,7 @@ export class AgentWidgetClient {
|
|
|
699
700
|
}
|
|
700
701
|
}
|
|
701
702
|
// Otherwise wait for more chunks (incomplete structured format)
|
|
703
|
+
// Don't emit message if parser hasn't extracted text yet
|
|
702
704
|
}).catch(() => {
|
|
703
705
|
// On error, treat as plain text
|
|
704
706
|
assistant.content += chunk;
|
|
@@ -727,14 +729,12 @@ export class AgentWidgetClient {
|
|
|
727
729
|
emitMessage(assistant);
|
|
728
730
|
}
|
|
729
731
|
// Otherwise wait for more chunks (incomplete structured format)
|
|
732
|
+
// Don't emit message if parser hasn't extracted text yet
|
|
730
733
|
}
|
|
731
734
|
|
|
732
|
-
//
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
assistant.content = currentText;
|
|
736
|
-
emitMessage(assistant);
|
|
737
|
-
}
|
|
735
|
+
// IMPORTANT: Don't call getExtractedText() and emit messages here
|
|
736
|
+
// This was causing raw JSON to be displayed because getExtractedText()
|
|
737
|
+
// wasn't extracting the "text" field correctly during streaming
|
|
738
738
|
}
|
|
739
739
|
if (payload.isComplete) {
|
|
740
740
|
const finalContent = payload.result?.response ?? assistant.content;
|
|
@@ -820,15 +820,18 @@ export class AgentWidgetClient {
|
|
|
820
820
|
if (parser) {
|
|
821
821
|
// First check if parser already extracted text during streaming
|
|
822
822
|
const currentExtractedText = parser.getExtractedText();
|
|
823
|
+
const rawBuffer = rawContentBuffers.get(assistant.id);
|
|
824
|
+
const contentToProcess = rawBuffer ?? ensureStringContent(finalContent);
|
|
825
|
+
|
|
826
|
+
// Always set rawContent so action parsers can access the raw JSON
|
|
827
|
+
assistant.rawContent = contentToProcess;
|
|
828
|
+
|
|
823
829
|
if (currentExtractedText !== null && currentExtractedText.trim() !== "") {
|
|
824
830
|
// We already have extracted text from streaming - use it
|
|
825
831
|
assistant.content = currentExtractedText;
|
|
826
832
|
hasExtractedText = true;
|
|
827
833
|
} else {
|
|
828
834
|
// No extracted text yet - try to extract from final content
|
|
829
|
-
const rawBuffer = rawContentBuffers.get(assistant.id);
|
|
830
|
-
const contentToProcess = rawBuffer ?? ensureStringContent(finalContent);
|
|
831
|
-
assistant.rawContent = contentToProcess;
|
|
832
835
|
|
|
833
836
|
// Try fast path first
|
|
834
837
|
const extractedText = extractTextFromJson(contentToProcess);
|
|
@@ -887,6 +890,12 @@ export class AgentWidgetClient {
|
|
|
887
890
|
}
|
|
888
891
|
}
|
|
889
892
|
|
|
893
|
+
// Ensure rawContent is set even if there's no parser (for action parsing)
|
|
894
|
+
if (!assistant.rawContent) {
|
|
895
|
+
const rawBuffer = rawContentBuffers.get(assistant.id);
|
|
896
|
+
assistant.rawContent = rawBuffer ?? ensureStringContent(finalContent);
|
|
897
|
+
}
|
|
898
|
+
|
|
890
899
|
// Only show raw content if we never extracted any text and no buffer was used
|
|
891
900
|
if (!hasExtractedText && !rawContentBuffers.has(assistant.id)) {
|
|
892
901
|
// No extracted text and no streaming happened - show raw content
|
|
@@ -45,8 +45,10 @@ describe("JSON Stream Parser", () => {
|
|
|
45
45
|
accumulatedContent += chunk;
|
|
46
46
|
const result = parser.processChunk(accumulatedContent);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
// Extract text from result (can be string or object with text property)
|
|
49
|
+
const text = typeof result === 'string' ? result : result?.text ?? null;
|
|
50
|
+
if (text !== null) {
|
|
51
|
+
extractedTexts.push(text);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
// Also check getExtractedText
|
|
@@ -101,7 +103,9 @@ describe("JSON Stream Parser", () => {
|
|
|
101
103
|
const parser = createJsonStreamParser();
|
|
102
104
|
const result = parser.processChunk(completeJson);
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
// Extract text from result (can be string or object with text property)
|
|
107
|
+
const text = typeof result === 'string' ? result : result?.text ?? null;
|
|
108
|
+
expect(text).toBe("Hello world!");
|
|
105
109
|
expect(parser.getExtractedText()).toBe("Hello world!");
|
|
106
110
|
});
|
|
107
111
|
|
|
@@ -146,7 +150,9 @@ describe("JSON Stream Parser", () => {
|
|
|
146
150
|
for (const chunk of textChunks) {
|
|
147
151
|
accumulated += chunk;
|
|
148
152
|
const result = parser.processChunk(accumulated);
|
|
149
|
-
|
|
153
|
+
// Extract text from result (can be string or object with text property)
|
|
154
|
+
const text = typeof result === 'string' ? result : result?.text ?? null;
|
|
155
|
+
allExtractedTexts.push(text);
|
|
150
156
|
}
|
|
151
157
|
|
|
152
158
|
// Should have many non-null results (incremental updates)
|