Normalize active session ids in frontend

This commit is contained in:
武阳
2026-05-07 15:38:37 +08:00
parent ce6445bf77
commit 61e3cd1681
5 changed files with 53 additions and 18 deletions
@@ -10,7 +10,7 @@ export async function GET(req: Request) {
const incoming = new URL(req.url); const incoming = new URL(req.url);
const url = new URL(`${CLAW_API_URL}/api/context-budget`); const url = new URL(`${CLAW_API_URL}/api/context-budget`);
url.searchParams.set("account_id", account.id); url.searchParams.set("account_id", account.id);
const sessionId = incoming.searchParams.get("session_id"); const sessionId = incoming.searchParams.get("session_id")?.trim();
if (sessionId) url.searchParams.set("session_id", sessionId); if (sessionId) url.searchParams.set("session_id", sessionId);
try { try {
@@ -10,7 +10,8 @@ export async function GET(
if (!account) if (!account)
return Response.json({ error: "请先登录账号" }, { status: 401 }); return Response.json({ error: "请先登录账号" }, { status: 401 });
const { sessionId } = await params; const { sessionId: rawSessionId } = await params;
const sessionId = rawSessionId.trim();
const url = new URL(`${CLAW_API_URL}/api/sessions/${sessionId}`); const url = new URL(`${CLAW_API_URL}/api/sessions/${sessionId}`);
url.searchParams.set("account_id", account.id); url.searchParams.set("account_id", account.id);
const response = await fetch(url, { cache: "no-store" }); const response = await fetch(url, { cache: "no-store" });
@@ -32,7 +33,8 @@ export async function DELETE(
if (!account) if (!account)
return Response.json({ error: "请先登录账号" }, { status: 401 }); return Response.json({ error: "请先登录账号" }, { status: 401 });
const { sessionId } = await params; const { sessionId: rawSessionId } = await params;
const sessionId = rawSessionId.trim();
const url = new URL(`${CLAW_API_URL}/api/sessions/${sessionId}`); const url = new URL(`${CLAW_API_URL}/api/sessions/${sessionId}`);
url.searchParams.set("account_id", account.id); url.searchParams.set("account_id", account.id);
const response = await fetch(url, { method: "DELETE", cache: "no-store" }); const response = await fetch(url, { method: "DELETE", cache: "no-store" });
@@ -54,7 +56,8 @@ export async function PATCH(
if (!account) if (!account)
return Response.json({ error: "请先登录账号" }, { status: 401 }); return Response.json({ error: "请先登录账号" }, { status: 401 });
const { sessionId } = await params; const { sessionId: rawSessionId } = await params;
const sessionId = rawSessionId.trim();
const url = new URL(`${CLAW_API_URL}/api/sessions/${sessionId}`); const url = new URL(`${CLAW_API_URL}/api/sessions/${sessionId}`);
url.searchParams.set("account_id", account.id); url.searchParams.set("account_id", account.id);
const response = await fetch(url, { const response = await fetch(url, {
@@ -209,13 +209,15 @@ function SessionFilesPanel({
useEffect(() => { useEffect(() => {
let cancelled = false; let cancelled = false;
async function loadFiles() { async function loadFiles() {
const activeSessionId = const activeSessionId = normalizeSessionId(
sessionId ?? window.localStorage.getItem("claw.activeSessionId"); sessionId ?? window.localStorage.getItem("claw.activeSessionId"),
);
if (!activeSessionId) { if (!activeSessionId) {
setPayload({ input: [], output: [] }); setPayload({ input: [], output: [] });
setStatus("当前还没有可用会话。"); setStatus("当前还没有可用会话。");
return; return;
} }
window.localStorage.setItem("claw.activeSessionId", activeSessionId);
setStatus("正在读取聊天中的文件..."); setStatus("正在读取聊天中的文件...");
try { try {
const url = new URL("/api/claw/files", window.location.origin); const url = new URL("/api/claw/files", window.location.origin);
@@ -280,6 +282,12 @@ function SessionFilesPanel({
); );
} }
function normalizeSessionId(value: unknown) {
if (typeof value !== "string") return null;
const trimmed = value.trim();
return trimmed || null;
}
function FileSection({ function FileSection({
title, title,
files, files,
@@ -519,8 +519,9 @@ function useComposerContextStatus() {
}); });
useEffect(() => { useEffect(() => {
if (typeof latestSessionId === "string" && latestSessionId) { const cleanLatestSessionId = normalizeSessionId(latestSessionId);
window.localStorage.setItem("claw.activeSessionId", latestSessionId); if (cleanLatestSessionId) {
window.localStorage.setItem("claw.activeSessionId", cleanLatestSessionId);
window.setTimeout(() => { window.setTimeout(() => {
window.dispatchEvent(new Event("claw-sessions-changed")); window.dispatchEvent(new Event("claw-sessions-changed"));
}, 0); }, 0);
@@ -548,11 +549,15 @@ function useComposerContextStatus() {
const statePayload = stateResponse.ok const statePayload = stateResponse.ok
? ((await stateResponse.json()) as ClawState) ? ((await stateResponse.json()) as ClawState)
: {}; : {};
const sessionId = const sessionId = normalizeSessionId(
(typeof latestSessionId === "string" && latestSessionId) || normalizeSessionId(latestSessionId) ||
window.localStorage.getItem("claw.activeSessionId") || normalizeSessionId(
statePayload.active_session_id || window.localStorage.getItem("claw.activeSessionId"),
null; ) ||
normalizeSessionId(statePayload.active_session_id),
);
if (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) {
@@ -680,6 +685,12 @@ function readMetadataValue(metadata: unknown, key: string): unknown {
return undefined; return undefined;
} }
function normalizeSessionId(value: unknown) {
if (typeof value !== "string") return null;
const trimmed = value.trim();
return trimmed || null;
}
function formatCompactTokenCount(value: number) { function formatCompactTokenCount(value: number) {
if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`; if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`;
if (value >= 1_000) return `${(value / 1_000).toFixed(1)}K`; if (value >= 1_000) return `${(value / 1_000).toFixed(1)}K`;
@@ -357,15 +357,20 @@ function useSidebarSessions(open: boolean) {
repository: ExportedMessageRepository, repository: ExportedMessageRepository,
) => void, ) => void,
) { ) {
window.localStorage.setItem("claw.activeSessionId", sessionId); const cleanSessionId = normalizeSessionId(sessionId);
if (!cleanSessionId) return;
window.localStorage.setItem("claw.activeSessionId", cleanSessionId);
setLoadingSessionId(sessionId); setLoadingSessionId(sessionId);
try { try {
const response = await fetch(`/api/claw/sessions/${sessionId}`, { const response = await fetch(`/api/claw/sessions/${cleanSessionId}`, {
cache: "no-store", cache: "no-store",
}); });
if (!response.ok) return; if (!response.ok) return;
const payload = (await response.json()) as ReplaySessionPayload; const payload = (await response.json()) as ReplaySessionPayload;
replaySession(sessionId, toReplayRepository(payload, sessionId)); replaySession(
cleanSessionId,
toReplayRepository(payload, cleanSessionId),
);
} finally { } finally {
setLoadingSessionId(null); setLoadingSessionId(null);
} }
@@ -430,13 +435,15 @@ function AccountMenu({
: []; : [];
setState(nextState); setState(nextState);
setSessions(nextSessions); setSessions(nextSessions);
const activeSessionId = const activeSessionId = normalizeSessionId(
window.localStorage.getItem("claw.activeSessionId") ?? window.localStorage.getItem("claw.activeSessionId") ??
nextState?.active_session_id; nextState?.active_session_id,
);
if (!activeSessionId) { if (!activeSessionId) {
setActiveSession(null); setActiveSession(null);
return; return;
} }
window.localStorage.setItem("claw.activeSessionId", activeSessionId);
const response = await fetch(`/api/claw/sessions/${activeSessionId}`, { const response = await fetch(`/api/claw/sessions/${activeSessionId}`, {
cache: "no-store", cache: "no-store",
}); });
@@ -610,6 +617,12 @@ function AccountMenu({
); );
} }
function normalizeSessionId(value: unknown) {
if (typeof value !== "string") return null;
const trimmed = value.trim();
return trimmed || null;
}
function aggregateUsageByModel(sessions: ClawSessionSummary[]) { function aggregateUsageByModel(sessions: ClawSessionSummary[]) {
const byModel = new Map< const byModel = new Map<
string, string,