@makemore/agent-frontend 1.6.0 → 1.7.1

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/README.md CHANGED
@@ -174,6 +174,41 @@ See `django-tts-example.py` for the complete Django backend implementation.
174
174
  | `showTTSButton` | boolean | `true` | Show TTS toggle button in header |
175
175
  | `showVoiceSettings` | boolean | `true` | Show voice settings button in header (works with proxy and direct API) |
176
176
  | `showExpandButton` | boolean | `true` | Show expand/minimize button in header |
177
+ | `onEvent` | function | `null` | Callback for SSE events: `(eventType, payload) => void` |
178
+
179
+ ### Event Callback
180
+
181
+ The `onEvent` callback allows your application to react to all SSE events from the agent:
182
+
183
+ ```javascript
184
+ ChatWidget.init({
185
+ backendUrl: 'http://localhost:8000',
186
+ agentKey: 'your-agent',
187
+ onEvent: (eventType, payload) => {
188
+ console.log('Event:', eventType, payload);
189
+
190
+ // Example: Navigate when a session is created
191
+ if (eventType === 'tool.result' && payload.result?.session_id) {
192
+ window.location.href = `/session/${payload.result.session_id}`;
193
+ }
194
+
195
+ // Example: Track tool usage
196
+ if (eventType === 'tool.call') {
197
+ analytics.track('Tool Called', { tool: payload.name });
198
+ }
199
+ },
200
+ });
201
+ ```
202
+
203
+ **Event Types:**
204
+ - `assistant.message` - Streaming assistant responses (payload: `{ content: string }`)
205
+ - `tool.call` - Tool being called (payload: `{ name: string, arguments: object }`)
206
+ - `tool.result` - Tool result (payload: `{ result: any }`)
207
+ - `run.succeeded` - Run completed successfully
208
+ - `run.failed` - Run failed (payload: `{ error: string }`)
209
+ - `run.cancelled` - Run was cancelled
210
+ - `run.timed_out` - Run timed out
211
+ - Custom events emitted by your agent
177
212
 
178
213
  ### Text-to-Speech (ElevenLabs)
179
214
 
@@ -70,6 +70,8 @@
70
70
  showTTSButton: true,
71
71
  showVoiceSettings: true,
72
72
  showExpandButton: true,
73
+ // Event callback
74
+ onEvent: null, // Callback for SSE events: (eventType, payload) => void
73
75
  };
74
76
 
75
77
  // State
