@astermind/cybernetic-chatbot-client 2.2.30 → 2.2.34
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/CyberneticSessionStorage.d.ts +117 -0
- package/dist/CyberneticSessionStorage.d.ts.map +1 -0
- package/dist/agentic/CyberneticIntentClassifier.d.ts.map +1 -1
- package/dist/agentic/SiteMapDiscovery.d.ts +42 -0
- package/dist/agentic/SiteMapDiscovery.d.ts.map +1 -1
- package/dist/cybernetic-chatbot-client-full.esm.js +268 -19
- package/dist/cybernetic-chatbot-client-full.esm.js.map +1 -1
- package/dist/cybernetic-chatbot-client-full.min.js +1 -1
- package/dist/cybernetic-chatbot-client-full.min.js.map +1 -1
- package/dist/cybernetic-chatbot-client-full.umd.js +268 -19
- package/dist/cybernetic-chatbot-client-full.umd.js.map +1 -1
- package/dist/cybernetic-chatbot-client.esm.js +268 -19
- package/dist/cybernetic-chatbot-client.esm.js.map +1 -1
- package/dist/cybernetic-chatbot-client.min.js +1 -1
- package/dist/cybernetic-chatbot-client.min.js.map +1 -1
- package/dist/cybernetic-chatbot-client.umd.js +268 -19
- package/dist/cybernetic-chatbot-client.umd.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -2663,6 +2663,68 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2663
2663
|
|
|
2664
2664
|
// src/agentic/SiteMapDiscovery.ts
|
|
2665
2665
|
// Multi-source sitemap discovery and merging for agentic navigation
|
|
2666
|
+
// Zero-config mode: automatically discovers routes on any site
|
|
2667
|
+
// ==================== SMART DEFAULTS ====================
|
|
2668
|
+
/**
|
|
2669
|
+
* Comprehensive default selectors that work on most sites
|
|
2670
|
+
* Covers semantic HTML, common CSS patterns, and framework conventions
|
|
2671
|
+
*/
|
|
2672
|
+
const DEFAULT_NAV_SELECTORS = [
|
|
2673
|
+
// Semantic HTML5 navigation
|
|
2674
|
+
'nav a[href]',
|
|
2675
|
+
'[role="navigation"] a[href]',
|
|
2676
|
+
'header a[href]',
|
|
2677
|
+
// Common CSS class patterns
|
|
2678
|
+
'.nav a[href]',
|
|
2679
|
+
'.navbar a[href]',
|
|
2680
|
+
'.navigation a[href]',
|
|
2681
|
+
'.sidebar a[href]',
|
|
2682
|
+
'.side-nav a[href]',
|
|
2683
|
+
'.sidenav a[href]',
|
|
2684
|
+
'.menu a[href]',
|
|
2685
|
+
'.main-menu a[href]',
|
|
2686
|
+
'.top-menu a[href]',
|
|
2687
|
+
// Data attributes
|
|
2688
|
+
'[data-nav] a[href]',
|
|
2689
|
+
'[data-menu] a[href]',
|
|
2690
|
+
'[data-navigation] a[href]',
|
|
2691
|
+
// ARIA patterns
|
|
2692
|
+
'[aria-label*="navigation" i] a[href]',
|
|
2693
|
+
'[aria-label*="menu" i] a[href]',
|
|
2694
|
+
// Breadcrumbs
|
|
2695
|
+
'[aria-label*="breadcrumb" i] a[href]',
|
|
2696
|
+
'.breadcrumb a[href]',
|
|
2697
|
+
'.breadcrumbs a[href]',
|
|
2698
|
+
// Generic internal links (fallback - caught last)
|
|
2699
|
+
'a[href^="/"]',
|
|
2700
|
+
];
|
|
2701
|
+
/**
|
|
2702
|
+
* Default paths to exclude from discovery
|
|
2703
|
+
* Covers common auth, API, and framework-internal routes
|
|
2704
|
+
*/
|
|
2705
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
2706
|
+
// Authentication routes
|
|
2707
|
+
'/login', '/logout', '/signin', '/signout', '/sign-in', '/sign-out',
|
|
2708
|
+
'/register', '/signup', '/sign-up', '/forgot-password', '/reset-password',
|
|
2709
|
+
'/auth/*', '/oauth/*', '/callback/*', '/sso/*',
|
|
2710
|
+
// API and framework internals
|
|
2711
|
+
'/api/*', '/_next/*', '/_nuxt/*', '/__*',
|
|
2712
|
+
// Special links
|
|
2713
|
+
'javascript:*', 'mailto:*', 'tel:*', '#*',
|
|
2714
|
+
];
|
|
2715
|
+
/**
|
|
2716
|
+
* Default maximum number of links to scan from DOM
|
|
2717
|
+
* Prevents performance issues on sites with many links
|
|
2718
|
+
*/
|
|
2719
|
+
const DEFAULT_MAX_DOM_LINKS = 200;
|
|
2720
|
+
/**
|
|
2721
|
+
* Default cache TTL for localStorage (5 minutes)
|
|
2722
|
+
*/
|
|
2723
|
+
const DEFAULT_CACHE_TTL = 5 * 60 * 1000;
|
|
2724
|
+
/**
|
|
2725
|
+
* Cache version - increment when cache format changes
|
|
2726
|
+
*/
|
|
2727
|
+
const CACHE_VERSION = '1';
|
|
2666
2728
|
/**
|
|
2667
2729
|
* Multi-source sitemap discovery and management
|
|
2668
2730
|
*
|
|
@@ -2670,6 +2732,9 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2670
2732
|
* 1. Static props (explicit configuration)
|
|
2671
2733
|
* 2. Framework auto-discovery (React Router, Vue Router, etc.)
|
|
2672
2734
|
* 3. Backend API (tenant-specific sitemaps)
|
|
2735
|
+
*
|
|
2736
|
+
* Zero-config mode: When agentic is enabled but no siteMapConfig provided,
|
|
2737
|
+
* automatically discovers routes using smart defaults.
|
|
2673
2738
|
*/
|
|
2674
2739
|
class SiteMapDiscovery {
|
|
2675
2740
|
constructor(config, apiUrl, apiKey) {
|
|
@@ -2679,6 +2744,8 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2679
2744
|
this.backendCache = null;
|
|
2680
2745
|
this.isInitialized = false;
|
|
2681
2746
|
this.initPromise = null;
|
|
2747
|
+
this.detectedFramework = 'generic';
|
|
2748
|
+
this.popstateHandler = null;
|
|
2682
2749
|
this.config = config;
|
|
2683
2750
|
this.apiUrl = apiUrl;
|
|
2684
2751
|
this.apiKey = apiKey;
|
|
@@ -2687,6 +2754,7 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2687
2754
|
this.staticEntries = this.enhanceEntries(config.static, 'props');
|
|
2688
2755
|
}
|
|
2689
2756
|
}
|
|
2757
|
+
// ==================== INITIALIZATION ====================
|
|
2690
2758
|
/**
|
|
2691
2759
|
* Initialize all sitemap sources
|
|
2692
2760
|
* Call this during widget initialization or first query based on loadStrategy
|
|
@@ -2701,8 +2769,19 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2701
2769
|
this.isInitialized = true;
|
|
2702
2770
|
}
|
|
2703
2771
|
async performInitialization() {
|
|
2772
|
+
// Wait for DOM to be ready before discovery
|
|
2773
|
+
await this.waitForDOMReady();
|
|
2774
|
+
// Try to load from localStorage cache first
|
|
2775
|
+
const cachedEntries = this.loadFromLocalStorage();
|
|
2776
|
+
if (cachedEntries) {
|
|
2777
|
+
this.discoveredEntries = cachedEntries;
|
|
2778
|
+
console.log(`[SiteMapDiscovery] Loaded ${cachedEntries.length} entries from cache`);
|
|
2779
|
+
// Still run discovery in background to refresh cache
|
|
2780
|
+
this.refreshInBackground();
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2704
2783
|
const tasks = [];
|
|
2705
|
-
// Source 2: Framework auto-discovery
|
|
2784
|
+
// Source 2: Framework auto-discovery (enabled by default)
|
|
2706
2785
|
if (this.config.discovery?.enabled !== false) {
|
|
2707
2786
|
tasks.push(this.discoverFromFramework());
|
|
2708
2787
|
}
|
|
@@ -2716,6 +2795,8 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2716
2795
|
const { entries, source } = result.value;
|
|
2717
2796
|
if (source === 'discovery') {
|
|
2718
2797
|
this.discoveredEntries = entries;
|
|
2798
|
+
// Save to localStorage
|
|
2799
|
+
this.saveToLocalStorage(entries);
|
|
2719
2800
|
}
|
|
2720
2801
|
else if (source === 'backend') {
|
|
2721
2802
|
this.backendEntries = entries;
|
|
@@ -2725,9 +2806,144 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2725
2806
|
console.warn('[SiteMapDiscovery] Source failed:', result.reason);
|
|
2726
2807
|
}
|
|
2727
2808
|
}
|
|
2809
|
+
// Set up popstate listener for SPA navigation
|
|
2810
|
+
this.setupPopstateListener();
|
|
2728
2811
|
const total = this.staticEntries.length + this.discoveredEntries.length + this.backendEntries.length;
|
|
2729
2812
|
console.log(`[SiteMapDiscovery] Initialized with ${total} total entries (${this.staticEntries.length} static, ${this.discoveredEntries.length} discovered, ${this.backendEntries.length} backend)`);
|
|
2730
2813
|
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Wait for DOM to be ready before scanning
|
|
2816
|
+
*/
|
|
2817
|
+
async waitForDOMReady() {
|
|
2818
|
+
if (typeof document === 'undefined')
|
|
2819
|
+
return;
|
|
2820
|
+
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
2821
|
+
// DOM is ready, but give frameworks a microtask to mount
|
|
2822
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
2823
|
+
return;
|
|
2824
|
+
}
|
|
2825
|
+
return new Promise((resolve) => {
|
|
2826
|
+
document.addEventListener('DOMContentLoaded', () => resolve(), { once: true });
|
|
2827
|
+
});
|
|
2828
|
+
}
|
|
2829
|
+
/**
|
|
2830
|
+
* Set up popstate listener to re-discover on SPA navigation
|
|
2831
|
+
*/
|
|
2832
|
+
setupPopstateListener() {
|
|
2833
|
+
if (typeof window === 'undefined')
|
|
2834
|
+
return;
|
|
2835
|
+
this.popstateHandler = () => {
|
|
2836
|
+
// Debounce re-discovery
|
|
2837
|
+
setTimeout(() => {
|
|
2838
|
+
this.refreshInBackground();
|
|
2839
|
+
}, 100);
|
|
2840
|
+
};
|
|
2841
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Refresh discovery in background without blocking
|
|
2845
|
+
*/
|
|
2846
|
+
async refreshInBackground() {
|
|
2847
|
+
try {
|
|
2848
|
+
const result = await this.discoverFromFramework();
|
|
2849
|
+
if (result.entries.length > 0) {
|
|
2850
|
+
this.discoveredEntries = result.entries;
|
|
2851
|
+
this.saveToLocalStorage(result.entries);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
catch (error) {
|
|
2855
|
+
// Silent fail for background refresh
|
|
2856
|
+
console.debug('[SiteMapDiscovery] Background refresh failed:', error);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
// ==================== LOCALSTORAGE CACHING ====================
|
|
2860
|
+
/**
|
|
2861
|
+
* Get localStorage cache key for current origin
|
|
2862
|
+
*/
|
|
2863
|
+
getCacheKey() {
|
|
2864
|
+
if (typeof window === 'undefined')
|
|
2865
|
+
return 'astermind-sitemap';
|
|
2866
|
+
return `astermind-sitemap-${window.location.origin}`;
|
|
2867
|
+
}
|
|
2868
|
+
/**
|
|
2869
|
+
* Load discovered entries from localStorage cache
|
|
2870
|
+
*/
|
|
2871
|
+
loadFromLocalStorage() {
|
|
2872
|
+
if (typeof localStorage === 'undefined')
|
|
2873
|
+
return null;
|
|
2874
|
+
try {
|
|
2875
|
+
const key = this.getCacheKey();
|
|
2876
|
+
const cached = localStorage.getItem(key);
|
|
2877
|
+
if (!cached)
|
|
2878
|
+
return null;
|
|
2879
|
+
const data = JSON.parse(cached);
|
|
2880
|
+
// Check version
|
|
2881
|
+
if (data.version !== CACHE_VERSION) {
|
|
2882
|
+
localStorage.removeItem(key);
|
|
2883
|
+
return null;
|
|
2884
|
+
}
|
|
2885
|
+
// Check TTL
|
|
2886
|
+
const ttl = this.config.discovery?.cacheTtl ?? DEFAULT_CACHE_TTL;
|
|
2887
|
+
if (Date.now() - data.timestamp > ttl) {
|
|
2888
|
+
localStorage.removeItem(key);
|
|
2889
|
+
return null;
|
|
2890
|
+
}
|
|
2891
|
+
this.detectedFramework = data.framework;
|
|
2892
|
+
return data.entries;
|
|
2893
|
+
}
|
|
2894
|
+
catch (error) {
|
|
2895
|
+
console.debug('[SiteMapDiscovery] Failed to load from cache:', error);
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Save discovered entries to localStorage cache
|
|
2901
|
+
*/
|
|
2902
|
+
saveToLocalStorage(entries) {
|
|
2903
|
+
if (typeof localStorage === 'undefined')
|
|
2904
|
+
return;
|
|
2905
|
+
if (this.config.discovery?.cacheRoutes === false)
|
|
2906
|
+
return;
|
|
2907
|
+
try {
|
|
2908
|
+
const key = this.getCacheKey();
|
|
2909
|
+
const data = {
|
|
2910
|
+
entries,
|
|
2911
|
+
url: typeof window !== 'undefined' ? window.location.href : '',
|
|
2912
|
+
timestamp: Date.now(),
|
|
2913
|
+
framework: this.detectedFramework,
|
|
2914
|
+
version: CACHE_VERSION
|
|
2915
|
+
};
|
|
2916
|
+
localStorage.setItem(key, JSON.stringify(data));
|
|
2917
|
+
}
|
|
2918
|
+
catch (error) {
|
|
2919
|
+
// localStorage might be full or disabled
|
|
2920
|
+
console.debug('[SiteMapDiscovery] Failed to save to cache:', error);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
/**
|
|
2924
|
+
* Clear localStorage cache
|
|
2925
|
+
*/
|
|
2926
|
+
clearCache() {
|
|
2927
|
+
if (typeof localStorage === 'undefined')
|
|
2928
|
+
return;
|
|
2929
|
+
localStorage.removeItem(this.getCacheKey());
|
|
2930
|
+
}
|
|
2931
|
+
// ==================== CLEANUP ====================
|
|
2932
|
+
/**
|
|
2933
|
+
* Dispose of resources (event listeners, etc.)
|
|
2934
|
+
*/
|
|
2935
|
+
dispose() {
|
|
2936
|
+
if (this.popstateHandler && typeof window !== 'undefined') {
|
|
2937
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
2938
|
+
this.popstateHandler = null;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Get detected framework name
|
|
2943
|
+
*/
|
|
2944
|
+
getDetectedFramework() {
|
|
2945
|
+
return this.detectedFramework;
|
|
2946
|
+
}
|
|
2731
2947
|
/**
|
|
2732
2948
|
* Get merged sitemap entries with deduplication
|
|
2733
2949
|
*/
|
|
@@ -2754,6 +2970,7 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2754
2970
|
try {
|
|
2755
2971
|
// Detect framework if not specified
|
|
2756
2972
|
const framework = config.framework || this.detectFramework();
|
|
2973
|
+
this.detectedFramework = framework;
|
|
2757
2974
|
let routes = [];
|
|
2758
2975
|
switch (framework) {
|
|
2759
2976
|
case 'react-router':
|
|
@@ -2937,26 +3154,31 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2937
3154
|
}
|
|
2938
3155
|
/**
|
|
2939
3156
|
* Discover routes by scanning DOM navigation elements
|
|
3157
|
+
* Uses smart defaults when no selectors provided
|
|
2940
3158
|
*/
|
|
2941
3159
|
discoverFromDOM(selectors) {
|
|
2942
3160
|
const routes = [];
|
|
2943
3161
|
if (typeof document === 'undefined')
|
|
2944
3162
|
return routes;
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
'header a[href]'
|
|
2953
|
-
];
|
|
2954
|
-
const allSelectors = selectors || defaultSelectors;
|
|
3163
|
+
// Use provided selectors or smart defaults
|
|
3164
|
+
const allSelectors = selectors || DEFAULT_NAV_SELECTORS;
|
|
3165
|
+
// Get max links limit from config or use default
|
|
3166
|
+
const maxLinks = this.config.discovery?.maxDomLinks ?? DEFAULT_MAX_DOM_LINKS;
|
|
3167
|
+
// Get exclude patterns (merge user-provided with defaults)
|
|
3168
|
+
const userExcludes = this.config.discovery?.excludePaths || [];
|
|
3169
|
+
const allExcludes = [...DEFAULT_EXCLUDE_PATTERNS, ...userExcludes];
|
|
2955
3170
|
const seen = new Set();
|
|
3171
|
+
let linkCount = 0;
|
|
2956
3172
|
for (const selector of allSelectors) {
|
|
3173
|
+
// Stop if we've hit the limit
|
|
3174
|
+
if (linkCount >= maxLinks)
|
|
3175
|
+
break;
|
|
2957
3176
|
try {
|
|
2958
3177
|
const links = document.querySelectorAll(selector);
|
|
2959
3178
|
for (const link of links) {
|
|
3179
|
+
// Stop if we've hit the limit
|
|
3180
|
+
if (linkCount >= maxLinks)
|
|
3181
|
+
break;
|
|
2960
3182
|
const href = link.getAttribute('href');
|
|
2961
3183
|
if (!href)
|
|
2962
3184
|
continue;
|
|
@@ -2965,14 +3187,31 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2965
3187
|
continue;
|
|
2966
3188
|
}
|
|
2967
3189
|
// Normalize path
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
3190
|
+
let path = href;
|
|
3191
|
+
if (href.startsWith('http')) {
|
|
3192
|
+
try {
|
|
3193
|
+
path = new URL(href).pathname;
|
|
3194
|
+
}
|
|
3195
|
+
catch {
|
|
3196
|
+
continue;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
else if (!href.startsWith('/')) {
|
|
3200
|
+
path = `/${href}`;
|
|
2972
3201
|
}
|
|
2973
|
-
seen.add(path);
|
|
2974
3202
|
// Clean path (remove query string and hash)
|
|
2975
3203
|
const cleanPath = path.split('?')[0].split('#')[0];
|
|
3204
|
+
// Skip duplicates
|
|
3205
|
+
if (seen.has(cleanPath))
|
|
3206
|
+
continue;
|
|
3207
|
+
// Skip root path
|
|
3208
|
+
if (cleanPath === '/')
|
|
3209
|
+
continue;
|
|
3210
|
+
// Skip paths matching exclude patterns
|
|
3211
|
+
if (this.shouldExcludeRoute(cleanPath, allExcludes))
|
|
3212
|
+
continue;
|
|
3213
|
+
seen.add(cleanPath);
|
|
3214
|
+
linkCount++;
|
|
2976
3215
|
routes.push({
|
|
2977
3216
|
path: cleanPath,
|
|
2978
3217
|
name: link.textContent?.trim() || this.pathToName(cleanPath)
|
|
@@ -2983,6 +3222,7 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
2983
3222
|
// Invalid selector - skip
|
|
2984
3223
|
}
|
|
2985
3224
|
}
|
|
3225
|
+
console.log(`[SiteMapDiscovery] DOM scan found ${routes.length} routes (limit: ${maxLinks})`);
|
|
2986
3226
|
return routes;
|
|
2987
3227
|
}
|
|
2988
3228
|
// ==================== SOURCE 3: BACKEND API ====================
|
|
@@ -3270,9 +3510,18 @@ LJ5AZXvOhHaXdHzMuYKX5BpK4w7TqbPvJ6QPvKmLKvHh1VKcUJ6mJQgJJw==
|
|
|
3270
3510
|
this.modalIndex = new Map();
|
|
3271
3511
|
// Build indexes for fast lookup
|
|
3272
3512
|
this.buildIndexes();
|
|
3273
|
-
//
|
|
3274
|
-
|
|
3275
|
-
|
|
3513
|
+
// Auto-enable sitemap discovery when agentic is enabled but no explicit config provided
|
|
3514
|
+
// This enables zero-config mode: just set enabled: true and discovery works automatically
|
|
3515
|
+
const siteMapConfig = config.siteMapConfig ?? (config.enabled && !config.siteMap?.length
|
|
3516
|
+
? { discovery: { enabled: true } }
|
|
3517
|
+
: undefined);
|
|
3518
|
+
// Initialize multi-source sitemap discovery
|
|
3519
|
+
if (siteMapConfig) {
|
|
3520
|
+
this.siteMapDiscovery = new SiteMapDiscovery(siteMapConfig, apiUrl, apiKey);
|
|
3521
|
+
// Log zero-config mode activation
|
|
3522
|
+
if (!config.siteMapConfig && config.enabled) {
|
|
3523
|
+
console.log('[CyberneticIntentClassifier] Zero-config mode: auto-discovery enabled');
|
|
3524
|
+
}
|
|
3276
3525
|
}
|
|
3277
3526
|
}
|
|
3278
3527
|
/**
|