(null);
- const resumedMessageCountRef = useRef(initialMessages?.length ?? 0);
+ // Refresh the /chat list — Claude has just written/appended this session's transcript.
const onTurnComplete = useCallback(() => {
- // Refresh the /chat list — Claude has just written/appended this session's transcript.
invalidateClaudeSessions();
- const id = savedIdRef.current;
- const sid = chatSessionRef.current;
- if (id != null && sid) {
- const count = resumedMessageCountRef.current;
- updateSessionMessages(id, sid, count > 0 ? count : undefined).catch(() => {});
- }
- }, [updateSessionMessages, invalidateClaudeSessions]);
+ }, [invalidateClaudeSessions]);
// context 'chat' tells the backend to run this session from the dedicated claude_sessions cwd, so
// its transcript lands in Claude's own store as an isolated project group (source of truth).
const chat = useChat(undefined, locationState?.model, { resumeSummary, resumeSessionId, initialMessages, onTurnComplete, context: 'chat' });
- chatSessionRef.current = chat.sessionId;
-
- const isSaved = savedId != null;
-
- const handleSave = async () => {
- if (!chat.sessionId || isSaving) return;
- setIsSaving(true);
- try {
- const result = await saveSession(chat.sessionId);
- setSavedId(result.id);
- } finally {
- setIsSaving(false);
- }
- };
const sandboxed = !isSuperAdmin;
// Run the session in the pwd chosen in the Sessions panel; null → backend default (claude_sessions).
@@ -145,15 +89,7 @@ function NewChat({ resumeSummary, resumeSessionId, initialMessages, savedId: ini
return (
-
+
{
);
}
- const savedId = selected.id.startsWith('saved:') ? Number(selected.id.replace('saved:', '')) : undefined;
-
return (
);
};
diff --git a/src/workspaces/officerdev/src/hooks/useChat.ts b/src/workspaces/officerdev/src/hooks/useChat.ts
index 75454413..573ac52e 100644
--- a/src/workspaces/officerdev/src/hooks/useChat.ts
+++ b/src/workspaces/officerdev/src/hooks/useChat.ts
@@ -207,79 +207,6 @@ export function useChat(initialSessionId?: string, initialModel?: string | null,
const { isConnected, send } = useChatWebSocket({ url: wsUrl, onMessage: handleMessage });
- // Load messages from server on mount when resuming a session
- useEffect(() => {
- if (storage) {
- storage
- .load()
- .then(({ sessionId: sid, messages: msgs }) => {
- if (sid) {
- sessionIdRef.current = sid;
- setSessionId(sid);
- }
- if (msgs.length > 0) {
- setMessages(msgs);
- setHasStarted(true);
- }
- })
- .catch(() => {});
- return;
- }
- if (!initialSessionId) return;
- client
- .get<{ session: { messages: Message[] } }>(`/pi/sessions/${initialSessionId}`)
- .then((data) => {
- if (data.session?.messages && data.session.messages.length > 0) {
- // Convert backend Message[] to ChatMessage[]
- const chatMessages = data.session.messages.map((m: Message): ChatMessage => {
- if (m.role === 'user') {
- return { role: 'user', text: m.text || '' };
- } else if (m.role === 'assistant') {
- return { role: 'assistant', id: m.id ?? crypto.randomUUID(), text: m.text || '' };
- } else if (m.role === 'tool') {
- return {
- role: 'tool',
- toolName: m.toolName || '',
- toolInput: m.toolInput || {},
- toolCallId: m.toolCallId || '',
- output: m.output,
- isError: m.isError,
- };
- }
- return { role: 'assistant', id: crypto.randomUUID(), text: '' }; // Fallback
- });
- setMessages(chatMessages);
- setHasStarted(true);
- }
- })
- .catch(() => {});
- }, [initialSessionId]);
-
- // Debounced save messages to server
- useEffect(() => {
- if (!sessionIdRef.current || messages.length === 0) return;
-
- if (saveTimerRef.current !== null) clearTimeout(saveTimerRef.current);
-
- const sid = sessionIdRef.current;
- const snapshot = messages;
- saveTimerRef.current = window.setTimeout(() => {
- if (storage) {
- storage.save(sid, snapshot).catch(() => {});
- } else {
- client.put(`/pi/sessions/${sid}/messages`, snapshot).catch(() => {});
- }
- saveTimerRef.current = null;
- }, SAVE_DEBOUNCE_MS);
-
- return () => {
- if (saveTimerRef.current !== null) {
- clearTimeout(saveTimerRef.current);
- saveTimerRef.current = null;
- }
- };
- }, [messages]);
-
// Clean up RAF on unmount
useEffect(() => {
return () => {
diff --git a/src/workspaces/officerdev/src/index.ts b/src/workspaces/officerdev/src/index.ts
index 0eb2a1de..66a1a493 100644
--- a/src/workspaces/officerdev/src/index.ts
+++ b/src/workspaces/officerdev/src/index.ts
@@ -17,7 +17,6 @@ export {
WebpageDialog,
EmbeddableChat,
useChat,
- ChatList,
useSlashCommands,
useAttachments,
useAudioRecording,
diff --git a/src/workspaces/state/src/index.ts b/src/workspaces/state/src/index.ts
index b266f4dd..deba72a5 100644
--- a/src/workspaces/state/src/index.ts
+++ b/src/workspaces/state/src/index.ts
@@ -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';
diff --git a/src/workspaces/state/src/useSavedSessions.ts b/src/workspaces/state/src/useSavedSessions.ts
deleted file mode 100644
index e8c11286..00000000
--- a/src/workspaces/state/src/useSavedSessions.ts
+++ /dev/null
@@ -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;
- 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({
- queryKey: ['SAVED_SESSIONS'],
- enabled: isAuthenticated,
- queryFn: () => client.get('/saved-sessions'),
- });
-
- async function saveSession(sessionId: string) {
- try {
- const result = await client.post('/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(['SAVED_SESSIONS'], (prev) => prev?.filter((s) => s.id !== id) ?? []);
- }
-
- async function resumeSession(id: number) {
- return client.post(`/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;