vanilla-agent 1.1.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-agent",
3
- "version": "1.1.0",
3
+ "version": "1.3.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",
@@ -116,7 +116,7 @@ export const initAgentWidget = (
116
116
  let controller = createAgentExperience(mount, options.config);
117
117
  options.onReady?.();
118
118
 
119
- return {
119
+ const handle: AgentWidgetInitHandle = {
120
120
  host,
121
121
  update(nextConfig: AgentWidgetConfig) {
122
122
  controller.update(nextConfig);
@@ -133,9 +133,32 @@ export const initAgentWidget = (
133
133
  clearChat() {
134
134
  controller.clearChat();
135
135
  },
136
+ setMessage(message: string) {
137
+ return controller.setMessage(message);
138
+ },
139
+ submitMessage(message?: string) {
140
+ return controller.submitMessage(message);
141
+ },
142
+ startVoiceRecognition() {
143
+ return controller.startVoiceRecognition();
144
+ },
145
+ stopVoiceRecognition() {
146
+ return controller.stopVoiceRecognition();
147
+ },
136
148
  destroy() {
137
149
  controller.destroy();
138
150
  host.remove();
151
+ // Clean up window reference if it was set
152
+ if (options.windowKey && typeof window !== 'undefined') {
153
+ delete (window as any)[options.windowKey];
154
+ }
139
155
  }
140
156
  };
157
+
158
+ // Store on window if windowKey is provided
159
+ if (options.windowKey && typeof window !== 'undefined') {
160
+ (window as any)[options.windowKey] = handle;
161
+ }
162
+
163
+ return handle;
141
164
  };
package/src/session.ts CHANGED
@@ -60,7 +60,7 @@ export class AgentWidgetSession {
60
60
  return this.streaming;
61
61
  }
62
62
 
63
- public async sendMessage(rawInput: string) {
63
+ public async sendMessage(rawInput: string, options?: { viaVoice?: boolean }) {
64
64
  const input = rawInput.trim();
65
65
  if (!input) return;
66
66
 
@@ -71,7 +71,8 @@ export class AgentWidgetSession {
71
71
  role: "user",
72
72
  content: input,
73
73
  createdAt: new Date().toISOString(),
74
- sequence: this.nextSequence()
74
+ sequence: this.nextSequence(),
75
+ viaVoice: options?.viaVoice || false
75
76
  };
76
77
 
77
78
  this.appendMessage(userMessage);
package/src/types.ts CHANGED
@@ -193,6 +193,22 @@ export type AgentWidgetToolCall = {
193
193
 
194
194
  export type AgentWidgetMessageVariant = "assistant" | "reasoning" | "tool";
195
195
 
196
+ /**
197
+ * Represents a message in the chat conversation.
198
+ *
199
+ * @property id - Unique message identifier
200
+ * @property role - Message role: "user", "assistant", or "system"
201
+ * @property content - Message text content
202
+ * @property createdAt - ISO timestamp when message was created
203
+ * @property streaming - Whether message is still streaming (for assistant messages)
204
+ * @property variant - Message variant for assistant messages: "assistant", "reasoning", or "tool"
205
+ * @property sequence - Message ordering number
206
+ * @property reasoning - Reasoning data for assistant reasoning messages
207
+ * @property toolCall - Tool call data for assistant tool messages
208
+ * @property tools - Array of tool calls
209
+ * @property viaVoice - Set to `true` when a user message is sent via voice recognition.
210
+ * Useful for implementing voice-specific behaviors like auto-reactivation.
211
+ */
196
212
  export type AgentWidgetMessage = {
197
213
  id: string;
198
214
  role: AgentWidgetMessageRole;
@@ -204,6 +220,7 @@ export type AgentWidgetMessage = {
204
220
  reasoning?: AgentWidgetReasoning;
205
221
  toolCall?: AgentWidgetToolCall;
206
222
  tools?: AgentWidgetToolCall[];
223
+ viaVoice?: boolean;
207
224
  };
208
225
 
209
226
  export type AgentWidgetEvent =
@@ -216,4 +233,5 @@ export type AgentWidgetInitOptions = {
216
233
  config?: AgentWidgetConfig;
217
234
  useShadowDom?: boolean;
218
235
  onReady?: () => void;
236
+ windowKey?: string; // If provided, stores the controller on window[windowKey] for global access
219
237
  };
package/src/ui.ts CHANGED
@@ -23,6 +23,10 @@ type Controller = {
23
23
  close: () => void;
24
24
  toggle: () => void;
25
25
  clearChat: () => void;
26
+ setMessage: (message: string) => boolean;
27
+ submitMessage: (message?: string) => boolean;
28
+ startVoiceRecognition: () => boolean;
29
+ stopVoiceRecognition: () => boolean;
26
30
  };
27
31
 
28
32
  const buildPostprocessor = (cfg?: AgentWidgetConfig): MessageTransform => {
@@ -465,7 +469,7 @@ export const createAgentExperience = (
465
469
  if (finalValue && speechRecognition && isRecording) {
466
470
  stopVoiceRecognition();
467
471
  textarea.value = "";
468
- session.sendMessage(finalValue);
472
+ session.sendMessage(finalValue, { viaVoice: true });
469
473
  }
470
474
  }, pauseDuration);
471
475
  }
@@ -484,7 +488,7 @@ export const createAgentExperience = (
484
488
  const finalValue = textarea.value.trim();
485
489
  if (finalValue && finalValue !== initialText.trim()) {
486
490
  textarea.value = "";
487
- session.sendMessage(finalValue);
491
+ session.sendMessage(finalValue, { viaVoice: true });
488
492
  }
489
493
  stopVoiceRecognition();
490
494
  }
@@ -1596,6 +1600,55 @@ export const createAgentExperience = (
1596
1600
  });
1597
1601
  window.dispatchEvent(clearEvent);
1598
1602
  },
1603
+ setMessage(message: string): boolean {
1604
+ if (!textarea) return false;
1605
+ if (session.isStreaming()) return false;
1606
+
1607
+ // Auto-open widget if closed and launcher is enabled
1608
+ if (!open && launcherEnabled) {
1609
+ setOpenState(true);
1610
+ }
1611
+
1612
+ textarea.value = message;
1613
+ // Trigger input event for any listeners
1614
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
1615
+ return true;
1616
+ },
1617
+ submitMessage(message?: string): boolean {
1618
+ if (session.isStreaming()) return false;
1619
+
1620
+ const valueToSubmit = message?.trim() || textarea.value.trim();
1621
+ if (!valueToSubmit) return false;
1622
+
1623
+ // Auto-open widget if closed and launcher is enabled
1624
+ if (!open && launcherEnabled) {
1625
+ setOpenState(true);
1626
+ }
1627
+
1628
+ textarea.value = "";
1629
+ session.sendMessage(valueToSubmit);
1630
+ return true;
1631
+ },
1632
+ startVoiceRecognition(): boolean {
1633
+ if (isRecording || session.isStreaming()) return false;
1634
+
1635
+ const SpeechRecognitionClass = getSpeechRecognitionClass();
1636
+ if (!SpeechRecognitionClass) return false;
1637
+
1638
+ // Auto-open widget if closed and launcher is enabled
1639
+ if (!open && launcherEnabled) {
1640
+ setOpenState(true);
1641
+ }
1642
+
1643
+ startVoiceRecognition();
1644
+ return true;
1645
+ },
1646
+ stopVoiceRecognition(): boolean {
1647
+ if (!isRecording) return false;
1648
+
1649
+ stopVoiceRecognition();
1650
+ return true;
1651
+ },
1599
1652
  destroy() {
1600
1653
  destroyCallbacks.forEach((cb) => cb());
1601
1654
  wrapper.remove();