@astermind/cybernetic-chatbot-client 2.2.59 → 2.2.62
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/CyberneticClient.d.ts +31 -0
- package/dist/CyberneticClient.d.ts.map +1 -1
- package/dist/cybernetic-chatbot-client-full.esm.js +293 -5
- 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 +293 -5
- package/dist/cybernetic-chatbot-client-full.umd.js.map +1 -1
- package/dist/cybernetic-chatbot-client.esm.js +294 -6
- 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 +294 -5
- package/dist/cybernetic-chatbot-client.umd.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1375,6 +1375,230 @@ class WebSocketTransport {
|
|
|
1375
1375
|
}
|
|
1376
1376
|
}
|
|
1377
1377
|
|
|
1378
|
+
// src/CyberneticSessionStorage.ts
|
|
1379
|
+
// Session storage manager for persisting chatbot session across page navigations
|
|
1380
|
+
const DEFAULT_CONFIG = {
|
|
1381
|
+
enabled: true,
|
|
1382
|
+
storageKey: 'astermind_session',
|
|
1383
|
+
sessionTtl: 30 * 60 * 1000, // 30 minutes
|
|
1384
|
+
persistMessages: true,
|
|
1385
|
+
maxMessages: 50
|
|
1386
|
+
};
|
|
1387
|
+
/**
|
|
1388
|
+
* Session storage manager for persisting chatbot sessions
|
|
1389
|
+
*
|
|
1390
|
+
* Uses sessionStorage to maintain conversation continuity across
|
|
1391
|
+
* page navigations within the same browser session.
|
|
1392
|
+
*/
|
|
1393
|
+
class CyberneticSessionStorage {
|
|
1394
|
+
constructor(config) {
|
|
1395
|
+
this.cachedSession = null;
|
|
1396
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
1397
|
+
// Load cached session on initialization
|
|
1398
|
+
if (this.config.enabled && this.isAvailable()) {
|
|
1399
|
+
this.cachedSession = this.loadFromStorage();
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Check if sessionStorage is available
|
|
1404
|
+
*/
|
|
1405
|
+
isAvailable() {
|
|
1406
|
+
if (typeof window === 'undefined')
|
|
1407
|
+
return false;
|
|
1408
|
+
try {
|
|
1409
|
+
const testKey = '__astermind_test__';
|
|
1410
|
+
window.sessionStorage.setItem(testKey, 'test');
|
|
1411
|
+
window.sessionStorage.removeItem(testKey);
|
|
1412
|
+
return true;
|
|
1413
|
+
}
|
|
1414
|
+
catch {
|
|
1415
|
+
return false;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Get the current session ID (if valid)
|
|
1420
|
+
*/
|
|
1421
|
+
getSessionId() {
|
|
1422
|
+
if (!this.config.enabled)
|
|
1423
|
+
return null;
|
|
1424
|
+
const session = this.getSession();
|
|
1425
|
+
return session?.sessionId ?? null;
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Get the full stored session (if valid)
|
|
1429
|
+
*/
|
|
1430
|
+
getSession() {
|
|
1431
|
+
if (!this.config.enabled)
|
|
1432
|
+
return null;
|
|
1433
|
+
// Use cached session if available
|
|
1434
|
+
if (this.cachedSession) {
|
|
1435
|
+
if (this.isSessionValid(this.cachedSession)) {
|
|
1436
|
+
return this.cachedSession;
|
|
1437
|
+
}
|
|
1438
|
+
// Session expired, clear it
|
|
1439
|
+
this.clear();
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
// Try loading from storage
|
|
1443
|
+
const session = this.loadFromStorage();
|
|
1444
|
+
if (session && this.isSessionValid(session)) {
|
|
1445
|
+
this.cachedSession = session;
|
|
1446
|
+
return session;
|
|
1447
|
+
}
|
|
1448
|
+
return null;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Save/update session ID
|
|
1452
|
+
*/
|
|
1453
|
+
saveSessionId(sessionId) {
|
|
1454
|
+
if (!this.config.enabled || !sessionId)
|
|
1455
|
+
return;
|
|
1456
|
+
const existingSession = this.getSession();
|
|
1457
|
+
const session = {
|
|
1458
|
+
sessionId,
|
|
1459
|
+
updatedAt: Date.now(),
|
|
1460
|
+
messages: existingSession?.messages ?? []
|
|
1461
|
+
};
|
|
1462
|
+
this.saveToStorage(session);
|
|
1463
|
+
this.cachedSession = session;
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Add a message to the conversation history
|
|
1467
|
+
*/
|
|
1468
|
+
addMessage(message) {
|
|
1469
|
+
if (!this.config.enabled || !this.config.persistMessages)
|
|
1470
|
+
return;
|
|
1471
|
+
const session = this.getSession();
|
|
1472
|
+
if (!session)
|
|
1473
|
+
return;
|
|
1474
|
+
const newMessage = {
|
|
1475
|
+
...message,
|
|
1476
|
+
id: this.generateMessageId(),
|
|
1477
|
+
timestamp: Date.now()
|
|
1478
|
+
};
|
|
1479
|
+
// Add new message and trim if necessary
|
|
1480
|
+
session.messages = session.messages ?? [];
|
|
1481
|
+
session.messages.push(newMessage);
|
|
1482
|
+
// Keep only the most recent messages
|
|
1483
|
+
if (session.messages.length > this.config.maxMessages) {
|
|
1484
|
+
session.messages = session.messages.slice(-this.config.maxMessages);
|
|
1485
|
+
}
|
|
1486
|
+
session.updatedAt = Date.now();
|
|
1487
|
+
this.saveToStorage(session);
|
|
1488
|
+
this.cachedSession = session;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Get conversation messages
|
|
1492
|
+
*/
|
|
1493
|
+
getMessages() {
|
|
1494
|
+
if (!this.config.enabled || !this.config.persistMessages)
|
|
1495
|
+
return [];
|
|
1496
|
+
const session = this.getSession();
|
|
1497
|
+
return session?.messages ?? [];
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Clear the stored session
|
|
1501
|
+
*/
|
|
1502
|
+
clear() {
|
|
1503
|
+
this.cachedSession = null;
|
|
1504
|
+
if (!this.isAvailable())
|
|
1505
|
+
return;
|
|
1506
|
+
try {
|
|
1507
|
+
window.sessionStorage.removeItem(this.config.storageKey);
|
|
1508
|
+
}
|
|
1509
|
+
catch (error) {
|
|
1510
|
+
console.warn('[CyberneticSessionStorage] Failed to clear session:', error);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Start a new session (clears existing and optionally sets new ID)
|
|
1515
|
+
*/
|
|
1516
|
+
startNewSession(sessionId) {
|
|
1517
|
+
this.clear();
|
|
1518
|
+
if (sessionId) {
|
|
1519
|
+
this.saveSessionId(sessionId);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Check if we have a valid stored session
|
|
1524
|
+
*/
|
|
1525
|
+
hasValidSession() {
|
|
1526
|
+
return this.getSession() !== null;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Get session info for debugging/status
|
|
1530
|
+
*/
|
|
1531
|
+
getSessionInfo() {
|
|
1532
|
+
const session = this.getSession();
|
|
1533
|
+
if (!session) {
|
|
1534
|
+
return {
|
|
1535
|
+
hasSession: false,
|
|
1536
|
+
sessionId: null,
|
|
1537
|
+
messageCount: 0,
|
|
1538
|
+
lastUpdated: null,
|
|
1539
|
+
ttlRemaining: null
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
const ttlRemaining = Math.max(0, this.config.sessionTtl - (Date.now() - session.updatedAt));
|
|
1543
|
+
return {
|
|
1544
|
+
hasSession: true,
|
|
1545
|
+
sessionId: session.sessionId,
|
|
1546
|
+
messageCount: session.messages?.length ?? 0,
|
|
1547
|
+
lastUpdated: new Date(session.updatedAt),
|
|
1548
|
+
ttlRemaining
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
// ==================== PRIVATE METHODS ====================
|
|
1552
|
+
/**
|
|
1553
|
+
* Check if a session is still valid (not expired)
|
|
1554
|
+
*/
|
|
1555
|
+
isSessionValid(session) {
|
|
1556
|
+
const age = Date.now() - session.updatedAt;
|
|
1557
|
+
return age < this.config.sessionTtl;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Load session from sessionStorage
|
|
1561
|
+
*/
|
|
1562
|
+
loadFromStorage() {
|
|
1563
|
+
if (!this.isAvailable())
|
|
1564
|
+
return null;
|
|
1565
|
+
try {
|
|
1566
|
+
const stored = window.sessionStorage.getItem(this.config.storageKey);
|
|
1567
|
+
if (!stored)
|
|
1568
|
+
return null;
|
|
1569
|
+
const session = JSON.parse(stored);
|
|
1570
|
+
// Validate structure
|
|
1571
|
+
if (!session.sessionId || typeof session.updatedAt !== 'number') {
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
return session;
|
|
1575
|
+
}
|
|
1576
|
+
catch (error) {
|
|
1577
|
+
console.warn('[CyberneticSessionStorage] Failed to load session:', error);
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Save session to sessionStorage
|
|
1583
|
+
*/
|
|
1584
|
+
saveToStorage(session) {
|
|
1585
|
+
if (!this.isAvailable())
|
|
1586
|
+
return;
|
|
1587
|
+
try {
|
|
1588
|
+
window.sessionStorage.setItem(this.config.storageKey, JSON.stringify(session));
|
|
1589
|
+
}
|
|
1590
|
+
catch (error) {
|
|
1591
|
+
console.warn('[CyberneticSessionStorage] Failed to save session:', error);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Generate a unique message ID
|
|
1596
|
+
*/
|
|
1597
|
+
generateMessageId() {
|
|
1598
|
+
return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1378
1602
|
// src/config.ts
|
|
1379
1603
|
// Configuration loading and validation
|
|
1380
1604
|
/** Default API URL when not specified */
|
|
@@ -2348,6 +2572,8 @@ class CyberneticClient {
|
|
|
2348
2572
|
licenseKey: config.licenseKey,
|
|
2349
2573
|
environment: config.environment,
|
|
2350
2574
|
});
|
|
2575
|
+
// Initialize session storage (auto-loads existing session from browser sessionStorage)
|
|
2576
|
+
this.sessionStorage = new CyberneticSessionStorage(config.session);
|
|
2351
2577
|
// Verify license asynchronously (non-blocking)
|
|
2352
2578
|
this.licenseManager.verify().then(() => {
|
|
2353
2579
|
// Check client feature after verification
|
|
@@ -2648,6 +2874,40 @@ class CyberneticClient {
|
|
|
2648
2874
|
getOfflineStorage() {
|
|
2649
2875
|
return this.offlineStorage;
|
|
2650
2876
|
}
|
|
2877
|
+
// ==================== SESSION PERSISTENCE METHODS (ADR-028) ====================
|
|
2878
|
+
/**
|
|
2879
|
+
* Get stored messages from a previous session for UI restoration.
|
|
2880
|
+
* Call after construction to check for resumable conversations.
|
|
2881
|
+
*/
|
|
2882
|
+
getStoredMessages() {
|
|
2883
|
+
return this.sessionStorage.getMessages();
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Check if a valid stored session exists that can be resumed.
|
|
2887
|
+
*/
|
|
2888
|
+
hasStoredSession() {
|
|
2889
|
+
return this.sessionStorage.hasValidSession();
|
|
2890
|
+
}
|
|
2891
|
+
/**
|
|
2892
|
+
* Get session info for debugging/status display.
|
|
2893
|
+
*/
|
|
2894
|
+
getSessionInfo() {
|
|
2895
|
+
return this.sessionStorage.getSessionInfo();
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Clear the stored session and start fresh.
|
|
2899
|
+
* Use for "New Conversation" actions.
|
|
2900
|
+
*/
|
|
2901
|
+
clearSession() {
|
|
2902
|
+
this.sessionStorage.clear();
|
|
2903
|
+
}
|
|
2904
|
+
/**
|
|
2905
|
+
* Start a new session, clearing any stored data.
|
|
2906
|
+
* Optionally sets a new sessionId immediately.
|
|
2907
|
+
*/
|
|
2908
|
+
startNewSession(sessionId) {
|
|
2909
|
+
this.sessionStorage.startNewSession(sessionId);
|
|
2910
|
+
}
|
|
2651
2911
|
// ==================== CORE METHODS ====================
|
|
2652
2912
|
/**
|
|
2653
2913
|
* Send a message to the chatbot
|
|
@@ -2661,6 +2921,11 @@ class CyberneticClient {
|
|
|
2661
2921
|
if (!message || typeof message !== 'string') {
|
|
2662
2922
|
return this.createErrorResponse('Message is required', 'none');
|
|
2663
2923
|
}
|
|
2924
|
+
// Auto-resolve sessionId: explicit option > stored session > none (ADR-028)
|
|
2925
|
+
const resolvedSessionId = options?.sessionId ?? this.sessionStorage.getSessionId() ?? undefined;
|
|
2926
|
+
const resolvedOptions = resolvedSessionId
|
|
2927
|
+
? { ...options, sessionId: resolvedSessionId }
|
|
2928
|
+
: options;
|
|
2664
2929
|
// Check maintenance mode before API call (ADR-200)
|
|
2665
2930
|
const settings = await this.checkSystemStatus();
|
|
2666
2931
|
if (settings.maintenanceMode || settings.forceOfflineClients) {
|
|
@@ -2681,10 +2946,16 @@ class CyberneticClient {
|
|
|
2681
2946
|
}
|
|
2682
2947
|
// Try API first
|
|
2683
2948
|
try {
|
|
2684
|
-
const response = await this.apiWithRetry(message,
|
|
2949
|
+
const response = await this.apiWithRetry(message, resolvedOptions);
|
|
2685
2950
|
this.setStatus('online');
|
|
2686
2951
|
// Process response through license manager (may add warning in production)
|
|
2687
2952
|
const processedReply = this.licenseManager.processResponse(response.reply);
|
|
2953
|
+
// Persist session state (ADR-028)
|
|
2954
|
+
if (response.sessionId) {
|
|
2955
|
+
this.sessionStorage.saveSessionId(response.sessionId);
|
|
2956
|
+
}
|
|
2957
|
+
this.sessionStorage.addMessage({ role: 'user', content: message });
|
|
2958
|
+
this.sessionStorage.addMessage({ role: 'assistant', content: processedReply, confidence: 'high' });
|
|
2688
2959
|
return {
|
|
2689
2960
|
reply: processedReply,
|
|
2690
2961
|
confidence: 'high',
|
|
@@ -2730,6 +3001,13 @@ class CyberneticClient {
|
|
|
2730
3001
|
});
|
|
2731
3002
|
return;
|
|
2732
3003
|
}
|
|
3004
|
+
// Auto-resolve sessionId: explicit option > stored session > none (ADR-028)
|
|
3005
|
+
const resolvedSessionId = options?.sessionId ?? this.sessionStorage.getSessionId() ?? undefined;
|
|
3006
|
+
const resolvedOptions = resolvedSessionId
|
|
3007
|
+
? { ...options, sessionId: resolvedSessionId }
|
|
3008
|
+
: options;
|
|
3009
|
+
// Save user message immediately (ADR-028)
|
|
3010
|
+
this.sessionStorage.addMessage({ role: 'user', content: message });
|
|
2733
3011
|
// Check maintenance mode before API call (ADR-200)
|
|
2734
3012
|
const settings = await this.checkSystemStatus();
|
|
2735
3013
|
if (settings.maintenanceMode || settings.forceOfflineClients) {
|
|
@@ -2742,14 +3020,19 @@ class CyberneticClient {
|
|
|
2742
3020
|
if (this.wsTransport && this.config.transport !== 'rest') {
|
|
2743
3021
|
try {
|
|
2744
3022
|
await this.wsTransport.chatStream(message, {
|
|
2745
|
-
sessionId:
|
|
2746
|
-
context:
|
|
3023
|
+
sessionId: resolvedOptions?.sessionId,
|
|
3024
|
+
context: resolvedOptions?.context,
|
|
2747
3025
|
onToken: callbacks.onToken,
|
|
2748
3026
|
onSources: callbacks.onSources,
|
|
2749
3027
|
onComplete: (response) => {
|
|
2750
3028
|
this.setStatus('online');
|
|
2751
3029
|
// Process through license manager
|
|
2752
3030
|
const processedReply = this.licenseManager.processResponse(response.reply);
|
|
3031
|
+
// Persist session state (ADR-028)
|
|
3032
|
+
if (response.sessionId) {
|
|
3033
|
+
this.sessionStorage.saveSessionId(response.sessionId);
|
|
3034
|
+
}
|
|
3035
|
+
this.sessionStorage.addMessage({ role: 'assistant', content: processedReply, confidence: 'high' });
|
|
2753
3036
|
callbacks.onComplete?.({
|
|
2754
3037
|
...response,
|
|
2755
3038
|
reply: processedReply
|
|
@@ -2759,7 +3042,7 @@ class CyberneticClient {
|
|
|
2759
3042
|
// In 'auto' mode, fall back to SSE on WS error
|
|
2760
3043
|
if (this.config.transport === 'auto') {
|
|
2761
3044
|
console.warn('[Cybernetic] WebSocket error, falling back to SSE:', error.message);
|
|
2762
|
-
this.streamViaSSE(message, callbacks,
|
|
3045
|
+
this.streamViaSSE(message, callbacks, resolvedOptions);
|
|
2763
3046
|
}
|
|
2764
3047
|
else {
|
|
2765
3048
|
// 'websocket' mode — no fallback
|
|
@@ -2785,7 +3068,7 @@ class CyberneticClient {
|
|
|
2785
3068
|
}
|
|
2786
3069
|
}
|
|
2787
3070
|
// REST+SSE path (on-prem, or fallback from WebSocket)
|
|
2788
|
-
await this.streamViaSSE(message, callbacks,
|
|
3071
|
+
await this.streamViaSSE(message, callbacks, resolvedOptions);
|
|
2789
3072
|
}
|
|
2790
3073
|
/**
|
|
2791
3074
|
* Stream chat via REST+SSE (original transport).
|
|
@@ -2802,6 +3085,11 @@ class CyberneticClient {
|
|
|
2802
3085
|
this.setStatus('online');
|
|
2803
3086
|
// Process response through license manager (may add warning in production)
|
|
2804
3087
|
const processedReply = this.licenseManager.processResponse(data.fullText);
|
|
3088
|
+
// Persist session state (ADR-028)
|
|
3089
|
+
if (data.sessionId) {
|
|
3090
|
+
this.sessionStorage.saveSessionId(data.sessionId);
|
|
3091
|
+
}
|
|
3092
|
+
this.sessionStorage.addMessage({ role: 'assistant', content: processedReply, confidence: 'high' });
|
|
2805
3093
|
callbacks.onComplete?.({
|
|
2806
3094
|
reply: processedReply,
|
|
2807
3095
|
confidence: 'high',
|
|
@@ -7179,5 +7467,5 @@ function createClient(config) {
|
|
|
7179
7467
|
return new CyberneticClient(config);
|
|
7180
7468
|
}
|
|
7181
7469
|
|
|
7182
|
-
export { ApiClient, CyberneticAgent, CyberneticCache, CyberneticClient, CyberneticIntentClassifier, CyberneticLocalRAG, CyberneticOfflineStorage, LicenseManager, OmegaOfflineRAG, REQUIRED_FEATURES, SiteMapDiscovery, WebSocketTransport, configLoaders, createClient, createDiscoveryConfig, createLicenseManager, deriveWsUrl, detectEnvironment, getEnforcementMode, getTokenExpiration, isValidJWTFormat, loadConfig, registerAgenticCapabilities, resolveWsUrl, useSiteMapDiscovery, validateConfig, verifyLicenseToken };
|
|
7470
|
+
export { ApiClient, CyberneticAgent, CyberneticCache, CyberneticClient, CyberneticIntentClassifier, CyberneticLocalRAG, CyberneticOfflineStorage, CyberneticSessionStorage, LicenseManager, OmegaOfflineRAG, REQUIRED_FEATURES, SiteMapDiscovery, WebSocketTransport, configLoaders, createClient, createDiscoveryConfig, createLicenseManager, deriveWsUrl, detectEnvironment, getEnforcementMode, getTokenExpiration, isValidJWTFormat, loadConfig, registerAgenticCapabilities, resolveWsUrl, useSiteMapDiscovery, validateConfig, verifyLicenseToken };
|
|
7183
7471
|
//# sourceMappingURL=cybernetic-chatbot-client.esm.js.map
|