chat: retire the old saved-session store (Stage 4a)
Deletes all session persistence that isn't Claude's native transcript store, per the "only harness-native session management survives" rule. Backend: delete api/pi/storage.ts (meta.json+messages.json file store), the api/saved-sessions router (+ unmount), the /pi/sessions REST endpoints, and the storage.save/loadSession calls in the chat WS handler (in-memory session-manager stays for live turns; no disk persistence — Claude's transcript is the record). Also drops the Postgres saved_sessions layer: schema/chat.ts, queries/saved-sessions.ts, its types and re-exports. Frontend: delete state/useSavedSessions, ChatList, and the ChatHistory Widget (all pure saved-session UI); slim ChatHeader to a label; strip the auto-load-latest + Save wiring from ChatPanelWrapper and ChatDetailPanel; drop the old resume path from useChat and SessionListPage; remove the /chat/saved/:id route and the useInitialData prefetch. Behavior removed (intended): the Save-session button, email/project panels auto-resuming the last chat, and /chat/saved/:id. /chat itself is unchanged — already fully on Claude transcripts. The orphaned saved_sessions Postgres table is dropped on the next `bun db:push`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,6 @@ export { useRecentModels } from './useRecentModels';
|
||||
export { usePlans } from './usePlans';
|
||||
export { useLandingPage } from './useLandingPage';
|
||||
export { useServerSettings } from './useServerSettings';
|
||||
export { useSavedSessions } from './useSavedSessions';
|
||||
export type { UseSavedSessionsType, SavedSessionEntry } from './useSavedSessions';
|
||||
export { useUserApps } from './useUserApps';
|
||||
export type { AppManifest } from './useUserApps';
|
||||
export { useServerEnvironment } from './useServerEnvironment';
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import { useAuth } from 'hooks/useAuth';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export type SavedSessionEntry = {
|
||||
id: number;
|
||||
provider: string;
|
||||
context: string | null;
|
||||
contextId: string | null;
|
||||
title: string;
|
||||
summary: string;
|
||||
cwd: string;
|
||||
cost: { inputTokens: number; outputTokens: number; totalUSD: number };
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
type RawMessage = {
|
||||
id: string;
|
||||
timestamp: number;
|
||||
role: 'user' | 'assistant' | 'tool';
|
||||
text?: string;
|
||||
model?: string;
|
||||
cost?: { inputTokens: number; outputTokens: number; totalUSD: number };
|
||||
toolCallId?: string;
|
||||
toolName?: string;
|
||||
toolInput?: Record<string, unknown>;
|
||||
output?: string;
|
||||
isError?: boolean;
|
||||
};
|
||||
|
||||
export type ResumeResult = {
|
||||
context: string | null;
|
||||
cwd: string;
|
||||
model: string;
|
||||
cost: { inputTokens: number; outputTokens: number; totalUSD: number };
|
||||
rawMessages: RawMessage[];
|
||||
};
|
||||
|
||||
export type { RawMessage };
|
||||
|
||||
export function messagesToTranscript(messages: RawMessage[]): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
for (const msg of messages) {
|
||||
if (msg.role === 'user' && msg.text) {
|
||||
lines.push(`User: ${msg.text}`);
|
||||
} else if (msg.role === 'assistant' && msg.text) {
|
||||
lines.push(`Assistant: ${msg.text}`);
|
||||
} else if (msg.role === 'tool') {
|
||||
const outcome = msg.isError ? `Error: ${msg.output}` : msg.output ? 'Success' : 'Pending';
|
||||
lines.push(`Tool [${msg.toolName}]: ${outcome}`);
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n\n');
|
||||
}
|
||||
|
||||
export function useSavedSessions() {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
const { data: sessions = [], isLoading } = useQuery<SavedSessionEntry[]>({
|
||||
queryKey: ['SAVED_SESSIONS'],
|
||||
enabled: isAuthenticated,
|
||||
queryFn: () => client.get<SavedSessionEntry[]>('/saved-sessions'),
|
||||
});
|
||||
|
||||
async function saveSession(sessionId: string) {
|
||||
try {
|
||||
const result = await client.post<SavedSessionEntry>('/saved-sessions', { sessionId });
|
||||
queryClient.invalidateQueries({ queryKey: ['SAVED_SESSIONS'] });
|
||||
toast.success('Session saved');
|
||||
return result;
|
||||
} catch (err) {
|
||||
toast.error('Failed to save session');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteSavedSession(id: number) {
|
||||
await client.delete(`/saved-sessions/${id}`);
|
||||
queryClient.setQueryData<SavedSessionEntry[]>(['SAVED_SESSIONS'], (prev) => prev?.filter((s) => s.id !== id) ?? []);
|
||||
}
|
||||
|
||||
async function resumeSession(id: number) {
|
||||
return client.post<ResumeResult>(`/saved-sessions/${id}/resume`);
|
||||
}
|
||||
|
||||
async function updateSessionMessages(savedId: number, sessionId: string, resumedMessageCount?: number) {
|
||||
await client.put(`/saved-sessions/${savedId}`, { sessionId, resumedMessageCount });
|
||||
}
|
||||
|
||||
return {
|
||||
sessions,
|
||||
isLoading,
|
||||
saveSession,
|
||||
deleteSavedSession,
|
||||
resumeSession,
|
||||
updateSessionMessages,
|
||||
};
|
||||
}
|
||||
|
||||
export type UseSavedSessionsType = ReturnType<typeof useSavedSessions>;
|
||||
Reference in New Issue
Block a user