Session Management

Build conversation history UI with session listing and loading.

Manage conversation history, allowing users to view their past conversations and continue where they left off.

Overview

Lens OS automatically saves conversations. You can use the Sessions API to let users:

  • View a list of past conversations
  • Load and continue previous conversations
  • Start new conversations

History Sidebar State

State management
interface Session {
  id: string;
  title: string;
  messageCount: number;
  created_at: string;
}

const [sessions, setSessions] = useState<Session[]>([]);
const [showHistory, setShowHistory] = useState(false);
const [loadingHistory, setLoadingHistory] = useState(false);

Fetch Sessions

Load session list
const loadSessions = async () => {
  setLoadingHistory(true);
  try {
    const res = await fetch("/api/lens/agent/sessions");
    if (res.ok) {
      const data = await res.json();
      if (data.success) {
        setSessions(data.sessions);
      }
    }
  } finally {
    setLoadingHistory(false);
  }
};

// Load when opening sidebar
const toggleHistory = () => {
  const opening = !showHistory;
  setShowHistory(opening);
  if (opening) loadSessions();
};

Load Session

Load and restore conversation
const { loadSession, sessionId } = useLensAgent({...});

const handleLoadSession = async (targetId: string) => {
  const res = await fetch(`/api/lens/agent/sessions/${targetId}`);
  if (!res.ok) return;

  const data = await res.json();
  if (data.success) {
    // Messages may need to be reversed
    loadSession(targetId, data.messages.reverse());
    setShowHistory(false);
  }
};

Format Date

Date formatting helper
const formatDate = (dateStr: string, id?: string) => {
  let date = new Date(dateStr);

  // Fallback: extract timestamp from session ID
  if (isNaN(date.getTime()) && id) {
    const match = id.match(/^session-(\d+)-/);
    if (match) date = new Date(Number(match[1]));
  }

  if (isNaN(date.getTime())) return "";

  const now = new Date();
  const diffDays = Math.floor(
    (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24)
  );

  if (diffDays === 0) return "Today";
  if (diffDays === 1) return "Yesterday";
  if (diffDays < 7) return `${diffDays} days ago`;

  return date.toLocaleDateString("en-US", {
    month: "short",
    day: "numeric",
  });
};

UI Implementation

History sidebar
{/* History Button */}
<button onClick={toggleHistory}>
  <svg /* icon */ />
  History
</button>

{/* Sidebar Backdrop */}
{showHistory && (
  <div
    className="fixed inset-0 bg-black/20 z-40"
    onClick={() => setShowHistory(false)}
  />
)}

{/* Sidebar */}
<div className={`
  fixed top-0 left-0 h-full w-72 bg-white shadow-lg z-50
  transform transition-transform
  ${showHistory ? "translate-x-0" : "-translate-x-full"}
`}>
  {/* Header */}
  <div className="flex items-center justify-between p-4 border-b">
    <span className="font-semibold">Conversation History</span>
    <button onClick={() => setShowHistory(false)}>✕</button>
  </div>

  {/* List */}
  <div className="overflow-auto h-[calc(100%-60px)]">
    {loadingHistory ? (
      <div className="p-4 text-center text-gray-400">Loading...</div>
    ) : sessions.length === 0 ? (
      <div className="p-4 text-center text-gray-400">No conversation history</div>
    ) : (
      sessions.map((s) => (
        <div
          key={s.id}
          className={`
            p-4 border-b cursor-pointer hover:bg-gray-50
            ${s.id === sessionId ? "bg-blue-50" : ""}
          `}
          onClick={() => handleLoadSession(s.id)}
        >
          <div className="font-medium truncate">
            {s.title || "New Conversation"}
          </div>
          <div className="text-xs text-gray-400 mt-1 flex justify-between">
            <span>{formatDate(s.created_at, s.id)}</span>
            <span>{s.messageCount} messages</span>
          </div>
        </div>
      ))
    )}
  </div>
</div>

New Session

Start new conversation
const { newSession } = useLensAgent({...});

const handleNewSession = () => {
  newSession();
  // Clear tool cards and other state
  setToolCards([]);
};

Full Example

Complete implementation
export function LensChat({ userId }: { userId: string }) {
  const [showHistory, setShowHistory] = useState(false);
  const [sessions, setSessions] = useState<Session[]>([]);
  const [loadingHistory, setLoadingHistory] = useState(false);

  const {
    messages,
    sessionId,
    loadSession,
    newSession,
    // ...
  } = useLensAgent({ endpoint: "/api/lens/agent/chat", userId });

  const loadSessions = async () => {
    setLoadingHistory(true);
    const res = await fetch("/api/lens/agent/sessions");
    const data = await res.json();
    if (data.success) setSessions(data.sessions);
    setLoadingHistory(false);
  };

  const toggleHistory = () => {
    setShowHistory(!showHistory);
    if (!showHistory) loadSessions();
  };

  const handleLoadSession = async (targetId: string) => {
    const res = await fetch(`/api/lens/agent/sessions/${targetId}`);
    const data = await res.json();
    if (data.success) {
      loadSession(targetId, data.messages.reverse());
      setShowHistory(false);
    }
  };

  return (
    <div className="relative">
      {/* Main Chat UI */}
      {/* ... */}

      {/* History Sidebar */}
      {/* ... */}
    </div>
  );
}

Note

Sessions are automatically managed by Lens OS. You don't need to handle session creation or storage manually.