Fix active session replay state
This commit is contained in:
@@ -19,6 +19,10 @@ import {
|
|||||||
SidebarProvider,
|
SidebarProvider,
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
import {
|
||||||
|
readActiveSessionId,
|
||||||
|
writeActiveSessionId,
|
||||||
|
} from "@/lib/claw-active-session";
|
||||||
import { ClawSessionReplayProvider } from "@/lib/claw-session-replay";
|
import { ClawSessionReplayProvider } from "@/lib/claw-session-replay";
|
||||||
import { ClawAccountGate, useClawAccount } from "./claw-account-gate";
|
import { ClawAccountGate, useClawAccount } from "./claw-account-gate";
|
||||||
|
|
||||||
@@ -31,21 +35,13 @@ export const Assistant = () => {
|
|||||||
prepareSendMessagesRequest: async (options) => {
|
prepareSendMessagesRequest: async (options) => {
|
||||||
const body = options.body as Record<string, unknown>;
|
const body = options.body as Record<string, unknown>;
|
||||||
const messages = options.messages as UIMessage[];
|
const messages = options.messages as UIMessage[];
|
||||||
const selectedSessionId =
|
const selectedSessionId = readActiveSessionId();
|
||||||
typeof window !== "undefined"
|
|
||||||
? window.localStorage.getItem("claw.activeSessionId")
|
|
||||||
: null;
|
|
||||||
const lastSessionId = getLastSessionId(messages);
|
const lastSessionId = getLastSessionId(messages);
|
||||||
const selectedResumeSessionId =
|
const selectedResumeSessionId =
|
||||||
messages.length > 1 ? selectedSessionId : null;
|
messages.length > 1 ? selectedSessionId : null;
|
||||||
const outgoingSessionId =
|
const outgoingSessionId =
|
||||||
lastSessionId ?? selectedResumeSessionId ?? options.id;
|
lastSessionId ?? selectedResumeSessionId ?? options.id;
|
||||||
if (typeof window !== "undefined" && outgoingSessionId) {
|
writeActiveSessionId(outgoingSessionId);
|
||||||
window.localStorage.setItem(
|
|
||||||
"claw.activeSessionId",
|
|
||||||
outgoingSessionId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
body: {
|
body: {
|
||||||
@@ -65,7 +61,7 @@ export const Assistant = () => {
|
|||||||
});
|
});
|
||||||
const replaySession = useCallback(
|
const replaySession = useCallback(
|
||||||
(sessionId: string, repository: ExportedMessageRepository) => {
|
(sessionId: string, repository: ExportedMessageRepository) => {
|
||||||
window.localStorage.setItem("claw.activeSessionId", sessionId);
|
writeActiveSessionId(sessionId);
|
||||||
runtime.thread.import(repository);
|
runtime.thread.import(repository);
|
||||||
},
|
},
|
||||||
[runtime],
|
[runtime],
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { ReactNode } from "react";
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { clearActiveSessionId } from "@/lib/claw-active-session";
|
||||||
|
|
||||||
export type ClawAccount = {
|
export type ClawAccount = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -131,7 +132,7 @@ export function ClawAccountBar({
|
|||||||
}) {
|
}) {
|
||||||
async function logout() {
|
async function logout() {
|
||||||
await fetch("/api/claw/auth/logout", { method: "POST" });
|
await fetch("/api/claw/auth/logout", { method: "POST" });
|
||||||
window.localStorage.removeItem("claw.activeSessionId");
|
clearActiveSessionId();
|
||||||
onLogout();
|
onLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ import {
|
|||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible";
|
} from "@/components/ui/collapsible";
|
||||||
|
import {
|
||||||
|
readActiveSessionId,
|
||||||
|
writeActiveSessionId,
|
||||||
|
} from "@/lib/claw-active-session";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
type ActivityContextValue = {
|
type ActivityContextValue = {
|
||||||
@@ -71,12 +75,7 @@ export function ActivityProvider({ children }: { children: ReactNode }) {
|
|||||||
}, []);
|
}, []);
|
||||||
const openFiles = useCallback((nextSessionId?: string | null) => {
|
const openFiles = useCallback((nextSessionId?: string | null) => {
|
||||||
setMode("files");
|
setMode("files");
|
||||||
setSessionId(
|
setSessionId(nextSessionId ?? readActiveSessionId());
|
||||||
nextSessionId ??
|
|
||||||
(typeof window !== "undefined"
|
|
||||||
? window.localStorage.getItem("claw.activeSessionId")
|
|
||||||
: null),
|
|
||||||
);
|
|
||||||
setFilesRefreshToken((value) => value + 1);
|
setFilesRefreshToken((value) => value + 1);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -257,7 +256,7 @@ function SessionFilesPanel({
|
|||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
async function loadFiles() {
|
async function loadFiles() {
|
||||||
const activeSessionId = normalizeSessionId(
|
const activeSessionId = normalizeSessionId(
|
||||||
sessionId ?? window.localStorage.getItem("claw.activeSessionId"),
|
sessionId ?? readActiveSessionId(),
|
||||||
);
|
);
|
||||||
setStatus("正在读取聊天中的文件...");
|
setStatus("正在读取聊天中的文件...");
|
||||||
try {
|
try {
|
||||||
@@ -285,10 +284,7 @@ function SessionFilesPanel({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (nextPayload.session_id) {
|
if (nextPayload.session_id) {
|
||||||
window.localStorage.setItem(
|
writeActiveSessionId(nextPayload.session_id);
|
||||||
"claw.activeSessionId",
|
|
||||||
nextPayload.session_id,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
setPayload(nextPayload);
|
setPayload(nextPayload);
|
||||||
setStatus("");
|
setStatus("");
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
clearActiveSessionId,
|
||||||
|
readActiveSessionId,
|
||||||
|
writeActiveSessionId,
|
||||||
|
} from "@/lib/claw-active-session";
|
||||||
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
||||||
|
|
||||||
type ClawSession = {
|
type ClawSession = {
|
||||||
@@ -106,7 +111,7 @@ const ThreadListNew: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClickCapture={() => {
|
onClickCapture={() => {
|
||||||
window.localStorage.removeItem("claw.activeSessionId");
|
clearActiveSessionId();
|
||||||
window.dispatchEvent(new Event("claw-active-session-cleared"));
|
window.dispatchEvent(new Event("claw-active-session-cleared"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -141,7 +146,7 @@ const ClawSessionList: FC = () => {
|
|||||||
const renameInputRef = useRef<HTMLInputElement | null>(null);
|
const renameInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveSessionId(window.localStorage.getItem("claw.activeSessionId"));
|
setActiveSessionId(readActiveSessionId());
|
||||||
const refreshSessions = () => {
|
const refreshSessions = () => {
|
||||||
fetch("/api/claw/sessions")
|
fetch("/api/claw/sessions")
|
||||||
.then((res) => (res.ok ? res.json() : []))
|
.then((res) => (res.ok ? res.json() : []))
|
||||||
@@ -247,10 +252,7 @@ const ClawSessionList: FC = () => {
|
|||||||
className="aui-thread-list-item-trigger flex h-full min-w-0 flex-1 items-center truncate px-3 text-start text-sm"
|
className="aui-thread-list-item-trigger flex h-full min-w-0 flex-1 items-center truncate px-3 text-start text-sm"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setOpenMenuSessionId(null);
|
setOpenMenuSessionId(null);
|
||||||
window.localStorage.setItem(
|
writeActiveSessionId(session.session_id);
|
||||||
"claw.activeSessionId",
|
|
||||||
session.session_id,
|
|
||||||
);
|
|
||||||
setActiveSessionId(session.session_id);
|
setActiveSessionId(session.session_id);
|
||||||
setLoadingSessionId(session.session_id);
|
setLoadingSessionId(session.session_id);
|
||||||
try {
|
try {
|
||||||
@@ -333,9 +335,7 @@ const ClawSessionList: FC = () => {
|
|||||||
);
|
);
|
||||||
setOpenMenuSessionId(null);
|
setOpenMenuSessionId(null);
|
||||||
if (activeSessionId === session.session_id) {
|
if (activeSessionId === session.session_id) {
|
||||||
window.localStorage.removeItem(
|
clearActiveSessionId();
|
||||||
"claw.activeSessionId",
|
|
||||||
);
|
|
||||||
setActiveSessionId(null);
|
setActiveSessionId(null);
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new Event("claw-active-session-cleared"),
|
new Event("claw-active-session-cleared"),
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ import {
|
|||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
|
import {
|
||||||
|
clearActiveSessionId,
|
||||||
|
readActiveSessionId,
|
||||||
|
writeActiveSessionId,
|
||||||
|
} from "@/lib/claw-active-session";
|
||||||
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -193,6 +198,15 @@ function useRefreshReplayedRun() {
|
|||||||
const runStatus = await fetchLatestRunStatus(activeSessionId);
|
const runStatus = await fetchLatestRunStatus(activeSessionId);
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
if (runStatus?.status === "queued" || runStatus?.status === "running") {
|
if (runStatus?.status === "queued" || runStatus?.status === "running") {
|
||||||
|
const response = await fetch(`/api/claw/sessions/${activeSessionId}`, {
|
||||||
|
cache: "no-store",
|
||||||
|
});
|
||||||
|
if (!response.ok || cancelled) return;
|
||||||
|
const payload = await response.json();
|
||||||
|
replaySession(
|
||||||
|
activeSessionId,
|
||||||
|
toReplayRepository(payload, activeSessionId, runStatus),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const response = await fetch(`/api/claw/sessions/${activeSessionId}`, {
|
const response = await fetch(`/api/claw/sessions/${activeSessionId}`, {
|
||||||
@@ -243,7 +257,7 @@ function useReplayedRunState() {
|
|||||||
});
|
});
|
||||||
const localSessionId =
|
const localSessionId =
|
||||||
typeof window !== "undefined"
|
typeof window !== "undefined"
|
||||||
? normalizeSessionId(window.localStorage.getItem("claw.activeSessionId"))
|
? normalizeSessionId(readActiveSessionId())
|
||||||
: null;
|
: null;
|
||||||
const sessionId = replaySessionId ?? latestSessionId ?? localSessionId;
|
const sessionId = replaySessionId ?? latestSessionId ?? localSessionId;
|
||||||
return useMemo(
|
return useMemo(
|
||||||
@@ -722,7 +736,7 @@ function useComposerContextStatus() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const cleanLatestSessionId = normalizeSessionId(latestSessionId);
|
const cleanLatestSessionId = normalizeSessionId(latestSessionId);
|
||||||
if (cleanLatestSessionId) {
|
if (cleanLatestSessionId) {
|
||||||
window.localStorage.setItem("claw.activeSessionId", cleanLatestSessionId);
|
writeActiveSessionId(cleanLatestSessionId);
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
window.dispatchEvent(new Event("claw-sessions-changed"));
|
window.dispatchEvent(new Event("claw-sessions-changed"));
|
||||||
}, 0);
|
}, 0);
|
||||||
@@ -752,13 +766,10 @@ function useComposerContextStatus() {
|
|||||||
: {};
|
: {};
|
||||||
let sessionId = normalizeSessionId(
|
let sessionId = normalizeSessionId(
|
||||||
normalizeSessionId(latestSessionId) ||
|
normalizeSessionId(latestSessionId) ||
|
||||||
normalizeSessionId(
|
normalizeSessionId(readActiveSessionId()) ||
|
||||||
window.localStorage.getItem("claw.activeSessionId"),
|
|
||||||
) ||
|
|
||||||
normalizeSessionId(statePayload.active_session_id),
|
normalizeSessionId(statePayload.active_session_id),
|
||||||
);
|
);
|
||||||
if (sessionId)
|
if (sessionId) writeActiveSessionId(sessionId);
|
||||||
window.localStorage.setItem("claw.activeSessionId", sessionId);
|
|
||||||
let sessionPayload: ClawStoredSession | null = null;
|
let sessionPayload: ClawStoredSession | null = null;
|
||||||
let contextBudget: ContextBudget | undefined;
|
let contextBudget: ContextBudget | undefined;
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
@@ -772,7 +783,7 @@ function useComposerContextStatus() {
|
|||||||
sessionPayload =
|
sessionPayload =
|
||||||
(await sessionResponse.json()) as ClawStoredSession;
|
(await sessionResponse.json()) as ClawStoredSession;
|
||||||
} else if (sessionResponse.status === 404) {
|
} else if (sessionResponse.status === 404) {
|
||||||
window.localStorage.removeItem("claw.activeSessionId");
|
clearActiveSessionId();
|
||||||
sessionId = null;
|
sessionId = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ import {
|
|||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
useSidebar,
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
import {
|
||||||
|
clearActiveSessionId,
|
||||||
|
readActiveSessionId,
|
||||||
|
writeActiveSessionId,
|
||||||
|
} from "@/lib/claw-active-session";
|
||||||
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
||||||
|
|
||||||
type ThreadListSidebarProps = React.ComponentProps<typeof Sidebar> & {
|
type ThreadListSidebarProps = React.ComponentProps<typeof Sidebar> & {
|
||||||
@@ -157,7 +162,7 @@ function CollapsedSidebarRail({
|
|||||||
<div className="flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center gap-3">
|
||||||
<div
|
<div
|
||||||
onClickCapture={() => {
|
onClickCapture={() => {
|
||||||
window.localStorage.removeItem("claw.activeSessionId");
|
clearActiveSessionId();
|
||||||
window.dispatchEvent(new Event("claw-active-session-cleared"));
|
window.dispatchEvent(new Event("claw-active-session-cleared"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -360,7 +365,7 @@ function useSidebarSessions(open: boolean) {
|
|||||||
) {
|
) {
|
||||||
const cleanSessionId = normalizeSessionId(sessionId);
|
const cleanSessionId = normalizeSessionId(sessionId);
|
||||||
if (!cleanSessionId) return;
|
if (!cleanSessionId) return;
|
||||||
window.localStorage.setItem("claw.activeSessionId", cleanSessionId);
|
writeActiveSessionId(cleanSessionId);
|
||||||
setLoadingSessionId(sessionId);
|
setLoadingSessionId(sessionId);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/claw/sessions/${cleanSessionId}`, {
|
const response = await fetch(`/api/claw/sessions/${cleanSessionId}`, {
|
||||||
@@ -438,14 +443,13 @@ function AccountMenu({
|
|||||||
setState(nextState);
|
setState(nextState);
|
||||||
setSessions(nextSessions);
|
setSessions(nextSessions);
|
||||||
const activeSessionId = normalizeSessionId(
|
const activeSessionId = normalizeSessionId(
|
||||||
window.localStorage.getItem("claw.activeSessionId") ??
|
readActiveSessionId() ?? nextState?.active_session_id,
|
||||||
nextState?.active_session_id,
|
|
||||||
);
|
);
|
||||||
if (!activeSessionId) {
|
if (!activeSessionId) {
|
||||||
setActiveSession(null);
|
setActiveSession(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.localStorage.setItem("claw.activeSessionId", activeSessionId);
|
writeActiveSessionId(activeSessionId);
|
||||||
const response = await fetch(`/api/claw/sessions/${activeSessionId}`, {
|
const response = await fetch(`/api/claw/sessions/${activeSessionId}`, {
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
});
|
});
|
||||||
@@ -460,7 +464,7 @@ function AccountMenu({
|
|||||||
|
|
||||||
async function logout() {
|
async function logout() {
|
||||||
await fetch("/api/claw/auth/logout", { method: "POST" });
|
await fetch("/api/claw/auth/logout", { method: "POST" });
|
||||||
window.localStorage.removeItem("claw.activeSessionId");
|
clearActiveSessionId();
|
||||||
onLogout();
|
onLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
const ACTIVE_SESSION_ID_KEY = "claw.activeSessionId";
|
||||||
|
|
||||||
|
export function readActiveSessionId() {
|
||||||
|
if (typeof window === "undefined") return null;
|
||||||
|
return (
|
||||||
|
normalizeSessionId(window.sessionStorage.getItem(ACTIVE_SESSION_ID_KEY)) ??
|
||||||
|
normalizeSessionId(window.localStorage.getItem(ACTIVE_SESSION_ID_KEY))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeActiveSessionId(sessionId: string | null | undefined) {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
const normalized = normalizeSessionId(sessionId);
|
||||||
|
if (!normalized) {
|
||||||
|
clearActiveSessionId();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.sessionStorage.setItem(ACTIVE_SESSION_ID_KEY, normalized);
|
||||||
|
window.localStorage.setItem(ACTIVE_SESSION_ID_KEY, normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearActiveSessionId() {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
window.sessionStorage.removeItem(ACTIVE_SESSION_ID_KEY);
|
||||||
|
window.localStorage.removeItem(ACTIVE_SESSION_ID_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSessionId(value: unknown) {
|
||||||
|
if (typeof value !== "string") return null;
|
||||||
|
const trimmed = value.trim();
|
||||||
|
return trimmed || null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user