@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.
@@ -1,4 +1,5 @@
1
1
  import { CyberneticOfflineStorage } from './CyberneticOfflineStorage.js';
2
+ import type { StoredMessage } from './CyberneticSessionStorage.js';
2
3
  import { LicenseManager } from './license/index.js';
3
4
  import type { CyberneticConfig, CyberneticResponse, CyberneticError, ConnectionStatus, AskOptions, StreamCallbacks, CacheStatus, SystemSettings, AgenticConfig, SourcesConfig } from './types.js';
4
5
  import type { LicenseState } from './license/types.js';
@@ -33,6 +34,7 @@ export declare class CyberneticClient {
33
34
  private licenseManager;
34
35
  private offlineStorage;
35
36
  private omegaRAG;
37
+ private sessionStorage;
36
38
  private offlineWarningShown;
37
39
  constructor(config: CyberneticConfig);
38
40
  /**
@@ -174,6 +176,35 @@ export declare class CyberneticClient {
174
176
  * Get offline storage instance for advanced operations
175
177
  */
176
178
  getOfflineStorage(): CyberneticOfflineStorage | null;
179
+ /**
180
+ * Get stored messages from a previous session for UI restoration.
181
+ * Call after construction to check for resumable conversations.
182
+ */
183
+ getStoredMessages(): StoredMessage[];
184
+ /**
185
+ * Check if a valid stored session exists that can be resumed.
186
+ */
187
+ hasStoredSession(): boolean;
188
+ /**
189
+ * Get session info for debugging/status display.
190
+ */
191
+ getSessionInfo(): {
192
+ hasSession: boolean;
193
+ sessionId: string | null;
194
+ messageCount: number;
195
+ lastUpdated: Date | null;
196
+ ttlRemaining: number | null;
197
+ };
198
+ /**
199
+ * Clear the stored session and start fresh.
200
+ * Use for "New Conversation" actions.
201
+ */
202
+ clearSession(): void;
203
+ /**
204
+ * Start a new session, clearing any stored data.
205
+ * Optionally sets a new sessionId immediately.
206
+ */
207
+ startNewSession(sessionId?: string): void;
177
208
  /**
178
209
  * Send a message to the chatbot
179
210
  * Always returns a response, never throws
@@ -1 +1 @@
1
- {"version":3,"file":"CyberneticClient.d.ts","sourceRoot":"","sources":["../src/CyberneticClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CyberneticClient.d.ts","sourceRoot":"","sources":["../src/CyberneticClient.ts"],"names":[],"mappings":"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"}
@@ -1374,6 +1374,230 @@ class WebSocketTransport {
1374
1374
  }
1375
1375
  }
1376
1376
 
1377
+ // src/CyberneticSessionStorage.ts
1378
+ // Session storage manager for persisting chatbot session across page navigations
1379
+ const DEFAULT_CONFIG = {
1380
+ enabled: true,
1381
+ storageKey: 'astermind_session',
1382
+ sessionTtl: 30 * 60 * 1000, // 30 minutes
1383
+ persistMessages: true,
1384
+ maxMessages: 50
1385
+ };
1386
+ /**
1387
+ * Session storage manager for persisting chatbot sessions
1388
+ *
1389
+ * Uses sessionStorage to maintain conversation continuity across
1390
+ * page navigations within the same browser session.
1391
+ */
1392
+ class CyberneticSessionStorage {
1393
+ constructor(config) {
1394
+ this.cachedSession = null;
1395
+ this.config = { ...DEFAULT_CONFIG, ...config };
1396
+ // Load cached session on initialization
1397
+ if (this.config.enabled && this.isAvailable()) {
1398
+ this.cachedSession = this.loadFromStorage();
1399
+ }
1400
+ }
1401
+ /**
1402
+ * Check if sessionStorage is available
1403
+ */
1404
+ isAvailable() {
1405
+ if (typeof window === 'undefined')
1406
+ return false;
1407
+ try {
1408
+ const testKey = '__astermind_test__';
1409
+ window.sessionStorage.setItem(testKey, 'test');
1410
+ window.sessionStorage.removeItem(testKey);
1411
+ return true;
1412
+ }
1413
+ catch {
1414
+ return false;
1415
+ }
1416
+ }
1417
+ /**
1418
+ * Get the current session ID (if valid)
1419
+ */
1420
+ getSessionId() {
1421
+ if (!this.config.enabled)
1422
+ return null;
1423
+ const session = this.getSession();
1424
+ return session?.sessionId ?? null;
1425
+ }
1426
+ /**
1427
+ * Get the full stored session (if valid)
1428
+ */
1429
+ getSession() {
1430
+ if (!this.config.enabled)
1431
+ return null;
1432
+ // Use cached session if available
1433
+ if (this.cachedSession) {
1434
+ if (this.isSessionValid(this.cachedSession)) {
1435
+ return this.cachedSession;
1436
+ }
1437
+ // Session expired, clear it
1438
+ this.clear();
1439
+ return null;
1440
+ }
1441
+ // Try loading from storage
1442
+ const session = this.loadFromStorage();
1443
+ if (session && this.isSessionValid(session)) {
1444
+ this.cachedSession = session;
1445
+ return session;
1446
+ }
1447
+ return null;
1448
+ }
1449
+ /**
1450
+ * Save/update session ID
1451
+ */
1452
+ saveSessionId(sessionId) {
1453
+ if (!this.config.enabled || !sessionId)
1454
+ return;
1455
+ const existingSession = this.getSession();
1456
+ const session = {
1457
+ sessionId,
1458
+ updatedAt: Date.now(),
1459
+ messages: existingSession?.messages ?? []
1460
+ };
1461
+ this.saveToStorage(session);
1462
+ this.cachedSession = session;
1463
+ }
1464
+ /**
1465
+ * Add a message to the conversation history
1466
+ */
1467
+ addMessage(message) {
1468
+ if (!this.config.enabled || !this.config.persistMessages)
1469
+ return;
1470
+ const session = this.getSession();
1471
+ if (!session)
1472
+ return;
1473
+ const newMessage = {
1474
+ ...message,
1475
+ id: this.generateMessageId(),
1476
+ timestamp: Date.now()
1477
+ };
1478
+ // Add new message and trim if necessary
1479
+ session.messages = session.messages ?? [];
1480
+ session.messages.push(newMessage);
1481
+ // Keep only the most recent messages
1482
+ if (session.messages.length > this.config.maxMessages) {
1483
+ session.messages = session.messages.slice(-this.config.maxMessages);
1484
+ }
1485
+ session.updatedAt = Date.now();
1486
+ this.saveToStorage(session);
1487
+ this.cachedSession = session;
1488
+ }
1489
+ /**
1490
+ * Get conversation messages
1491
+ */
1492
+ getMessages() {
1493
+ if (!this.config.enabled || !this.config.persistMessages)
1494
+ return [];
1495
+ const session = this.getSession();
1496
+ return session?.messages ?? [];
1497
+ }
1498
+ /**
1499
+ * Clear the stored session
1500
+ */
1501
+ clear() {
1502
+ this.cachedSession = null;
1503
+ if (!this.isAvailable())
1504
+ return;
1505
+ try {
1506
+ window.sessionStorage.removeItem(this.config.storageKey);
1507
+ }
1508
+ catch (error) {
1509
+ console.warn('[CyberneticSessionStorage] Failed to clear session:', error);
1510
+ }
1511
+ }
1512
+ /**
1513
+ * Start a new session (clears existing and optionally sets new ID)
1514
+ */
1515
+ startNewSession(sessionId) {
1516
+ this.clear();
1517
+ if (sessionId) {
1518
+ this.saveSessionId(sessionId);
1519
+ }
1520
+ }
1521
+ /**
1522
+ * Check if we have a valid stored session
1523
+ */
1524
+ hasValidSession() {
1525
+ return this.getSession() !== null;
1526
+ }
1527
+ /**
1528
+ * Get session info for debugging/status
1529
+ */
1530
+ getSessionInfo() {
1531
+ const session = this.getSession();
1532
+ if (!session) {
1533
+ return {
1534
+ hasSession: false,
1535
+ sessionId: null,
1536
+ messageCount: 0,
1537
+ lastUpdated: null,
1538
+ ttlRemaining: null
1539
+ };
1540
+ }
1541
+ const ttlRemaining = Math.max(0, this.config.sessionTtl - (Date.now() - session.updatedAt));
1542
+ return {
1543
+ hasSession: true,
1544
+ sessionId: session.sessionId,
1545
+ messageCount: session.messages?.length ?? 0,
1546
+ lastUpdated: new Date(session.updatedAt),
1547
+ ttlRemaining
1548
+ };
1549
+ }
1550
+ // ==================== PRIVATE METHODS ====================
1551
+ /**
1552
+ * Check if a session is still valid (not expired)
1553
+ */
1554
+ isSessionValid(session) {
1555
+ const age = Date.now() - session.updatedAt;
1556
+ return age < this.config.sessionTtl;
1557
+ }
1558
+ /**
1559
+ * Load session from sessionStorage
1560
+ */
1561
+ loadFromStorage() {
1562
+ if (!this.isAvailable())
1563
+ return null;
1564
+ try {
1565
+ const stored = window.sessionStorage.getItem(this.config.storageKey);
1566
+ if (!stored)
1567
+ return null;
1568
+ const session = JSON.parse(stored);
1569
+ // Validate structure
1570
+ if (!session.sessionId || typeof session.updatedAt !== 'number') {
1571
+ return null;
1572
+ }
1573
+ return session;
1574
+ }
1575
+ catch (error) {
1576
+ console.warn('[CyberneticSessionStorage] Failed to load session:', error);
1577
+ return null;
1578
+ }
1579
+ }
1580
+ /**
1581
+ * Save session to sessionStorage
1582
+ */
1583
+ saveToStorage(session) {
1584
+ if (!this.isAvailable())
1585
+ return;
1586
+ try {
1587
+ window.sessionStorage.setItem(this.config.storageKey, JSON.stringify(session));
1588
+ }
1589
+ catch (error) {
1590
+ console.warn('[CyberneticSessionStorage] Failed to save session:', error);
1591
+ }
1592
+ }
1593
+ /**
1594
+ * Generate a unique message ID
1595
+ */
1596
+ generateMessageId() {
1597
+ return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1598
+ }
1599
+ }
1600
+
1377
1601
  // src/config.ts
1378
1602
  // Configuration loading and validation
1379
1603
  /** Default API URL when not specified */
@@ -2338,6 +2562,8 @@ class CyberneticClient {
2338
2562
  licenseKey: config.licenseKey,
2339
2563
  environment: config.environment,
2340
2564
  });