@@ -492,6 +494,12 @@
492
494
  eventSource.addEventListener('assistant.message', (event) => {
493
495
  try {
494
496
  const data = JSON.parse(event.data);
497
+
498
+ // Call onEvent callback if provided
499
+ if (config.onEvent && typeof config.onEvent === 'function') {
500
+ config.onEvent('assistant.message', data.payload);
501
+ }
502
+
495
503
  const content = data.payload.content;
496
504
  if (content) {
497
505
  assistantContent += content;
@@ -518,18 +526,25 @@
518
526
 
519
527
  // Handler for tool calls (debug mode)
520
528
  eventSource.addEventListener('tool.call', (event) => {
521
- if (!state.debugMode) return;
522
529
  try {
523
530
  const data = JSON.parse(event.data);
524
- state.messages.push({
525
- id: 'tool-call-' + Date.now(),
526
- role: 'system',
527
- content: `🔧 Tool: ${data.payload.name}`,
528
- timestamp: new Date(),
529
- type: 'tool_call',
530
- metadata: { name: data.payload.name, arguments: data.payload.arguments },
531
- });
532
- render();
531
+
532
+ // Call onEvent callback if provided
533
+ if (config.onEvent && typeof config.onEvent === 'function') {
534
+ config.onEvent('tool.call', data.payload);
535
+ }
536
+
537
+ if (state.debugMode) {
538
+ state.messages.push({
539
+ id: 'tool-call-' + Date.now(),
540
+ role: 'system',
541
+ content: `🔧 Tool: ${data.payload.name}`,
542
+ timestamp: new Date(),
543
+ type: 'tool_call',
544
+ metadata: { name: data.payload.name, arguments: data.payload.arguments },
545
+ });
546
+ render();
547
+ }
533
548
  } catch (err) {
534
549
  console.error('[ChatWidget] Failed to parse tool.call:', err);
535
550
  }
@@ -537,19 +552,26 @@
537
552
 
538
553
  // Handler for tool results (debug mode)
539
554
  eventSource.addEventListener('tool.result', (event) => {
540
- if (!state.debugMode) return;
541
555
  try {
542
556
  const data = JSON.parse(event.data);
543
- const result = data.payload.result || '';
544
- state.messages.push({
545
- id: 'tool-result-' + Date.now(),
546
- role: 'system',
547
- content: `✅ Result: ${result.substring(0, 100)}${result.length > 100 ? '...' : ''}`,
548
- timestamp: new Date(),
549
- type: 'tool_result',
550
- metadata: { result },
551
- });
552
- render();
557
+
558
+ // Call onEvent callback if provided
559
+ if (config.onEvent && typeof config.onEvent === 'function') {
560
+ config.onEvent('tool.result', data.payload);
561
+ }
562
+
563
+ if (state.debugMode) {
564
+ const result = data.payload.result || '';
565
+ state.messages.push({
566
+ id: 'tool-result-' + Date.now(),
567
+ role: 'system',
568
+ content: `✅ Result: ${result.substring(0, 100)}${result.length > 100 ? '...' : ''}`,
569
+ timestamp: new Date(),
570
+ type: 'tool_result',
571
+ metadata: { result },
572
+ });
573
+ render();
574
+ }
553
575
  } catch (err) {
554
576
  console.error('[ChatWidget] Failed to parse tool.result:', err);
555
577
  }
@@ -559,6 +581,12 @@
559
581
  const handleTerminal = (event) => {
560
582
  try {
561
583
  const data = JSON.parse(event.data);
584
+
585
+ // Call onEvent callback if provided
586
+ if (config.onEvent && typeof config.onEvent === 'function') {
587
+ config.onEvent(data.type, data.payload);
588
+ }
589
+
562
590
  if (data.type === 'run.failed') {
563
591
  state.error = data.payload.error || 'Agent run failed';
564
592
  state.messages.push({
@@ -604,6 +632,22 @@
604
632
  eventSource.addEventListener('run.cancelled', handleTerminal);
605
633
  eventSource.addEventListener('run.timed_out', handleTerminal);
606
634
 
635
+ // Generic handler for any other custom events
636
+ eventSource.onmessage = (event) => {
637
+ try {
638
+ const data = JSON.parse(event.data);
639
+
640
+ // Call onEvent callback for any unhandled events
641
+ if (config.onEvent && typeof config.onEvent === 'function') {
642
+ // Extract event type from data or use 'message' as default
643
+ const eventType = data.type || 'message';
644
+ config.onEvent(eventType, data.payload || data);
645
+ }
646
+ } catch (err) {
647
+ console.debug('[ChatWidget] Received non-JSON SSE message:', event.data);
648
+ }
649
+ };
650
+
607
651
  eventSource.onerror = () => {
608
652
  if (eventSource.readyState !== EventSource.CLOSED) {
609
653
  console.debug('[ChatWidget] SSE connection closed');
@@ -645,7 +689,7 @@
645
689
  state.isSimulating = false;
646
690
 
647
691
  // Speak simulated user message if TTS enabled
648
- if (config.enableTTS && config.ttsVoices.user) {
692
+ if (config.enableTTS) {
649
693
  await speakText(data.response, 'user');
650
694
  }
651
695
 
@@ -670,9 +714,15 @@
670
714
 
671
715
  const journey = config.journeyTypes[journeyType];
672
716
  if (journey?.initialMessage) {
673
- setTimeout(() => {
717
+ setTimeout(async () => {
674
718
  state.isSimulating = true;
675
719
  render();
720
+
721
+ // Speak initial message if TTS enabled
722
+ if (config.enableTTS) {
723
+ await speakText(journey.initialMessage, 'user');
724
+ }
725
+
676
726
  sendMessage(journey.initialMessage).then(() => {
677
727
  state.isSimulating = false;
678
728
  render();
@@ -1009,6 +1059,13 @@
1009
1059
  if (messagesEl) {
1010
1060
  messagesEl.scrollTop = messagesEl.scrollHeight;
1011
1061
  }
1062
+
1063
+ // Focus input field
1064
+ const inputEl = container.querySelector('.cw-input');
1065
+ if (inputEl && !state.isLoading) {
1066
+ // Use setTimeout to ensure focus happens after render completes
1067
+ setTimeout(() => inputEl.focus(), 0);
1068
+ }
1012
1069
  }
1013
1070
 
1014
1071
  function attachEventListeners() {
@@ -1058,6 +1115,8 @@
1058
1115
  if (input && input.value.trim()) {
1059
1116
  sendMessage(input.value);
1060
1117
  input.value = '';
1118
+ // Keep focus on input after sending
1119
+ input.focus();
1061
1120
  }
1062
1121
  });
1063
1122
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makemore/agent-frontend",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
4
4
  "description": "A standalone, zero-dependency chat widget for AI agents. Embed conversational AI into any website with a single script tag.",
5
5
  "main": "dist/chat-widget.js",
6
6
  "files": [