File size: 3,975 Bytes
962191f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
 * Shown inline in a chat when the backend no longer recognizes the
 * session id (typically: Space was restarted). Lets the user catch the
 * agent up with a summary of the prior conversation, or start over.
 */
import { useState, useCallback } from 'react';
import { Box, Button, CircularProgress, Typography } from '@mui/material';
import { apiFetch } from '@/utils/api';
import { useSessionStore } from '@/store/sessionStore';
import { useAgentStore } from '@/store/agentStore';
import { loadBackendMessages } from '@/lib/backend-message-store';
import { loadMessages } from '@/lib/chat-message-store';
import { uiMessagesToLLMMessages } from '@/lib/convert-llm-messages';
import { logger } from '@/utils/logger';

interface Props {
  sessionId: string;
}

export default function ExpiredBanner({ sessionId }: Props) {
  const { renameSession, deleteSession } = useSessionStore();
  const [busy, setBusy] = useState<'catch-up' | 'start-over' | null>(null);
  const [error, setError] = useState<string | null>(null);

  const handleCatchUp = useCallback(async () => {
    setBusy('catch-up');
    setError(null);
    try {
      // Prefer the raw backend-message cache; fall back to reconstructing
      // from UIMessages (for sessions that predate the backend cache).
      let messages = loadBackendMessages(sessionId);
      if (!messages || messages.length === 0) {
        const uiMsgs = loadMessages(sessionId);
        if (uiMsgs.length > 0) messages = uiMessagesToLLMMessages(uiMsgs);
      }
      if (!messages || messages.length === 0) {
        setError('Nothing to summarize from this chat.');
        setBusy(null);
        return;
      }

      const res = await apiFetch('/api/session/restore-summary', {
        method: 'POST',
        body: JSON.stringify({ messages }),
      });
      if (!res.ok) throw new Error(`restore-summary failed: ${res.status}`);
      const data = await res.json();
      const newId = data.session_id as string | undefined;
      if (!newId) throw new Error('no session_id in response');

      useAgentStore.getState().clearSessionState(sessionId);
      renameSession(sessionId, newId);
    } catch (e) {
      logger.warn('Catch-up failed:', e);
      setError("Couldn't catch up — try starting over.");
      setBusy(null);
    }
  }, [sessionId, renameSession]);

  const handleStartOver = useCallback(() => {
    setBusy('start-over');
    useAgentStore.getState().clearSessionState(sessionId);
    deleteSession(sessionId);
  }, [sessionId, deleteSession]);

  return (
    <Box
      sx={{
        mx: { xs: 2, md: 'auto' },
        my: 2,
        maxWidth: 720,
        p: 2.5,
        borderRadius: 2,
        border: '1px solid',
        borderColor: 'divider',
        bgcolor: 'background.paper',
        boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
      }}
    >
      <Typography variant="body1" sx={{ fontWeight: 600, mb: 0.5 }}>
        Where were we?
      </Typography>
      <Typography variant="body2" sx={{ color: 'text.secondary', mb: 2 }}>
        Let me skim the conversation so far and pick up right where we left
        off — or we can start something new.
      </Typography>
      <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
        <Button
          variant="contained"
          onClick={handleCatchUp}
          disabled={busy !== null}
          startIcon={busy === 'catch-up' ? <CircularProgress size={16} color="inherit" /> : null}
          sx={{ textTransform: 'none' }}
        >
          {busy === 'catch-up' ? 'Catching up…' : 'Catch me up'}
        </Button>
        <Button
          variant="outlined"
          onClick={handleStartOver}
          disabled={busy !== null}
          sx={{ textTransform: 'none' }}
        >
          Start fresh
        </Button>
      </Box>
      {error && (
        <Typography variant="caption" sx={{ display: 'block', mt: 1.5, color: 'error.main' }}>
          {error}
        </Typography>
      )}
    </Box>
  );
}