nothumanallowed 13.5.104 → 13.5.106

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": "nothumanallowed",
3
- "version": "13.5.104",
3
+ "version": "13.5.106",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2731,6 +2731,7 @@ export async function cmdUI(args) {
2731
2731
  const hasWriting = /scrivi|write|articolo|article|blog|testo|text|documento|document/i.test(taskLow);
2732
2732
  const hasData = /dati|data|dataset|csv|json|analizza i dati|pattern|statistich/i.test(taskLow);
2733
2733
  const hasTranslate = /traduci|translate|traduzione|translation/i.test(taskLow);
2734
+ const hasTravel = /ristorante|restaurant|b&b|hotel|albergo|agriturismo|locanda|osteria|prenotaz|vacanz|romantico|sushi|giapponese|cinese|pizza|cena|dinner|pranzo|lunch|soggiorno|weekend|pernottament|posto\s+dove\s+mangiare|posto\s+dove\s+dormire|dove\s+mangiare|dove\s+dormire|posto\s+romantico|gita|escursione/i.test(taskLow);
2734
2735
 
2735
2736
  const it = plannerLang === 'Italian';
2736
2737
 
@@ -2754,6 +2755,7 @@ export async function cmdUI(args) {
2754
2755
  const pdfName = body.pdfName || 'documento allegato';
2755
2756
  steps.push({icon:'\u{1F4C4}',agent:'DocumentReaderAgent',label:it?'Leggi documento':'Read document',reason:it?'Allegato PDF rilevato \u2014 estraggo dati tecnici prima di ogni altra operazione':'PDF attachment detected \u2014 extracting technical data first',prompt:`Extract all technical specifications, model numbers, part codes, product names, manufacturer, dimensions, ratings, and any other key data from the attached document "${pdfName}". List every technical detail precisely.`});
2756
2757
  }
2758
+ if (hasTravel) steps.push({icon:'\u{1F374}',agent:'TravelAgent', label:it?'Ricerca ristoranti & hotel':'Search restaurants & hotels', reason:it?'Task di viaggio/prenotazione: cerco disponibilità reale su TheFork, Booking, TripAdvisor con browser automation':'Travel/booking task: searching real availability on TheFork, Booking, TripAdvisor with browser automation', prompt:task});
2757
2759
  if (hasEmail) steps.push({icon:'\u{1F4E7}',agent:'EmailAgent', label:it?'Controlla email':'Check emails', reason:it?'Parola chiave email/mail/posta rilevata nel task':'Keyword email/mail detected in task', prompt:'Read the latest unread emails and identify urgent items, deadlines, and required actions'});
2758
2760
  if (hasCalendar) steps.push({icon:'\u{1F4C5}',agent:'CalendarAgent', label:it?'Rivedi calendario':'Review calendar', reason:it?'Parola chiave calendario/agenda/eventi rilevata nel task':'Keyword calendar/agenda/events detected in task', prompt:'Check today\'s events and identify any scheduling conflicts or important meetings'});
2759
2761
  if (hasGitHub) steps.push({icon:'\u{1F4BB}',agent:'GitHubAgent', label:'GitHub', reason:it?'Parola chiave GitHub/git/issue/PR rilevata nel task':'Keyword GitHub/git/issue/PR detected in task', prompt:'Read open issues and pull requests, identify what needs attention'});
@@ -2761,7 +2763,7 @@ export async function cmdUI(args) {
2761
2763
  if (hasNotion) steps.push({icon:'\u{1F4DD}',agent:'NotionAgent', label:'Notion', reason:it?'Parola chiave Notion/note rilevata nel task':'Keyword Notion/note detected in task', prompt:'Search Notion for relevant pages and notes'});
2762
2764
  // When PDF is present: always search web (to find where to buy, similar products etc.)
2763
2765
  // The search query will be refined at runtime using the extracted PDF specs as context
2764
- if (hasPdf || hasSearch || hasReputation || (!hasEmail && !hasCalendar && !hasGitHub && !hasSlack)) {
2766
+ if (!hasTravel && (hasPdf || hasSearch || hasReputation || (!hasEmail && !hasCalendar && !hasGitHub && !hasSlack))) {
2765
2767
  const searchPrompt = hasPdf
2766
2768
  ? (it ? 'Usando le specifiche tecniche estratte dal documento (codice prodotto, modello, costruttore, caratteristiche), cerca online dove acquistare il prodotto o articoli equivalenti. Usa i codici esatti dal documento come query di ricerca.' : 'Using the technical specifications extracted from the document (product code, model, manufacturer, specs), search online for where to buy this product or equivalent alternatives. Use exact codes from the document as search queries.')
2767
2769
  : searchQuery;
@@ -2851,7 +2853,7 @@ Review the plan above. You may:
2851
2853
  - ADJUST the "prompt" field of any step to better match the task
2852
2854
  - KEEP steps that are correct as-is
2853
2855
 
2854
- Available agents: WebSearchAgent, DocumentReaderAgent, EmailAgent, CalendarAgent, GitHubAgent, SlackAgent, NotionAgent, HERALD, ORACLE, ATHENA, CASSANDRA, MERCURY, QUILL, DataAnalystAgent, polyglot, CanvasAgent (last, only if visual output needed).
2856
+ Available agents: TravelAgent (restaurants/hotels/bookings with real browser automation on TheFork/Booking/TripAdvisor), WebSearchAgent, DocumentReaderAgent, EmailAgent, CalendarAgent, GitHubAgent, SlackAgent, NotionAgent, HERALD, ORACLE, ATHENA, CASSANDRA, MERCURY, QUILL, DataAnalystAgent, polyglot, CanvasAgent (last, only if visual output needed).
2855
2857
 
2856
2858
  Output ONLY:
2857
2859
  {"steps":[{"icon":"EMOJI","agent":"AGENT_NAME","label":"LABEL","reason":"WHY THIS AGENT","prompt":"INSTRUCTION"}]}
@@ -2869,7 +2871,7 @@ Language for labels: ${plannerLang}.
2869
2871
 
2870
2872
  Build a workflow plan for this task.
2871
2873
 
2872
- Available agents: WebSearchAgent, DocumentReaderAgent, EmailAgent, CalendarAgent, GitHubAgent, SlackAgent, NotionAgent, HERALD, ORACLE, ATHENA, CASSANDRA, MERCURY, QUILL, DataAnalystAgent, polyglot, CanvasAgent.
2874
+ Available agents: TravelAgent (restaurants/hotels/bookings with real browser automation on TheFork/Booking/TripAdvisor), WebSearchAgent, DocumentReaderAgent, EmailAgent, CalendarAgent, GitHubAgent, SlackAgent, NotionAgent, HERALD, ORACLE, ATHENA, CASSANDRA, MERCURY, QUILL, DataAnalystAgent, polyglot, CanvasAgent.
2873
2875
 
2874
2876
  Output ONLY:
2875
2877
  {"steps":[{"icon":"EMOJI","agent":"AGENT_NAME","label":"LABEL","reason":"WHY THIS AGENT","prompt":"INSTRUCTION"}]}
@@ -3170,6 +3172,200 @@ ${rawText.slice(0, 18000)}`;
3170
3172
  }
3171
3173
  } catch (e) { toolData = toolData || `Web search failed: ${e.message}`; }
3172
3174
 
3175
+ } else if (agent === 'TravelAgent') {
3176
+ sendToken('[TravelAgent: analyzing your request...] ');
3177
+ try {
3178
+ const be = await import('../services/browser-engine.mjs');
3179
+ const travelTask = stepPrompt || task;
3180
+
3181
+ // ── Extract parameters from task ──
3182
+ // Date: "16 maggio", "16/05", "16-05-2026", "sabato 16", etc.
3183
+ // ── PARAMETER EXTRACTION ──
3184
+ const monthNames = {gennaio:1,febbraio:2,marzo:3,aprile:4,maggio:5,giugno:6,luglio:7,agosto:8,settembre:9,ottobre:10,novembre:11,dicembre:12};
3185
+ const dateMatch = travelTask.match(/(\d{1,2})\s+(gennaio|febbraio|marzo|aprile|maggio|giugno|luglio|agosto|settembre|ottobre|novembre|dicembre)/i)
3186
+ || travelTask.match(/(\d{1,2})[\/\-](\d{1,2})(?:[\/\-](\d{4}))?/);
3187
+ let targetDate = null;
3188
+ let targetDateStr = null;
3189
+ if (dateMatch) {
3190
+ if (dateMatch[2] && isNaN(Number(dateMatch[2]))) {
3191
+ const day = parseInt(dateMatch[1]);
3192
+ const month = monthNames[(dateMatch[2] || '').toLowerCase()];
3193
+ if (day && month) {
3194
+ const year = new Date().getFullYear();
3195
+ targetDate = new Date(year, month - 1, day);
3196
+ targetDateStr = String(day).padStart(2, '0') + '/' + String(month).padStart(2, '0') + '/' + year;
3197
+ }
3198
+ } else if (dateMatch[1] && dateMatch[2]) {
3199
+ const day = parseInt(dateMatch[1]);
3200
+ const month = parseInt(dateMatch[2]);
3201
+ const year = dateMatch[3] ? parseInt(dateMatch[3]) : new Date().getFullYear();
3202
+ if (day >= 1 && day <= 31 && month >= 1 && month <= 12) {
3203
+ targetDate = new Date(year, month - 1, day);
3204
+ targetDateStr = String(day).padStart(2, '0') + '/' + String(month).padStart(2, '0') + '/' + year;
3205
+ }
3206
+ }
3207
+ }
3208
+
3209
+ // City: "a Mantova", "in Bologna", "near Milan", "vicino a Firenze"
3210
+ const cityMatch = travelTask.match(/\b(?:a|in|near|vicino\s+a|at|around)\s+([A-Za-z][a-zA-Z\s]{2,25}?)(?:\s*[,\.!?\n]|\s+(?:il|la|per|con|un|una|mi|mia|del|della|per\s+la|il\s+|la\s+)|$)/i);
3211
+ const city = cityMatch ? cityMatch[1].trim().replace(/\s+$/, '') : null;
3212
+
3213
+ // Cuisine type → OSM cuisine tag
3214
+ const cuisineMap = {sushi:'sushi',pizza:'pizza',giapponese:'japanese',japanese:'japanese',italiano:'italian',italian:'italian',cinese:'chinese',chinese:'chinese',indiano:'indian',indian:'indian',steakhouse:'steak_house',trattoria:'italian',osteria:'italian'};
3215
+ const cuisineMatch = travelTask.match(/\b(sushi|pizza|giapponese|japanese|italiano|italian|cinese|chinese|indiano|indian|steakhouse|trattoria|osteria)\b/i);
3216
+ const cuisineRaw = cuisineMatch ? cuisineMatch[1].toLowerCase() : null;
3217
+ const cuisineOSM = cuisineRaw ? (cuisineMap[cuisineRaw] || cuisineRaw) : null;
3218
+
3219
+ const hasAccommodation = /b&b|bed\s*(?:&|and)\s*breakfast|hotel|albergo|agriturismo|locanda|ostello|hostel|pernottament|soggiorno/i.test(travelTask);
3220
+ const hasRestaurant = /ristorante|restaurant|sushi|cena|dinner|pranzo|lunch|mangiare|eat/i.test(travelTask);
3221
+
3222
+ const summaryParts = [];
3223
+ if (city) summaryParts.push('city: ' + city);
3224
+ if (targetDateStr) summaryParts.push('date: ' + targetDateStr);
3225
+ if (cuisineRaw) summaryParts.push('cuisine: ' + cuisineRaw);
3226
+ if (hasAccommodation) summaryParts.push('accommodation: yes');
3227
+ if (hasRestaurant) summaryParts.push('restaurant: yes');
3228
+ sendToken('[Extracted — ' + (summaryParts.join(', ') || 'general search') + '] ');
3229
+
3230
+ const UA = 'NHA-TravelAgent/1.0 (nothumanallowed.com)';
3231
+ const portalResults = [];
3232
+
3233
+ // ── TIER 1: Nominatim geocoding → get lat/lon for city ──
3234
+ let lat = null, lon = null;
3235
+ if (city) {
3236
+ sendToken('[Geocoding ' + city + '...] ');
3237
+ try {
3238
+ const geoUrl = 'https://nominatim.openstreetmap.org/search?' + new URLSearchParams({ q: city, format: 'json', limit: '1', addressdetails: '0' }).toString();
3239
+ const geoRes = await withTimeout(fetch(geoUrl, { headers: { 'User-Agent': UA, 'Accept': 'application/json' } }), 8000);
3240
+ if (geoRes.ok) {
3241
+ const geoData = await geoRes.json();
3242
+ if (geoData && geoData[0]) {
3243
+ lat = parseFloat(geoData[0].lat);
3244
+ lon = parseFloat(geoData[0].lon);
3245
+ sendToken('[Found: ' + lat.toFixed(4) + ', ' + lon.toFixed(4) + '] ');
3246
+ }
3247
+ }
3248
+ } catch {}
3249
+ }
3250
+
3251
+ // ── TIER 1: Overpass API — real OSM data ──
3252
+ if (lat !== null && lon !== null) {
3253
+ const radius = 5000; // 5km radius
3254
+ const formatPlace = (el) => {
3255
+ const t = el.tags || {};
3256
+ const parts = [];
3257
+ if (t.name) parts.push('**' + t.name + '**');
3258
+ if (t['addr:street']) parts.push(t['addr:street'] + (t['addr:housenumber'] ? ' ' + t['addr:housenumber'] : ''));
3259
+ if (t['addr:city']) parts.push(t['addr:city']);
3260
+ if (t.phone || t['contact:phone']) parts.push('Tel: ' + (t.phone || t['contact:phone']));
3261
+ if (t.website || t['contact:website']) parts.push('Web: ' + (t.website || t['contact:website']));
3262
+ if (t.opening_hours) parts.push('Orari: ' + t.opening_hours);
3263
+ if (t.email || t['contact:email']) parts.push('Email: ' + (t.email || t['contact:email']));
3264
+ if (t.cuisine) parts.push('Cucina: ' + t.cuisine);
3265
+ if (t.stars) parts.push('Stelle: ' + t.stars);
3266
+ const coordLat = el.lat || (el.center && el.center.lat);
3267
+ const coordLon = el.lon || (el.center && el.center.lon);
3268
+ if (coordLat && coordLon) parts.push('Maps: https://www.openstreetmap.org/?mlat=' + coordLat + '&mlon=' + coordLon + '&zoom=17');
3269
+ return parts.join(' | ');
3270
+ };
3271
+
3272
+ // Restaurants query
3273
+ if (hasRestaurant) {
3274
+ sendToken('[Overpass: searching restaurants' + (cuisineOSM ? ' (' + cuisineOSM + ')' : '') + '...] ');
3275
+ try {
3276
+ const cuisineFilter = cuisineOSM ? '["cuisine"="' + cuisineOSM + '"]' : '';
3277
+ const restQuery = '[out:json][timeout:20];(node["amenity"="restaurant"]' + cuisineFilter + '(around:' + radius + ',' + lat + ',' + lon + ');way["amenity"="restaurant"]' + cuisineFilter + '(around:' + radius + ',' + lat + ',' + lon + '););out center tags;';
3278
+ const restRes = await withTimeout(fetch('https://overpass.kumi.systems/api/interpreter', {
3279
+ method: 'POST',
3280
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': UA },
3281
+ body: 'data=' + encodeURIComponent(restQuery)
3282
+ }), 25000);
3283
+ if (restRes.ok) {
3284
+ const restData = await restRes.json();
3285
+ const elements = (restData.elements || []).filter(el => el.tags && el.tags.name);
3286
+ if (elements.length > 0) {
3287
+ const lines = ['## Ristoranti trovati via OpenStreetMap (' + elements.length + ' risultati, raggio ' + (radius/1000) + 'km da ' + city + ')'];
3288
+ elements.slice(0, 15).forEach((el, i) => { lines.push((i+1) + '. ' + formatPlace(el)); });
3289
+ portalResults.push(lines.join('\n'));
3290
+ sendToken('[Found ' + elements.length + ' restaurants] ');
3291
+ } else if (cuisineOSM) {
3292
+ // Retry without cuisine filter — broader search
3293
+ sendToken('[No ' + cuisineOSM + ' found, retrying broader...] ');
3294
+ const broadQuery = '[out:json][timeout:20];(node["amenity"="restaurant"](around:' + radius + ',' + lat + ',' + lon + ');way["amenity"="restaurant"](around:' + radius + ',' + lat + ',' + lon + '););out center tags;';
3295
+ const broadRes = await withTimeout(fetch('https://overpass.kumi.systems/api/interpreter', {
3296
+ method: 'POST',
3297
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': UA },
3298
+ body: 'data=' + encodeURIComponent(broadQuery)
3299
+ }), 25000);
3300
+ if (broadRes.ok) {
3301
+ const broadData = await broadRes.json();
3302
+ const broadEl = (broadData.elements || []).filter(el => el.tags && el.tags.name);
3303
+ if (broadEl.length > 0) {
3304
+ const lines = ['## Ristoranti a ' + city + ' (tutti i tipi, ' + broadEl.length + ' trovati — nessun ristorante ' + cuisineRaw + ' su OpenStreetMap in questa zona)'];
3305
+ broadEl.slice(0, 10).forEach((el, i) => { lines.push((i+1) + '. ' + formatPlace(el)); });
3306
+ portalResults.push(lines.join('\n'));
3307
+ }
3308
+ }
3309
+ }
3310
+ }
3311
+ } catch (e) { sendToken('[Overpass restaurants error: ' + e.message.slice(0,50) + '] '); }
3312
+ }
3313
+
3314
+ // Accommodation query
3315
+ if (hasAccommodation) {
3316
+ sendToken('[Overpass: searching accommodation...] ');
3317
+ try {
3318
+ const accQuery = '[out:json][timeout:20];(node["tourism"~"hotel|guest_house|bed_and_breakfast|hostel|motel"](around:' + radius + ',' + lat + ',' + lon + ');way["tourism"~"hotel|guest_house|bed_and_breakfast|hostel|motel"](around:' + radius + ',' + lat + ',' + lon + ');node["amenity"="hotel"](around:' + radius + ',' + lat + ',' + lon + '););out center tags;';
3319
+ const accRes = await withTimeout(fetch('https://overpass.kumi.systems/api/interpreter', {
3320
+ method: 'POST',
3321
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': UA },
3322
+ body: 'data=' + encodeURIComponent(accQuery)
3323
+ }), 25000);
3324
+ if (accRes.ok) {
3325
+ const accData = await accRes.json();
3326
+ const elements = (accData.elements || []).filter(el => el.tags && el.tags.name);
3327
+ if (elements.length > 0) {
3328
+ const lines = ['## Alloggi trovati via OpenStreetMap (' + elements.length + ' risultati, raggio ' + (radius/1000) + 'km da ' + city + ')'];
3329
+ elements.slice(0, 15).forEach((el, i) => {
3330
+ const t = el.tags || {};
3331
+ const type = t.tourism === 'guest_house' ? 'B&B/Guest house' : t.tourism === 'hotel' ? 'Hotel' : t.tourism === 'bed_and_breakfast' ? 'B&B' : (t.tourism || 'Alloggio');
3332
+ lines.push((i+1) + '. [' + type + '] ' + formatPlace(el));
3333
+ });
3334
+ portalResults.push(lines.join('\n'));
3335
+ sendToken('[Found ' + elements.length + ' accommodations] ');
3336
+ }
3337
+ }
3338
+ } catch (e) { sendToken('[Overpass accommodation error: ' + e.message.slice(0,50) + '] '); }
3339
+ }
3340
+ } else if (city) {
3341
+ sendToken('[Geocoding failed, skipping Overpass] ');
3342
+ }
3343
+
3344
+ // ── TIER 2: Web search fallback — targeted queries ──
3345
+ const dataFound = portalResults.length > 0 && portalResults.some(r => r.includes('trovati') && !r.includes('0 risultati'));
3346
+ if (!dataFound) {
3347
+ sendToken('[Web search fallback...] ');
3348
+ try {
3349
+ const queries = [
3350
+ [cuisineRaw, 'ristorante', city].filter(Boolean).join(' '),
3351
+ hasAccommodation ? ['b&b romantico', city, targetDateStr ? targetDateStr.slice(0,5) : ''].filter(Boolean).join(' ') : null
3352
+ ].filter(Boolean);
3353
+ for (const q of queries) {
3354
+ const sr = await withTimeout(executeTool('web_search', { query: q, deep: true }, config), 30000);
3355
+ if (sr && sr.length > 100) portalResults.push('## Web search: "' + q + '"\n' + (typeof sr === 'string' ? sr : JSON.stringify(sr)));
3356
+ }
3357
+ } catch {}
3358
+ }
3359
+
3360
+ // ── Build final output ──
3361
+ if (portalResults.length > 0) {
3362
+ const header = '# TravelAgent — Risultati per: ' + (city || 'zona non specificata') + (targetDateStr ? ' | Data: ' + targetDateStr : '') + (cuisineRaw ? ' | Cucina: ' + cuisineRaw : '') + '\n\n';
3363
+ toolData = header + portalResults.join('\n\n');
3364
+ } else {
3365
+ toolData = '# TravelAgent — Nessun risultato trovato\n\nNon sono stati trovati ristoranti o alloggi per i parametri specificati.\n\n**Parametri cercati:**\n- Città: ' + (city || 'non specificata') + '\n- Data: ' + (targetDateStr || 'non specificata') + '\n- Tipo cucina: ' + (cuisineRaw || 'qualsiasi') + '\n\n**Suggerimento:** Specifica la città esatta (es. "a Mantova") e il tipo di cucina per ottenere risultati migliori.';
3366
+ }
3367
+ } catch (e) { toolData = toolData || 'TravelAgent failed: ' + e.message; }
3368
+
3173
3369
  } else if (agent === 'BrowserAgent') {
3174
3370
  // Collect all URLs from stepPrompt + task, plus infer subpaths mentioned (e.g. /about, /docs)
3175
3371
  const allUrlMatches = [...new Set((stepPrompt + ' ' + task).match(/https?:\/\/[^\s"'<>)]+/g) || [])];
@@ -3378,7 +3574,7 @@ ${rawText.slice(0, 18000)}`;
3378
3574
  const today = new Date().toISOString().split('T')[0];
3379
3575
  const isCanvasAgent = agent === 'CanvasAgent';
3380
3576
  // Tool-data agents: fetch real live data and use buildSystemPrompt (tool calls allowed)
3381
- const isLiveDataAgent = ['CalendarAgent','EmailAgent','GitHubAgent','NotionAgent','SlackAgent','DriveAgent','BrowserAgent','WebSearchAgent','ResearchAgent','FileReaderAgent','CodeExecutorAgent'].includes(agent);
3577
+ const isLiveDataAgent = ['CalendarAgent','EmailAgent','GitHubAgent','NotionAgent','SlackAgent','DriveAgent','BrowserAgent','WebSearchAgent','ResearchAgent','FileReaderAgent','CodeExecutorAgent','TravelAgent'].includes(agent);
3382
3578
 
3383
3579
  // ── Canvas HTML template — built server-side, guaranteed CSS ─────
3384
3580
  // The LLM outputs ONLY the <body> inner HTML (no <html>, no <style>)
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '13.5.104';
8
+ export const VERSION = '13.5.106';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -6144,6 +6144,7 @@ function renderStudio(el) {
6144
6144
 
6145
6145
  // Agent catalog
6146
6146
  var STUDIO_AGENTS = [
6147
+ {icon:'&#127860;',name:'TravelAgent',desc:'Restaurants, hotels & bookings (browser automation)'},
6147
6148
  {icon:'&#128269;',name:'WebSearchAgent',desc:'Search the web'},
6148
6149
  {icon:'&#127760;',name:'BrowserAgent',desc:'Navigate & screenshot pages'},
6149
6150
  {icon:'&#128140;',name:'EmailAgent',desc:'Read & summarize emails'},