ttp-agent-sdk 2.38.1 → 2.39.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.
@@ -68,9 +68,8 @@ class AudioProcessor extends AudioWorkletProcessor {
68
68
  case 'setForceContinuous':
69
69
  this.forceContinuous = data.enabled;
70
70
  this.isProcessing = true;
71
- // FIXED: Don't set isCurrentlyStreaming = true here - let VAD control it
72
- // VAD will set isCurrentlyStreaming = true when voice is detected
73
- this.isCurrentlyStreaming = false;
71
+ // iOS: bypass client VAD - always send. Desktop: keep VAD (saves bandwidth, reduces noise).
72
+ this.isCurrentlyStreaming = data.enabled && (data.bypassVad === true);
74
73
  break;
75
74
 
76
75
  case 'flush':
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <title>TTP E-Commerce Widget - Test</title>
6
+ <title>TTP Hotels Flavor - Test</title>
7
7
  <style>
8
8
  body {
9
9
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
@@ -43,7 +43,7 @@
43
43
  .status.error { background: #FEE2E2; color: #991B1B; }
44
44
 
45
45
  button {
46
- background: #4F46E5;
46
+ background: #2563EB;
47
47
  color: white;
48
48
  border: none;
49
49
  padding: 12px 24px;
@@ -52,13 +52,11 @@
52
52
  font-size: 16px;
53
53
  margin: 10px 10px 10px 0;
54
54
  }
55
- button:hover { background: #4338CA; }
55
+ button:hover { background: #1D4ED8; }
56
56
  button.secondary { background: #6B7280; }
57
57
  button.secondary:hover { background: #4B5563; }
58
58
  button.success { background: #059669; }
59
59
  button.success:hover { background: #047857; }
60
- button.warning { background: #D97706; }
61
- button.warning:hover { background: #B45309; }
62
60
 
63
61
  label {
64
62
  display: flex;
@@ -68,7 +66,7 @@
68
66
  color: #374151;
69
67
  }
70
68
 
71
- select, input[type="text"], input[type="number"] {
69
+ select, input[type="text"] {
72
70
  padding: 8px 12px;
73
71
  border: 1px solid #D1D5DB;
74
72
  border-radius: 6px;
@@ -110,21 +108,6 @@
110
108
  color: #111827;
111
109
  }
112
110
 
113
- .cart-display {
114
- background: #F0FDF4;
115
- border: 1px solid #BBF7D0;
116
- border-radius: 8px;
117
- padding: 16px;
118
- margin: 16px 0;
119
- font-family: monospace;
120
- font-size: 13px;
121
- }
122
-
123
- .cart-display pre {
124
- margin: 0;
125
- white-space: pre-wrap;
126
- }
127
-
128
111
  @media (max-width: 768px) {
129
112
  body { margin: 0; padding: 10px; }
130
113
  .container { padding: 16px; border-radius: 8px; }
@@ -136,16 +119,16 @@
136
119
  </head>
137
120
  <body>
138
121
  <div class="container">
139
- <h1>🛒 TTP E-Commerce Widget Test</h1>
122
+ <h1>TTP Hotels Flavor Test</h1>
140
123
 
141
124
  <div class="info">
142
- <strong>What is TTPEcommerceWidget?</strong>
125
+ <strong>How it works:</strong>
143
126
  <ul>
144
- <li>Wraps the standard TTPChatWidget with an e-commerce layer</li>
145
- <li>Displays product cards when the agent sends <code>show_products</code> messages</li>
146
- <li>Manages a local cart with toast notifications and cart summary</li>
147
- <li>Supports partner integration via <code>addToCartFn</code> / <code>getCartFn</code> callbacks</li>
148
- <li>Test API available at <code>window.__TTP_ECOMMERCE__</code></li>
127
+ <li>Uses the standard <code>TTPChatWidget</code> with <code>flavor: { type: 'hotels', partnerId: 'mock-hotel' }</code></li>
128
+ <li>The backend injects hotel tools (<code>search_rooms</code>, <code>select_room</code>, <code>add_extra</code>, <code>get_booking</code>)</li>
129
+ <li>Room cards display when the agent sends <code>show_items</code> messages</li>
130
+ <li>Booking summary updates on <code>booking_updated</code> messages</li>
131
+ <li>Gallery (<code>show_media</code>) opens fullscreen with widget minimized</li>
149
132
  </ul>
150
133
  </div>
151
134
 
@@ -155,9 +138,7 @@
155
138
  <div class="settings-grid">
156
139
  <label>Agent
157
140
  <select id="agentSelect" onchange="handleAgentChange()">
158
- <option value="agent_87c4a55a1|app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC|en|ltr">e-commerce_english (agent_87c4a55a1)</option>
159
- <option value="agent_ed18369b3|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|he|rtl">e-commerce_hebrew (agent_ed18369b3)</option>
160
- <option value="agent_d119003d8|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|es|ltr">ecommerce_spanish (agent_d119003d8)</option>
141
+ <option value="agent_d0774f1af|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo">Hotels Demo (agent_d0774f1af)</option>
161
142
  <option value="custom">Custom...</option>
162
143
  </select>
163
144
  </label>
@@ -199,68 +180,74 @@
199
180
  <button id="destroyBtn" class="secondary">Destroy Widget</button>
200
181
  </div>
201
182
 
202
- <h2>E-Commerce Test Actions</h2>
183
+ <h2>Test Actions (Simulated Messages)</h2>
203
184
 
204
185
  <div class="test-section">
205
- <h3>Show Products (simulated)</h3>
186
+ <h3>Show Rooms</h3>
206
187
  <p style="color: #6B7280; font-size: 14px; margin-bottom: 12px;">
207
- Injects a <code>show_products</code> message into the ecommerce layer — no voice call needed.
188
+ Injects a <code>show_items</code> message with mock hotel rooms — no voice call needed.
208
189
  </p>
209
- <div class="settings-grid">
210
- <label>Search Query
211
- <input id="productQuery" type="text" value="milk" />
212
- </label>
213
- <label>Layout
214
- <select id="productLayout">
215
- <option value="auto">Auto (cards &le;4, list 5+)</option>
216
- <option value="cards">Cards</option>
217
- <option value="list">List</option>
218
- </select>
219
- </label>
220
- </div>
221
- <button id="showProductsBtn" class="success">Show Products</button>
190
+ <button id="showRoomsBtn" class="success">Show Rooms</button>
222
191
  </div>
223
192
 
224
193
  <div class="test-section">
225
- <h3>Cart Updated (simulated)</h3>
194
+ <h3>Show Gallery</h3>
226
195
  <p style="color: #6B7280; font-size: 14px; margin-bottom: 12px;">
227
- Injects a <code>cart_updated</code> message to test toast and summary UI.
196
+ Injects a <code>show_media</code> message to test fullscreen gallery.
228
197
  </p>
229
- <button id="cartAddBtn" class="success">Simulate: Product Added</button>
230
- <button id="cartClearBtn" class="secondary">Clear Cart</button>
198
+ <button id="showGalleryBtn" class="success">Show Gallery</button>
231
199
  </div>
232
200
 
233
201
  <div class="test-section">
234
- <h3>Cart State</h3>
235
- <div id="cartDisplay" class="cart-display">
236
- <pre>No widget created yet</pre>
237
- </div>
238
- <button id="refreshCartBtn" class="secondary">Refresh Cart State</button>
202
+ <h3>Booking Updated</h3>
203
+ <p style="color: #6B7280; font-size: 14px; margin-bottom: 12px;">
204
+ Injects a <code>booking_updated</code> message to test the booking summary bar.
205
+ </p>
206
+ <button id="bookingBtn" class="success">Simulate Booking</button>
207
+ <button id="clearBookingBtn" class="secondary">Clear Booking</button>
239
208
  </div>
240
209
 
241
210
  <h2>Console Commands</h2>
242
- <div class="code-block">// Show products via test API (fetches from mock API)
243
- window.__TTP_ECOMMERCE__.showProducts('milk');
244
- window.__TTP_ECOMMERCE__.showProducts('bread', 3);
245
-
246
- // Inject raw messages
247
- window.__TTP_ECOMMERCE__.handleMessage({
248
- t: 'show_products',
249
- products: [
250
- { id: '1', name: 'Test Product', price: 9.99, image: '' }
211
+ <div class="code-block">// Access the widget instance
212
+ window.testWidget
213
+
214
+ // Inject messages manually via the flavor's messageHandlers
215
+ window.testWidget._flavor.messageHandlers['show_items']({
216
+ t: 'show_items',
217
+ items: [
218
+ { roomId: 'room-1', roomName: 'Test Room', imageUrl: '...', pricePerNight: 150, currency: 'USD', beds: 2, amenities: ['WiFi', 'Pool'] }
251
219
  ],
252
- title: 'Test Results'
220
+ title: 'Test Rooms'
253
221
  });
254
222
 
255
- // Cart operations
256
- window.__TTP_ECOMMERCE__.getCart();
257
- window.__TTP_ECOMMERCE__.clearCart();</div>
223
+ window.testWidget._flavor.messageHandlers['show_media']({
224
+ t: 'show_media',
225
+ images: [{ url: 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=800', caption: 'Standard Room' }],
226
+ title: 'Hotel Photos'
227
+ });</div>
258
228
  </div>
259
229
 
260
- <script src="/agent-widget.js"></script>
230
+ <script>
231
+ (function() {
232
+ var isNgrok = window.location.hostname.includes('ngrok');
233
+ if (isNgrok) {
234
+ var s = document.createElement('script');
235
+ s.type = 'text/javascript';
236
+ document.head.appendChild(s);
237
+ fetch('/agent-widget.js', { headers: { 'ngrok-skip-browser-warning': '1' } })
238
+ .then(function(r) { return r.text(); })
239
+ .then(function(code) { s.textContent = code; })
240
+ .catch(function(e) { console.error('SDK fetch failed:', e); });
241
+ } else {
242
+ var s = document.createElement('script');
243
+ s.src = '/agent-widget.js';
244
+ document.head.appendChild(s);
245
+ }
246
+ })();
247
+ </script>
261
248
 
262
249
  <script>
263
- let ecomWidget = null;
250
+ let chatWidget = null;
264
251
 
265
252
  function updateStatus(msg, type = '') {
266
253
  const el = document.getElementById('status');
@@ -268,27 +255,11 @@ window.__TTP_ECOMMERCE__.clearCart();</div>
268
255
  el.className = 'status ' + type;
269
256
  }
270
257
 
271
- function refreshCartDisplay() {
272
- const el = document.getElementById('cartDisplay');
273
- if (!window.__TTP_ECOMMERCE__) {
274
- el.innerHTML = '<pre>No widget created yet</pre>';
275
- return;
276
- }
277
- const cart = window.__TTP_ECOMMERCE__.getCart();
278
- el.innerHTML = '<pre>' + JSON.stringify(cart, null, 2) + '</pre>';
279
- }
280
-
281
258
  function handleAgentChange() {
282
259
  const sel = document.getElementById('agentSelect');
283
260
  const isCustom = sel.value === 'custom';
284
261
  document.getElementById('customAgentLabel').style.display = isCustom ? '' : 'none';
285
262
  document.getElementById('customAppLabel').style.display = isCustom ? '' : 'none';
286
-
287
- if (!isCustom) {
288
- const [, , lang, dir] = sel.value.split('|');
289
- document.getElementById('languageSelect').value = lang;
290
- document.getElementById('dirSelect').value = dir;
291
- }
292
263
  }
293
264
 
294
265
  function getAgentConfig() {
@@ -304,20 +275,20 @@ window.__TTP_ECOMMERCE__.clearCart();</div>
304
275
  }
305
276
 
306
277
  function createWidget() {
307
- if (ecomWidget) {
308
- ecomWidget.destroy();
309
- ecomWidget = null;
278
+ if (chatWidget) {
279
+ chatWidget.destroy();
280
+ chatWidget = null;
310
281
  }
311
282
 
312
283
  const TTPAgentSDK = window.TTPAgentSDK;
313
- if (!TTPAgentSDK || !TTPAgentSDK.TTPEcommerceWidget) {
314
- updateStatus('TTPEcommerceWidget not available in SDK', 'error');
284
+ if (!TTPAgentSDK || !TTPAgentSDK.TTPChatWidget) {
285
+ updateStatus('TTPChatWidget not available in SDK', 'error');
315
286
  return;
316
287
  }
317
288
 
318
289
  const agent = getAgentConfig();
319
290
 
320
- ecomWidget = new TTPAgentSDK.TTPEcommerceWidget({
291
+ chatWidget = new TTPAgentSDK.TTPChatWidget({
321
292
  agentId: agent.agentId,
322
293
  appId: agent.appId,
323
294
  language: document.getElementById('languageSelect').value,
@@ -326,101 +297,114 @@ window.__TTP_ECOMMERCE__.clearCart();</div>
326
297
  mode: document.getElementById('modeSelect').value,
327
298
  startOpen: document.getElementById('startOpenSelect').value === 'true'
328
299
  },
300
+ flavor: {
301
+ type: 'hotels',
302
+ partnerId: 'mock-hotel'
303
+ },
329
304
  icon: {
330
305
  type: 'custom',
331
306
  customImage: 'https://talktopc.com/logo192.png',
332
307
  size: 'medium',
333
308
  backgroundColor: '#FFFFFF'
334
- },
335
- partner: { name: 'TestStore' },
336
- cart: {
337
- addToCartFn: async (productId, qty) => {
338
- console.log('[TestPage] addToCartFn called:', productId, qty);
339
- return { ok: true };
340
- },
341
- getCartFn: async () => {
342
- console.log('[TestPage] getCartFn called');
343
- return window.__TTP_ECOMMERCE__?.getCart() || { items: [], total: 0, count: 0 };
344
- }
345
309
  }
346
310
  });
347
311
 
348
- window.testWidget = ecomWidget;
349
- updateStatus('TTPEcommerceWidget created successfully', 'success');
350
- refreshCartDisplay();
312
+ window.testWidget = chatWidget;
313
+ updateStatus('TTPChatWidget created with hotels flavor (partnerId: mock-hotel)', 'success');
351
314
  }
352
315
 
353
316
  // Button handlers
354
317
  document.getElementById('createBtn').onclick = createWidget;
355
318
 
356
319
  document.getElementById('destroyBtn').onclick = () => {
357
- if (ecomWidget) {
358
- ecomWidget.destroy();
359
- ecomWidget = null;
320
+ if (chatWidget) {
321
+ chatWidget.destroy();
322
+ chatWidget = null;
360
323
  updateStatus('Widget destroyed', '');
361
- refreshCartDisplay();
362
324
  }
363
325
  };
364
326
 
365
- document.getElementById('showProductsBtn').onclick = async () => {
366
- if (!window.__TTP_ECOMMERCE__) {
327
+ // Show Rooms inject mock show_items
328
+ document.getElementById('showRoomsBtn').onclick = () => {
329
+ if (!chatWidget || !chatWidget._flavor) {
367
330
  updateStatus('Create widget first', 'error');
368
331
  return;
369
332
  }
370
- const query = document.getElementById('productQuery').value || 'milk';
371
- try {
372
- await window.__TTP_ECOMMERCE__.showProducts(query);
373
- updateStatus('showProducts("' + query + '") sent', 'success');
374
- } catch (e) {
375
- updateStatus('showProducts failed: ' + e.message, 'error');
376
- }
333
+ chatWidget._flavor.messageHandlers['show_items']({
334
+ t: 'show_items',
335
+ items: [
336
+ { roomId: 'room-standard', roomName: 'Standard Room', imageUrl: 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=400&h=300&fit=crop', pricePerNight: 120, currency: 'USD', beds: 1, amenities: ['WiFi', 'TV', 'Air Conditioning'], available: true },
337
+ { roomId: 'room-deluxe', roomName: 'Deluxe Room', imageUrl: 'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?w=400&h=300&fit=crop', pricePerNight: 200, currency: 'USD', beds: 2, amenities: ['WiFi', 'TV', 'Pool', 'Mini Bar'], available: true },
338
+ { roomId: 'room-suite', roomName: 'Executive Suite', imageUrl: 'https://images.unsplash.com/photo-1566665797739-1674de7a421a?w=400&h=300&fit=crop', pricePerNight: 350, currency: 'USD', beds: 2, amenities: ['WiFi', 'TV', 'Pool', 'Spa', 'Balcony'], available: true },
339
+ { roomId: 'room-penthouse', roomName: 'Penthouse Suite', imageUrl: 'https://images.unsplash.com/photo-1578683010236-d716f9a3f461?w=400&h=300&fit=crop', pricePerNight: 600, currency: 'USD', beds: 3, amenities: ['WiFi', 'TV', 'Pool', 'Spa', 'Balcony', 'Butler'], available: true }
340
+ ],
341
+ title: '4 rooms available'
342
+ });
343
+ updateStatus('show_items injected with 4 rooms', 'success');
377
344
  };
378
345
 
379
- document.getElementById('cartAddBtn').onclick = async () => {
380
- if (!window.__TTP_ECOMMERCE__) {
346
+ // Show Gallery inject mock show_media
347
+ document.getElementById('showGalleryBtn').onclick = () => {
348
+ if (!chatWidget || !chatWidget._flavor) {
381
349
  updateStatus('Create widget first', 'error');
382
350
  return;
383
351
  }
384
- try {
385
- const baseUrl = window.__TTP_MOCK_API__ || 'https://backend.talktopc.com';
386
- const response = await fetch(`${baseUrl}/api/partner/mock-store/products/search?q=milk&limit=1&sessionId=test`);
387
- const data = await response.json();
388
- const product = data.products[0];
389
- window.__TTP_ECOMMERCE__.handleMessage({
390
- t: 'cart_updated',
391
- product: product,
392
- action: 'added',
393
- cartTotal: product.price,
394
- cartItemCount: 1
395
- });
396
- updateStatus('Simulated cart_updated for "' + product.name + '"', 'success');
397
- setTimeout(refreshCartDisplay, 200);
398
- } catch (e) {
399
- updateStatus('Failed to fetch product: ' + e.message, 'error');
400
- }
352
+ chatWidget._flavor.messageHandlers['show_media']({
353
+ t: 'show_media',
354
+ images: [
355
+ { url: 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=800', caption: 'Standard Room' },
356
+ { url: 'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?w=800', caption: 'Deluxe Room' },
357
+ { url: 'https://images.unsplash.com/photo-1566665797739-1674de7a421a?w=800', caption: 'Executive Suite' },
358
+ { url: 'https://images.unsplash.com/photo-1578683010236-d716f9a3f461?w=800', caption: 'Penthouse Suite' }
359
+ ],
360
+ title: 'Hotel Rooms'
361
+ });
362
+ updateStatus('show_media injected with 4 images (fullscreen gallery)', 'success');
401
363
  };
402
364
 
403
- document.getElementById('cartClearBtn').onclick = () => {
404
- if (!window.__TTP_ECOMMERCE__) {
365
+ // Booking Updated inject mock booking_updated
366
+ document.getElementById('bookingBtn').onclick = () => {
367
+ if (!chatWidget || !chatWidget._flavor) {
405
368
  updateStatus('Create widget first', 'error');
406
369
  return;
407
370
  }
408
- window.__TTP_ECOMMERCE__.clearCart();
409
- updateStatus('Cart cleared', '');
410
- refreshCartDisplay();
371
+ chatWidget._flavor.messageHandlers['booking_updated']({
372
+ t: 'booking_updated',
373
+ booking: {
374
+ roomId: 'room-deluxe',
375
+ roomName: 'Deluxe Room',
376
+ nights: 3,
377
+ guests: 2,
378
+ pricePerNight: 200,
379
+ totalPrice: 650,
380
+ currency: 'USD',
381
+ extras: [{ extraId: 'breakfast', name: 'Breakfast Buffet', price: 25, quantity: 1 }]
382
+ }
383
+ });
384
+ updateStatus('booking_updated injected (Deluxe Room, 3 nights, breakfast)', 'success');
411
385
  };
412
386
 
413
- document.getElementById('refreshCartBtn').onclick = refreshCartDisplay;
387
+ document.getElementById('clearBookingBtn').onclick = () => {
388
+ if (!chatWidget || !chatWidget._flavor) {
389
+ updateStatus('Create widget first', 'error');
390
+ return;
391
+ }
392
+ chatWidget._flavor.messageHandlers['booking_updated']({
393
+ t: 'booking_updated',
394
+ booking: {}
395
+ });
396
+ updateStatus('Booking cleared', '');
397
+ };
414
398
 
415
- // Wait for SDK and auto-create widget
399
+ // Wait for SDK and auto-create
416
400
  function waitForSDK() {
417
401
  return new Promise((resolve, reject) => {
418
- if (window.TTPAgentSDK?.TTPEcommerceWidget) { resolve(); return; }
402
+ if (window.TTPAgentSDK?.TTPChatWidget) { resolve(); return; }
419
403
  let attempts = 0;
420
404
  const iv = setInterval(() => {
421
405
  attempts++;
422
- if (window.TTPAgentSDK?.TTPEcommerceWidget) { clearInterval(iv); resolve(); }
423
- else if (attempts >= 50) { clearInterval(iv); reject(new Error('SDK failed to load')); }
406
+ if (window.TTPAgentSDK?.TTPChatWidget) { clearInterval(iv); resolve(); }
407
+ else if (attempts >= 150) { clearInterval(iv); reject(new Error('SDK failed to load')); }
424
408
  }, 100);
425
409
  });
426
410
  }