/**
 * useAgentStream — Agent 流式事件监听 Hook
 *
 * 监听 window.metona.agent.onStreamEvent / onStateChange，将事件分发到 Zustand Store。
 *
 * 架构设计：
 * - onStateChange：迭代号追踪 + 消息卡片创建 + Trace 步骤管理 + Agent 状态映射
 *   每轮迭代只创建一个 TraceStep，状态转换时更新当前步骤的 state 字段（避免碎片化）
 * - onStreamEvent：纯内容更新（reasoning、text、tool_call、tool_result、usage、done、error）
 *   tool_result 反向搜索包含对应 toolCallId 的 TraceStep（修复工具结果丢失）
 */

import { useEffect, useRef } from 'react';
import { useAgentStore, genMsgId, type ToolCallInfo, type AgentStatus } from '@renderer/stores/agent-store';

/**
 * Agent 流式事件监听 Hook
 *
 * 在 App 根组件调用一次，自动监听当前会话的流式事件。
 */
export function useAgentStream(): void {
  const cleanupRef = useRef<(() => void) | null>(null);

  // ===== 流式内容事件 =====
  useEffect(() => {
    if (!window.metona?.agent?.onStreamEvent) return;

    // F5: text_delta rAF 批处理
    // 问题：每个 text_delta 直接调用 updateLastAssistantMessage + updateLastTraceStep，
    //       频率 30-50 次/秒，每次触发 store 更新 + React re-render。
    // 方案：累积 delta 到缓冲区，用 rAF 每帧 commit 一次，合并多次 store 写入。
    //       done/error 时立即 flush，避免最后一段 delta 丢失。
    //       新迭代卡片创建逻辑（needsNewCard）立即处理，不缓冲。
    let textDeltaBuffer = '';
    let textBufferSessionId: string | undefined;
    let textRafId: number | null = null;

    const flushTextDelta = () => {
      textRafId = null;
      if (!textDeltaBuffer) return;
      const delta = textDeltaBuffer;
      const bufferedSessionId = textBufferSessionId;
      textDeltaBuffer = '';
      textBufferSessionId = undefined;

      const store = useAgentStore.getState();
      // 会话切换保护：如果缓冲时的会话与当前会话不一致，丢弃（避免跨会话污染）
      if (bufferedSessionId && store.currentSessionId !== bufferedSessionId) return;

      store.updateLastAssistantMessage(delta);

      // 无 reasoning 模式下，将文本内容也记入 Trace thought
      const messages = store.messages;
      const lastMsg = messages[messages.length - 1];
      const steps = store.traceSteps;
      const step = steps[steps.length - 1];
      if (step && lastMsg?.role === 'assistant' && !lastMsg.reasoningContent) {
        store.updateLastTraceStep({
          thought: (step.thought ?? '') + delta,
        });
      }
    };

    // F9: reasoning_delta 的 traceSteps thought 更新走 rAF 批处理
    // 问题：reasoning_delta 每个 delta 调用 updateLastTraceStep，频率 10-30 次/秒，
    //       触发 TraceViewer 等订阅者 re-render（即使不可见也有 selector 调用开销）。
    // 方案：累积 reasoning delta 到缓冲区，rAF 每帧 commit 一次。
    //       message.reasoningContent 保持即时更新（ThoughtBlock 需实时显示思考内容）。
    //       新迭代卡片创建时的 thought 更新保持即时（新卡片需立即显示）。
    let traceThoughtBuffer = '';
    let traceRafId: number | null = null;

    const flushTraceThought = () => {
      traceRafId = null;
      if (!traceThoughtBuffer) return;
      const delta = traceThoughtBuffer;
      traceThoughtBuffer = '';
      const store = useAgentStore.getState();
      const steps = store.traceSteps;
      const step = steps[steps.length - 1];
      if (step) {
        store.updateLastTraceStep({
          thought: (step.thought ?? '') + delta,
        });
      }
    };

    const unsubscribe = window.metona.agent.onStreamEvent((event: unknown) => {
      const data = event as {
        type?: string;
        requestId?: string;
        sessionId?: string;
        iteration?: number;
        /** 事件序列号（与 MetonaStreamEvent.seq 对齐，规范必填字段） */
        seq?: number;
        /** 事件时间戳（与 MetonaStreamEvent.timestamp 对齐，规范必填字段） */
        timestamp?: number;
        /** 当前 run 的唯一标识（前端用于过滤旧流事件，abort 后重发场景） */
        runId?: string;
        delta?: string;
        content?: string;
        /** 工具调用增量（流式参数拼接） */
        toolCallDelta?: { index: number; name?: string; argsDelta?: string };
        toolCall?: { id: string; name: string; args: Record<string, unknown> };
        toolResult?: { toolCallId: string; success: boolean; result?: unknown; error?: string; durationMs?: number };
        usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };
        /** v0.3.18 修复: 上下文压缩事件数据 */
        savedTokens?: number;
        originalTokens?: number;
        compressedTokens?: number;
        error?: { code: string; message: string };
        state?: string;
      };

      // 会话过滤：忽略非当前会话的事件（防止切换会话后旧事件污染）
      if (data.sessionId && data.sessionId !== useAgentStore.getState().currentSessionId) return;

      // H-6/C-4: runId 过滤 — 忽略旧 run 的事件（abort 后重发场景）
      // 修复 abort 后旧 DONE 污染新 run：currentRunId=null 时忽略终止事件
      const currentState = useAgentStore.getState();
      if (data.runId) {
        if (currentState.currentRunId) {
          // currentRunId 已设置：只接受匹配的事件
          if (data.runId !== currentState.currentRunId) return;
        } else {
          // currentRunId 未设置：
          // done/error 是终止事件 — currentRunId 为 null 说明已被 abort 或尚未开始，
          // 忽略旧 run 的延迟终止事件，避免错误地 setStreaming(false)
          if (data.type === 'done' || data.type === 'error') return;
          // 首个活跃事件，设置 currentRunId
          currentState.setCurrentRunId(data.runId);
        }
      }

      // 每次都从 store 读取最新状态（避免闭包捕获过期快照）
      const getStore = () => useAgentStore.getState();

      switch (data.type) {
        // 推理内容增量
        case 'reasoning_delta':
          if (data.delta) {
            // C-1: 如果 delta 属于新迭代，先创建新卡片（不依赖 stateChange 到达顺序）
            if (data.iteration != null) {
              const msgs = getStore().messages;
              const last = msgs[msgs.length - 1];
              const needsNewCard = !last || last.role !== 'assistant' ||
                (last.iteration != null && last.iteration !== data.iteration);
              if (needsNewCard) {
                getStore().addMessage({
                  id: genMsgId('assistant'),
                  role: 'assistant',
                  content: '',
                  reasoningContent: data.delta,
                  timestamp: Date.now(),
                  iteration: data.iteration,
                });
                if (data.iteration !== getStore().currentIteration) {
                  getStore().setCurrentIteration(data.iteration);
                }
                // 同步更新当前 Trace 步骤的 thought 字段
                const ts = getStore().traceSteps;
                const step = ts[ts.length - 1];
                if (step) {
                  getStore().updateLastTraceStep({ thought: (step.thought ?? '') + data.delta });
                }
                break;
              }
            }
            const messages = getStore().messages;
            const lastMsg = messages[messages.length - 1];
            if (lastMsg?.role === 'assistant') {
              // 追加到最后一条 assistant 消息（不可变更新）
              getStore().updateMessage(lastMsg.id, {
                reasoningContent: (lastMsg.reasoningContent ?? '') + data.delta,
              });
            } else {
              // 还没有 assistant 消息，先创建一条（仅含思考内容）
              getStore().addMessage({
                id: genMsgId('assistant'),
                role: 'assistant',
                content: '',
                reasoningContent: data.delta,
                timestamp: Date.now(),
                iteration: getStore().currentIteration || undefined,
              });
            }

            // F9: traceSteps thought 更新走 rAF 批处理（减少 TraceViewer re-render 频率）
            // message.reasoningContent 保持即时更新（ThoughtBlock 需实时显示）
            traceThoughtBuffer += data.delta;
            if (traceRafId === null) {
              traceRafId = requestAnimationFrame(flushTraceThought);
            }
          }
          break;

        // 文本增量
        case 'text_delta':
          if (data.delta) {
            // C-1: 如果 delta 属于新迭代，先创建新卡片（不依赖 stateChange 到达顺序）
            if (data.iteration != null) {
              const msgs = getStore().messages;
              const last = msgs[msgs.length - 1];
              const needsNewCard = !last || last.role !== 'assistant' ||
                (last.iteration != null && last.iteration !== data.iteration);
              if (needsNewCard) {
                // F5: 新迭代前先 flush 旧缓冲区（属于上一条消息的 delta）
                if (textRafId !== null) {
                  cancelAnimationFrame(textRafId);
                  flushTextDelta();
                }
                getStore().addMessage({
                  id: genMsgId('assistant'),
                  role: 'assistant',
                  content: data.delta,
                  timestamp: Date.now(),
                  iteration: data.iteration,
                });
                if (data.iteration !== getStore().currentIteration) {
                  getStore().setCurrentIteration(data.iteration);
                }
                break;
              }
            }
            // F5: 累积 delta 到缓冲区，用 rAF 每帧 commit 一次
            // 首次缓冲时记录 sessionId（用于会话切换保护）
            if (textDeltaBuffer === '') {
              textBufferSessionId = data.sessionId;
            }
            textDeltaBuffer += data.delta;
            if (textRafId === null) {
              textRafId = requestAnimationFrame(flushTextDelta);
            }
          }
          break;

        // M-1: 工具调用增量（流式参数拼接）— 仅更新 UI 占位，完整调用由 tool_call_complete 处理
        case 'tool_call_delta':
          if (data.toolCallDelta) {
            const msgs = getStore().messages;
            const lastAssistant = msgs[msgs.length - 1];
            const { index, name } = data.toolCallDelta;
            // 如果最后一条 assistant 消息还没有该 index 的占位工具调用，添加一个 pending 占位
            if (lastAssistant?.role === 'assistant' && name) {
              const existing = (lastAssistant.toolCalls ?? []).find((_, i) => i === index);
              if (!existing) {
                const placeholder: ToolCallInfo = {
                  id: `tc_pending_${index}`,
                  name,
                  args: {},
                  status: 'pending',
                };
                getStore().updateMessage(lastAssistant.id, {
                  toolCalls: [...(lastAssistant.toolCalls ?? []), placeholder],
                });
              }
            }
          }
          break;

        // 工具调用完成
        case 'tool_call_complete':
          if (data.toolCall) {
            const tc: ToolCallInfo = {
              id: data.toolCall.id,
              name: data.toolCall.name,
              args: data.toolCall.args,
              status: 'executing',
            };

            // 追加到当前 assistant 消息（替换同 index 的 pending 占位）
            const msgs = getStore().messages;
            const lastAssistant = msgs[msgs.length - 1];
            if (lastAssistant?.role === 'assistant') {
              const existingTcs = lastAssistant.toolCalls ?? [];
              // 检查是否有同 index 的 pending 占位需要替换
              const pendingIdx = existingTcs.findIndex((t) => t.id.startsWith('tc_pending_'));
              if (pendingIdx >= 0) {
                const updated = [...existingTcs];
                updated[pendingIdx] = tc;
                getStore().updateMessage(lastAssistant.id, { toolCalls: updated });
              } else {
                getStore().updateMessage(lastAssistant.id, {
                  toolCalls: [...existingTcs, tc],
                });
              }
            }

            // 更新当前 Trace 步骤（添加工具调用信息）
            const curSteps = getStore().traceSteps;
            const lastStep = curSteps[curSteps.length - 1];
            if (lastStep) {
              getStore().updateLastTraceStep({
                toolCalls: [...(lastStep.toolCalls ?? []), tc],
              });
            }
          }
          break;

        // 工具执行结果
        case 'tool_result': {
          if (data.toolResult) {
            const msgs = getStore().messages;
            const lastMsg = msgs[msgs.length - 1];
            if (lastMsg?.role === 'assistant' && lastMsg?.toolCalls) {
              const updatedToolCalls = lastMsg.toolCalls.map((tc) =>
                tc.id === data.toolResult!.toolCallId
                  ? {
                      ...tc,
                      status: data.toolResult!.success ? 'success' as const : 'error' as const,
                      result: data.toolResult!.result,
                      error: data.toolResult!.error,
                      durationMs: data.toolResult!.durationMs,
                    }
                  : tc,
              );
              getStore().updateMessage(lastMsg.id, { toolCalls: updatedToolCalls });
            }

            // 反向搜索包含该 toolCallId 的 TraceStep（修复工具结果丢失）
            // tool_call_complete 在 THINKING 阶段写入，tool_result 在 EXECUTING 阶段到达
            // 两者可能在同一轮迭代但不同状态转换，需反向查找
            const steps = getStore().traceSteps;
            for (let i = steps.length - 1; i >= 0; i--) {
              const trace = steps[i];
              if (trace.toolCalls?.some((tc) => tc.id === data.toolResult!.toolCallId)) {
                const updatedTraceToolCalls = trace.toolCalls.map((tc) =>
                  tc.id === data.toolResult!.toolCallId
                    ? {
                        ...tc,
                        status: data.toolResult!.success ? 'success' as const : 'error' as const,
                        result: data.toolResult!.result,
                        error: data.toolResult!.error,
                        durationMs: data.toolResult!.durationMs,
                      }
                    : tc,
                );
                // L-2: 按 ID 精确匹配 traceStep（避免 iteration 碰撞）
                getStore().updateTraceStepById(trace.id, { toolCalls: updatedTraceToolCalls });
                break;
              }
            }
          }
          break;
        }

        // Token 使用统计
        case 'usage':
          if (data.usage) {
            const cur = getStore().tokenUsage;
            getStore().updateTokenUsage({
              // 累计值：所有轮次累加，只增不减
              inputTokens: cur.inputTokens + (data.usage.inputTokens ?? 0),
              outputTokens: cur.outputTokens + (data.usage.outputTokens ?? 0),
              totalTokens: cur.totalTokens + (data.usage.totalTokens ?? 0),
              // v0.3.18 修复: 单次上下文占用 — 替换不累加
              // 反映当前对话上下文的真实大小，压缩后会大幅下降
              lastInputTokens: data.usage.inputTokens ?? 0,
            });

            // 同步更新当前 Trace 步骤的 token 用量
            getStore().updateLastTraceStep({
              tokenUsage: {
                promptTokens: data.usage.inputTokens ?? 0,
                completionTokens: data.usage.outputTokens ?? 0,
                totalTokens: data.usage.totalTokens ?? 0,
              },
            });
          }
          break;

        // v0.3.18 修复: 上下文压缩事件 — 记录节省的 token，供 UI 显示压缩效果
        case 'compressed': {
          getStore().applyCompression(data.savedTokens ?? 0);
          break;
        }

        // 流结束
        case 'done':
          // F5: 流结束前立即 flush 缓冲区，避免最后一段 delta 丢失
          if (textRafId !== null) {
            cancelAnimationFrame(textRafId);
            flushTextDelta();
          }
          // F9: flush traceThought 缓冲区，避免最后一段 reasoning delta 丢失
          if (traceRafId !== null) {
            cancelAnimationFrame(traceRafId);
            flushTraceThought();
          }
          getStore().setStreaming(false);
          getStore().setCurrentRunId(null);
          // 不覆盖 error 状态 — error handler 已设置 agentStatus='error'
          if (getStore().agentStatus !== 'error') {
            getStore().setAgentStatus('idle');
          }
          // 标记最后一个 Trace 步骤为已完成
          getStore().updateLastTraceStep({ completedAt: Date.now() });
          getStore().saveTraceData();
          break;

        // 错误
        case 'error':
          // F5: 错误前立即 flush 缓冲区，保留已接收的内容
          if (textRafId !== null) {
            cancelAnimationFrame(textRafId);
            flushTextDelta();
          }
          // F9: flush traceThought 缓冲区，保留已接收的 reasoning 内容
          if (traceRafId !== null) {
            cancelAnimationFrame(traceRafId);
            flushTraceThought();
          }
          getStore().setStreaming(false);
          getStore().setCurrentRunId(null);
          getStore().setAgentStatus('error');
          getStore().updateLastTraceStep({ completedAt: Date.now() });
          // v0.3.17: 对 content_filtered 错误码显示更友好的提示
          const errorCode = data.error?.code;
          const errorMessage = data.error?.message ?? '未知错误';
          getStore().addMessage({
            id: genMsgId('error'),
            role: 'system',
            content: errorCode === 'content_filtered'
              ? `⚠️ ${errorMessage}`
              : `错误: ${errorMessage}`,
            timestamp: Date.now(),
          });
          break;
      }
    });

    cleanupRef.current = unsubscribe;

    return () => {
      // F5: 组件卸载时清理挂起的 rAF，并 flush 残留 delta（保留已接收内容）
      if (textRafId !== null) {
        cancelAnimationFrame(textRafId);
        flushTextDelta();
      }
      // F9: 清理 traceThought 的 rAF
      if (traceRafId !== null) {
        cancelAnimationFrame(traceRafId);
        flushTraceThought();
      }
      cleanupRef.current?.();
    };
  }, []);

  // ===== 状态变化事件（迭代追踪 + Trace 步骤 + 消息卡片） =====
  useEffect(() => {
    if (!window.metona?.agent?.onStateChange) return;

    const unsubscribe = window.metona.agent.onStateChange((state: unknown) => {
      const data = state as {
        sessionId?: string;
        iteration?: number;
        state?: string;
        previous?: string;
        current?: string;
        runId?: string;
      };

      const store = useAgentStore.getState();

      // 会话过滤：忽略非当前会话的状态变化
      if (data.sessionId && data.sessionId !== store.currentSessionId) return;

      // H-6/C-4: runId 过滤 — 忽略旧 run 的状态变化
      // 修复 abort 后旧 TERMINATED 污染：currentRunId=null 时只有 INIT 设置 currentRunId，其他忽略
      if (data.runId) {
        if (store.currentRunId) {
          // currentRunId 已设置：只接受匹配的状态变化
          if (data.runId !== store.currentRunId) return;
        } else {
          // currentRunId 未设置：
          // 只有 INIT 是新 run 的第一个状态，设置 currentRunId；
          // 其他状态（如旧 run 的 TERMINATED）忽略，避免污染
          if (data.state === 'INIT') {
            store.setCurrentRunId(data.runId);
          } else {
            return;
          }
        }
      }

      // --- 迭代号更新 + 新消息卡片创建 ---
      if (data.iteration != null && data.iteration !== store.currentIteration) {
        const prevIteration = store.currentIteration;
        store.setCurrentIteration(data.iteration);

        // M-7: 统一所有迭代的卡片创建路径（包括首轮）
        // 迭代号增大 → 新一轮 ReAct 迭代开始，创建新的 assistant 消息卡片
        // 如果最后一条已经是当前迭代的 assistant 卡片（delta handler 提前创建），不重复
        if (data.iteration > prevIteration) {
          const messages = store.messages;
          const lastMsg = messages[messages.length - 1];
          const isAlreadyCurrentIteration = lastMsg?.role === 'assistant' && lastMsg.iteration === data.iteration;

          if (!isAlreadyCurrentIteration) {
            // 首轮不要求上一轮有内容（上一轮是用户消息）
            const isFirstIteration = prevIteration === 0;
            const prevHasContent = lastMsg?.role === 'assistant' &&
              (lastMsg.content || lastMsg.toolCalls?.length || lastMsg.reasoningContent);

            if (isFirstIteration || prevHasContent) {
              store.addMessage({
                id: genMsgId('assistant'),
                role: 'assistant',
                content: '',
                timestamp: Date.now(),
                iteration: data.iteration,
              });
            }
          }
        }
      }

      // --- Trace 步骤管理（每轮迭代一个步骤，状态转换时追加到 states 数组）---
      if (data.state && data.iteration != null) {
        // 跳过 INIT (iteration=0)：引擎初始化阶段无实际内容，不创建 trace step
        if (data.state === 'INIT' && data.iteration === 0) {
          // 仍需更新 Agent 状态映射
        } else {
          const traceSteps = store.traceSteps;
          const lastStep = traceSteps[traceSteps.length - 1];

          // 判断是否需要创建新步骤：同一 runId + 同一迭代的首次状态创建新步骤
          // 后续状态转换（EXECUTING/OBSERVING等）追加到 states 数组
          // runId 判定：防止跨 run 的事件被误判为同一迭代（如 traceSteps 未清空时残留的旧 step）
          const isSameIteration = lastStep && lastStep.iteration === data.iteration && lastStep.runId === data.runId;

          if (isSameIteration) {
            // 同一迭代内的状态转换 → 追加状态到 states 数组，更新当前 state
            const currentStates = lastStep.states ?? [lastStep.state];
            if (currentStates[currentStates.length - 1] !== data.state) {
              store.updateLastTraceStep({
                state: data.state,
                states: [...currentStates, data.state],
                // MT-2 修复: TERMINATED 状态到达时，标记步骤为已完成
                // 防止 abort 后最后一个步骤永远显示转圈
                ...(data.state === 'TERMINATED' ? { completedAt: Date.now() } : {}),
              });
            }
          } else {
            // MT-2 修复: 当 state === 'TERMINATED' 且 iteration 不匹配时，
            // 说明 abort 发生在循环条件检查时，currentIteration 已自增但没有对应 step。
            // 此时不应创建孤立的只含 TERMINATED 的步骤，而是更新最后一个步骤的 states 数组
            // runId 一致性检查：仅当 lastStep 属于当前 run 时才 append，避免污染上一条消息的 step
            if (data.state === 'TERMINATED' && lastStep && lastStep.runId === data.runId) {
              const currentStates = lastStep.states ?? [lastStep.state];
              if (currentStates[currentStates.length - 1] !== 'TERMINATED') {
                store.updateLastTraceStep({
                  state: 'TERMINATED',
                  states: [...currentStates, 'TERMINATED'],
                  completedAt: Date.now(),
                });
              }
            } else if (data.state === 'TERMINATED' && (!lastStep || lastStep.runId !== data.runId)) {
              // 跨 run 的孤立 TERMINATED 事件：上一条消息已结束/已 abort，没有当前 run 的 step 可更新
              // 不创建孤立的只含 TERMINATED 的 step（无意义），仅更新 Agent 状态
            } else {
              // 新迭代 → 标记上一步完成（仅当 lastStep 属于当前 run），创建新步骤
              if (lastStep && !lastStep.completedAt && lastStep.runId === data.runId) {
                store.updateLastTraceStep({ completedAt: Date.now() });
              }
              store.addTraceStep({
                id: `trace_${data.iteration}_${data.state}_${Date.now()}`,
                iteration: data.iteration,
                state: data.state,
                states: [data.state],
                startedAt: Date.now(),
                runId: data.runId,
              });
            }
          }
        }

        // --- Agent 状态映射 ---
        // INIT 不映射 — 避免 engine 的 transitionTo(INIT) 覆盖 sendMessage 设置的 'thinking' 状态
        const stateToStatus: Record<string, AgentStatus> = {
          THINKING: 'thinking',
          EXECUTING: 'executing',
          PARSING: 'thinking',
          OBSERVING: 'thinking', // L-4: 观察工具结果更接近"思考"而非"执行"
          REFLECTING: 'thinking',
          COMPRESSING: 'thinking',
          TERMINATED: 'idle',
        };
        const newStatus = stateToStatus[data.state];
        if (newStatus) {
          store.setAgentStatus(newStatus);
        }
      }
    });

    return () => unsubscribe();
  }, []);

  // 监听 Provider 切换通知
  useEffect(() => {
    if (!window.metona?.agent?.onProviderSwitched) return;

    const unsubscribe = window.metona.agent.onProviderSwitched((data: unknown) => {
      const { from, to, reason, sessionId } = data as { from?: string; to?: string; reason?: string; sessionId?: string };
      // L-3: 仅在当前会话中显示 Provider 切换消息
      const store = useAgentStore.getState();
      if (sessionId && store.currentSessionId && sessionId !== store.currentSessionId) return;
      store.addMessage({
        id: genMsgId('system'),
        role: 'system',
        content: `Provider 已切换: ${from ?? '未知'} → ${to ?? '未知'}${reason ? ` (${reason})` : ''}`,
        timestamp: Date.now(),
      });
    });

    return () => unsubscribe();
  }, []);

  // v0.3.17: 监听配置变更广播，实时更新 store 中的配置字段
  // 解决场景：用户在 SettingsModal 修改 agent.maxIterations 后，详情栏分母不刷新
  useEffect(() => {
    if (!window.metona?.config?.onChanged) return;

    const unsubscribe = window.metona.config.onChanged((data: { key: string; value: unknown }) => {
      const store = useAgentStore.getState();
      const { key, value } = data;

      // 按需更新 store 中缓存的配置字段
      if (key === 'agent.maxIterations' && typeof value === 'number') {
        store.setMaxIterations(value);
      }
      // 其他 agent.* 配置项若 store 有对应字段，可在此扩展
    });

    return () => unsubscribe();
  }, []);
}
