import { create } from 'zustand'; import type { HumanSession, Mail, RenameProposal, Session, SessionBudget } from '../types'; import * as api from '../api/client'; interface SessionState { sessions: HumanSession[]; currentSession: Session | null; currentSessionMails: Mail[]; loading: boolean; error: string | null; /** Agent 在正文里提的改名建议;null = 无待处理建议 */ renameProposal: RenameProposal | null; fetchSessions: () => Promise; selectSession: (sessionId: string) => Promise; clearSession: () => void; /** 重新拉取当前会话的改名建议(收到新邮件时调) */ refreshRenameProposal: () => Promise; /** 接受建议:改名成功后清掉提示条并刷新会话 */ acceptRename: () => Promise; /** 驳回建议:服务端记下来,不再反复弹同一个 */ dismissRename: () => Promise; /** 本任务的往返预算;null = 尚未取到 */ budget: SessionBudget | null; /** 改本会话预算(对话页里随时调)。reset 把已用次数归零。 */ setBudget: (patch: { max_rounds?: number; reset?: boolean }) => Promise; /** 重新拉取预算(Agent 发信后剩余会变) */ refreshBudget: () => Promise; /** 归档后若正查看该会话则退出 */ dropSessionIfCurrent: (sessionId: string) => void; } export const useSessionStore = create((set, get) => ({ sessions: [], currentSession: null, currentSessionMails: [], renameProposal: null, budget: null, loading: false, error: null, fetchSessions: async () => { set({ loading: true, error: null }); try { const { sessions } = await api.getHumanSessions(); set({ sessions: sessions || [], loading: false }); } catch (err) { set({ error: err instanceof Error ? err.message : String(err), loading: false }); } }, selectSession: async sessionId => { set({ loading: true, error: null, renameProposal: null, budget: null }); try { const detail = await api.getSessionDetail(sessionId); set({ currentSession: detail.session, currentSessionMails: detail.mails || [], loading: false }); // 改名建议与预算单独取:拿不到不该让整个会话打不开 get().refreshRenameProposal(); get().refreshBudget(); } catch (err) { set({ error: err instanceof Error ? err.message : String(err), loading: false }); } }, refreshRenameProposal: async () => { const id = get().currentSession?.session_id; if (!id) return; try { const { proposal } = await api.getRenameProposal(id); // 期间用户可能已切走,别把上一会话的建议贴到新会话上 if (get().currentSession?.session_id === id) { set({ renameProposal: proposal }); } } catch { // 建议是锦上添花,失败静默 } }, acceptRename: async () => { const s = get(); const id = s.currentSession?.session_id; const alias = s.renameProposal?.alias; if (!id || !alias) return; try { await api.updateSessionAlias(id, alias); set({ renameProposal: null }); // 别名变了,会话详情与列表里的地址都要跟着更新 await get().selectSession(id); await get().fetchSessions(); } catch (err) { // 别名被别人占用(409)等情况要让用户看到,不能默默失败 set({ error: err instanceof Error ? err.message : String(err) }); } }, dismissRename: async () => { const id = get().currentSession?.session_id; if (!id) return; // 先隐藏提示条再发请求:驳回是纯本地意图,等一个往返才消失显得迟钝 set({ renameProposal: null }); try { await api.dismissRenameProposal(id); } catch { // 记不下来最坏的后果是下次打开又弹一次,不值得打扰用户 } }, refreshBudget: async () => { const id = get().currentSession?.session_id; if (!id) return; try { const b = await api.getSessionBudget(id); // 期间用户可能已切走,别把上一会话的预算贴到新会话上 if (get().currentSession?.session_id === id) set({ budget: b }); } catch { // 预算读不到不影响看邮件,静默 } }, setBudget: async patch => { const id = get().currentSession?.session_id; if (!id) return; try { const b = await api.updateSessionBudget(id, patch); set({ budget: b }); // 列表里也显示预算,改完要跟着刷新 await get().fetchSessions(); } catch (err) { set({ error: err instanceof Error ? err.message : String(err) }); } }, clearSession: () => set({ currentSession: null, currentSessionMails: [], renameProposal: null, budget: null }), dropSessionIfCurrent: sessionId => { if (get().currentSession?.session_id === sessionId) { set({ currentSession: null, currentSessionMails: [], renameProposal: null, budget: null }); } set(state => ({ sessions: state.sessions.filter(s => s.session_id !== sessionId) })); } }));