2565
+ // Initialize session storage (auto-loads existing session from browser sessionStorage)
2566
+ this.sessionStorage = new CyberneticSessionStorage(config.session);
2341
2567
  // Verify license asynchronously (non-blocking)
2342
2568
  this.licenseManager.verify().then(() => {
2343
2569
  // Check client feature after verification
@@ -2638,6 +2864,40 @@ class CyberneticClient {
2638
2864
  getOfflineStorage() {
2639
2865
  return this.offlineStorage;
2640
2866
  }
2867
+ // ==================== SESSION PERSISTENCE METHODS (ADR-028) ====================
2868
+ /**
2869
+ * Get stored messages from a previous session for UI restoration.
2870
+ * Call after construction to check for resumable conversations.
2871
+ */
2872
+ getStoredMessages() {
2873
+ return this.sessionStorage.getMessages();
2874
+ }
2875
+ /**
2876
+ * Check if a valid stored session exists that can be resumed.
2877
+ */
2878
+ hasStoredSession() {
2879
+ return this.sessionStorage.hasValidSession();
2880
+ }
2881
+ /**
2882
+ * Get session info for debugging/status display.
2883
+ */
2884
+ getSessionInfo() {
2885
+ return this.sessionStorage.getSessionInfo();
2886
+ }
2887
+ /**
2888
+ * Clear the stored session and start fresh.
2889
+ * Use for "New Conversation" actions.
2890
+ */
2891
+ clearSession() {
2892
+ this.sessionStorage.clear();
2893
+ }
2894
+ /**
2895
+ * Start a new session, clearing any stored data.
2896
+ * Optionally sets a new sessionId immediately.
2897
+ */
2898
+ startNewSession(sessionId) {
2899
+ this.sessionStorage.startNewSession(sessionId);
2900
+ }
2641
2901
  // ==================== CORE METHODS ====================
2642
2902
  /**
2643
2903
  * Send a message to the chatbot
@@ -2651,6 +2911,11 @@ class CyberneticClient {
2651
2911
  if (!message || typeof message !== 'string') {
2652
2912
  return this.createErrorResponse('Message is required', 'none');
2653
2913
  }
2914
+ // Auto-resolve sessionId: explicit option > stored session > none (ADR-028)
2915
+ const resolvedSessionId = options?.sessionId ?? this.sessionStorage.getSessionId() ?? undefined;
2916
+ const resolvedOptions = resolvedSessionId
2917
+ ? { ...options, sessionId: resolvedSessionId }
2918
+ : options;
2654
2919
  // Check maintenance mode before API call (ADR-200)
2655
2920
  const settings = await this.checkSystemStatus();
2656
2921
  if (settings.maintenanceMode || settings.forceOfflineClients) {
@@ -2671,10 +2936,16 @@ class CyberneticClient {
2671
2936
  }
2672
2937
  // Try API first
2673
2938
  try {
2674
- const response = await this.apiWithRetry(message, options);
2939
+ const response = await this.apiWithRetry(message, resolvedOptions);
2675
2940
  this.setStatus('online');
2676
2941
  // Process response through license manager (may add warning in production)
2677
2942
  const processedReply = this.licenseManager.processResponse(response.reply);
2943
+ // Persist session state (ADR-028)
2944
+ if (response.sessionId) {
2945
+ this.sessionStorage.saveSessionId(response.sessionId);
2946
+ }
2947
+ this.sessionStorage.addMessage({ role: 'user', content: message });
2948
+ this.sessionStorage.addMessage({ role: 'assistant', content: processedReply, confidence: 'high' });
2678
2949
  return {
2679
2950
  reply: processedReply,
2680
2951
  confidence: 'high',
@@ -2720,6 +2991,13 @@ class CyberneticClient {
2720
2991
  });
2721
2992
  return;
2722
2993
  }
2994
+ // Auto-resolve sessionId: explicit option > stored session > none (ADR-028)
2995
+ const resolvedSessionId = options?.sessionId ?? this.sessionStorage.getSessionId() ?? undefined;
2996
+ const resolvedOptions = resolvedSessionId
2997
+ ? { ...options, sessionId: resolvedSessionId }
2998
+ : options;
2999
+ // Save user message immediately (ADR-028)
3000
+ this.sessionStorage.addMessage({ role: 'user', content: message });
2723
3001
  // Check maintenance mode before API call (ADR-200)
2724
3002
  const settings = await this.checkSystemStatus();
2725
3003
  if (settings.maintenanceMode || settings.forceOfflineClients) {
@@ -2732,14 +3010,19 @@ class CyberneticClient {
2732
3010
  if (this.wsTransport && this.config.transport !== 'rest') {
2733
3011
  try {
2734
3012
  await this.wsTransport.chatStream(message, {
2735
- sessionId: options?.sessionId,
2736
- context: options?.context,
3013
+ sessionId: resolvedOptions?.sessionId,
3014
+ context: resolvedOptions?.context,
2737
3015
  onToken: callbacks.onToken,
2738
3016
  onSources: callbacks.onSources,
2739
3017
  onComplete: (response) => {
2740
3018
  this.setStatus('online');
2741
3019
  // Process through license manager
2742
3020
  const processedReply = this.licenseManager.processResponse(response.reply);
3021
+ // Persist session state (ADR-028)
3022
+ if (response.sessionId) {
3023
+ this.sessionStorage.saveSessionId(response.sessionId);
3024
+ }
3025
+ this.sessionStorage.addMessage({ role: 'assistant', content: processedReply, confidence: 'high' });
2743
3026
  callbacks.onComplete?.({
2744
3027
  ...response,
2745
3028
  reply: processedReply
@@ -2749,7 +3032,7 @@ class CyberneticClient {
2749
3032
  // In 'auto' mode, fall back to SSE on WS error
2750
3033
  if (this.config.transport === 'auto') {
2751
3034
  console.warn('[Cybernetic] WebSocket error, falling back to SSE:', error.message);
2752
- this.streamViaSSE(message, callbacks, options);
3035
+ this.streamViaSSE(message, callbacks, resolvedOptions);
2753
3036
  }
2754
3037
  else {
2755
3038
  // 'websocket' mode — no fallback
@@ -2775,7 +3058,7 @@ class CyberneticClient {
2775
3058
  }
2776
3059
  }
2777
3060
  // REST+SSE path (on-prem, or fallback from WebSocket)
2778
- await this.streamViaSSE(message, callbacks, options);
3061
+ await this.streamViaSSE(message, callbacks, resolvedOptions);
2779
3062
  }
2780
3063
  /**
2781
3064
  * Stream chat via REST+SSE (original transport).
@@ -2792,6 +3075,11 @@ class CyberneticClient {
2792
3075
  this.setStatus('online');
2793
3076
  // Process response through license manager (may add warning in production)
2794
3077
  const processedReply = this.licenseManager.processResponse(data.fullText);
3078
+ // Persist session state (ADR-028)
3079
+ if (data.sessionId) {
3080
+ this.sessionStorage.saveSessionId(data.sessionId);
3081
+ }
3082
+ this.sessionStorage.addMessage({ role: 'assistant', content: processedReply, confidence: 'high' });
2795
3083
  callbacks.onComplete?.({
2796
3084
  reply: processedReply,
2797
3085
  confidence: 'high',