@blade-hq/agent-react 2610.0.0-beta.8 → 2610.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -2
- package/dist/components/AgentLoopBlock.d.ts +1 -4
- package/dist/components/AskUserQuestionBlock.d.ts +9 -0
- package/dist/components/AssistantTurnBlock.d.ts +20 -6
- package/dist/components/ChatInput.d.ts +4 -1
- package/dist/components/ChatSurface.d.ts +6 -1
- package/dist/components/ContextCard.d.ts +20 -0
- package/dist/components/MarkdownContent.d.ts +2 -0
- package/dist/components/MessageList.d.ts +2 -1
- package/dist/components/PlanUpdateBlock.d.ts +31 -0
- package/dist/components/SessionMemoryToggle.d.ts +19 -0
- package/dist/components/ToolCallBlock.d.ts +4 -2
- package/dist/components/UserMessageBubble.d.ts +1 -1
- package/dist/components/WhatIfUserBubble.d.ts +7 -0
- package/dist/components/display-utils.d.ts +10 -0
- package/dist/context.d.ts +1 -0
- package/dist/embed/entry.d.ts +13 -0
- package/dist/hooks/use-agent-session.d.ts +5 -0
- package/dist/hooks/use-message-pin.d.ts +22 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +2356 -758
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-computer-command.d.ts +34 -0
- package/dist/lib/whatif-prompt.d.ts +18 -0
- package/dist/style.css +78 -2
- package/dist/style.full.css +79 -3
- package/package.json +2 -2
- package/public-api.md +423 -7
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/hooks/use-agent-session.ts","../src/hooks/use-replay.ts","../src/hooks/use-llm-chat.ts","../src/components/AgentChat.tsx","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/shared/src/utils.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/defaultAttributes.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/Icon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/createLucideIcon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-up-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-up.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/bot.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/brain.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/check.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-down.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/circle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/copy.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/download.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file-text.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/film.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/globe.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/layers.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lightbulb.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/loader-circle.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lock-keyhole.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square-more.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/play.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/settings-2.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/sparkles.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/triangle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/x.ts","../src/lib/utils.ts","../src/components/ReplayBar.tsx","../src/components/ReplayMismatchPrompt.tsx","../src/components/ChatInput.tsx","../src/components/ConnectionBanner.tsx","../src/components/MessageList.tsx","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js","../src/components/AssistantTurnBlock.tsx","../src/components/AgentLoopBlock.tsx","../src/components/display-utils.ts","../src/components/MarkdownContent.tsx","../src/components/Shimmer.tsx","../src/components/ToolCallBlock.tsx","../src/components/AskUserQuestionBlock.tsx","../src/components/RenderErrorBoundary.tsx","../src/components/PostChatFollowupBlock.tsx","../src/components/UserMessageBubble.tsx","../src/components/ChatSurface.tsx","../src/components/LlmChat.tsx","../src/components/LlmAdvancedSettings.tsx","../src/components/ChatView.tsx","../src/index.ts"],"sourcesContent":["import type { BladeClient } from \"@blade-hq/agent-client\"\nimport { createContext, useContext, type ReactNode } from \"react\"\n\nconst BladeClientContext = createContext<BladeClient | null>(null)\n\nexport interface BladeProviderProps {\n client: BladeClient\n children: ReactNode\n}\n\n/**\n * 提供 client 级共享(一条连接、一份鉴权)。不含任何全局状态:\n * 同一 Provider 下多个 ChatView / useAgentSession 各自独立。\n */\nexport function BladeProvider({ client, children }: BladeProviderProps) {\n return <BladeClientContext.Provider value={client}>{children}</BladeClientContext.Provider>\n}\n\n/** 读取当前 BladeProvider 提供的 client。不在 Provider 内使用时抛错。 */\nexport function useBladeClient(): BladeClient {\n const client = useContext(BladeClientContext)\n if (!client) {\n throw new Error(\"useBladeClient 必须在 <BladeProvider> 内使用\")\n }\n return client\n}\n","import type {\n AgentSession,\n CreateSessionRequest,\n SessionState,\n} from \"@blade-hq/agent-client\"\nimport { useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\"\nimport { useBladeClient } from \"../context\"\n\nexport interface UseAgentSessionOptions {\n /** 不传 sessionId 时按此配置新建会话;两者都不传则新建默认对话。 */\n createOptions?: CreateSessionRequest\n /** 自动创建出的 sessionId 通过此回调交还调用方持有(重挂载复用的依据)。 */\n onSessionCreated?: (sessionId: string) => void\n}\n\nexport interface UseAgentSessionResult {\n /** 连接/创建完成前为 null。 */\n session: AgentSession | null\n /** 会话状态快照;session 未就绪时为 null。 */\n state: SessionState | null\n /** 连接或创建失败的错误。 */\n error: Error | null\n}\n\nconst EMPTY_SUBSCRIBE = () => () => {}\n\n/** 卸载后延迟释放引用:StrictMode/路由抖动的立即重挂载会在窗口内重新\n * connect(引用计数 +1),延迟 dispose 只把计数减回去,实例得以复用;\n * 真正的卸载则在窗口结束后正常释放订阅。 */\nconst DISPOSE_DELAY_MS = 1000\n\n/**\n * 连接(或创建)一个会话并订阅其状态。\n *\n * - 优先级:sessionId(连接既有会话)> createOptions(按配置新建)> 默认新建。\n * - 同一 sessionId 重复调用复用同一 AgentSession 实例(SessionHub 引用计数)。\n * - 自动创建只发生一次(含 StrictMode 双跑);创建出的 id 经 onSessionCreated\n * 交还调用方。\n *\n * **连接与 sessionId 变更分离**:连接在挂载时建立、仅在卸载时释放;只有\n * 当 sessionId 明确切到一个「与当前已连接会话不同」的 id 时才重连。这样宿主\n * (如 Vue 把自动创建的 id 写回 `session-id` 属性)回填同一个 id 不会触发\n * 拆–重连——否则会把正在流式的会话换成新实例、当前渲染整个丢失。\n */\nexport function useAgentSession(\n sessionId?: string,\n options: UseAgentSessionOptions = {},\n): UseAgentSessionResult {\n const client = useBladeClient()\n const [session, setSession] = useState<AgentSession | null>(null)\n const [error, setError] = useState<Error | null>(null)\n\n // 当前连接:id + 实例 + 一个递增的「代」号,用于让过期的异步连接结果作废。\n const connRef = useRef<{ id: string | null; session: AgentSession | null; gen: number }>({\n id: null,\n session: null,\n gen: 0,\n })\n // 自动创建去重:StrictMode 双跑时同一组件实例只创建一次会话。\n const createdIdPromiseRef = useRef<Promise<string> | null>(null)\n const onCreatedRef = useRef(options.onSessionCreated)\n onCreatedRef.current = options.onSessionCreated\n const createOptionsRef = useRef(options.createOptions)\n createOptionsRef.current = options.createOptions\n // 最新的目标 sessionId,供挂载 effect 的异步链读取(不进依赖,避免重连)。\n const sessionIdRef = useRef(sessionId)\n sessionIdRef.current = sessionId\n\n // 把「连接到某个 id / 自动创建」封成一次操作;gen 作废过期结果。\n const connect = useMemo(() => {\n return (targetId: string | undefined) => {\n const gen = ++connRef.current.gen\n const idPromise = targetId\n ? Promise.resolve(targetId)\n : // biome-ignore lint/suspicious/noAssignInExpressions: ??= 挂 ref 是 StrictMode 下\"只创建一次\"的关键\n (createdIdPromiseRef.current ??= client.sessions\n .create(createOptionsRef.current ?? {})\n .then((created) => {\n // create() 的实例引用立即归还,由下面的 connect() 统一取,避免多挂一份计数\n const id = created.sessionId\n created.dispose()\n onCreatedRef.current?.(id)\n return id\n }))\n\n idPromise\n .then((id) => client.hub.connect(id))\n .then((next) => {\n if (connRef.current.gen !== gen) {\n next.dispose() // 已被更新的连接请求取代\n return\n }\n connRef.current.id = next.sessionId\n connRef.current.session = next\n setSession(next)\n setError(null)\n })\n .catch((err) => {\n if (connRef.current.gen !== gen) return\n createdIdPromiseRef.current = null // 创建失败允许下次重试\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n }\n }, [client])\n\n // 挂载时建立连接,仅在卸载(或 client 变化)时释放。\n useEffect(() => {\n connect(sessionIdRef.current)\n return () => {\n connRef.current.gen++ // 作废在途的连接结果\n const toRelease = connRef.current.session\n connRef.current.id = null\n connRef.current.session = null\n setSession(null)\n if (toRelease) setTimeout(() => toRelease.dispose(), DISPOSE_DELAY_MS)\n }\n }, [connect])\n\n // 仅当 sessionId 明确切到「与已连接会话不同」的 id 时才重连。\n // 回填自动创建的 id(sessionId === 已连接 id)在此被跳过。\n useEffect(() => {\n if (!sessionId) return\n if (connRef.current.id === null) return // 首连仍在进行,交给挂载 effect\n if (sessionId === connRef.current.id) return\n const previous = connRef.current.session\n connRef.current.id = null\n connRef.current.session = null\n if (previous) setTimeout(() => previous.dispose(), DISPOSE_DELAY_MS)\n connect(sessionId)\n }, [sessionId, connect])\n\n const store = useMemo(\n () => ({\n subscribe: session ? (listener: () => void) => session.subscribe(listener) : EMPTY_SUBSCRIBE,\n getSnapshot: () => (session ? session.getState() : null),\n }),\n [session],\n )\n const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n\n return { session, state, error }\n}\n","import { DEFAULT_REPLAY_SPEED } from \"@blade-hq/agent-client\"\nimport type { AgentSession, ReplaySpeed, SessionState } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\n\n/** 回放中用户说的话和录制内容对不上,需要当场决定往哪走。 */\nexport interface ReplayMismatch {\n /** 用户这次实际发出的内容。 */\n actualMessage: string\n /** 源会话里录制的下一句。 */\n expectedMessage: string\n /** keep_replay:按录制内容继续;continue_replay:从这里转为真实运行。 */\n resolve: (decision: \"keep_replay\" | \"continue_replay\") => void\n}\n\nexport interface UseReplayResult {\n /** 当前会话是不是正在回放(转自主运行后为 false)。 */\n isReplay: boolean\n /** 当前播放倍速,非回放会话固定为 1。 */\n speed: ReplaySpeed\n setSpeed: (speed: ReplaySpeed) => Promise<void>\n /** 退出回放,之后的对话交给真实模型运行。 */\n exitToAutonomous: () => Promise<void>\n /** 待决策的输入冲突;处理完自动清空。 */\n mismatch: ReplayMismatch | null\n /** 能不能改回放(改回放是 owner 专属)。只读身份下仍然显示\"在回放\",但不能操作。 */\n canControl: boolean\n\n /** 当前会话能否作为回放来源(并行子智能体等场景不支持)。 */\n canReplay: boolean\n /** canReplay 为 false 时的原因,可直接展示。 */\n unsupportedReason: string | null\n /** 从当前会话派生一个回放会话,返回新会话 id。 */\n startReplay: (speed?: ReplaySpeed) => Promise<string>\n isStarting: boolean\n /** 最近一次回放操作失败的原因。 */\n error: Error | null\n}\n\n/** 正在创建回放会话的源会话;按会话共享,多个 useReplay 实例也只会建出一个。 */\nconst replayCreating = new Set<string>()\n\n/**\n * 会话回放(演示 / 彩排模式)。\n *\n * 回放会话不会自动播放:用户照常发消息,只要这句和源会话录制的下一句意图一致,\n * 服务端就按倍速重放当时的回复和工具调用,完全不调用模型;对不上就抛出\n * `mismatch` 交给界面决定。\n *\n * 同一个 hook 覆盖两种身份——传入的会话既可能是回放会话(isReplay / setSpeed /\n * exitToAutonomous / mismatch),也可能是准备被回放的源会话(canReplay /\n * startReplay)。\n *\n * 回放状态本身住在 `SessionState.replay` 里(非 React 宿主直接订阅它即可),\n * 这里只做 React 绑定:订阅快照 + 接管冲突 + 拉一次 preview。\n *\n * **注册即接管**:底层的 `replayMismatch` 只交给最先注册的处理器。调用本 hook\n * 就意味着由它接管冲突;不调用则维持 SDK 默认行为——直接转为真实运行。\n */\nexport function useReplay(session: AgentSession | null): UseReplayResult {\n const client = useBladeClient()\n const sessionId = session?.sessionId ?? null\n\n const [replay, setReplay] = useState<SessionState[\"replay\"]>(null)\n const [viewerRole, setViewerRole] = useState<SessionState[\"viewerRole\"]>(null)\n // 运行中的会话不能当回放素材(服务端也这么判),跑完要重新问一次能不能回放。\n const [sourceRunning, setSourceRunning] = useState(false)\n const [mismatch, setMismatch] = useState<ReplayMismatch | null>(null)\n // 加载中不能当\"支持\"用:慢网络下一打开就点,会绕过这道检查发出必然失败的请求。\n const [preview, setPreview] = useState<\n { status: \"loading\" } | { status: \"ready\"; supported: boolean; reason: string | null }\n >({ status: \"loading\" })\n const [isStarting, setIsStarting] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n // 还没开始回放时倍速只是个本地选择,startReplay 时才带给服务端。\n const [draftSpeed, setDraftSpeed] = useState<ReplaySpeed>(DEFAULT_REPLAY_SPEED)\n\n // 回放状态跟着会话快照走;AgentSession 在 bootstrap 时就填好了初始值。\n useEffect(() => {\n if (!session) {\n setReplay(null)\n setViewerRole(null)\n setSourceRunning(false)\n return\n }\n const pull = () => {\n const state = session.getState()\n setReplay(state.replay)\n setViewerRole(state.viewerRole)\n setSourceRunning(state.isStreaming || state.status === \"running\")\n }\n pull()\n return session.subscribe(pull)\n }, [session])\n\n // 换会话才重置这些:倍速是跟着源会话走的选择,上一个会话选的 1x 不该带过来;\n // 冲突和错误同理。在渲染期比较而不是用 effect——effect 会先渲染一帧旧值,\n // 而且不能和下面的 preview 查询合并(那个还要跟着运行状态重跑,会把用户\n // 刚选的倍速冲掉)。\n const [lastSessionId, setLastSessionId] = useState(sessionId)\n if (sessionId !== lastSessionId) {\n setLastSessionId(sessionId)\n setMismatch(null)\n setError(null)\n setDraftSpeed(DEFAULT_REPLAY_SPEED)\n }\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: sourceRunning 跨过\"运行中\"这条线就是重新查询的触发条件——这一轮跑完了本该能回放,新一轮开始了就不该再放行\n useEffect(() => {\n setPreview({ status: \"loading\" })\n if (!sessionId) return\n let alive = true\n client.sessions\n .getReplayPreview(sessionId)\n .then((result) => {\n if (!alive) return\n setPreview({\n status: \"ready\",\n supported: result.supported,\n reason: result.reason ?? null,\n })\n })\n .catch(() => {\n // 查不到就放行,让服务端在真正创建时把关——但那是\"问过了没结论\",\n // 和\"还没问\"不是一回事,后者必须继续禁用。\n if (alive) setPreview({ status: \"ready\", supported: true, reason: null })\n })\n return () => {\n alive = false\n }\n }, [client, sessionId, sourceRunning])\n\n useEffect(() => {\n if (!session) return\n return session.on(\"replayMismatch\", ({ actualMessage, expectedMessage, respond }) => {\n setMismatch({\n actualMessage: actualMessage ?? \"\",\n expectedMessage: expectedMessage ?? \"\",\n resolve: (decision) => {\n setMismatch(null)\n respond(decision)\n },\n })\n })\n }, [session])\n\n const isReplay = replay?.isReplay ?? false\n\n const setSpeed = useCallback(\n async (next: ReplaySpeed) => {\n setDraftSpeed(next)\n if (!session) return\n setError(null)\n try {\n await session.setReplaySpeed(next)\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n },\n [session],\n )\n\n const exitToAutonomous = useCallback(async () => {\n if (!session) return\n setError(null)\n // 冲突还没决定就点退出:那条消息已经发出去、输入框锁着,只改回放状态的话\n // 卡片会留在屏幕上、对话卡死。\"不回放了\"对应的决定就是让它真的运行,\n // 而 continue_replay 本身就会转自主,不用再 PATCH 一次。\n const pending = mismatch\n if (pending) {\n pending.resolve(\"continue_replay\")\n return\n }\n try {\n await session.exitReplay()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n }, [session, mismatch])\n\n const startReplay = useCallback(\n async (speed?: ReplaySpeed) => {\n if (!sessionId) throw new Error(\"当前没有可回放的会话\")\n // 不能只看 isStarting:state 在同一次渲染里还没更新,多个组件各挂一个\n // hook 实例时也各有各的 state。\n if (replayCreating.has(sessionId)) throw new Error(\"正在创建回放会话,请稍候\")\n replayCreating.add(sessionId)\n setIsStarting(true)\n setError(null)\n try {\n const result = await client.sessions.startReplaySession(sessionId, speed ?? draftSpeed)\n return result.session_id\n } catch (err) {\n const failure = err instanceof Error ? err : new Error(String(err))\n setError(failure)\n throw failure\n } finally {\n replayCreating.delete(sessionId)\n setIsStarting(false)\n }\n },\n [client, sessionId, draftSpeed],\n )\n\n return {\n isReplay,\n speed: isReplay ? (replay?.speed ?? 1) : draftSpeed,\n setSpeed,\n exitToAutonomous,\n mismatch,\n canControl: viewerRole !== \"viewer\",\n canReplay:\n Boolean(sessionId) &&\n preview.status === \"ready\" &&\n preview.supported &&\n viewerRole !== \"viewer\",\n unsupportedReason:\n preview.status === \"ready\" && !preview.supported ? preview.reason : null,\n startReplay,\n isStarting,\n error,\n }\n}\n","import type { ChatMessage, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { useCallback, useMemo, useRef, useState } from \"react\"\n\n/** OpenAI 标准的工具声明,原样发给模型服务。 */\nexport interface ChatCompletionTool {\n type: \"function\"\n function: {\n name: string\n description?: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface LlmToolCall {\n id: string\n name: string\n /** 模型生成的 JSON 字符串,是不可信输入,用之前自己校验。 */\n arguments: string\n}\n\nexport interface LlmChatOptions {\n /** OpenAI 兼容根地址,如 `/api/llm`(自己后端的透传路径)或 `http://host:30000/v1`。 */\n baseURL: string\n model: string\n /**\n * 直连模型服务时才需要。默认形态是让 `baseURL` 指向自己的后端,由后端补密钥转发,\n * 浏览器不持有密钥。\n */\n apiKey?: string\n headers?: Record<string, string>\n /** 系统提示词,合成到消息最前面。 */\n system?: string\n /** 每次带上最近多少轮,默认 12。越长越贵,也越容易超上下文。 */\n historyTurns?: number\n temperature?: number\n /** 透传给模型服务的额外字段。 */\n extraBody?: Record<string, unknown>\n tools?: ChatCompletionTool[]\n /** 执行模型请求的工具,返回值会被序列化后回喂给模型。 */\n onToolCall?: (call: LlmToolCall) => Promise<unknown>\n /** 工具最多来回几轮,默认 4,防止模型绕圈烧 token。 */\n maxToolRounds?: number\n fetchImpl?: typeof fetch\n}\n\nexport interface UseLlmChatResult {\n messages: ChatMessage[]\n isStreaming: boolean\n /** 出错原因;下一次发送会清掉。 */\n error: string | null\n send: (text: string) => Promise<boolean>\n stop: () => void\n reset: () => void\n}\n\ninterface OpenAiToolCall {\n id: string\n type: \"function\"\n function: { name: string; arguments: string }\n}\n\ntype OpenAiMessage =\n | { role: \"user\" | \"assistant\"; content: string; tool_calls?: OpenAiToolCall[] }\n | { role: \"tool\"; tool_call_id: string; content: string }\n\nconst DEFAULT_HISTORY_TURNS = 12\nconst DEFAULT_MAX_TOOL_ROUNDS = 4\n\n/** 补 id 用的序号。全局递增即可,只要同一段对话里不重复。 */\nlet syntheticIdSeq = 0\n\n/**\n * 纯 LLM 对话的状态机,协议就是 **OpenAI Chat Completions**——任何兼容实现都能直接接,\n * 不需要为这个组件专门写后端。\n *\n * 工具调用走 OpenAI 标准的多轮流程:模型返回 tool_calls → 调 onToolCall 执行 →\n * 结果作为 role:\"tool\" 消息回喂 → 再请求一次。\n */\nexport function useLlmChat(options: LlmChatOptions): UseLlmChatResult {\n const [history, setHistory] = useState<OpenAiMessage[]>([])\n const [streamingText, setStreamingText] = useState<string | null>(null)\n const [streamingCalls, setStreamingCalls] = useState<OpenAiToolCall[]>([])\n const [failedToolIds, setFailedToolIds] = useState<string[]>([])\n const [error, setError] = useState<string | null>(null)\n const [isStreaming, setIsStreaming] = useState(false)\n const abortRef = useRef<AbortController | null>(null)\n // reset 会把对话清空;正在跑的那轮不能事后再往里补消息\n const generationRef = useRef(0)\n // 用 ref 读:send 不该因为消息或配置变化而换引用,否则宿主的 useEffect 会连环重跑\n const historyRef = useRef<OpenAiMessage[]>([])\n const optionsRef = useRef(options)\n optionsRef.current = options\n historyRef.current = history\n\n const stop = useCallback(() => {\n abortRef.current?.abort()\n abortRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n generationRef.current += 1\n stop()\n setHistory([])\n setStreamingText(null)\n setStreamingCalls([])\n setFailedToolIds([])\n setError(null)\n setIsStreaming(false)\n }, [stop])\n\n const send = useCallback(async (text: string): Promise<boolean> => {\n const content = text.trim()\n if (!content || abortRef.current) return false\n const opts = optionsRef.current\n const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS\n\n const commit = (message: OpenAiMessage) => {\n historyRef.current = [...historyRef.current, message]\n setHistory(historyRef.current)\n }\n\n setError(null)\n setIsStreaming(true)\n setStreamingText(\"\")\n setStreamingCalls([])\n commit({ role: \"user\", content })\n // 停止时要把已经吐出来的部分留在对话里,所以本地也累一份——state 在闭包里读不到\n let partial = \"\"\n\n const controller = new AbortController()\n abortRef.current = controller\n const generation = generationRef.current\n try {\n for (let round = 0; ; round += 1) {\n const result = await streamOnce(opts, historyRef.current, controller.signal, {\n onText: (chunk) => {\n partial += chunk\n setStreamingText((prev) => `${prev ?? \"\"}${chunk}`)\n },\n onToolCalls: (calls) => setStreamingCalls(calls),\n })\n\n const assistant: OpenAiMessage = {\n role: \"assistant\",\n content: result.content,\n ...(result.toolCalls.length ? { tool_calls: result.toolCalls } : {}),\n }\n setStreamingText(null)\n setStreamingCalls([])\n commit(assistant)\n\n if (!result.toolCalls.length) return true\n // 提交过的 assistant 带着 tool_calls,就必须有对应的 role:\"tool\" 回复,\n // 否则下一轮请求里是一组没人应答的工具调用,严格的 OpenAI 兼容服务直接 400,\n // 这段对话从此就废了。提前收尾时补上说明,让模型知道没执行。\n // 没执行成功的要标成失败,否则界面给个绿勾,等于骗人说查过了\n const bail = (reason: string) => {\n for (const call of result.toolCalls) {\n commit({ role: \"tool\", tool_call_id: call.id, content: JSON.stringify({ error: reason }) })\n }\n setFailedToolIds((prev) => [...prev, ...result.toolCalls.map((call) => call.id)])\n }\n if (!opts.onToolCall) {\n bail(\"这个应用没有提供工具执行入口\")\n return true\n }\n if (round >= maxRounds) {\n bail(`工具调用已达上限 ${maxRounds} 轮,没有执行`)\n setError(`工具调用超过 ${maxRounds} 轮仍未给出结论,已停下。`)\n return false\n }\n\n for (const call of result.toolCalls) {\n let output: string\n try {\n const value = await opts.onToolCall({\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n })\n output = typeof value === \"string\" ? value : JSON.stringify(value ?? null)\n } catch (err) {\n // 执行失败也要回喂给模型,让它自己决定换个方式还是告诉用户\n output = JSON.stringify({ error: err instanceof Error ? err.message : String(err) })\n setFailedToolIds((prev) => [...prev, call.id])\n }\n commit({ role: \"tool\", tool_call_id: call.id, content: output })\n }\n setStreamingText(\"\")\n }\n } catch (err) {\n setStreamingText(null)\n setStreamingCalls([])\n if (controller.signal.aborted) {\n // reset 也走 abort,但它是\"把这段对话清空\",不能再往里补消息\n if (generation !== generationRef.current) return false\n // 保留已经看到的内容:清成\"(已停止)\"等于把用户刚读到的答案抹掉,\n // 下一轮的上下文也跟着丢了\n commit({ role: \"assistant\", content: partial ? `${partial}(已停止)` : \"(已停止)\" })\n return false\n }\n // 和\"停止\"一样保留已经吐出来的部分:网关在流中途下发 error(限流、上游断连)\n // 时把半屏答案抹掉,用户刚读的内容没了,重试时模型也看不到这段上下文\n if (partial && generation === generationRef.current) {\n commit({ role: \"assistant\", content: partial })\n }\n setError(err instanceof Error && err.message ? err.message : \"对话失败,请重试\")\n return false\n } finally {\n if (abortRef.current === controller) abortRef.current = null\n setIsStreaming(false)\n }\n }, [])\n\n const messages = useMemo(\n () => toChatMessages(history, streamingText, streamingCalls, failedToolIds),\n [history, streamingText, streamingCalls, failedToolIds],\n )\n\n return { messages, isStreaming, error, send, stop, reset }\n}\n\ninterface StreamResult {\n content: string\n toolCalls: OpenAiToolCall[]\n}\n\n/** 发一次请求并消费流;返回这一轮模型给出的完整内容与工具调用。 */\nasync function streamOnce(\n options: LlmChatOptions,\n history: OpenAiMessage[],\n signal: AbortSignal,\n handlers: { onText: (chunk: string) => void; onToolCalls: (calls: OpenAiToolCall[]) => void },\n): Promise<StreamResult> {\n const {\n baseURL,\n model,\n apiKey,\n headers,\n system,\n historyTurns = DEFAULT_HISTORY_TURNS,\n temperature,\n extraBody,\n tools,\n fetchImpl,\n } = options\n\n const trimmed = trimHistory(history, historyTurns)\n const payload = {\n model,\n stream: true,\n messages: system ? [{ role: \"system\", content: system }, ...trimmed] : trimmed,\n ...(tools?.length ? { tools } : {}),\n ...(temperature === undefined ? {} : { temperature }),\n ...extraBody,\n }\n\n const doFetch = fetchImpl ?? globalThis.fetch\n const url = `${baseURL.replace(/\\/$/, \"\")}/chat/completions`\n let response: Response\n try {\n response = await doFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n ...headers,\n },\n body: JSON.stringify(payload),\n signal,\n })\n } catch (err) {\n if (signal.aborted) throw err\n // 浏览器给的原始信息是 \"Failed to fetch\",看不出是哪台服务连不上。\n // 地址填错是最常见的故障,必须把地址显示出来。\n throw new Error(`连不上模型服务 ${url}:${err instanceof Error ? err.message : String(err)}`)\n }\n if (!response.ok) {\n throw new Error(await describeHttpError(response))\n }\n if (!response.body) {\n throw new Error(\"模型服务没有返回流式响应\")\n }\n\n let content = \"\"\n const calls: OpenAiToolCall[] = []\n for await (const chunk of readSse(response.body, signal)) {\n if (chunk.error) {\n throw new Error(chunk.error)\n }\n if (chunk.text) {\n content += chunk.text\n handlers.onText(chunk.text)\n }\n if (chunk.toolCallDeltas) {\n for (const delta of chunk.toolCallDeltas) {\n // 工具调用按 index 分片下发:id 与函数名通常只在第一片,参数逐片拼\n const index = delta.index ?? 0\n const existing = calls[index] ?? { id: \"\", type: \"function\" as const, function: { name: \"\", arguments: \"\" } }\n calls[index] = {\n id: delta.id ?? existing.id,\n type: \"function\",\n function: {\n name: delta.function?.name ?? existing.function.name,\n arguments: `${existing.function.arguments}${delta.function?.arguments ?? \"\"}`,\n },\n }\n }\n handlers.onToolCalls(calls.filter(Boolean).map((call) => ({ ...call })))\n }\n }\n const normalized = calls\n .filter(Boolean)\n // 流里会出现只有 arguments、没有函数名的分片;累积后如果仍然没名字,说明它不是\n // 一次真的调用(网关分片的产物)。留着会让宿主的 onToolCall 收到空名字,\n // 只能抛\"没有这个函数\",整轮就废了。\n .filter((call) => call.function.name)\n // 有的实现不给 id,补一个,否则回喂时对不上。序号必须跨轮唯一:\n // 每轮都从 call_0 开始的话,工具结果和失败标记会串到上一轮的卡片上。\n .map((call) => {\n if (call.id) return call\n syntheticIdSeq += 1\n return { ...call, id: `call_auto_${syntheticIdSeq}` }\n })\n return { content, toolCalls: normalized }\n}\n\n/** 按轮数截断历史,但不能把工具消息与它所属的助手消息切散。 */\nfunction trimHistory(history: OpenAiMessage[], historyTurns: number): OpenAiMessage[] {\n const userIndexes = history.reduce<number[]>((acc, msg, i) => {\n if (msg.role === \"user\") acc.push(i)\n return acc\n }, [])\n if (userIndexes.length <= historyTurns) return history\n return history.slice(userIndexes[userIndexes.length - historyTurns])\n}\n\nasync function describeHttpError(response: Response): Promise<string> {\n const raw = await response.text().catch(() => \"\")\n try {\n const parsed = JSON.parse(raw) as { error?: { message?: string } | string; detail?: unknown }\n const message =\n typeof parsed.error === \"string\"\n ? parsed.error\n : (parsed.error?.message ??\n (typeof parsed.detail === \"string\" ? parsed.detail : undefined))\n if (message) return `模型服务返回 ${response.status}:${message}`\n } catch {\n // 不是 JSON 就用原文\n }\n const snippet = raw.trim().slice(0, 120)\n return snippet ? `模型服务返回 ${response.status}:${snippet}` : `模型服务返回 ${response.status}`\n}\n\ninterface StreamChunk {\n text?: string\n toolCallDeltas?: Array<{\n index?: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n error?: string\n}\n\n/**\n * 读 OpenAI 的流式响应。事件之间用空行分隔,`data: [DONE]` 收尾。\n * 必须按 `\\n\\n` 缓冲——一个事件可能横跨两个网络分片,这是实测踩过的。\n */\nasync function* readSse(\n stream: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n): AsyncGenerator<StreamChunk> {\n const reader = stream.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n try {\n while (!signal.aborted) {\n const { done, value } = await reader.read()\n if (done) {\n // 不少兼容网关最后一个 data: 后面不补空行、也不发 [DONE]。不收尾解析的话,\n // 回答的最后一段会静默丢失;丢的要是工具参数的最后一片,宿主拿到的就是\n // 截断的 JSON。\n const tail = parseSse(buffer)\n if (tail && tail !== \"done\") yield tail\n break\n }\n // SSE 规范允许 CRLF;只认 \\n\\n 的话,用 CRLF 的服务永远解析不出事件。\n // 归一必须在拼接之后做:网络分片可能正好切在 \\r 和 \\n 中间,先替换再拼会\n // 留下一个 \\n\\r\\n,边界就再也认不出来了。\n buffer = `${buffer}${decoder.decode(value, { stream: true })}`.replace(/\\r\\n/g, \"\\n\")\n let boundary = buffer.indexOf(\"\\n\\n\")\n while (boundary !== -1) {\n const chunk = parseSse(buffer.slice(0, boundary))\n buffer = buffer.slice(boundary + 2)\n if (chunk === \"done\") return\n if (chunk) yield chunk\n boundary = buffer.indexOf(\"\\n\\n\")\n }\n }\n } finally {\n reader.cancel().catch(() => {})\n }\n}\n\nfunction parseSse(raw: string): StreamChunk | \"done\" | null {\n const dataLines: string[] = []\n for (const line of raw.split(\"\\n\")) {\n if (line.startsWith(\"data:\")) dataLines.push(line.slice(5).trim())\n }\n if (!dataLines.length) return null\n const data = dataLines.join(\"\\n\")\n if (data === \"[DONE]\") return \"done\"\n let parsed: {\n choices?: Array<{ delta?: { content?: string; tool_calls?: StreamChunk[\"toolCallDeltas\"] } }>\n error?: { message?: string } | string\n }\n try {\n parsed = JSON.parse(data)\n } catch {\n return null\n }\n if (parsed.error) {\n const message = typeof parsed.error === \"string\" ? parsed.error : parsed.error.message\n return { error: message || \"模型服务返回了错误\" }\n }\n const delta = parsed.choices?.[0]?.delta\n if (!delta) return null\n return { text: delta.content, toolCallDeltas: delta.tool_calls }\n}\n\n/** OpenAI 形状 → SDK 的 ChatMessage:工具结果并进对应的 tool_calls,不单独成条。 */\nfunction toChatMessages(\n history: OpenAiMessage[],\n streamingText: string | null,\n streamingCalls: OpenAiToolCall[],\n failedToolIds: string[],\n): ChatMessage[] {\n const results = new Map<string, string>()\n for (const msg of history) {\n if (msg.role === \"tool\") results.set(msg.tool_call_id, msg.content)\n }\n\n const messages: ChatMessage[] = []\n for (const msg of history) {\n if (msg.role === \"tool\") continue\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content, status: \"completed\" })\n continue\n }\n messages.push({\n role: \"assistant\",\n content: msg.content,\n status: \"completed\",\n ...(msg.tool_calls?.length\n ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n\n if (streamingText !== null) {\n messages.push({\n role: \"assistant\",\n content: streamingText,\n status: \"streaming\",\n ...(streamingCalls.length\n ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n return messages\n}\n\nfunction toToolCallInfo(\n call: OpenAiToolCall,\n results: Map<string, string>,\n failedToolIds: string[],\n): ToolCallInfo {\n const result = results.get(call.id)\n const failed = failedToolIds.includes(call.id)\n return {\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n ...(result === undefined ? {} : { result }),\n status: result === undefined ? \"pending\" : failed ? \"error\" : \"done\",\n }\n}\n","import type {\n AgentSession,\n BladeClient,\n CreateSessionRequest,\n ResultFeedback,\n} from \"@blade-hq/agent-client\"\nimport { BladeApiError, latestPostChatFollowup } from \"@blade-hq/agent-client\"\nimport { LockKeyhole } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\nimport { useAgentSession } from \"../hooks/use-agent-session\"\nimport { useReplay } from \"../hooks/use-replay\"\nimport { ReplayBar } from \"./ReplayBar\"\nimport { ReplayMismatchPrompt } from \"./ReplayMismatchPrompt\"\nimport { cn } from \"../lib/utils\"\nimport { ChatSurface, themeAttr, type ChatPresentationProps } from \"./ChatSurface\"\nimport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\n\nexport interface AgentChatProps extends ChatPresentationProps {\n /** follow-up、成果和评分交互;SDK 不绑定任何分析厂商。 */\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n /** 连接既有会话;不传则按 createOptions 自动新建。 */\n sessionId?: string\n createOptions?: CreateSessionRequest\n /** 自动创建出的 sessionId 通过此回调交还调用方持有。 */\n onSessionCreated?: (sessionId: string) => void\n /** 会话连接完成后回调(页面协作场景拿 AgentSession 实例用)。 */\n onSessionReady?: (session: AgentSession) => void\n /** 智能体下发给宿主页面的指令处理器(action → handler)。 */\n commands?: Record<string, (data: unknown) => void>\n}\n\nfunction isUnauthorizedError(error: Error | null): boolean {\n return error instanceof BladeApiError && error.status === 401\n}\n\n/** 登录引导卡片:未持有令牌或令牌失效时展示,按钮走弹窗授权。 */\nfunction LoginCard({ client, onLoggedIn }: { client: BladeClient; onLoggedIn: () => void }) {\n const [loggingIn, setLoggingIn] = useState(false)\n const [loginError, setLoginError] = useState<string | null>(null)\n\n const handleLogin = async () => {\n setLoggingIn(true)\n setLoginError(null)\n try {\n await client.login()\n onLoggedIn()\n } catch (error) {\n setLoginError(error instanceof Error && error.message ? error.message : \"登录失败,请重试\")\n } finally {\n setLoggingIn(false)\n }\n }\n\n return (\n <div className=\"blade-chat-login flex flex-1 items-center justify-center p-6\">\n <div className=\"flex w-full max-w-sm flex-col items-center gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-6 py-8 text-center\">\n <LockKeyhole size={28} className=\"text-[hsl(var(--muted-foreground))]\" />\n <div className=\"text-base font-medium text-[hsl(var(--foreground))]\">需要登录后使用</div>\n <div className=\"text-sm text-[hsl(var(--muted-foreground))]\">\n 登录后即可与智能体对话,你的会话内容仅自己可见。\n </div>\n <button\n type=\"button\"\n onClick={handleLogin}\n disabled={loggingIn}\n className=\"w-full rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-semibold text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:opacity-60\"\n >\n {loggingIn ? \"登录中…\" : \"登录\"}\n </button>\n {loginError && <div className=\"text-xs text-[hsl(var(--destructive))]\">{loginError}</div>}\n </div>\n </div>\n )\n}\n\n/**\n * 智能体模式的聊天界面:连接(或新建)一个 Blade 会话,渲染消息流与输入框。\n * 状态全部来自 useAgentSession,不依赖任何全局 store。\n */\nexport function AgentChat(props: AgentChatProps) {\n const client = useBladeClient()\n // attempt 作为会话子树的 key:登录成功后整体重挂载,重新走连接/创建流程\n const [attempt, setAttempt] = useState(0)\n const [needLogin, setNeedLogin] = useState(() => !client.hasToken())\n\n if (needLogin) {\n return (\n <div\n data-theme={themeAttr(props.theme)}\n className={cn(\"blade-chat flex min-h-0 flex-1 flex-col\", props.classNames?.root)}\n >\n <LoginCard\n client={client}\n onLoggedIn={() => {\n setNeedLogin(false)\n setAttempt((value) => value + 1)\n }}\n />\n </div>\n )\n }\n\n return <ChatSessionView key={attempt} {...props} onUnauthorized={() => setNeedLogin(true)} />\n}\n\nfunction ChatSessionView({\n sessionId,\n createOptions,\n onSessionCreated,\n onSessionReady,\n commands,\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onFollowupInteraction,\n onUnauthorized,\n}: AgentChatProps & { onUnauthorized: () => void }) {\n const client = useBladeClient()\n const { session, state, error } = useAgentSession(sessionId, {\n createOptions,\n onSessionCreated,\n })\n // 开箱即用的聊天界面自带回放处理:不接的话,用它打开回放会话就是个普通聊天\n // ——看不出在回放、退不出去,说错话还会走\"无人监听\"兜底静默转成真实运行。\n const replay = useReplay(session)\n const [stopRequested, setStopRequested] = useState(false)\n const [inputText, setInputText] = useState(\"\")\n const [resultFeedback, setResultFeedback] = useState<ResultFeedback[]>([])\n const resolvedSessionId = session?.sessionId\n const isViewer = state?.viewerRole === \"viewer\"\n\n useEffect(() => {\n setResultFeedback([])\n if (!resolvedSessionId || isViewer) return\n let cancelled = false\n void client.sessions.listResultFeedback(resolvedSessionId).then(\n (items) => {\n if (cancelled) return\n setResultFeedback((current) => {\n const merged = new Map(items.map((item) => [item.assistant_entry_id, item]))\n for (const item of current) {\n const fetched = merged.get(item.assistant_entry_id)\n if (!fetched || fetched.updated_at < item.updated_at) {\n merged.set(item.assistant_entry_id, item)\n }\n }\n return [...merged.values()]\n })\n },\n () => {},\n )\n return () => {\n cancelled = true\n }\n }, [client, isViewer, resolvedSessionId])\n const resultFeedbackByEntry = useMemo(\n () => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),\n [resultFeedback],\n )\n const handleResultFeedbackSaved = useCallback((saved: ResultFeedback) => {\n setResultFeedback((current) => [\n ...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),\n saved,\n ])\n }, [])\n\n // 会话就绪后交还实例\n useEffect(() => {\n if (session) {\n onSessionReady?.(session)\n }\n }, [session, onSessionReady])\n\n // session.attach() / insertText() 注入到输入框:页面把业务数据(如地图选中\n // 的点位)塞进输入框,随下一条消息发给智能体。\n useEffect(() => {\n if (!session) return\n const offAttach = session.on(\"attachRequested\", ({ label, content }) => {\n setInputText((prev) => `${prev ? `${prev}\\n\\n` : \"\"}[${label}]\\n${content}`)\n })\n const offInsert = session.on(\"insertTextRequested\", ({ text }) => {\n setInputText((prev) => prev + text)\n })\n return () => {\n offAttach()\n offInsert()\n }\n }, [session])\n\n // 令牌失效(401)时回到登录引导\n useEffect(() => {\n if (isUnauthorizedError(error)) {\n onUnauthorized()\n }\n }, [error, onUnauthorized])\n\n // 注册宿主页面指令:卸载或 commands 变化时反注册\n useEffect(() => {\n if (!session || !commands) return\n const unsubscribes = Object.entries(commands).map(([action, handler]) =>\n session.onCommand(action, (payload) => handler(payload)),\n )\n return () => {\n for (const unsubscribe of unsubscribes) unsubscribe()\n }\n }, [session, commands])\n\n const isStreaming = state?.isStreaming ?? false\n const isStopping = stopRequested && isStreaming\n const connectError =\n error && !isUnauthorizedError(error)\n ? error.message || \"连接失败,请稍后重试\"\n : null\n // 回放控制失败(改倍速 / 退出)也要让人看见,否则点了毫无反馈。\n const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null\n\n const handleSend = (text: string): Promise<boolean> => {\n setStopRequested(false)\n return session?.send(text, { mode: state?.mode ?? undefined }) ?? Promise.resolve(false)\n }\n\n const handleStop = () => {\n setStopRequested(true)\n void session?.stop()\n }\n\n return (\n <ChatSurface\n theme={theme}\n onFollowupInteraction={onFollowupInteraction}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n connection={state?.connection ?? \"connecting\"}\n banner={\n <>\n <ReplayBar\n isReplay={replay.isReplay}\n speed={replay.speed}\n canControl={replay.canControl}\n onSpeedChange={(next) => void replay.setSpeed(next)}\n onExit={() => void replay.exitToAutonomous()}\n />\n <ReplayMismatchPrompt mismatch={replay.mismatch} />\n </>\n }\n errorMessage={errorMessage}\n messages={state?.messages ?? []}\n postChatFollowup={latestPostChatFollowup(state?.turns ?? [])}\n resultFeedbackByEntry={resultFeedbackByEntry}\n onResultFeedbackSaved={handleResultFeedbackSaved}\n isStreaming={isStreaming}\n isStopping={isStopping}\n inputText={inputText}\n onInputChange={setInputText}\n onSuggestion={setInputText}\n onSend={handleSend}\n onStop={handleStop}\n sessionStatus={state?.status ?? undefined}\n askAnswers={state?.askAnswers}\n onAnswer={(answer, toolCallId, answerData) => {\n void session?.send(answer, {\n mode: state?.mode ?? undefined,\n askUserAnswer: { tool_call_id: toolCallId, ...answerData },\n })\n }}\n // 用 session 自己持有的 id,不是 sessionId prop——后者是可选的,\n // 自动建会话时一直是 undefined,文件卡片会永远不可用。\n sessionId={resolvedSessionId}\n isViewer={isViewer}\n />\n )\n}\n","import { CamelToPascal } from './utility-types';\n\n/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n\n/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","import { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses } from '@lucide/shared';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n {\n color = 'currentColor',\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = '',\n children,\n iconNode,\n ...rest\n },\n ref,\n ) => {\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,\n className: mergeClasses('lucide', className),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),\n ...props,\n }),\n );\n\n Component.displayName = `${iconName}`;\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowRight = createLucideIcon('ArrowRight', [\n ['path', { d: 'M5 12h14', key: '1ays0h' }],\n ['path', { d: 'm12 5 7 7-7 7', key: 'xquz4c' }],\n]);\n\nexport default ArrowRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowUpRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUpRight = createLucideIcon('ArrowUpRight', [\n ['path', { d: 'M7 7h10v10', key: '1tivn9' }],\n ['path', { d: 'M7 17 17 7', key: '1vkiza' }],\n]);\n\nexport default ArrowUpRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowUp\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUp = createLucideIcon('ArrowUp', [\n ['path', { d: 'm5 12 7-7 7 7', key: 'hav0vg' }],\n ['path', { d: 'M12 19V5', key: 'x0mq9r' }],\n]);\n\nexport default ArrowUp;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Bot\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/bot\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Bot = createLucideIcon('Bot', [\n ['path', { d: 'M12 8V4H8', key: 'hb8ula' }],\n ['rect', { width: '16', height: '12', x: '4', y: '8', rx: '2', key: 'enze0r' }],\n ['path', { d: 'M2 14h2', key: 'vft8re' }],\n ['path', { d: 'M20 14h2', key: '4cs60a' }],\n ['path', { d: 'M15 13v2', key: '1xurst' }],\n ['path', { d: 'M9 13v2', key: 'rq6x2g' }],\n]);\n\nexport default Bot;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Brain\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/brain\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Brain = createLucideIcon('Brain', [\n [\n 'path',\n {\n d: 'M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z',\n key: 'l5xja',\n },\n ],\n [\n 'path',\n {\n d: 'M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z',\n key: 'ep3f8r',\n },\n ],\n ['path', { d: 'M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4', key: '1p4c4q' }],\n ['path', { d: 'M17.599 6.5a3 3 0 0 0 .399-1.375', key: 'tmeiqw' }],\n ['path', { d: 'M6.003 5.125A3 3 0 0 0 6.401 6.5', key: '105sqy' }],\n ['path', { d: 'M3.477 10.896a4 4 0 0 1 .585-.396', key: 'ql3yin' }],\n ['path', { d: 'M19.938 10.5a4 4 0 0 1 .585.396', key: '1qfode' }],\n ['path', { d: 'M6 18a4 4 0 0 1-1.967-.516', key: '2e4loj' }],\n ['path', { d: 'M19.967 17.484A4 4 0 0 1 18 18', key: '159ez6' }],\n]);\n\nexport default Brain;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Check\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/check\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Check = createLucideIcon('Check', [['path', { d: 'M20 6 9 17l-5-5', key: '1gmf2c' }]]);\n\nexport default Check;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronDown\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-down\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronDown = createLucideIcon('ChevronDown', [\n ['path', { d: 'm6 9 6 6 6-6', key: 'qrunsl' }],\n]);\n\nexport default ChevronDown;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronRight = createLucideIcon('ChevronRight', [\n ['path', { d: 'm9 18 6-6-6-6', key: 'mthhwq' }],\n]);\n\nexport default ChevronRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name CircleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/circle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst CircleAlert = createLucideIcon('CircleAlert', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['line', { x1: '12', x2: '12', y1: '8', y2: '12', key: '1pkeuh' }],\n ['line', { x1: '12', x2: '12.01', y1: '16', y2: '16', key: '4dfq90' }],\n]);\n\nexport default CircleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Copy\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/copy\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Copy = createLucideIcon('Copy', [\n ['rect', { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2', key: '17jyea' }],\n ['path', { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2', key: 'zix9uf' }],\n]);\n\nexport default Copy;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Download\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/download\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Download = createLucideIcon('Download', [\n ['path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4', key: 'ih7n3h' }],\n ['polyline', { points: '7 10 12 15 17 10', key: '2ggqvy' }],\n ['line', { x1: '12', x2: '12', y1: '15', y2: '3', key: '1vk2je' }],\n]);\n\nexport default Download;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name FileText\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-text\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FileText = createLucideIcon('FileText', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n ['path', { d: 'M10 9H8', key: 'b1mrlr' }],\n ['path', { d: 'M16 13H8', key: 't4e002' }],\n ['path', { d: 'M16 17H8', key: 'z1uh3a' }],\n]);\n\nexport default FileText;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name File\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst File = createLucideIcon('File', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n]);\n\nexport default File;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Film\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/film\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Film = createLucideIcon('Film', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n ['path', { d: 'M7 3v18', key: 'bbkbws' }],\n ['path', { d: 'M3 7.5h4', key: 'zfgn84' }],\n ['path', { d: 'M3 12h18', key: '1i2n21' }],\n ['path', { d: 'M3 16.5h4', key: '1230mu' }],\n ['path', { d: 'M17 3v18', key: 'in4fa5' }],\n ['path', { d: 'M17 7.5h4', key: 'myr1c1' }],\n ['path', { d: 'M17 16.5h4', key: 'go4c1d' }],\n]);\n\nexport default Film;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Globe\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/globe\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Globe = createLucideIcon('Globe', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['path', { d: 'M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20', key: '13o1zl' }],\n ['path', { d: 'M2 12h20', key: '9i4pu4' }],\n]);\n\nexport default Globe;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Layers\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/layers\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Layers = createLucideIcon('Layers', [\n [\n 'path',\n {\n d: 'M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z',\n key: 'zw3jo',\n },\n ],\n [\n 'path',\n {\n d: 'M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12',\n key: '1wduqc',\n },\n ],\n [\n 'path',\n {\n d: 'M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17',\n key: 'kqbvx6',\n },\n ],\n]);\n\nexport default Layers;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Lightbulb\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lightbulb\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Lightbulb = createLucideIcon('Lightbulb', [\n [\n 'path',\n {\n d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5',\n key: '1gvzjb',\n },\n ],\n ['path', { d: 'M9 18h6', key: 'x1upvd' }],\n ['path', { d: 'M10 22h4', key: 'ceow96' }],\n]);\n\nexport default Lightbulb;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LoaderCircle\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/loader-circle\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LoaderCircle = createLucideIcon('LoaderCircle', [\n ['path', { d: 'M21 12a9 9 0 1 1-6.219-8.56', key: '13zald' }],\n]);\n\nexport default LoaderCircle;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LockKeyhole\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lock-keyhole\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LockKeyhole = createLucideIcon('LockKeyhole', [\n ['circle', { cx: '12', cy: '16', r: '1', key: '1au0dj' }],\n ['rect', { x: '3', y: '10', width: '18', height: '12', rx: '2', key: '6s8ecr' }],\n ['path', { d: 'M7 10V7a5 5 0 0 1 10 0v3', key: '1pqi11' }],\n]);\n\nexport default LockKeyhole;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquareMore\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square-more\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquareMore = createLucideIcon('MessageSquareMore', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n ['path', { d: 'M8 10h.01', key: '19clt8' }],\n ['path', { d: 'M12 10h.01', key: '1nrarc' }],\n ['path', { d: 'M16 10h.01', key: '1m94wz' }],\n]);\n\nexport default MessageSquareMore;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquare\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquare = createLucideIcon('MessageSquare', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n]);\n\nexport default MessageSquare;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Play\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/play\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Play = createLucideIcon('Play', [\n ['polygon', { points: '6 3 20 12 6 21 6 3', key: '1oa8hb' }],\n]);\n\nexport default Play;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Settings2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/settings-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Settings2 = createLucideIcon('Settings2', [\n ['path', { d: 'M20 7h-9', key: '3s1dr2' }],\n ['path', { d: 'M14 17H5', key: 'gfn3mx' }],\n ['circle', { cx: '17', cy: '17', r: '3', key: '18b49y' }],\n ['circle', { cx: '7', cy: '7', r: '3', key: 'dfmy0x' }],\n]);\n\nexport default Settings2;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Sparkles\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/sparkles\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Sparkles = createLucideIcon('Sparkles', [\n [\n 'path',\n {\n d: 'M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z',\n key: '4pj2yx',\n },\n ],\n ['path', { d: 'M20 3v4', key: '1olli1' }],\n ['path', { d: 'M22 5h-4', key: '1gvqau' }],\n ['path', { d: 'M4 17v2', key: 'vumght' }],\n ['path', { d: 'M5 18H3', key: 'zchphs' }],\n]);\n\nexport default Sparkles;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Square\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Square = createLucideIcon('Square', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n]);\n\nexport default Square;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name TriangleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/triangle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst TriangleAlert = createLucideIcon('TriangleAlert', [\n [\n 'path',\n {\n d: 'm21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3',\n key: 'wmoenq',\n },\n ],\n ['path', { d: 'M12 9v4', key: 'juzpu7' }],\n ['path', { d: 'M12 17h.01', key: 'p32p05' }],\n]);\n\nexport default TriangleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name X\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/x\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst X = createLucideIcon('X', [\n ['path', { d: 'M18 6 6 18', key: '1bl5f8' }],\n ['path', { d: 'm6 6 12 12', key: 'd8bk6v' }],\n]);\n\nexport default X;\n","import { clsx, type ClassValue } from \"clsx\"\n\n/** 合并 className(SDK 内不引入 tailwind-merge,按序拼接即可)。 */\nexport function cn(...inputs: ClassValue[]): string {\n return clsx(inputs)\n}\n\n/** 复制文本到剪贴板,返回是否成功。 */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n return false\n }\n}\n","import type { ReplaySpeed } from \"@blade-hq/agent-client\"\nimport { Play } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst SPEED_OPTIONS: ReplaySpeed[] = [1, 2, 5]\n\nexport interface ReplayBarProps {\n /** 非回放会话传 false 时整条不渲染。 */\n isReplay: boolean\n speed: ReplaySpeed\n onSpeedChange: (speed: ReplaySpeed) => void\n onExit: () => void\n /** 传 useReplay().canControl。只读身份下仍然显示\"在回放\",但操作按钮禁用。 */\n canControl?: boolean\n className?: string\n}\n\n/** 回放会话的顶部状态条:说明当前在回放、切换倍速、随时退出回放。 */\nexport function ReplayBar({\n isReplay,\n speed,\n onSpeedChange,\n onExit,\n canControl = true,\n className,\n}: ReplayBarProps) {\n if (!isReplay) return null\n\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs\",\n className,\n )}\n >\n <span className=\"inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]\">\n <Play size={13} />\n 回放模式\n </span>\n <span className=\"text-[hsl(var(--muted-foreground))]\">\n 正在重现之前的对话\n </span>\n <div className=\"ml-auto flex items-center gap-2\">\n <div className=\"flex items-center gap-0.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-0.5\">\n {SPEED_OPTIONS.map((option) => (\n <button\n key={option}\n type=\"button\"\n onClick={() => onSpeedChange(option)}\n aria-pressed={speed === option}\n disabled={!canControl}\n className={cn(\n \"h-6 rounded px-2 font-medium transition-colors\",\n speed === option\n ? \"bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]\"\n : \"text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50\",\n )}\n >\n {option}x\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={onExit}\n disabled={!canControl}\n className={cn(\n \"h-7 rounded-md border border-[hsl(var(--border))] px-2.5 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n 退出回放\n </button>\n </div>\n </div>\n )\n}\n","import type { ReplayMismatch } from \"../hooks/use-replay\"\nimport { cn } from \"../lib/utils\"\n\nexport interface ReplayMismatchPromptProps {\n /** 来自 useReplay().mismatch;为 null 时不渲染。 */\n mismatch: ReplayMismatch | null\n className?: string\n}\n\n/** 回放中输入和录制内容对不上时的选择卡:按录制继续,还是从这里真跑。 */\nexport function ReplayMismatchPrompt({ mismatch, className }: ReplayMismatchPromptProps) {\n if (!mismatch) return null\n\n return (\n <div\n className={cn(\n \"mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm\",\n className,\n )}\n >\n <div className=\"font-medium text-[hsl(var(--foreground))]\">\n 这句话和之前录制的不一样\n </div>\n <dl className=\"mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]\">\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">录制的是</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.expectedMessage || \"(空)\"}\n </dd>\n </div>\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">你输入的</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.actualMessage || \"(空)\"}\n </dd>\n </div>\n </dl>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"keep_replay\")}\n className=\"h-8 rounded-md bg-[hsl(var(--primary))] px-3 text-xs font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90\"\n >\n 按录制内容继续\n </button>\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"continue_replay\")}\n className=\"h-8 rounded-md border border-[hsl(var(--border))] px-3 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\"\n >\n 从这里开始真的运行\n </button>\n </div>\n </div>\n )\n}\n","import { ArrowUp, Loader2, Square } from \"lucide-react\"\nimport type { KeyboardEvent } from \"react\"\nimport { cn } from \"../lib/utils\"\n\ninterface Props {\n /** 受控输入内容(由 ChatView 持有,便于 attach/insertText 注入)。 */\n value: string\n onValueChange: (value: string) => void\n onSend: (text: string) => boolean | Promise<boolean>\n onStop: () => void\n isStreaming: boolean\n isStopping?: boolean\n placeholder?: string\n className?: string\n}\n\n/**\n * 轻量聊天输入框:textarea(Enter 发送 / Shift+Enter 换行)+ 发送/停止按钮。\n * 不含第一方版本的富文本编辑、@ 补全、技能菜单、语音输入与模型选择。\n * 内容受控,session.attach() / insertText() 通过 ChatView 注入到这里。\n */\nexport function ChatInput({\n value,\n onValueChange,\n onSend,\n onStop,\n isStreaming,\n isStopping = false,\n placeholder = \"输入消息…\",\n className,\n}: Props) {\n const trimmed = value.trim()\n const canSend = trimmed.length > 0 && !isStreaming\n\n const handleSend = async () => {\n if (!canSend) return\n const accepted = await onSend(trimmed)\n if (!accepted) return\n onValueChange(\"\")\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n // 输入法组合中回车用于选字,不能触发发送\n if (event.key === \"Enter\" && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault()\n void handleSend()\n }\n }\n\n return (\n <div className={cn(\"blade-chat-input border-t border-[hsl(var(--border))] py-3\", className)}>\n <div className=\"blade-chat-input-inner mx-auto flex max-w-[748px] items-end gap-2 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2\">\n <textarea\n value={value}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={handleKeyDown}\n onInput={(event) => {\n // 内容驱动的高度自适应:先复位再按 scrollHeight 撑开,上限约 8 行\n const el = event.currentTarget\n el.style.height = \"auto\"\n el.style.height = `${Math.min(el.scrollHeight, 192)}px`\n }}\n rows={1}\n placeholder={placeholder}\n aria-label=\"聊天输入\"\n className=\"blade-chat-textarea max-h-48 min-h-[28px] flex-1 resize-none bg-transparent py-1 text-sm leading-6 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.6)]\"\n />\n {isStreaming ? (\n <button\n type=\"button\"\n onClick={onStop}\n disabled={isStopping}\n aria-label={isStopping ? \"正在停止\" : \"停止回复\"}\n title={isStopping ? \"正在停止\" : \"停止回复\"}\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--muted))] text-[hsl(var(--foreground))] transition-opacity hover:opacity-90 disabled:opacity-60\"\n >\n {isStopping ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : (\n <Square size={12} fill=\"currentColor\" />\n )}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={handleSend}\n disabled={!canSend}\n aria-label=\"发送消息\"\n title=\"发送消息\"\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ArrowUp size={15} />\n </button>\n )}\n </div>\n </div>\n )\n}\n","import type { ConnectionStatus } from \"@blade-hq/agent-client\"\nimport { AlertTriangle, LoaderCircle } from \"lucide-react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\n\nconst CONNECTION_NOTICE_DELAY_MS = 3_000\nconst CONNECTION_ERROR_DELAY_MS = 15_000\n\ntype ConnectionNoticePhase = \"hidden\" | \"recovering\" | \"failed\"\n\ninterface Props {\n connection: ConnectionStatus\n className?: string\n}\n\n/** 与内置 Web 的 ConnectionBanner 保持同样的前台恢复容错时序。 */\nfunction useConnectionNoticePhase(connected: boolean): ConnectionNoticePhase {\n const [phase, setPhase] = useState<ConnectionNoticePhase>(\"hidden\")\n const connectedRef = useRef(connected)\n const timersRef = useRef<ReturnType<typeof setTimeout>[]>([])\n\n connectedRef.current = connected\n\n useEffect(() => {\n const clearTimers = () => {\n for (const timer of timersRef.current) clearTimeout(timer)\n timersRef.current = []\n }\n const startGracePeriod = () => {\n clearTimers()\n setPhase(\"hidden\")\n timersRef.current = [\n setTimeout(() => setPhase(\"recovering\"), CONNECTION_NOTICE_DELAY_MS),\n setTimeout(() => setPhase(\"failed\"), CONNECTION_ERROR_DELAY_MS),\n ]\n }\n\n if (connected) {\n clearTimers()\n setPhase(\"hidden\")\n } else {\n startGracePeriod()\n }\n\n const handleForeground = () => {\n if (!connectedRef.current) startGracePeriod()\n }\n const handleVisibilityChange = () => {\n if (document.visibilityState === \"visible\") handleForeground()\n }\n\n window.addEventListener(\"blade:app-active\", handleForeground)\n window.addEventListener(\"focus\", handleForeground)\n window.addEventListener(\"pageshow\", handleForeground)\n document.addEventListener(\"visibilitychange\", handleVisibilityChange)\n return () => {\n clearTimers()\n window.removeEventListener(\"blade:app-active\", handleForeground)\n window.removeEventListener(\"focus\", handleForeground)\n window.removeEventListener(\"pageshow\", handleForeground)\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange)\n }\n }, [connected])\n\n return phase\n}\n\n/** 连接状态提示条:短暂断线静默,超过容错时间后再逐级提示。 */\nexport function ConnectionBanner({ connection, className }: Props) {\n const hasConnectedRef = useRef(connection === \"connected\" || connection === \"reconnecting\")\n if (connection === \"connected\") hasConnectedRef.current = true\n const connected = connection === \"connected\"\n const phase = useConnectionNoticePhase(connected)\n if (connected || phase === \"hidden\") return null\n\n const recovering = phase === \"recovering\"\n const firstConnection = !hasConnectedRef.current\n\n return (\n <div className={cn(\"blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3\", className)}>\n <div\n className={cn(\n \"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3\",\n recovering\n ? \"border-amber-500/25 bg-amber-500/10 text-amber-100\"\n : \"border-rose-500/25 bg-rose-500/10 text-rose-100\",\n )}\n >\n <span className=\"mt-0.5 shrink-0\">\n {recovering ? (\n <LoaderCircle size={14} className=\"animate-spin\" />\n ) : (\n <AlertTriangle size={14} />\n )}\n </span>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium\">\n {recovering\n ? firstConnection\n ? \"正在连接…\"\n : \"正在恢复连接…\"\n : \"暂时无法连接\"}\n </div>\n <div className=\"text-xs opacity-80\">\n {recovering\n ? \"恢复后会自动同步最新消息,请稍候\"\n : \"请检查网络或服务状态,系统会继续自动重试\"}\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import type {\n AskUserAnswerData,\n ChatMessage,\n PostChatFollowup,\n ResultFeedback,\n} from \"@blade-hq/agent-client\"\nimport { isHiddenInternalMessage } from \"@blade-hq/agent-client\"\nimport { ChevronDown, Layers, Lightbulb, MessageSquare } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\"\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\"\nimport { cn } from \"../lib/utils\"\nimport { AssistantTurnBlock } from \"./AssistantTurnBlock\"\nimport { RenderErrorBoundary } from \"./RenderErrorBoundary\"\nimport {\n HistoricalResultFeedback,\n PostChatFollowupBlock,\n type FollowupInteractionEvent,\n} from \"./PostChatFollowupBlock\"\nimport type { ToolCallRenderer } from \"./ToolCallBlock\"\nimport {\n ErrorMessageBlock,\n UserMessageBubble,\n isErrorMessage,\n isUserMessage,\n} from \"./UserMessageBubble\"\n\ntype RenderBlock =\n | { type: \"message\"; message: ChatMessage; key: string }\n | { type: \"assistant_turn\"; messages: ChatMessage[]; key: string; isStreaming: boolean }\n | { type: \"compaction\"; key: string }\n | { type: \"planning_divider\"; kind: \"enter\" | \"exit\"; key: string }\n\nfunction parseModeChange(message: ChatMessage): { from: string; to: string } | null {\n if (message.kind !== \"mode_change\" || typeof message.content !== \"string\") {\n return null\n }\n try {\n const parsed = JSON.parse(message.content) as { from?: unknown; to?: unknown }\n if (typeof parsed.from === \"string\" && typeof parsed.to === \"string\") {\n return { from: parsed.from, to: parsed.to }\n }\n } catch {}\n return null\n}\n\nfunction getPlanningDividerKind(message: ChatMessage): \"enter\" | \"exit\" | null {\n const modeChange = parseModeChange(message)\n if (modeChange?.to === \"planning\") return \"enter\"\n if (modeChange?.from === \"planning\") return \"exit\"\n return null\n}\n\nfunction getMessageResetSignature(messages: ChatMessage[]): string {\n return messages\n .map((message) => {\n const contentSize =\n typeof message.content === \"string\" ? message.content.length : message.content.length\n const toolSignature =\n message.tool_calls\n ?.map((toolCall) => `${toolCall.id}:${toolCall.status ?? \"\"}`)\n .join(\",\") ?? \"\"\n return [\n message.entry_id ?? message.timestamp ?? message.role,\n message.status ?? \"\",\n contentSize,\n toolSignature,\n ].join(\":\")\n })\n .join(\"|\")\n}\n\ninterface Props {\n messages: ChatMessage[]\n postChatFollowup?: PostChatFollowup | null\n onSuggestion?: (value: string) => void\n isStreaming: boolean\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n toolCallRenderer?: ToolCallRenderer\n emptyState?: ReactNode\n className?: string\n /** 会话 id;传给助手消息的统一上下文。 */\n sessionId?: string\n isViewer?: boolean\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n resultFeedbackByEntry?: ReadonlyMap<string, ResultFeedback>\n onResultFeedbackSaved?: (feedback: ResultFeedback) => void\n}\n\n/**\n * 消息列表:use-stick-to-bottom 自动滚动 + 按轮次分组渲染。\n * 相比第一方版本去掉了轮次导航栏、吸顶状态条与规划摘要卡片。\n */\nexport function MessageList({\n messages,\n postChatFollowup,\n onSuggestion,\n isStreaming,\n sessionStatus,\n askAnswers,\n onAnswer,\n toolCallRenderer,\n emptyState,\n className,\n sessionId,\n isViewer = false,\n onFollowupInteraction,\n resultFeedbackByEntry = new Map(),\n onResultFeedbackSaved,\n}: Props) {\n const renderBlocks = useMemo(() => {\n const visible = messages.filter((message) => {\n if ((message.loop_name ?? \"root\") !== \"root\") return false\n if (isHiddenInternalMessage(message)) return false\n if (message.kind === \"compaction\") return true\n return message.role !== \"tool\" || getPlanningDividerKind(message) !== null\n })\n\n const blocks: RenderBlock[] = []\n let assistantBuffer: ChatMessage[] = []\n let assistantTurnCount = 0\n\n const flushAssistant = () => {\n if (assistantBuffer.length === 0) return\n assistantTurnCount += 1\n const first = assistantBuffer[0]\n blocks.push({\n type: \"assistant_turn\",\n messages: assistantBuffer,\n key: first.entry_id ?? `assistant-${assistantTurnCount}`,\n isStreaming: assistantBuffer.some((message) => message.status === \"streaming\"),\n })\n assistantBuffer = []\n }\n\n for (const message of visible) {\n const dividerKind = getPlanningDividerKind(message)\n if (dividerKind) {\n flushAssistant()\n blocks.push({\n type: \"planning_divider\",\n kind: dividerKind,\n key: message.entry_id ?? `${dividerKind}-${blocks.length}`,\n })\n continue\n }\n\n if (message.kind === \"compaction\") {\n flushAssistant()\n blocks.push({ type: \"compaction\", key: message.entry_id ?? `compaction-${blocks.length}` })\n continue\n }\n\n if (message.role === \"assistant\") {\n assistantBuffer.push(message)\n continue\n }\n\n flushAssistant()\n blocks.push({\n type: \"message\",\n message,\n key: message.render_id ?? message.entry_id ?? `${message.role}-${blocks.length}`,\n })\n }\n\n flushAssistant()\n\n // 流式中但最后一块还是用户消息:补一个空的助手轮占位,立即出现\"正在生成\"\n const last = blocks[blocks.length - 1]\n if (isStreaming) {\n if (last?.type === \"assistant_turn\") {\n last.isStreaming = true\n } else if (last?.type === \"message\" && isUserMessage(last.message)) {\n blocks.push({\n type: \"assistant_turn\",\n messages: [\n {\n role: \"assistant\",\n content: \"\",\n entry_id: `pending-assistant-${last.key}`,\n status: \"streaming\",\n loop_name: \"root\",\n },\n ],\n key: `pending-assistant-${last.key}`,\n isStreaming: true,\n })\n }\n }\n\n return blocks\n }, [messages, isStreaming])\n\n return (\n <div className={cn(\"blade-chat-messages relative min-h-0 flex-1\", className)}>\n <StickToBottom className=\"h-full overflow-y-hidden\" initial=\"instant\" resize=\"instant\">\n <StickToBottom.Content className=\"blade-chat-messages-scroll\">\n <div className=\"blade-chat-messages-content mx-auto max-w-[748px]\">\n <div className=\"flex min-w-0 flex-col\">\n {renderBlocks.length === 0\n ? (emptyState ?? (\n <div className=\"blade-chat-empty\">\n <MessageSquare size={40} strokeWidth={1.5} />\n <span className=\"text-base font-medium\">开始对话</span>\n <span className=\"text-sm opacity-60\">在下方输入消息开始聊天</span>\n </div>\n ))\n : renderBlocks.map((block) => {\n if (block.type === \"message\") {\n return (\n <div key={block.key} data-entry-id={block.message.entry_id}>\n {isUserMessage(block.message) ? (\n <UserMessageBubble message={block.message} />\n ) : isErrorMessage(block.message) ? (\n <ErrorMessageBlock message={block.message} />\n ) : null}\n </div>\n )\n }\n\n if (block.type === \"assistant_turn\") {\n const blockFeedback = block.messages\n .map((message) =>\n message.entry_id\n ? resultFeedbackByEntry.get(message.entry_id)\n : undefined,\n )\n .find((feedback): feedback is ResultFeedback => feedback != null)\n const hasActiveFollowup = Boolean(\n postChatFollowup &&\n block.messages.some(\n (message) => message.entry_id === postChatFollowup.assistant_entry_id,\n ),\n )\n return (\n <div key={block.key} data-entry-id={block.messages[0]?.entry_id}>\n <RenderErrorBoundary\n label=\"助手消息\"\n details={block.key}\n resetKey={getMessageResetSignature(block.messages)}\n >\n <AssistantTurnBlock\n messages={block.messages}\n isStreaming={block.isStreaming}\n askAnswers={askAnswers}\n onAnswer={onAnswer}\n sessionStatus={sessionStatus}\n toolCallRenderer={toolCallRenderer}\n sessionId={sessionId}\n />\n {blockFeedback && !hasActiveFollowup ? (\n <HistoricalResultFeedback feedback={blockFeedback} />\n ) : null}\n {hasActiveFollowup && postChatFollowup ? (\n <PostChatFollowupBlock\n followup={postChatFollowup}\n sessionId={sessionId}\n onSuggestion={onSuggestion}\n isViewer={isViewer}\n onInteraction={onFollowupInteraction}\n savedFeedback={blockFeedback}\n onFeedbackSaved={onResultFeedbackSaved}\n />\n ) : null}\n </RenderErrorBoundary>\n </div>\n )\n }\n\n if (block.type === \"compaction\") {\n return (\n <div\n key={block.key}\n className=\"flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <Layers size={12} />\n <span>上下文已压缩</span>\n </div>\n )\n }\n\n return <PlanningDivider key={block.key} kind={block.kind} />\n })}\n\n {sessionStatus === \"interrupted\" && !isStreaming ? (\n <div className=\"flex\">\n <div className=\"rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300\">\n 已中断\n </div>\n </div>\n ) : null}\n </div>\n </div>\n </StickToBottom.Content>\n <AutoScrollOnUserSend userMessageCount={messages.filter((m) => isUserMessage(m)).length} />\n <ScrollToBottomButton />\n </StickToBottom>\n </div>\n )\n}\n\n/** 用户每发一条消息就滚到底部;流式期间用户向上翻阅历史则不打扰。 */\nfunction AutoScrollOnUserSend({ userMessageCount }: { userMessageCount: number }) {\n const { scrollToBottom } = useStickToBottomContext()\n const previousCountRef = useRef(userMessageCount)\n useEffect(() => {\n if (userMessageCount > previousCountRef.current) {\n scrollToBottom(\"instant\")\n }\n previousCountRef.current = userMessageCount\n }, [userMessageCount, scrollToBottom])\n return null\n}\n\nfunction ScrollToBottomButton() {\n const { isAtBottom, scrollToBottom } = useStickToBottomContext()\n const [visible, setVisible] = useState(false)\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // 防抖:isAtBottom 变为 true 时不立即隐藏,等 300ms 确认稳定后再隐藏\n // 避免流式内容高度变化导致 isAtBottom 在 true/false 之间快速切换闪烁\n useEffect(() => {\n if (isAtBottom) {\n // 启动延迟隐藏定时器\n if (!hideTimerRef.current) {\n hideTimerRef.current = setTimeout(() => {\n setVisible(false)\n hideTimerRef.current = null\n }, 300)\n }\n } else {\n // 不在底部,取消所有待执行的隐藏,立即显示\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(true)\n }\n\n return () => {\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n }, [isAtBottom])\n\n // hook 必须在任何早退之前调用,否则 visible 变化时 hook 顺序不一致\n const handleClick = useCallback(() => {\n // 点击滚动到底部时立即隐藏按钮,避免闪烁\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(false)\n scrollToBottom()\n }, [scrollToBottom])\n\n if (!visible) return null\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n aria-label=\"滚动到底部\"\n className=\"blade-chat-scroll-bottom absolute bottom-4 right-4 flex items-center gap-1 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-1.5 text-xs text-[hsl(var(--muted-foreground))] shadow-lg transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\"\n >\n <ChevronDown size={14} />\n {/* 窄容器里这行字会横跨近半个宽度,收起来只留图标;可访问名由 aria-label 保住。 */}\n <span className=\"blade-chat-scroll-bottom-label\">滚动到底部</span>\n </button>\n )\n}\n\nfunction PlanningDivider({ kind }: { kind: \"enter\" | \"exit\" }) {\n return (\n <div className=\"flex items-center gap-3 py-1\">\n <div className=\"h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent\" />\n <div className=\"inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300\">\n <Lightbulb size={12} />\n <span>{kind === \"enter\" ? \"进入规划模式\" : \"规划完成\"}</span>\n </div>\n <div className=\"h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent\" />\n </div>\n )\n}\n","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport { useCallback, useMemo, useRef, useState, } from \"react\";\nconst DEFAULT_SPRING_ANIMATION = {\n /**\n * A value from 0 to 1, on how much to damp the animation.\n * 0 means no damping, 1 means full damping.\n *\n * @default 0.7\n */\n damping: 0.7,\n /**\n * The stiffness of how fast/slow the animation gets up to speed.\n *\n * @default 0.05\n */\n stiffness: 0.05,\n /**\n * The inertial mass associated with the animation.\n * Higher numbers make the animation slower.\n *\n * @default 1.25\n */\n mass: 1.25,\n};\nconst STICK_TO_BOTTOM_OFFSET_PX = 70;\nconst SIXTY_FPS_INTERVAL_MS = 1000 / 60;\nconst RETAIN_ANIMATION_DURATION_MS = 350;\nlet mouseDown = false;\nglobalThis.document?.addEventListener(\"mousedown\", () => {\n mouseDown = true;\n});\nglobalThis.document?.addEventListener(\"mouseup\", () => {\n mouseDown = false;\n});\nglobalThis.document?.addEventListener(\"click\", () => {\n mouseDown = false;\n});\nexport const useStickToBottom = (options = {}) => {\n const [escapedFromLock, updateEscapedFromLock] = useState(false);\n const [isAtBottom, updateIsAtBottom] = useState(options.initial !== false);\n const [isNearBottom, setIsNearBottom] = useState(false);\n const optionsRef = useRef(null);\n optionsRef.current = options;\n const isSelecting = useCallback(() => {\n if (!mouseDown) {\n return false;\n }\n const selection = window.getSelection();\n if (!selection || !selection.rangeCount) {\n return false;\n }\n const range = selection.getRangeAt(0);\n return (range.commonAncestorContainer.contains(scrollRef.current) ||\n scrollRef.current?.contains(range.commonAncestorContainer));\n }, []);\n const setIsAtBottom = useCallback((isAtBottom) => {\n state.isAtBottom = isAtBottom;\n updateIsAtBottom(isAtBottom);\n }, []);\n const setEscapedFromLock = useCallback((escapedFromLock) => {\n state.escapedFromLock = escapedFromLock;\n updateEscapedFromLock(escapedFromLock);\n }, []);\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const state = useMemo(() => {\n let lastCalculation;\n return {\n escapedFromLock,\n isAtBottom,\n resizeDifference: 0,\n accumulated: 0,\n velocity: 0,\n listeners: new Set(),\n get scrollTop() {\n return scrollRef.current?.scrollTop ?? 0;\n },\n set scrollTop(scrollTop) {\n if (scrollRef.current) {\n scrollRef.current.scrollTop = scrollTop;\n state.ignoreScrollToTop = scrollRef.current.scrollTop;\n }\n },\n get targetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n return (scrollRef.current.scrollHeight - 1 - scrollRef.current.clientHeight);\n },\n get calculatedTargetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n const { targetScrollTop } = this;\n if (!options.targetScrollTop) {\n return targetScrollTop;\n }\n if (lastCalculation?.targetScrollTop === targetScrollTop) {\n return lastCalculation.calculatedScrollTop;\n }\n const calculatedScrollTop = Math.max(Math.min(options.targetScrollTop(targetScrollTop, {\n scrollElement: scrollRef.current,\n contentElement: contentRef.current,\n }), targetScrollTop), 0);\n lastCalculation = { targetScrollTop, calculatedScrollTop };\n requestAnimationFrame(() => {\n lastCalculation = undefined;\n });\n return calculatedScrollTop;\n },\n get scrollDifference() {\n return this.calculatedTargetScrollTop - this.scrollTop;\n },\n get isNearBottom() {\n return this.scrollDifference <= STICK_TO_BOTTOM_OFFSET_PX;\n },\n };\n }, []);\n const scrollToBottom = useCallback((scrollOptions = {}) => {\n if (typeof scrollOptions === \"string\") {\n scrollOptions = { animation: scrollOptions };\n }\n if (!scrollOptions.preserveScrollPosition) {\n setIsAtBottom(true);\n }\n const waitElapsed = Date.now() + (Number(scrollOptions.wait) || 0);\n const behavior = mergeAnimations(optionsRef.current, scrollOptions.animation);\n const { ignoreEscapes = false } = scrollOptions;\n let durationElapsed;\n let startTarget = state.calculatedTargetScrollTop;\n if (scrollOptions.duration instanceof Promise) {\n scrollOptions.duration.finally(() => {\n durationElapsed = Date.now();\n });\n }\n else {\n durationElapsed = waitElapsed + (scrollOptions.duration ?? 0);\n }\n const next = async () => {\n const promise = new Promise(requestAnimationFrame).then(() => {\n if (!state.isAtBottom) {\n state.animation = undefined;\n return false;\n }\n const { scrollTop } = state;\n const tick = performance.now();\n const tickDelta = (tick - (state.lastTick ?? tick)) / SIXTY_FPS_INTERVAL_MS;\n state.animation || (state.animation = { behavior, promise, ignoreEscapes });\n if (state.animation.behavior === behavior) {\n state.lastTick = tick;\n }\n if (isSelecting()) {\n return next();\n }\n if (waitElapsed > Date.now()) {\n return next();\n }\n if (scrollTop < Math.min(startTarget, state.calculatedTargetScrollTop)) {\n if (state.animation?.behavior === behavior) {\n if (behavior === \"instant\") {\n state.scrollTop = state.calculatedTargetScrollTop;\n return next();\n }\n state.velocity =\n (behavior.damping * state.velocity +\n behavior.stiffness * state.scrollDifference) /\n behavior.mass;\n state.accumulated += state.velocity * tickDelta;\n state.scrollTop += state.accumulated;\n if (state.scrollTop !== scrollTop) {\n state.accumulated = 0;\n }\n }\n return next();\n }\n if (durationElapsed > Date.now()) {\n startTarget = state.calculatedTargetScrollTop;\n return next();\n }\n state.animation = undefined;\n /**\n * If we're still below the target, then queue\n * up another scroll to the bottom with the last\n * requested animatino.\n */\n if (state.scrollTop < state.calculatedTargetScrollTop) {\n return scrollToBottom({\n animation: mergeAnimations(optionsRef.current, optionsRef.current.resize),\n ignoreEscapes,\n duration: Math.max(0, durationElapsed - Date.now()) || undefined,\n });\n }\n return state.isAtBottom;\n });\n return promise.then((isAtBottom) => {\n requestAnimationFrame(() => {\n if (!state.animation) {\n state.lastTick = undefined;\n state.velocity = 0;\n }\n });\n return isAtBottom;\n });\n };\n if (scrollOptions.wait !== true) {\n state.animation = undefined;\n }\n if (state.animation?.behavior === behavior) {\n return state.animation.promise;\n }\n return next();\n }, [setIsAtBottom, isSelecting, state]);\n const stopScroll = useCallback(() => {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }, [setEscapedFromLock, setIsAtBottom]);\n const handleScroll = useCallback(({ target }) => {\n if (target !== scrollRef.current) {\n return;\n }\n const { scrollTop, ignoreScrollToTop } = state;\n let { lastScrollTop = scrollTop } = state;\n state.lastScrollTop = scrollTop;\n state.ignoreScrollToTop = undefined;\n if (ignoreScrollToTop && ignoreScrollToTop > scrollTop) {\n /**\n * When the user scrolls up while the animation plays, the `scrollTop` may\n * not come in separate events; if this happens, to make sure `isScrollingUp`\n * is correct, set the lastScrollTop to the ignored event.\n */\n lastScrollTop = ignoreScrollToTop;\n }\n setIsNearBottom(state.isNearBottom);\n /**\n * Scroll events may come before a ResizeObserver event,\n * so in order to ignore resize events correctly we use a\n * timeout.\n *\n * @see https://github.com/WICG/resize-observer/issues/25#issuecomment-248757228\n */\n setTimeout(() => {\n /**\n * When theres a resize difference ignore the resize event.\n */\n if (state.resizeDifference || scrollTop === ignoreScrollToTop) {\n return;\n }\n if (isSelecting()) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n return;\n }\n const isScrollingDown = scrollTop > lastScrollTop;\n const isScrollingUp = scrollTop < lastScrollTop;\n if (state.animation?.ignoreEscapes) {\n state.scrollTop = lastScrollTop;\n return;\n }\n if (isScrollingUp) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n if (isScrollingDown) {\n setEscapedFromLock(false);\n }\n if (!state.escapedFromLock && state.isNearBottom) {\n setIsAtBottom(true);\n }\n }, 1);\n }, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);\n const handleWheel = useCallback(({ target, deltaY }) => {\n let element = target;\n while (![\"scroll\", \"auto\"].includes(getComputedStyle(element).overflow)) {\n if (!element.parentElement) {\n return;\n }\n element = element.parentElement;\n }\n /**\n * The browser may cancel the scrolling from the mouse wheel\n * if we update it from the animation in meantime.\n * To prevent this, always escape when the wheel is scrolled up.\n */\n if (element === scrollRef.current &&\n deltaY < 0 &&\n scrollRef.current.scrollHeight > scrollRef.current.clientHeight &&\n !state.animation?.ignoreEscapes) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n }, [setEscapedFromLock, setIsAtBottom, state]);\n const scrollRef = useRefCallback((scroll) => {\n scrollRef.current?.removeEventListener(\"scroll\", handleScroll);\n scrollRef.current?.removeEventListener(\"wheel\", handleWheel);\n scroll?.addEventListener(\"scroll\", handleScroll, { passive: true });\n scroll?.addEventListener(\"wheel\", handleWheel, { passive: true });\n }, []);\n const contentRef = useRefCallback((content) => {\n state.resizeObserver?.disconnect();\n if (!content) {\n return;\n }\n let previousHeight;\n state.resizeObserver = new ResizeObserver(([entry]) => {\n const { height } = entry.contentRect;\n const difference = height - (previousHeight ?? height);\n state.resizeDifference = difference;\n /**\n * Sometimes the browser can overscroll past the target,\n * so check for this and adjust appropriately.\n */\n if (state.scrollTop > state.targetScrollTop) {\n state.scrollTop = state.targetScrollTop;\n }\n setIsNearBottom(state.isNearBottom);\n if (difference >= 0) {\n /**\n * If it's a positive resize, scroll to the bottom when\n * we're already at the bottom.\n */\n const animation = mergeAnimations(optionsRef.current, previousHeight\n ? optionsRef.current.resize\n : optionsRef.current.initial);\n scrollToBottom({\n animation,\n wait: true,\n preserveScrollPosition: true,\n duration: animation === \"instant\" ? undefined : RETAIN_ANIMATION_DURATION_MS,\n });\n }\n else {\n /**\n * Else if it's a negative resize, check if we're near the bottom\n * if we are want to un-escape from the lock, because the resize\n * could have caused the container to be at the bottom.\n */\n if (state.isNearBottom) {\n setEscapedFromLock(false);\n setIsAtBottom(true);\n }\n }\n previousHeight = height;\n /**\n * Reset the resize difference after the scroll event\n * has fired. Requires a rAF to wait for the scroll event,\n * and a setTimeout to wait for the other timeout we have in\n * resizeObserver in case the scroll event happens after the\n * resize event.\n */\n requestAnimationFrame(() => {\n setTimeout(() => {\n if (state.resizeDifference === difference) {\n state.resizeDifference = 0;\n }\n }, 1);\n });\n });\n state.resizeObserver?.observe(content);\n }, []);\n return {\n contentRef,\n scrollRef,\n scrollToBottom,\n stopScroll,\n isAtBottom: isAtBottom || isNearBottom,\n isNearBottom,\n escapedFromLock,\n state,\n };\n};\nfunction useRefCallback(callback, deps) {\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const result = useCallback((ref) => {\n result.current = ref;\n return callback(ref);\n }, deps);\n return result;\n}\nconst animationCache = new Map();\nfunction mergeAnimations(...animations) {\n const result = { ...DEFAULT_SPRING_ANIMATION };\n let instant = false;\n for (const animation of animations) {\n if (animation === \"instant\") {\n instant = true;\n continue;\n }\n if (typeof animation !== \"object\") {\n continue;\n }\n instant = false;\n result.damping = animation.damping ?? result.damping;\n result.stiffness = animation.stiffness ?? result.stiffness;\n result.mass = animation.mass ?? result.mass;\n }\n const key = JSON.stringify(result);\n if (!animationCache.has(key)) {\n animationCache.set(key, Object.freeze(result));\n }\n return instant ? \"instant\" : animationCache.get(key);\n}\n","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport * as React from \"react\";\nimport { createContext, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, } from \"react\";\nimport { useStickToBottom, } from \"./useStickToBottom.js\";\nconst StickToBottomContext = createContext(null);\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\nexport function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {\n const customTargetScrollTop = useRef(null);\n const targetScrollTop = React.useCallback((target, elements) => {\n const get = context?.targetScrollTop ?? currentTargetScrollTop;\n return get?.(target, elements) ?? target;\n }, [currentTargetScrollTop]);\n const defaultInstance = useStickToBottom({\n mass,\n damping,\n stiffness,\n resize,\n initial,\n targetScrollTop,\n });\n const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state, } = instance ?? defaultInstance;\n const context = useMemo(() => ({\n scrollToBottom,\n stopScroll,\n scrollRef,\n isAtBottom,\n escapedFromLock,\n contentRef,\n state,\n get targetScrollTop() {\n return customTargetScrollTop.current;\n },\n set targetScrollTop(targetScrollTop) {\n customTargetScrollTop.current = targetScrollTop;\n },\n }), [\n scrollToBottom,\n isAtBottom,\n contentRef,\n scrollRef,\n stopScroll,\n escapedFromLock,\n state,\n ]);\n useImperativeHandle(contextRef, () => context, [context]);\n useIsomorphicLayoutEffect(() => {\n if (!scrollRef.current) {\n return;\n }\n if (getComputedStyle(scrollRef.current).overflow === \"visible\") {\n scrollRef.current.style.overflow = \"auto\";\n }\n }, []);\n return (React.createElement(StickToBottomContext.Provider, { value: context },\n React.createElement(\"div\", { ...props }, typeof children === \"function\" ? children(context) : children)));\n}\n(function (StickToBottom) {\n function Content({ children, scrollClassName, ...props }) {\n const context = useStickToBottomContext();\n return (React.createElement(\"div\", { ref: context.scrollRef, style: {\n height: \"100%\",\n width: \"100%\",\n scrollbarGutter: \"stable both-edges\",\n }, className: scrollClassName },\n React.createElement(\"div\", { ...props, ref: context.contentRef }, typeof children === \"function\" ? children(context) : children)));\n }\n StickToBottom.Content = Content;\n})(StickToBottom || (StickToBottom = {}));\n/**\n * Use this hook inside a <StickToBottom> component to gain access to whether the component is at the bottom of the scrollable area.\n */\nexport function useStickToBottomContext() {\n const context = useContext(StickToBottomContext);\n if (!context) {\n throw new Error(\"use-stick-to-bottom component context must be used within a StickToBottom component\");\n }\n return context;\n}\n","import type { AskUserAnswerData, ChatMessage } from \"@blade-hq/agent-client\"\nimport { getTextContent, normalizeMessageContent } from \"@blade-hq/agent-client\"\nimport { Brain, ChevronDown } from \"lucide-react\"\nimport { useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { AgentLoopBlock } from \"./AgentLoopBlock\"\nimport { MarkdownContent } from \"./MarkdownContent\"\nimport { Shimmer } from \"./Shimmer\"\nimport { ToolCallBlock, type ToolCallRenderer } from \"./ToolCallBlock\"\nimport { formatToolName } from \"./display-utils\"\n\ninterface Props {\n messages: ChatMessage[]\n isStreaming?: boolean\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n sessionStatus?: string\n toolCallRenderer?: ToolCallRenderer\n /** 会话 id;传给 Markdown 渲染的统一上下文。 */\n sessionId?: string\n}\n\n/** 思考折叠块:折叠状态归组件自身,不依赖全局 store。 */\nfunction ThinkingBlock({ reasoning, isStreaming }: { reasoning: string; isStreaming: boolean }) {\n const [open, setOpen] = useState(false)\n\n return (\n <div className=\"blade-chat-thinking ml-4 text-sm\">\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n className=\"inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]\"\n >\n <Brain size={12} className=\"shrink-0\" />\n {isStreaming ? <Shimmer className=\"text-xs\">正在思考</Shimmer> : <span>思考过程</span>}\n <span className=\"text-[hsl(var(--muted-foreground))]/70\">\n · {new Intl.NumberFormat(\"zh-CN\").format(reasoning.length)} 字\n </span>\n <ChevronDown\n size={12}\n className={cn(\"shrink-0 transition-transform\", open && \"rotate-180\")}\n />\n </button>\n {open && (\n <div className=\"mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]\">\n {reasoning}\n </div>\n )}\n </div>\n )\n}\n\nfunction getMessageText(message: ChatMessage): string {\n return getTextContent(normalizeMessageContent(message.content)).trim()\n}\n\nfunction findLatestReasoningMessageIndex(messages: readonly ChatMessage[]): number {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (messages[index].reasoning) return index\n }\n return -1\n}\n\n/**\n * 一轮助手回复:按消息顺序渲染 thinking 折叠块、正文 Markdown 与工具调用。\n * 相比第一方版本去掉了紧凑/详细模式切换、文件交付卡片与内联资源 iframe。\n */\nexport function AssistantTurnBlock({\n messages,\n isStreaming = false,\n askAnswers,\n onAnswer,\n sessionStatus,\n toolCallRenderer,\n sessionId,\n}: Props) {\n const hasInterrupted = messages.some((message) => message.status === \"interrupted\")\n const hasAnyContent = messages.some(\n (message) =>\n getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0,\n )\n const latestReasoningIndex = isStreaming\n ? findLatestReasoningMessageIndex(messages)\n : -1\n\n return (\n <div className=\"blade-chat-assistant-turn flex flex-col gap-3\">\n {hasInterrupted && (\n <div className=\"ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300\">\n 已中断\n </div>\n )}\n {messages.map((message, index) => {\n const isLast = index === messages.length - 1\n const streamingThis = isStreaming && isLast\n const text = getMessageText(message)\n const toolCalls = message.tool_calls ?? []\n const showReasoning =\n !!message.reasoning && (!isStreaming || index === latestReasoningIndex)\n\n return (\n <div\n key={message.entry_id ?? `${message.timestamp ?? \"assistant\"}-${index}`}\n className=\"flex flex-col gap-3\"\n >\n {showReasoning && message.reasoning && (\n <ThinkingBlock reasoning={message.reasoning} isStreaming={streamingThis && !text} />\n )}\n {text && (\n <div className=\"blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]\">\n <MarkdownContent\n mode={streamingThis ? \"streaming\" : \"static\"}\n className=\"blade-chat-prose\"\n sessionId={sessionId}\n >\n {text}\n </MarkdownContent>\n </div>\n )}\n {toolCalls.length > 0 && (\n <div className=\"flex flex-col gap-2\">\n {toolCalls.map((toolCall) =>\n formatToolName(toolCall.name) === \"Agent\" ? (\n <AgentLoopBlock key={toolCall.id} toolCall={toolCall} />\n ) : (\n <ToolCallBlock\n key={toolCall.id}\n toolCall={toolCall}\n answerData={askAnswers?.[toolCall.id]}\n onAnswer={onAnswer}\n answered={sessionStatus !== \"waiting_for_input\"}\n sessionStatus={sessionStatus}\n renderer={toolCallRenderer}\n />\n ),\n )}\n </div>\n )}\n </div>\n )\n })}\n {isStreaming && !hasAnyContent && <Shimmer className=\"ml-4\">正在生成...</Shimmer>}\n </div>\n )\n}\n","import type { ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { Bot, Check, ChevronRight, Loader2, X } from \"lucide-react\"\nimport { useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { formatToolDuration, formatToolResult } from \"./display-utils\"\n\ninterface Props {\n toolCall: ToolCallInfo\n}\n\nfunction parseAgentDescription(argumentsJson: string): string {\n try {\n const parsed = JSON.parse(argumentsJson)\n return typeof parsed?.description === \"string\" && parsed.description\n ? parsed.description\n : \"子智能体\"\n } catch {\n return \"子智能体\"\n }\n}\n\n/**\n * 子智能体(fork Agent)状态块的 SDK 简化版:只展示描述、状态与最终结果,\n * 不再嵌套渲染子循环的完整消息流(那是第一方界面的能力)。\n */\nexport function AgentLoopBlock({ toolCall }: Props) {\n const [expanded, setExpanded] = useState(false)\n const description = parseAgentDescription(toolCall.arguments)\n const running = toolCall.status === \"pending\" || toolCall.status === \"awaiting_answer\"\n const failed = toolCall.status === \"error\" || toolCall.status === \"cancelled\"\n\n return (\n <div className=\"blade-chat-agent-loop ml-4 text-xs\">\n <div\n className={cn(\n \"border-l-[3px] flex items-center gap-2 px-3 py-2\",\n failed\n ? \"border-l-[hsl(var(--muted-foreground)/0.5)]\"\n : running\n ? \"border-l-blue-500\"\n : \"border-l-[hsl(var(--primary))]\",\n )}\n >\n <button\n type=\"button\"\n onClick={() => setExpanded(!expanded)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none\"\n aria-expanded={expanded}\n >\n <ChevronRight\n size={11}\n className={cn(\n \"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform\",\n expanded && \"rotate-90\",\n )}\n />\n <Bot size={12} className=\"shrink-0 text-[hsl(var(--muted-foreground))]\" />\n <span\n className={cn(\n \"flex shrink-0 items-center gap-1 text-[10px]\",\n failed\n ? \"text-[hsl(var(--muted-foreground))]\"\n : running\n ? \"text-blue-300\"\n : \"text-[hsl(var(--primary))]\",\n )}\n >\n {running ? (\n <Loader2 size={11} className=\"animate-spin\" />\n ) : failed ? (\n <X size={11} />\n ) : (\n <Check size={11} />\n )}\n <span>{running ? \"执行中\" : failed ? \"已终止\" : \"完成\"}</span>\n </span>\n <span className=\"min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]\">\n 子智能体:{description}\n </span>\n </button>\n {typeof toolCall.duration_ms === \"number\" && toolCall.duration_ms > 0 && (\n <span className=\"shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {formatToolDuration(toolCall.duration_ms)}\n </span>\n )}\n </div>\n {expanded && toolCall.result != null && (\n <div className=\"ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3\">\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 结果\n </div>\n <pre className=\"max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolResult(toolCall.result)}\n </pre>\n </div>\n )}\n </div>\n )\n}\n","import type { ToolCallInfo } from \"@blade-hq/agent-client\"\n\n/** 工具名归一化别名表:把后端各种命名风格统一为展示用的驼峰名。 */\nconst TOOL_NAME_ALIASES: Record<string, string> = {\n agent: \"Agent\",\n ask_user_question: \"AskUserQuestion\",\n bash: \"Bash\",\n bg_bash: \"BgBash\",\n edit: \"Edit\",\n exit_plan_mode: \"ExitPlanMode\",\n file_edit: \"Edit\",\n file_read: \"Read\",\n file_write: \"Write\",\n finish_task: \"FinishTask\",\n glob: \"Glob\",\n grep: \"Grep\",\n ls: \"Ls\",\n read: \"Read\",\n read_skill: \"ReadSkill\",\n web_fetch: \"WebFetch\",\n web_search: \"WebSearch\",\n write: \"Write\",\n}\n\n/** 系统工具的中文展示名。 */\nconst TOOL_DISPLAY_LABELS: Record<string, string> = {\n Bash: \"执行命令\",\n BgBash: \"后台执行命令\",\n Read: \"读取文件\",\n Write: \"写入文件\",\n Edit: \"编辑文件\",\n Ls: \"列出目录\",\n Glob: \"匹配文件\",\n Grep: \"搜索文本\",\n WebSearch: \"搜索网页\",\n WebFetch: \"整理网页内容\",\n Agent: \"派生子智能体\",\n AskUserQuestion: \"向用户提问\",\n ReadSkill: \"读取技能\",\n FinishTask: \"任务完成\",\n ExitPlanMode: \"提交计划\",\n ListSessions: \"列出历史会话\",\n GetSessionHistory: \"读取会话历史\",\n}\n\nexport type ToolTone = \"emerald\" | \"blue\" | \"amber\" | \"red\"\n\nfunction safeParseJson(value: string | null | undefined): unknown {\n if (!value) return null\n try {\n return JSON.parse(value)\n } catch {\n return null\n }\n}\n\nfunction getStringArgValue(args: Record<string, unknown> | null, key: string): string {\n const value = args?.[key]\n return typeof value === \"string\" ? value.trim() : \"\"\n}\n\n/** 归一化工具名:去掉命名空间前缀并映射到别名表(如 \"fork:Agent\" → \"Agent\")。 */\nexport function formatToolName(name: string): string {\n const trimmed = name.trim()\n if (!trimmed) return name\n\n const stripped = trimmed.split(\":\").pop()?.split(\"/\").pop()?.split(\".\").pop()?.trim() || trimmed\n const normalized = stripped\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .toLowerCase()\n\n return TOOL_NAME_ALIASES[normalized] ?? stripped\n}\n\n/**\n * 返回工具调用的中文展示标签:优先取参数里的 description,\n * 其次取后端下发的 display_name / 系统工具中文名。\n */\nexport function getToolDisplayLabel(toolCall: ToolCallInfo): string {\n const normalized = formatToolName(toolCall.name)\n const args = safeParseJson(toolCall.arguments) as Record<string, unknown> | null\n const displayName = toolCall.display_name?.trim() ?? \"\"\n const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized\n const metaDisplayName = getStringArgValue(args, \"_meta_display_name\")\n if (metaDisplayName) {\n return metaDisplayName\n }\n\n const description = getStringArgValue(args, \"description\")\n\n if (normalized === \"BgBash\") {\n return description ? `后台执行:${description}` : \"后台执行命令\"\n }\n if (normalized === \"ReadSkill\") {\n const skillName = getStringArgValue(args, \"skill\") || getStringArgValue(args, \"skill_name\")\n return skillName ? `${baseLabel}「${skillName}」` : baseLabel\n }\n if (normalized === \"FinishTask\") {\n const title = getStringArgValue(args, \"title\")\n return title ? `${baseLabel}:${title}` : baseLabel\n }\n\n return description || baseLabel\n}\n\nexport function getToolTone(status?: ToolCallInfo[\"status\"]): ToolTone {\n if (status === \"error\" || status === \"cancelled\") return \"red\"\n if (status === \"awaiting_answer\") return \"amber\"\n if (status === \"pending\") return \"blue\"\n return \"emerald\"\n}\n\nexport function getToolStatusLabel(status?: ToolCallInfo[\"status\"]): string {\n if (status === \"pending\") return \"运行中\"\n if (status === \"awaiting_answer\") return \"等待回答\"\n if (status === \"error\") return \"错误\"\n if (status === \"cancelled\") return \"已取消\"\n return \"完成\"\n}\n\nexport function formatToolDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`\n const seconds = ms / 1000\n if (seconds < 60) return `${seconds.toFixed(1)}s`\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.round(seconds % 60)\n return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`\n}\n\n/** 参数 JSON 美化:无法解析时按原文展示。 */\nexport function formatToolArgs(args: string): string {\n try {\n return JSON.stringify(JSON.parse(args), null, 2)\n } catch {\n return args\n }\n}\n\nconst RESULT_PREVIEW_LIMIT = 4000\n\n/** 工具结果的文本化 + 截断:长结果只在折叠面板里展示前若干字符。 */\nexport function formatToolResult(result: unknown): string {\n const text = typeof result === \"string\" ? result : JSON.stringify(result, null, 2)\n if (text == null) return \"\"\n if (text.length <= RESULT_PREVIEW_LIMIT) return text\n return `${text.slice(0, RESULT_PREVIEW_LIMIT)}\\n…(结果过长,已截断)`\n}\n","import { Check, Copy } from \"lucide-react\"\nimport {\n type ComponentProps,\n type ComponentPropsWithRef,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\"\nimport { type ExtraProps, Streamdown } from \"streamdown\"\nimport { cn, copyToClipboard } from \"../lib/utils\"\n\ntype StreamdownProps = ComponentProps<typeof Streamdown>\n\n/** 平台内部提醒标记:不属于给用户看的内容,渲染前整段剥掉。 */\nconst SYSTEM_REMINDER_RE = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi\n\ntype PreProps = ComponentPropsWithRef<\"pre\"> & ExtraProps\n\n/** 代码块外壳:常驻头部栏(语言标签 + 复制按钮),代码区浅底。 */\nfunction CodeBlockPre({ children, node: _node, ...props }: PreProps) {\n const preRef = useRef<HTMLPreElement>(null)\n const [copied, setCopied] = useState(false)\n const [language, setLanguage] = useState(\"\")\n\n // DOM 测量:语言标签写在内层 code 的 className 上,只能挂载后读取。\n useEffect(() => {\n const codeEl = preRef.current?.querySelector(\"code\")\n setLanguage(codeEl?.className.match(/language-(\\S+)/)?.[1] ?? \"\")\n }, [])\n\n const handleCopy = async () => {\n const ok = await copyToClipboard(preRef.current?.querySelector(\"code\")?.textContent ?? \"\")\n if (ok) {\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }\n\n return (\n <div className=\"blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]\">\n <div className=\"blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5\">\n <span className=\"font-mono text-[12px] text-[hsl(var(--muted-foreground))]\">\n {language || \"code\"}\n </span>\n <button\n type=\"button\"\n onClick={handleCopy}\n className={cn(\n \"flex h-6 items-center gap-1 rounded-md px-1.5 text-[11px] transition-colors\",\n copied\n ? \"text-[hsl(var(--primary))]\"\n : \"text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n )}\n >\n {copied ? <Check size={12} /> : <Copy size={12} />}\n <span>{copied ? \"已复制\" : \"复制\"}</span>\n </button>\n </div>\n <pre\n ref={preRef}\n {...props}\n className={cn(props.className, \"blade-chat-codeblock-body m-0 overflow-x-auto\")}\n >\n {children}\n </pre>\n </div>\n )\n}\n\ntype AnchorProps = ComponentPropsWithRef<\"a\"> & ExtraProps\n\n/** 链接一律新窗口打开并断开 opener,SDK 不接管站内跳转。 */\nfunction ExternalAnchor({ node: _node, children, ...props }: AnchorProps) {\n return (\n <a {...props} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n )\n}\n\nconst MARKDOWN_COMPONENTS = {\n pre: CodeBlockPre,\n a: ExternalAnchor,\n} as NonNullable<StreamdownProps[\"components\"]>\n\nexport interface MarkdownContentProps {\n children: string\n className?: string\n /** 流式渲染传 \"streaming\",历史静态内容默认 \"static\"。 */\n mode?: \"streaming\" | \"static\"\n /** 会话 id;保留给聊天组件统一透传上下文。 */\n sessionId?: string\n}\n\n/**\n * SDK 版 Markdown 渲染:附带代码块复制按钮;相比第一方版本去掉了\n * mermaid、外链确认弹窗与文件预览。\n */\nexport function MarkdownContent({ children, className, mode, sessionId }: MarkdownContentProps) {\n const resolvedChildren = useMemo(() => {\n return children.replace(SYSTEM_REMINDER_RE, \"\")\n }, [children])\n\n return (\n <Streamdown\n // Streamdown 外层 memo 不比较 components,光换 sessionId 可能留着旧闭包,\n // 卡片就会拿旧会话去下载。挂 key 强制重建,切会话本来也要换内容。\n key={sessionId ?? \"no-session\"}\n className={cn(\"blade-chat-markdown break-words\", className)}\n mode={mode ?? \"static\"}\n components={MARKDOWN_COMPONENTS}\n >\n {resolvedChildren}\n </Streamdown>\n )\n}\n","import { cn } from \"../lib/utils\"\n\ninterface ShimmerProps {\n children?: string\n className?: string\n}\n\n/** 流式等待时的呼吸文字效果(样式见 style.css 的 .blade-shimmer-text)。 */\nexport function Shimmer({ children = \"正在思考...\", className }: ShimmerProps) {\n return (\n <span className={cn(\"blade-shimmer-text text-sm font-medium\", className)}>{children}</span>\n )\n}\n","import type { AskUserAnswerData, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { Check, ChevronRight, Loader2, MessageSquareMore, X } from \"lucide-react\"\nimport { useState, type ReactNode } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { AskUserQuestionBlock, parseAskUserQuestion } from \"./AskUserQuestionBlock\"\nimport {\n formatToolArgs,\n formatToolDuration,\n formatToolName,\n formatToolResult,\n getToolDisplayLabel,\n getToolStatusLabel,\n getToolTone,\n} from \"./display-utils\"\n\n/** 接入方自定义渲染器:返回 null 时回落到默认渲染。 */\nexport type ToolCallRenderer = (info: ToolCallInfo) => ReactNode | null\n\ninterface Props {\n toolCall: ToolCallInfo\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n answered?: boolean\n answerData?: AskUserAnswerData\n sessionStatus?: string\n renderer?: ToolCallRenderer\n}\n\n/**\n * 判断提问卡片该等待作答还是展示成已作答,只认工具状态。\n * 与 apps/web 的 ToolCallBlock 里同名函数保持一致:两处算出不同结果时,\n * 同一份会话在内置页面能点、在 SDK 里点不动。\n *\n * 不要再拿 sessionStatus 兜底。历史投影算错时用 waiting_for_input 补救,\n * 会同时把已经回答过的旧提问重新变成可作答(历史加载不会带回 answerData),\n * 而且会盖住投影层的错误——这个 bug 就是这么藏住的。\n */\nexport function resolveAskQuestionState({\n toolStatus,\n hasAnswerData,\n fallbackAnswered,\n}: {\n toolStatus: ToolCallInfo[\"status\"]\n hasAnswerData: boolean\n fallbackAnswered?: boolean\n}) {\n const awaitingAnswer = !hasAnswerData && toolStatus === \"awaiting_answer\"\n return {\n awaitingAnswer,\n answered:\n hasAnswerData ||\n (!awaitingAnswer &&\n (Boolean(fallbackAnswered) ||\n toolStatus === \"done\" ||\n toolStatus === \"cancelled\" ||\n toolStatus === \"error\")),\n }\n}\n\n/**\n * 极简工具调用块:状态 + 展示名一行,点开后是参数/结果的 <pre> 折叠展示。\n * AskUserQuestion 特殊处理为提问选项卡片。\n */\nexport function ToolCallBlock({\n toolCall,\n onAnswer,\n answered,\n answerData,\n sessionStatus,\n renderer,\n}: Props) {\n const [expanded, setExpanded] = useState(false)\n const normalizedName = formatToolName(toolCall.name)\n\n if (renderer) {\n const custom = renderer(toolCall)\n if (custom !== null && custom !== undefined) {\n return <>{custom}</>\n }\n }\n\n // AskUserQuestion:渲染成可作答的提问卡片\n if (normalizedName === \"AskUserQuestion\") {\n const askData =\n parseAskUserQuestion(typeof toolCall.result === \"string\" ? toolCall.result : null) ??\n parseAskUserQuestion(buildAskUserPayload(toolCall.arguments))\n const questionState = resolveAskQuestionState({\n toolStatus: toolCall.status,\n hasAnswerData: Boolean(answerData),\n fallbackAnswered: answered,\n })\n const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer)\n if (askData) {\n return (\n <AskUserQuestionBlock\n data={askData}\n answered={questionState.answered}\n toolCallId={toolCall.id}\n sessionStatus={sessionStatus ?? \"\"}\n answerData={answerData}\n onAnswer={canAnswer ? onAnswer : undefined}\n />\n )\n }\n if (toolCall.status === \"pending\") {\n return (\n <div className=\"ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={14} className=\"animate-spin\" />\n <span>正在准备问题…</span>\n </div>\n )\n }\n return (\n <div className=\"ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]\">\n <div className=\"font-semibold\">选择题内容暂时无法显示</div>\n <div className=\"mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]\">\n 收到的交互数据不完整。请让智能体重新提问。\n </div>\n </div>\n )\n }\n\n const tone = getToolTone(toolCall.status)\n const displayName = getToolDisplayLabel(toolCall)\n const toneClass =\n tone === \"red\"\n ? \"border-l-[hsl(var(--muted-foreground)/0.5)]\"\n : tone === \"amber\"\n ? \"border-l-amber-400\"\n : tone === \"blue\"\n ? \"border-l-blue-500\"\n : \"border-l-[hsl(var(--primary))]\"\n\n const statusIcon =\n toolCall.status === \"pending\" ? (\n <Loader2 size={11} className=\"animate-spin\" />\n ) : toolCall.status === \"awaiting_answer\" ? (\n <MessageSquareMore size={11} />\n ) : toolCall.status === \"cancelled\" || toolCall.status === \"error\" ? (\n <X size={11} />\n ) : (\n <Check size={11} />\n )\n\n const statusTextClass =\n tone === \"red\"\n ? \"text-[hsl(var(--muted-foreground))]\"\n : tone === \"amber\"\n ? \"text-amber-300\"\n : tone === \"blue\"\n ? \"text-blue-300\"\n : \"text-[hsl(var(--primary))]\"\n\n return (\n <div className=\"blade-chat-tool ml-4 text-xs\">\n <div className={cn(\"border-l-[3px] flex items-center gap-2 px-3 py-2\", toneClass)}>\n <button\n type=\"button\"\n onClick={() => setExpanded(!expanded)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none\"\n aria-expanded={expanded}\n >\n <ChevronRight\n size={11}\n className={cn(\n \"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform\",\n expanded && \"rotate-90\",\n )}\n />\n <span className={cn(\"flex shrink-0 items-center gap-1 text-[10px]\", statusTextClass)}>\n {statusIcon}\n <span>{getToolStatusLabel(toolCall.status)}</span>\n </span>\n <span className=\"min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]\">\n {displayName}\n </span>\n </button>\n {typeof toolCall.duration_ms === \"number\" && toolCall.duration_ms > 0 && (\n <span className=\"shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {formatToolDuration(toolCall.duration_ms)}\n </span>\n )}\n </div>\n {expanded && (\n <div className=\"blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3\">\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 工具\n </div>\n <div className=\"mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {normalizedName}\n </div>\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 参数\n </div>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolArgs(toolCall.arguments)}\n </pre>\n {toolCall.result != null && (\n <>\n <div className=\"mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 结果\n </div>\n <pre className=\"max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolResult(toolCall.result)}\n </pre>\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction buildAskUserPayload(argumentsJson: string): string | null {\n try {\n const parsed = JSON.parse(argumentsJson)\n if (\n parsed &&\n typeof parsed === \"object\" &&\n Array.isArray((parsed as { questions?: unknown }).questions)\n ) {\n return JSON.stringify({ questions: (parsed as { questions: unknown[] }).questions })\n }\n } catch {\n return null\n }\n return null\n}\n","import type { AskUserAnswerData } from \"@blade-hq/agent-client\"\nimport { Check, Loader2, MessageSquareMore } from \"lucide-react\"\nimport { useEffect, useMemo, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { MarkdownContent } from \"./MarkdownContent\"\n\ninterface OptionItem {\n label: string\n description: string\n}\n\ninterface QuestionItem {\n question: string\n options: OptionItem[]\n multiSelect?: boolean\n}\n\ninterface SourceLoopInfo {\n loop_name: string\n description: string\n}\n\nexport interface AskUserQuestionData {\n questions: QuestionItem[]\n source_loop?: SourceLoopInfo | null\n}\n\ninterface Props {\n data: AskUserQuestionData\n answered: boolean\n toolCallId: string\n sessionStatus: string\n answerData?: AskUserAnswerData | undefined\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n}\n\n/** 提问选项卡片:单选/多选 + 自定义输入,回答通过 onAnswer 回调交还 ChatView。 */\nexport function AskUserQuestionBlock({\n data,\n answered,\n toolCallId,\n sessionStatus,\n answerData,\n onAnswer,\n}: Props) {\n // 每个问题独立的选择状态:选项下标集合或自定义文本\n const [selections, setSelections] = useState<Map<number, Set<number>>>(new Map())\n const [customTexts, setCustomTexts] = useState<Map<number, string>>(new Map())\n const [usingCustom, setUsingCustom] = useState<Set<number>>(new Set())\n const [submitted, setSubmitted] = useState(false)\n\n useEffect(() => {\n if (sessionStatus === \"failed\" || sessionStatus === \"interrupted\") {\n setSubmitted(false)\n }\n }, [sessionStatus])\n\n // 已作答时按历史答案展示,未作答时展示本地编辑状态\n const displayAnswerState = useMemo(() => {\n if (!(answered && answerData)) {\n return { selections, customTexts, usingCustom }\n }\n\n const nextSelections = new Map<number, Set<number>>()\n const nextCustomTexts = new Map<number, string>()\n const nextUsingCustom = new Set<number>()\n\n for (const [questionKey, optionIndexes] of Object.entries(answerData.selections)) {\n nextSelections.set(Number(questionKey), new Set(optionIndexes))\n }\n\n for (const [questionKey, text] of Object.entries(answerData.custom)) {\n const qIdx = Number(questionKey)\n nextCustomTexts.set(qIdx, text)\n nextUsingCustom.add(qIdx)\n }\n\n return {\n selections: nextSelections,\n customTexts: nextCustomTexts,\n usingCustom: nextUsingCustom,\n }\n }, [answerData, answered, customTexts, selections, usingCustom])\n\n const toggleOption = (qIdx: number, optIdx: number, multi: boolean) => {\n if (answered || submitted) return\n setSelections((prev) => {\n const next = new Map(prev)\n const current = new Set(next.get(qIdx) ?? [])\n if (multi) {\n if (current.has(optIdx)) current.delete(optIdx)\n else current.add(optIdx)\n } else {\n current.clear()\n current.add(optIdx)\n }\n next.set(qIdx, current)\n return next\n })\n setUsingCustom((prev) => {\n const next = new Set(prev)\n next.delete(qIdx)\n return next\n })\n }\n\n const handleCustomFocus = (qIdx: number) => {\n if (answered || submitted) return\n setSelections((prev) => {\n const next = new Map(prev)\n next.delete(qIdx)\n return next\n })\n setUsingCustom((prev) => new Set(prev).add(qIdx))\n }\n\n const setCustomText = (qIdx: number, text: string) => {\n if (answered || submitted) return\n setCustomTexts((prev) => new Map(prev).set(qIdx, text))\n }\n\n const getAnswer = (qIdx: number): string | null => {\n const q = data.questions[qIdx]\n if (usingCustom.has(qIdx)) {\n const text = (customTexts.get(qIdx) ?? \"\").trim()\n return text || null\n }\n const sel = selections.get(qIdx)\n if (!sel || sel.size === 0) return null\n return [...sel]\n .sort()\n .map((i) => q.options[i].label)\n .join(\", \")\n }\n\n const allAnswered = data.questions.every((_, i) => getAnswer(i) !== null)\n\n const handleSubmit = () => {\n if (answered || submitted || !allAnswered || !onAnswer) return\n\n const nextAnswerData: AskUserAnswerData = {\n selections: Object.fromEntries(\n Array.from(selections.entries()).map(([qIdx, optionIndexes]) => [\n qIdx,\n Array.from(optionIndexes).sort((a, b) => a - b),\n ]),\n ) as Record<number, number[]>,\n custom: Object.fromEntries(\n Array.from(usingCustom)\n .map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? \"\").trim()] as const)\n .filter(([, text]) => text.length > 0),\n ) as Record<number, string>,\n }\n\n const parts = data.questions.map((q, i) => `- ${q.question} -> ${getAnswer(i)}`)\n const text = `关于需要确认的问题,用户的回答如下:\\n${parts.join(\"\\n\")}`\n setSubmitted(true)\n onAnswer(text, toolCallId, nextAnswerData)\n }\n\n return (\n <div\n className={cn(\n \"blade-chat-ask-card ml-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]\",\n answered\n ? \"max-w-2xl space-y-3 p-3 text-xs text-[hsl(var(--muted-foreground))] opacity-80\"\n : \"max-w-lg space-y-5 p-4 text-sm\",\n )}\n >\n {data.source_loop?.description && (\n <div className=\"rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]\">\n 子智能体「{data.source_loop.description}」在等待你的回答\n </div>\n )}\n {data.questions.map((q, qIdx) => (\n <QuestionCard\n key={q.question}\n question={q}\n qIdx={qIdx}\n answered={answered}\n selected={displayAnswerState.selections.get(qIdx) ?? new Set()}\n isCustom={displayAnswerState.usingCustom.has(qIdx)}\n customText={displayAnswerState.customTexts.get(qIdx) ?? \"\"}\n onToggle={toggleOption}\n onCustomFocus={handleCustomFocus}\n onCustomChange={setCustomText}\n />\n ))}\n\n {!answered && !submitted && onAnswer && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!allAnswered}\n className=\"w-full rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {allAnswered ? \"确认\" : \"请先选择一个选项\"}\n </button>\n )}\n\n {submitted && !answered && (\n <button\n type=\"button\"\n disabled\n className=\"flex w-full items-center justify-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] opacity-80\"\n >\n <Loader2 size={14} className=\"animate-spin\" />\n 确认中\n </button>\n )}\n </div>\n )\n}\n\nfunction QuestionCard({\n question,\n qIdx,\n answered,\n selected,\n isCustom,\n customText,\n onToggle,\n onCustomFocus,\n onCustomChange,\n}: {\n question: QuestionItem\n qIdx: number\n answered: boolean\n selected: Set<number>\n isCustom: boolean\n customText: string\n onToggle: (qIdx: number, optIdx: number, multi: boolean) => void\n onCustomFocus: (qIdx: number) => void\n onCustomChange: (qIdx: number, text: string) => void\n}) {\n const multi = question.multiSelect ?? false\n\n return (\n <div>\n <div className={cn(\"flex items-start gap-2\", answered ? \"mb-2\" : \"mb-3\")}>\n <MessageSquareMore\n size={answered ? 12 : 13}\n className=\"mt-0.5 shrink-0 text-[hsl(var(--primary))]\"\n />\n <div\n className={cn(\n \"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]\",\n answered ? \"text-xs\" : \"text-sm\",\n )}\n >\n <MarkdownContent className=\"blade-chat-prose\">{question.question}</MarkdownContent>\n </div>\n </div>\n\n <div className={cn(\"flex flex-col pl-5\", answered ? \"gap-1\" : \"gap-1.5\")}>\n {question.options.map((opt, optIdx) => {\n const isSel = selected.has(optIdx)\n return (\n <button\n key={opt.label}\n type=\"button\"\n disabled={answered}\n onClick={() => onToggle(qIdx, optIdx, multi)}\n className={cn(\n \"flex items-start gap-2.5 rounded-lg border text-left transition-all\",\n answered ? \"px-2.5 py-1.5\" : \"px-3 py-2.5\",\n isSel && !answered\n ? \"border-[hsl(var(--primary)/0.8)] bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]\"\n : isSel\n ? \"border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.08)] text-[hsl(var(--foreground))]\"\n : \"border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)]\",\n !answered &&\n !isSel &&\n \"hover:border-[hsl(var(--ring)/0.4)] hover:bg-[hsl(var(--accent))]\",\n answered && \"cursor-default opacity-70\",\n )}\n >\n {multi && (\n <div\n className={cn(\n \"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors\",\n isSel && !answered\n ? \"border-[hsl(var(--primary-foreground)/0.6)] bg-[hsl(var(--primary-foreground)/0.2)]\"\n : isSel\n ? \"border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.12)]\"\n : \"border-[hsl(var(--border))]\",\n )}\n >\n {isSel && (\n <Check\n size={9}\n className={\n answered\n ? \"text-[hsl(var(--primary))]\"\n : \"text-[hsl(var(--primary-foreground))]\"\n }\n />\n )}\n </div>\n )}\n <div className=\"min-w-0\">\n <div className={cn(\"font-medium\", answered ? \"text-xs\" : \"text-[13px]\")}>\n {opt.label}\n </div>\n {opt.description && (\n <div\n className={cn(\n \"mt-0.5\",\n answered ? \"text-[11px]\" : \"text-xs\",\n isSel && !answered ? \"opacity-75\" : \"text-[hsl(var(--muted-foreground))]\",\n )}\n >\n {opt.description}\n </div>\n )}\n </div>\n </button>\n )\n })}\n\n {/* 「其他」内联输入 */}\n {answered && !isCustom ? null : (\n <div\n className={cn(\n \"flex items-center gap-2 rounded-lg border transition-all\",\n answered ? \"px-2.5 py-1.5\" : \"px-3 py-2.5\",\n isCustom\n ? \"border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]\"\n : \"border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]\",\n answered && \"cursor-default opacity-70\",\n )}\n >\n <span className=\"shrink-0 text-xs text-[hsl(var(--muted-foreground))]\">其他:</span>\n <input\n type=\"text\"\n value={customText}\n disabled={answered}\n onChange={(e) => onCustomChange(qIdx, e.target.value)}\n onFocus={() => onCustomFocus(qIdx)}\n aria-label=\"自定义回答\"\n placeholder=\"输入你的答案...\"\n className={cn(\n \"min-w-0 flex-1 bg-transparent text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]\",\n answered ? \"text-xs\" : \"text-sm\",\n )}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/** 解析 AskUserQuestion 的工具结果/参数,容错 LLM 把 questions 输出为字符串的情况。 */\nexport function parseAskUserQuestion(\n toolResult: string | null | undefined,\n): AskUserQuestionData | null {\n if (!toolResult) return null\n try {\n const parsed = JSON.parse(toolResult)\n let questions = parsed?.questions\n if (typeof questions === \"string\") {\n try {\n questions = JSON.parse(questions)\n } catch {\n return null\n }\n }\n if (Array.isArray(questions) && questions.length > 0) {\n const normalized = questions.map(normalizeQuestionItem)\n if (normalized.every((question): question is QuestionItem => question !== null)) {\n return { ...parsed, questions: normalized } as AskUserQuestionData\n }\n }\n } catch {\n // 非 JSON,忽略\n }\n return null\n}\n\nfunction normalizeQuestionItem(value: unknown): QuestionItem | null {\n if (!value || typeof value !== \"object\") return null\n const item = value as {\n question?: unknown\n options?: unknown\n multiSelect?: unknown\n multi_select?: unknown\n }\n if (typeof item.question !== \"string\" || !Array.isArray(item.options)) return null\n const options = item.options.map(normalizeOptionItem)\n if (!options.every((option): option is OptionItem => option !== null)) return null\n return {\n question: item.question,\n options,\n multiSelect: item.multiSelect === true || item.multi_select === true,\n }\n}\n\nfunction normalizeOptionItem(value: unknown): OptionItem | null {\n if (typeof value === \"string\") return { label: value, description: \"\" }\n if (!value || typeof value !== \"object\") return null\n const option = value as { label?: unknown; description?: unknown }\n if (typeof option.label !== \"string\") return null\n return {\n label: option.label,\n description: typeof option.description === \"string\" ? option.description : \"\",\n }\n}\n","import { AlertTriangle } from \"lucide-react\"\nimport { Component, type ErrorInfo, type ReactNode } from \"react\"\n\ninterface Props {\n label: string\n details?: string\n resetKey?: string\n children: ReactNode\n}\n\ninterface State {\n componentStack: string\n error: Error | null\n}\n\nfunction getFirstComponentName(componentStack: string): string | null {\n const match = componentStack.match(/\\n\\s+at\\s+([^\\s(]+)/)\n return match?.[1] ?? null\n}\n\n/** 消息级渲染错误边界:单条消息渲染崩溃时降级为提示卡片,不拖垮整个列表。 */\nexport class RenderErrorBoundary extends Component<Props, State> {\n state: State = {\n componentStack: \"\",\n error: null,\n }\n\n static getDerivedStateFromError(error: Error): State {\n return {\n componentStack: \"\",\n error,\n }\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n const componentStack = info.componentStack ?? \"\"\n console.error(`${this.props.label} render error:`, error, info)\n this.setState({ componentStack })\n }\n\n componentDidUpdate(prevProps: Props) {\n if (this.state.error && prevProps.resetKey !== this.props.resetKey) {\n this.setState({ componentStack: \"\", error: null })\n }\n }\n\n render() {\n const { children, details, label } = this.props\n const { componentStack, error } = this.state\n\n if (!error) {\n return children\n }\n\n const componentName = getFirstComponentName(componentStack)\n\n return (\n <div className=\"blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100\">\n <div className=\"flex items-start gap-2\">\n <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-amber-300\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"font-medium\">{label}渲染失败</div>\n <div className=\"mt-1 break-words text-xs leading-5 text-amber-100/75\">\n {componentName ? `组件:${componentName}。` : null}\n {error.message || \"发生了未预期的渲染错误。\"}\n </div>\n {details ? (\n <div className=\"mt-1 truncate text-xs text-amber-100/55\">{details}</div>\n ) : null}\n </div>\n </div>\n </div>\n )\n }\n}\n","import type { FinalArtifact, PostChatFollowup, ResultFeedback as SavedResultFeedback, ResultFeedbackReason } from \"@blade-hq/agent-client\"\nimport { ArrowRight, ArrowUpRight, ChevronDown, Download, File, Film, Globe, Sparkles } from \"lucide-react\"\nimport { useCallback, useEffect, useRef, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\n\nexport type FollowupInteractionEvent =\n | {\n type: \"suggestions_shown\"\n sessionId?: string\n assistantEntryId: string\n count: number\n }\n | {\n type: \"suggestion_adopted\"\n sessionId?: string\n assistantEntryId: string\n suggestionIndex: number\n }\n | {\n type: \"artifact_shown\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: FinalArtifact[\"kind\"]\n }\n | {\n type: \"artifact_opened\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: FinalArtifact[\"kind\"]\n }\n | {\n type: \"artifact_download_started\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: \"file\"\n }\n | {\n type: \"artifact_download_succeeded\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: \"file\"\n }\n | {\n type: \"result_feedback_shown\"\n sessionId?: string\n assistantEntryId: string\n }\n | {\n type: \"result_feedback_submitted\"\n sessionId?: string\n assistantEntryId: string\n helpful: boolean\n reason: ResultFeedbackReason | null\n updatedAt: string\n }\n\nfunction emitInteraction(\n callback: ((event: FollowupInteractionEvent) => void) | undefined,\n event: FollowupInteractionEvent,\n) {\n try {\n callback?.(event)\n } catch {\n // 宿主的分析回调不得阻断 SDK 交互。\n }\n}\n\nfunction basename(path: string): string {\n return path.split(/[\\\\/]/).filter(Boolean).pop() || path\n}\n\nfunction isVideo(path: string): boolean {\n return /\\.(?:mp4|mov|webm|mkv|avi|m4v)$/i.test(path)\n}\n\nfunction ArtifactCard({\n artifact,\n sessionId,\n assistantEntryId,\n artifactIndex,\n onInteraction,\n onArtifactOpened,\n}: {\n artifact: FinalArtifact\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n onInteraction?: (event: FollowupInteractionEvent) => void\n onArtifactOpened: (artifactIndex: number, artifactKind: FinalArtifact[\"kind\"]) => void\n}) {\n const client = useBladeClient()\n const [downloading, setDownloading] = useState(false)\n const name = artifact.label || basename(artifact.target)\n\n if (artifact.kind === \"link\") {\n return (\n <a\n href={artifact.target}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={() => onArtifactOpened(artifactIndex, \"link\")}\n title={`${name}\\n${artifact.target}`}\n className=\"group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))]\"\n >\n <Globe size={15} className=\"shrink-0 text-[hsl(var(--primary))]\" />\n <span className=\"min-w-0 flex-1 truncate font-medium\">{name}</span>\n <ArrowUpRight size={13} className=\"absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100\" />\n </a>\n )\n }\n\n const Icon = isVideo(artifact.target) ? Film : File\n return (\n <button\n type=\"button\"\n disabled={!sessionId || downloading}\n onClick={async () => {\n if (!sessionId || downloading) return\n setDownloading(true)\n emitInteraction(onInteraction, {\n type: \"artifact_download_started\",\n sessionId,\n assistantEntryId,\n artifactIndex,\n artifactKind: \"file\",\n })\n try {\n await client.sessions.downloadFile(sessionId, artifact.target, basename(artifact.target))\n emitInteraction(onInteraction, {\n type: \"artifact_download_succeeded\",\n sessionId,\n assistantEntryId,\n artifactIndex,\n artifactKind: \"file\",\n })\n } catch {\n // 下载失败不能误报成功,也不影响其余聊天交互。\n } finally {\n setDownloading(false)\n }\n }}\n title={name}\n aria-label={`下载 ${name}`}\n className=\"group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-left text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))] disabled:opacity-60\"\n >\n <Icon size={15} className=\"shrink-0 text-[hsl(var(--primary))]\" />\n <span className=\"min-w-0 flex-1 truncate font-medium\">{name}</span>\n <Download size={13} className=\"absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100\" />\n </button>\n )\n}\n\nconst FEEDBACK_REASONS: Array<{ value: ResultFeedbackReason; label: string }> = [\n { value: \"not_solved\", label: \"没解决问题\" },\n { value: \"inaccurate\", label: \"内容不准确\" },\n { value: \"incomplete\", label: \"内容不完整\" },\n { value: \"needs_major_changes\", label: \"需要大量修改\" },\n { value: \"too_slow\", label: \"太慢\" },\n { value: \"other\", label: \"其他\" },\n]\n\nfunction feedbackReasonLabel(reason: ResultFeedbackReason | null): string | null {\n return FEEDBACK_REASONS.find((item) => item.value === reason)?.label ?? null\n}\n\nexport function HistoricalResultFeedback({ feedback }: { feedback: SavedResultFeedback }) {\n const label = feedbackReasonLabel(feedback.reason)\n return (\n <section\n aria-label=\"历史结果反馈\"\n className=\"mt-3 w-fit max-w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <span>你对此轮结果的评价:{feedback.helpful ? \"有帮助\" : \"没帮助\"}</span>\n {label ? <span> · {label}</span> : null}\n </section>\n )\n}\n\nfunction ResultFeedback({\n followup,\n sessionId,\n isViewer,\n onInteraction,\n savedFeedback,\n onFeedbackSaved,\n}: {\n followup: PostChatFollowup\n sessionId?: string\n isViewer: boolean\n onInteraction?: (event: FollowupInteractionEvent) => void\n savedFeedback?: SavedResultFeedback\n onFeedbackSaved?: (feedback: SavedResultFeedback) => void\n}) {\n const client = useBladeClient()\n const [saved, setSaved] = useState<SavedResultFeedback | null>(savedFeedback ?? null)\n const [helpful, setHelpful] = useState<boolean | null>(savedFeedback?.helpful ?? null)\n const [reason, setReason] = useState<ResultFeedbackReason | null>(savedFeedback?.reason ?? null)\n const [saving, setSaving] = useState(false)\n const [saveError, setSaveError] = useState(false)\n const reportedShown = useRef(false)\n const latestChoice = useRef<{\n helpful: boolean\n reason: ResultFeedbackReason | null\n } | null>(null)\n const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer\n\n useEffect(() => {\n if (!eligible || reportedShown.current) return\n reportedShown.current = true\n emitInteraction(onInteraction, {\n type: \"result_feedback_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n })\n }, [eligible, followup.assistant_entry_id, onInteraction, sessionId])\n\n useEffect(() => {\n if (!savedFeedback || latestChoice.current) return\n setSaved(savedFeedback)\n setHelpful(savedFeedback.helpful)\n setReason(savedFeedback.reason)\n }, [savedFeedback])\n\n const submit = useCallback(\n async (nextHelpful: boolean, nextReason: ResultFeedbackReason | null) => {\n if (!sessionId) return\n const choice = { helpful: nextHelpful, reason: nextReason }\n latestChoice.current = choice\n setHelpful(nextHelpful)\n setReason(nextReason)\n setSaving(true)\n setSaveError(false)\n try {\n const result = await client.sessions.putResultFeedback(\n sessionId,\n followup.assistant_entry_id,\n choice,\n )\n if (latestChoice.current !== choice) return\n setSaved(result)\n onFeedbackSaved?.(result)\n emitInteraction(onInteraction, {\n type: \"result_feedback_submitted\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n helpful: result.helpful,\n reason: result.reason,\n updatedAt: result.updated_at,\n })\n } catch {\n if (latestChoice.current === choice) setSaveError(true)\n } finally {\n if (latestChoice.current === choice) setSaving(false)\n }\n },\n [client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId],\n )\n\n if (!eligible) return null\n return (\n <section\n aria-label=\"结果反馈\"\n className=\"flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3\"\n >\n <div className=\"text-xs font-medium text-[hsl(var(--muted-foreground))]\">\n 目前的整体结果有帮助吗?\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n <button\n type=\"button\"\n aria-pressed={helpful === true}\n disabled={saving}\n onClick={() => void submit(true, null)}\n className=\"rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60\"\n >\n 有帮助\n </button>\n <button\n type=\"button\"\n aria-pressed={helpful === false}\n disabled={saving}\n onClick={() => void submit(false, reason)}\n className=\"rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60\"\n >\n 没帮助\n </button>\n </div>\n {helpful === false ? (\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"没帮助的主要原因\">\n {FEEDBACK_REASONS.map((item) => (\n <button\n key={item.value}\n type=\"button\"\n aria-pressed={reason === item.value}\n disabled={saving}\n onClick={() => void submit(false, item.value)}\n className=\"rounded-full bg-[hsl(var(--muted)/0.62)] px-2.5 py-1 text-[11px] aria-pressed:bg-[hsl(var(--primary)/0.14)] disabled:opacity-60\"\n >\n {item.label}\n </button>\n ))}\n </div>\n ) : null}\n {saveError ? (\n <div className=\"flex items-center gap-2 text-xs text-[hsl(var(--destructive))]\">\n <span>反馈暂未保存,可重试</span>\n <button\n type=\"button\"\n className=\"underline\"\n onClick={() => {\n const choice = latestChoice.current\n if (choice) void submit(choice.helpful, choice.reason)\n }}\n >\n 重试\n </button>\n </div>\n ) : saved ? (\n <div className=\"text-[11px] text-[hsl(var(--muted-foreground))]\">已保存,可随时修改</div>\n ) : null}\n </section>\n )\n}\n\nexport function PostChatFollowupBlock({\n followup,\n sessionId,\n onSuggestion,\n isViewer = false,\n onInteraction,\n savedFeedback,\n onFeedbackSaved,\n}: {\n followup: PostChatFollowup\n sessionId?: string\n onSuggestion?: (value: string) => void\n isViewer?: boolean\n onInteraction?: (event: FollowupInteractionEvent) => void\n savedFeedback?: SavedResultFeedback\n onFeedbackSaved?: (feedback: SavedResultFeedback) => void\n}) {\n const [expanded, setExpanded] = useState(false)\n const adopted = useRef(new Set<number>())\n const reportedSuggestions = useRef(false)\n const reportedArtifacts = useRef(new Set<number>())\n const openedArtifacts = useRef(new Set<number>())\n const artifacts = followup.final_artifacts ?? []\n const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3)\n useEffect(() => {\n if (!reportedSuggestions.current && followup.suggestions.length > 0) {\n reportedSuggestions.current = true\n emitInteraction(onInteraction, {\n type: \"suggestions_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n count: followup.suggestions.length,\n })\n }\n for (let artifactIndex = 0; artifactIndex < visibleArtifacts.length; artifactIndex += 1) {\n if (reportedArtifacts.current.has(artifactIndex)) continue\n const artifact = visibleArtifacts[artifactIndex]\n if (!artifact) continue\n reportedArtifacts.current.add(artifactIndex)\n emitInteraction(onInteraction, {\n type: \"artifact_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n artifactIndex,\n artifactKind: artifact.kind,\n })\n }\n }, [\n followup.assistant_entry_id,\n followup.suggestions.length,\n onInteraction,\n sessionId,\n visibleArtifacts,\n ])\n const reportArtifactOpened = useCallback(\n (artifactIndex: number, artifactKind: FinalArtifact[\"kind\"]) => {\n if (openedArtifacts.current.has(artifactIndex)) return\n openedArtifacts.current.add(artifactIndex)\n emitInteraction(onInteraction, {\n type: \"artifact_opened\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n artifactIndex,\n artifactKind,\n })\n },\n [followup.assistant_entry_id, onInteraction, sessionId],\n )\n if (\n !followup.recaption &&\n artifacts.length === 0 &&\n followup.suggestions.length === 0 &&\n !followup.feedback_eligible\n )\n return null\n\n return (\n <div className=\"mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]\">\n {followup.recaption || artifacts.length > 0 ? (\n <section aria-label=\"本轮小结\" className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]\">\n <Sparkles size={14} />\n 本轮小结\n </div>\n {followup.recaption ? <p className=\"text-[13px] leading-5\">{followup.recaption}</p> : null}\n {artifacts.length > 0 ? (\n <>\n <div className=\"grid max-w-full grid-cols-3 gap-1.5\">\n {visibleArtifacts.map((artifact, artifactIndex) => (\n <ArtifactCard\n key={`${artifact.kind}:${artifactIndex}`}\n artifact={artifact}\n sessionId={sessionId}\n assistantEntryId={followup.assistant_entry_id}\n artifactIndex={artifactIndex}\n onInteraction={onInteraction}\n onArtifactOpened={reportArtifactOpened}\n />\n ))}\n </div>\n {artifacts.length > 3 ? (\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n aria-expanded={expanded}\n className=\"flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]\"\n >\n {expanded ? \"收起\" : `展开 ${artifacts.length - 3} 个`}\n <ChevronDown size={13} className={expanded ? \"rotate-180\" : undefined} />\n </button>\n ) : null}\n </>\n ) : null}\n </section>\n ) : null}\n {followup.suggestions.length > 0 ? (\n <section aria-label=\"下一步建议\" className=\"flex flex-col gap-1.5\">\n <div className=\"text-xs font-medium text-[hsl(var(--muted-foreground))]\">下一步可以</div>\n {followup.suggestions.map((suggestion, suggestionIndex) => (\n <button\n key={suggestion}\n type=\"button\"\n disabled={isViewer}\n onClick={() => {\n if (!adopted.current.has(suggestionIndex)) {\n adopted.current.add(suggestionIndex)\n emitInteraction(onInteraction, {\n type: \"suggestion_adopted\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n suggestionIndex,\n })\n }\n onSuggestion?.(suggestion)\n }}\n className=\"group flex items-center gap-2 rounded-xl bg-[hsl(var(--muted)/0.62)] px-3 py-2 text-left text-[13px] disabled:cursor-default disabled:opacity-60\"\n >\n <span>{suggestion}</span>\n <ArrowRight size={14} className=\"ml-auto shrink-0\" />\n </button>\n ))}\n </section>\n ) : null}\n <ResultFeedback\n followup={followup}\n sessionId={sessionId}\n isViewer={isViewer}\n onInteraction={onInteraction}\n savedFeedback={savedFeedback}\n onFeedbackSaved={onFeedbackSaved}\n />\n </div>\n )\n}\n","import type { ChatMessage } from \"@blade-hq/agent-client\"\nimport { getFileParts, getImageParts, getTextContent } from \"@blade-hq/agent-client\"\nimport { FileText, Loader2 } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { MarkdownContent } from \"./MarkdownContent\"\n\nexport type UserChatMessage = ChatMessage & { role: \"user\" }\nexport type ErrorChatMessage = ChatMessage & { role: \"error\" }\n\nexport function isUserMessage(message: ChatMessage): message is UserChatMessage {\n return message.role === \"user\"\n}\n\nexport function isErrorMessage(message: ChatMessage): message is ErrorChatMessage {\n return message.role === \"error\"\n}\n\ninterface UserMessageProps {\n message: UserChatMessage\n className?: string\n}\n\nconst isSending = (message: ChatMessage) => message.status === \"streaming\"\n\n/** 用户消息气泡:文本 + 图片/文件附件展示(不含第一方的预览弹窗与 what-if 徽章)。 */\nexport function UserMessageBubble({ message, className }: UserMessageProps) {\n const text = getTextContent(message.content).trim()\n const fileParts = getFileParts(message.content)\n const imageParts = getImageParts(message.content)\n\n return (\n <div className={cn(\"blade-chat-user-row flex justify-end\", className)}>\n <div className=\"blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3\">\n {imageParts.length > 0 && (\n <div className=\"grid gap-2\">\n {imageParts.map((part) => (\n <img\n key={part.image_url.url}\n src={part.image_url.url}\n alt=\"用户上传的图片\"\n className=\"max-h-64 rounded-xl border border-[hsl(var(--user-msg-border))] object-cover\"\n />\n ))}\n </div>\n )}\n {fileParts.length > 0 && (\n <div className=\"flex flex-col items-end gap-1.5\">\n {fileParts.map((part) => (\n <div\n key={`${part.name}-${part.data.length}`}\n className=\"flex items-center gap-1.5 rounded-lg border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <FileText size={12} className=\"shrink-0\" />\n <span className=\"max-w-56 truncate\" title={part.name}>\n {part.name}\n </span>\n </div>\n ))}\n </div>\n )}\n {text && (\n <div className=\"blade-chat-user-bubble max-w-full rounded-[20px] rounded-br-[6px] border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-[18px] py-[13px] text-sm leading-[1.65] text-[hsl(var(--user-msg-fg))]\">\n <MarkdownContent className=\"blade-chat-prose\">{text}</MarkdownContent>\n </div>\n )}\n {text && isSending(message) && (\n <div className=\"flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]\">\n <Loader2 size={11} className=\"animate-spin\" aria-hidden=\"true\" />\n <span>发送中</span>\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/** 错误消息:居中红色提示条。 */\nexport function ErrorMessageBlock({\n message,\n className,\n}: {\n message: ErrorChatMessage\n className?: string\n}) {\n const text = getTextContent(message.content)\n return (\n <div className={cn(\"blade-chat-error-row flex justify-center\", className)}>\n {/* 与内置 Web 的 ErrorMessageBlock 保持一致:消息流里的错误不用红色。 */}\n <div className=\"blade-chat-error-block max-w-[85%] border-l-[3px] border-[hsl(var(--border))] px-4 py-1 text-sm leading-7 text-[hsl(var(--muted-foreground))]\">\n {text}\n </div>\n </div>\n )\n}\n","import type {\n AskUserAnswerData,\n ChatMessage,\n ConnectionStatus,\n PostChatFollowup,\n} from \"@blade-hq/agent-client\"\nimport { CircleAlert } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { ChatInput } from \"./ChatInput\"\nimport { ConnectionBanner } from \"./ConnectionBanner\"\nimport { MessageList } from \"./MessageList\"\nimport type { ToolCallRenderer } from \"./ToolCallBlock\"\nimport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\nimport type { ResultFeedback } from \"@blade-hq/agent-client\"\n\nexport interface ChatViewClassNames {\n root?: string\n banner?: string\n messageList?: string\n chatInput?: string\n}\n\nexport interface ChatViewRenderers {\n /** 自定义工具调用渲染;返回 null 走默认渲染。 */\n toolCall?: ToolCallRenderer\n}\n\nexport interface ChatViewSlots {\n /** 消息列表上方的自定义区域。 */\n header?: ReactNode\n /** 无消息时的空状态。 */\n emptyState?: ReactNode\n /** 输入框下方的自定义区域。 */\n footer?: ReactNode\n}\n\n/** 两种模式共用的外观属性。 */\nexport interface ChatPresentationProps {\n classNames?: ChatViewClassNames\n renderers?: ChatViewRenderers\n slots?: ChatViewSlots\n placeholder?: string\n /** 配色主题,默认浅色。宿主已经在祖先元素上写了 data-theme 时不用传。 */\n theme?: \"light\" | \"dark\"\n}\n\n/** 主题落到聊天根节点上:深色变量挂在 [data-theme=\"dark\"] 上,由它继承给整棵子树。 */\nexport function themeAttr(theme: ChatPresentationProps[\"theme\"]): \"dark\" | undefined {\n return theme === \"dark\" ? \"dark\" : undefined\n}\n\ninterface ChatSurfaceProps extends ChatPresentationProps {\n /** follow-up、成果和评分交互;SDK 不绑定任何分析厂商。 */\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n connection: ConnectionStatus\n errorMessage: string | null\n messages: ChatMessage[]\n postChatFollowup?: PostChatFollowup | null\n isStreaming: boolean\n isStopping: boolean\n inputText: string\n onInputChange: (value: string) => void\n onSuggestion?: (value: string) => void\n onSend: (text: string) => Promise<boolean>\n onStop: () => void\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n sessionId?: string\n isViewer?: boolean\n resultFeedbackByEntry?: ReadonlyMap<string, ResultFeedback>\n onResultFeedbackSaved?: (feedback: ResultFeedback) => void\n /** 输入框上方的内部区域(纯 LLM 模式的高级设置用)。不传时不渲染任何节点。 */\n beforeInput?: ReactNode\n /** 连接状态条下方的横幅(回放模式条用)。不传时不渲染任何节点。 */\n banner?: ReactNode\n}\n\n/**\n * 消息流 + 输入框:智能体模式与纯 LLM 模式共用同一套外观,差别只在谁喂数据。\n *\n * 这里的 DOM 结构受 tests/chat-view-compat.test.tsx 的快照保护——接入方拿\n * .blade-chat-* 类名写了自己的样式,结构一动他们的页面就歪。\n */\nexport function ChatSurface({\n theme,\n classNames,\n renderers,\n slots,\n placeholder,\n connection,\n errorMessage,\n messages,\n postChatFollowup,\n isStreaming,\n isStopping,\n inputText,\n onInputChange,\n onSuggestion,\n onSend,\n onStop,\n sessionStatus,\n askAnswers,\n onAnswer,\n sessionId,\n isViewer,\n resultFeedbackByEntry,\n onResultFeedbackSaved,\n onFollowupInteraction,\n beforeInput,\n banner,\n}: ChatSurfaceProps) {\n return (\n <div\n data-theme={themeAttr(theme)}\n className={cn(\n \"blade-chat flex min-h-0 flex-1 flex-col overflow-hidden\",\n classNames?.root,\n )}\n >\n <ConnectionBanner connection={connection} className={classNames?.banner} />\n {banner}\n {/* 出错条的配色由 .blade-chat-error-bar 的 --destructive 变量给,别写死 red-*:\n Tailwind 的 dark: 变体跟的是系统偏好,会和 data-theme 主题打架。 */}\n {errorMessage && (\n <div className=\"blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm\">\n <CircleAlert size={16} className=\"mt-0.5 shrink-0\" />\n <span>{errorMessage}</span>\n </div>\n )}\n {slots?.header}\n <MessageList\n messages={messages}\n postChatFollowup={postChatFollowup}\n onSuggestion={onSuggestion}\n isStreaming={isStreaming}\n sessionStatus={sessionStatus}\n askAnswers={askAnswers}\n onAnswer={onAnswer}\n toolCallRenderer={renderers?.toolCall}\n emptyState={slots?.emptyState}\n className={classNames?.messageList}\n sessionId={sessionId}\n isViewer={isViewer}\n resultFeedbackByEntry={resultFeedbackByEntry}\n onResultFeedbackSaved={onResultFeedbackSaved}\n onFollowupInteraction={onFollowupInteraction}\n />\n {beforeInput}\n <ChatInput\n value={inputText}\n onValueChange={onInputChange}\n onSend={onSend}\n onStop={onStop}\n isStreaming={isStreaming}\n isStopping={isStopping}\n placeholder={placeholder}\n className={classNames?.chatInput}\n />\n {slots?.footer}\n </div>\n )\n}\n","import { useEffect, useMemo, useState } from \"react\"\nimport { useLlmChat, type LlmChatOptions } from \"../hooks/use-llm-chat\"\nimport { ChatSurface, type ChatPresentationProps } from \"./ChatSurface\"\nimport {\n LlmAdvancedSettingsBar,\n readOverride,\n writeOverride,\n type LlmAdvancedSettings,\n type LlmOverride,\n} from \"./LlmAdvancedSettings\"\n\n/** 纯 LLM 模式的操作句柄,对应智能体模式的 session.insertText() / send()。 */\nexport interface LlmChatHandle {\n /** 把文本放进输入框交给用户确认,不直接发出去。 */\n insertText: (text: string) => void\n send: (text: string) => Promise<boolean>\n /** 清空这段对话。 */\n reset: () => void\n}\n\nexport interface LlmChatProps extends LlmChatOptions, ChatPresentationProps {\n /** 拿到操作句柄:业务界面把选中的数据塞进输入框,或直接发一句。 */\n onReady?: (handle: LlmChatHandle) => void\n /**\n * 打开输入框上方的「高级设置」,让用户自己改模型服务地址(默认还能改模型名)。\n * 不传就一个节点都不渲染。传 `true` 用默认配置,或用对象细调。\n */\n advanced?: boolean | LlmAdvancedSettings\n /** 用户改了覆盖值时回调,方便宿主自己也记一份。 */\n onOverrideChange?: (override: LlmOverride) => void\n}\n\n/**\n * 纯 LLM 模式的聊天界面:不建会话、不连实时通道、也不要求登录 Blade,\n * 只按 **OpenAI Chat Completions** 标准协议跟模型服务一问一答。\n *\n * 默认形态是把 `baseURL` 指向自己后端的透传路径,由后端补密钥再转发,浏览器不持有密钥。\n */\nexport function LlmChat({\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onReady,\n advanced,\n onOverrideChange,\n ...options\n}: LlmChatProps) {\n // 用户在高级设置里填的值盖住调用方传进来的默认值;本轮请求用哪套由这里决定\n const [override, setOverride] = useState<LlmOverride>(() => readOverride(advanced, options.baseURL))\n // 每次渲染新建对象也没关系:useLlmChat 每轮都把配置写进 ref,send 用的是调用那一刻的值\n const effective = { ...options, ...override }\n const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective)\n const [inputText, setInputText] = useState(\"\")\n const [stopRequested, setStopRequested] = useState(false)\n\n // 句柄引用要稳定:随渲染换新对象的话,宿主放进 useEffect 依赖里会连环重跑\n const handle = useMemo<LlmChatHandle>(\n () => ({\n insertText: (text) => setInputText((prev) => (prev ? `${prev}\\n${text}` : text)),\n send,\n reset,\n }),\n [send, reset],\n )\n useEffect(() => {\n onReady?.(handle)\n }, [handle, onReady])\n\n return (\n <ChatSurface\n theme={theme}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n // 没有\"连接\"这回事:不建会话也不连实时通道,状态条恒为已连接\n connection=\"connected\"\n errorMessage={error}\n messages={messages}\n isStreaming={isStreaming}\n isStopping={stopRequested && isStreaming}\n inputText={inputText}\n onInputChange={setInputText}\n onSend={async (text) => {\n setStopRequested(false)\n if (!text.trim() || isStreaming) return false\n // 只等\"这条已经收下\",不等整段流跑完:等下去的话输入框在整个生成期间都清不掉,\n // 用户中途敲的下一句还会被清掉。失败通过出错条反馈,不靠输入框。\n void send(text)\n return true\n }}\n onStop={() => {\n setStopRequested(true)\n stop()\n }}\n beforeInput={\n advanced ? (\n <LlmAdvancedSettingsBar\n settings={advanced}\n defaults={{ baseURL: options.baseURL, model: options.model }}\n override={override}\n onChange={(next) => {\n setOverride(next)\n writeOverride(advanced, options.baseURL, next)\n onOverrideChange?.(next)\n }}\n />\n ) : undefined\n }\n />\n )\n}\n","import { Settings2 } from \"lucide-react\"\nimport { useState } from \"react\"\n\n/** 纯 LLM 模式的高级设置:让用户自己改模型服务地址。 */\nexport interface LlmAdvancedSettings {\n /** 允许改模型服务地址,默认允许。 */\n baseURL?: boolean\n /** 允许改模型名,默认允许。 */\n model?: boolean\n /**\n * 允许在界面上填密钥,默认**不允许**。\n * 打开意味着密钥存在浏览器里,只适合受控内网、演示,或密钥本身就属于当前用户的场景。\n */\n apiKey?: boolean\n /** 覆盖值存哪:`local` 记住用户的选择(默认),`memory` 只在本次会话有效。 */\n storage?: \"local\" | \"memory\"\n /** localStorage 的键,默认按组件拿到的 baseURL 生成。 */\n storageKey?: string\n}\n\nexport interface LlmOverride {\n baseURL?: string\n model?: string\n apiKey?: string\n}\n\nconst FIELDS: Array<{ id: keyof LlmOverride; label: string; placeholder: string; secret?: boolean }> = [\n { id: \"baseURL\", label: \"模型服务地址\", placeholder: \"http://内网地址/v1\" },\n { id: \"model\", label: \"模型\", placeholder: \"模型名称\" },\n { id: \"apiKey\", label: \"密钥\", placeholder: \"留空表示不改\", secret: true },\n]\n\nexport function normalizeAdvanced(\n value: boolean | LlmAdvancedSettings | undefined,\n): Required<Pick<LlmAdvancedSettings, \"baseURL\" | \"model\" | \"apiKey\" | \"storage\">> | null {\n if (!value) return null\n const config = value === true ? {} : value\n return {\n baseURL: config.baseURL ?? true,\n model: config.model ?? true,\n apiKey: config.apiKey ?? false,\n storage: config.storage ?? \"local\",\n }\n}\n\nfunction storageKeyFor(settings: boolean | LlmAdvancedSettings | undefined, baseURL: string): string {\n const explicit = typeof settings === \"object\" ? settings.storageKey : undefined\n return `blade-llm-override:${explicit ?? baseURL}`\n}\n\nexport function readOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n): LlmOverride {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return {}\n try {\n const raw = localStorage.getItem(storageKeyFor(settings, baseURL))\n if (!raw) return {}\n const stored = JSON.parse(raw) as LlmOverride\n // 只认当前还允许改的字段:宿主把密钥字段关掉之后,之前存下的密钥不能继续\n // 被悄悄发出去(同一个 storageKey 被别处写过也一样)。\n return Object.fromEntries(\n Object.entries(stored).filter(([key]) => normalized[key as keyof LlmOverride]),\n ) as LlmOverride\n } catch {\n // 隐私模式等场景读不到就当没设过\n return {}\n }\n}\n\nfunction writeOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n override: LlmOverride,\n): void {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return\n try {\n const key = storageKeyFor(settings, baseURL)\n if (Object.keys(override).length === 0) localStorage.removeItem(key)\n else localStorage.setItem(key, JSON.stringify(override))\n } catch {\n // 写不进去就退化成只在本次会话有效\n }\n}\n\ninterface Props {\n settings: boolean | LlmAdvancedSettings\n /** 组件拿到的默认值,用来做占位提示与\"恢复默认\" */\n defaults: { baseURL: string; model: string }\n override: LlmOverride\n onChange: (next: LlmOverride) => void\n}\n\n/**\n * 输入框上方的一行「高级设置」:默认收起,展开后可以改模型服务地址与模型。\n * 只有调用方显式打开才渲染——不传 advanced 时这里一个节点都不会出现。\n */\nexport function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }: Props) {\n const normalized = normalizeAdvanced(settings)\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState<LlmOverride>(override)\n if (!normalized) return null\n\n const fields = FIELDS.filter((field) => normalized[field.id])\n const dirty = Object.keys(override).length > 0\n\n return (\n <div className=\"blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs\">\n <button\n type=\"button\"\n onClick={() => {\n setDraft(override)\n setOpen((value) => !value)\n }}\n className=\"flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]\"\n >\n <Settings2 size={13} />\n 高级设置\n {dirty && <span className=\"text-[hsl(var(--primary))]\">(已自定义)</span>}\n </button>\n {open && (\n <div className=\"mt-2 flex flex-col gap-2\">\n {fields.map((field) => (\n <label key={field.id} className=\"flex flex-col gap-1\">\n <span className=\"text-[hsl(var(--muted-foreground))]\">{field.label}</span>\n <input\n type={field.secret ? \"password\" : \"text\"}\n value={draft[field.id] ?? \"\"}\n placeholder={\n field.id === \"apiKey\"\n ? field.placeholder\n : (defaults[field.id as \"baseURL\" | \"model\"] || field.placeholder)\n }\n onChange={(event) => setDraft({ ...draft, [field.id]: event.target.value })}\n className=\"rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none\"\n />\n </label>\n ))}\n {normalized.apiKey && (\n <p className=\"text-[hsl(var(--muted-foreground))]\">\n 密钥会存在这台浏览器里。只在你信得过这台机器时填。\n </p>\n )}\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n // 空字符串等于没改,不要把空值当成覆盖发出去\n const next = Object.fromEntries(\n Object.entries(draft).filter(([, value]) => String(value ?? \"\").trim() !== \"\"),\n ) as LlmOverride\n onChange(next)\n setOpen(false)\n }}\n className=\"rounded-md bg-[hsl(var(--primary))] px-2.5 py-1 font-medium text-[hsl(var(--primary-foreground))]\"\n >\n 保存\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setDraft({})\n onChange({})\n setOpen(false)\n }}\n className=\"rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-[hsl(var(--foreground))]\"\n >\n 恢复默认\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport { writeOverride }\n","import { AgentChat, type AgentChatProps } from \"./AgentChat\"\nimport { LlmChat, type LlmChatHandle } from \"./LlmChat\"\nimport type { LlmAdvancedSettings, LlmOverride } from \"./LlmAdvancedSettings\"\nimport type { LlmChatOptions } from \"../hooks/use-llm-chat\"\n\nexport type {\n ChatViewClassNames,\n ChatViewRenderers,\n ChatViewSlots,\n} from \"./ChatSurface\"\nexport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\n\nexport interface ChatViewProps extends AgentChatProps {\n /** 挂哪个下层组件。默认 `agent`——不传就是接 Blade 智能体会话的原有行为。 */\n mode?: \"agent\" | \"llm\"\n /** `mode=\"llm\"` 时的模型服务配置;`advanced` 打开输入框上方的「高级设置」。 */\n llm?: LlmChatOptions & {\n advanced?: boolean | LlmAdvancedSettings\n onOverrideChange?: (override: LlmOverride) => void\n }\n /** `mode=\"llm\"` 时拿到操作句柄;智能体模式请用 `onSessionReady`。 */\n onLlmReady?: (handle: LlmChatHandle) => void\n}\n\n/**\n * 聊天界面。默认接 Blade 智能体会话,`mode=\"llm\"` 时改接标准 OpenAI 对话接口。\n *\n * 这一层只做分流:两种模式各自是独立组件,已经确定要哪一种的可以直接用\n * `<AgentChat>` 或 `<LlmChat>`。\n */\nexport function ChatView(props: ChatViewProps) {\n // 参数保持单个 props、在函数体里解构:公共面报告会把参数列表原样印出来,\n // 在参数位置解构会让签名行发生变化,而这一行是接入方看兼容性的依据。\n const { mode, llm, onLlmReady, ...rest } = props\n if (mode === \"llm\") {\n if (!llm) {\n throw new Error('ChatView: mode=\"llm\" 需要同时传 llm={{ baseURL, model }}')\n }\n return (\n <LlmChat\n {...llm}\n classNames={rest.classNames}\n renderers={rest.renderers}\n slots={rest.slots}\n placeholder={rest.placeholder}\n theme={rest.theme}\n onReady={onLlmReady}\n />\n )\n }\n return <AgentChat {...rest} />\n}\n","// ============================== React 绑定 ==============================\nexport { BladeProvider, useBladeClient } from \"./context\"\nexport type { BladeProviderProps } from \"./context\"\nexport { useAgentSession } from \"./hooks/use-agent-session\"\nexport type { UseAgentSessionOptions, UseAgentSessionResult } from \"./hooks/use-agent-session\"\nexport { useReplay } from \"./hooks/use-replay\"\nexport type { ReplayMismatch, UseReplayResult } from \"./hooks/use-replay\"\nexport { useLlmChat } from \"./hooks/use-llm-chat\"\nexport type {\n ChatCompletionTool,\n LlmChatOptions,\n LlmToolCall,\n UseLlmChatResult,\n} from \"./hooks/use-llm-chat\"\n\n// ============================== 聊天界面 ==============================\nexport { ChatView } from \"./components/ChatView\"\nexport type {\n ChatViewClassNames,\n ChatViewProps,\n ChatViewRenderers,\n ChatViewSlots,\n FollowupInteractionEvent,\n} from \"./components/ChatView\"\n// 下层组件:已经确定要哪种模式的可以直接用,不必经 ChatView 分流\nexport { AgentChat } from \"./components/AgentChat\"\nexport type { AgentChatProps } from \"./components/AgentChat\"\nexport { LlmChat } from \"./components/LlmChat\"\nexport type { LlmChatHandle, LlmChatProps } from \"./components/LlmChat\"\nexport type { LlmAdvancedSettings, LlmOverride } from \"./components/LlmAdvancedSettings\"\nexport { MarkdownContent } from \"./components/MarkdownContent\"\nexport type { MarkdownContentProps } from \"./components/MarkdownContent\"\nexport { ReplayBar } from \"./components/ReplayBar\"\nexport type { ReplayBarProps } from \"./components/ReplayBar\"\nexport { ReplayMismatchPrompt } from \"./components/ReplayMismatchPrompt\"\nexport type { ReplayMismatchPromptProps } from \"./components/ReplayMismatchPrompt\"\nexport type { ToolCallRenderer } from \"./components/ToolCallBlock\"\n\n// ============================== 客户端全量转出 ==============================\n// 方便接入方只装一个包:agent-client 的全部公共面从这里原样可用。\nexport * from \"@blade-hq/agent-client\"\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","useCallback","useMemo","useRef","useState","Component","forwardRef","createElement","useCallback","useEffect","useMemo","useState","jsx","jsx","jsxs","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useCallback","useEffect","useMemo","useRef","useState","useCallback","useMemo","useRef","useState","isAtBottom","escapedFromLock","createContext","useContext","useEffect","useMemo","useRef","createContext","useEffect","useRef","useMemo","targetScrollTop","StickToBottom","useContext","useState","useState","jsx","jsxs","useState","useEffect","useMemo","useRef","useState","jsx","jsxs","useRef","useState","useEffect","useMemo","jsx","useState","useEffect","useMemo","useState","jsx","jsxs","useState","useEffect","useMemo","text","jsx","jsxs","useState","jsx","jsxs","useState","jsx","jsxs","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","Icon","useRef","useEffect","useCallback","getTextContent","jsx","jsxs","getTextContent","jsx","jsxs","useMemo","useRef","useEffect","useState","useCallback","jsx","jsxs","Fragment","jsx","jsxs","useState","useEffect","useMemo","useCallback","useEffect","useMemo","useState","useState","jsx","jsxs","jsx","useState","useMemo","useEffect","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/hooks/use-agent-session.ts","../src/hooks/use-replay.ts","../src/hooks/use-llm-chat.ts","../src/hooks/use-message-pin.ts","../src/components/AgentChat.tsx","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/shared/src/utils.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/defaultAttributes.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/Icon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/createLucideIcon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-up-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-up.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/book-open.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/bot.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/check.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-down.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/circle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/circle-dot.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/circle.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/copy.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/earth.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file-pen-line.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file-text.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/globe.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/layers.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lightbulb.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/list-checks.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/loader-circle.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lock-keyhole.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square-more.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/play.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/refresh-ccw.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/search.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/settings-2.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/sparkles.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/terminal.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/triangle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/wrench.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/x.ts","../src/lib/utils.ts","../src/components/ReplayBar.tsx","../src/components/ReplayMismatchPrompt.tsx","../src/components/PlanUpdateBlock.tsx","../src/components/display-utils.ts","../src/components/ChatSurface.tsx","../src/components/ChatInput.tsx","../src/components/ConnectionBanner.tsx","../src/components/MessageList.tsx","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js","../src/components/AssistantTurnBlock.tsx","../src/components/AgentLoopBlock.tsx","../src/components/MarkdownContent.tsx","../src/components/Shimmer.tsx","../src/components/ToolCallBlock.tsx","../src/components/AskUserQuestionBlock.tsx","../src/components/RenderErrorBoundary.tsx","../src/components/PostChatFollowupBlock.tsx","../src/components/UserMessageBubble.tsx","../src/lib/whatif-prompt.ts","../src/components/WhatIfUserBubble.tsx","../src/components/LlmChat.tsx","../src/components/LlmAdvancedSettings.tsx","../src/components/ChatView.tsx","../src/components/ContextCard.tsx","../src/components/SessionMemoryToggle.tsx","../src/lib/agent-computer-command.ts","../src/index.ts"],"sourcesContent":["import type { BladeClient } from \"@blade-hq/agent-client\"\nimport { createContext, useContext, type ReactNode } from \"react\"\n\nconst BladeClientContext = createContext<BladeClient | null>(null)\n\nexport interface BladeProviderProps {\n client: BladeClient\n children: ReactNode\n}\n\n/**\n * 提供 client 级共享(一条连接、一份鉴权)。不含任何全局状态:\n * 同一 Provider 下多个 ChatView / useAgentSession 各自独立。\n */\nexport function BladeProvider({ client, children }: BladeProviderProps) {\n return <BladeClientContext.Provider value={client}>{children}</BladeClientContext.Provider>\n}\n\n/** 读取当前 BladeProvider 提供的 client。不在 Provider 内使用时抛错。 */\nexport function useBladeClient(): BladeClient {\n const client = useContext(BladeClientContext)\n if (!client) {\n throw new Error(\"useBladeClient 必须在 <BladeProvider> 内使用\")\n }\n return client\n}\n\nexport function useOptionalBladeClient(): BladeClient | null {\n return useContext(BladeClientContext)\n}\n","import type {\n AgentSession,\n CreateSessionRequest,\n SessionState,\n} from \"@blade-hq/agent-client\"\nimport { useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\"\nimport { useBladeClient } from \"../context\"\n\nexport interface UseAgentSessionOptions {\n /** 不传 sessionId 时按此配置新建会话;两者都不传则新建默认对话。 */\n createOptions?: CreateSessionRequest\n /** 自动创建出的 sessionId 通过此回调交还调用方持有(重挂载复用的依据)。 */\n onSessionCreated?: (sessionId: string) => void\n /**\n * 会话进入实时事件路由后、历史加载与房间订阅开始前同步执行。\n * 返回的清理函数会在切换会话或卸载时执行。\n */\n onSessionConnected?: (session: AgentSession) => () => void\n}\n\nexport interface UseAgentSessionResult {\n /** 连接/创建完成前为 null。 */\n session: AgentSession | null\n /** 会话状态快照;session 未就绪时为 null。 */\n state: SessionState | null\n /** 连接或创建失败的错误。 */\n error: Error | null\n}\n\nconst EMPTY_SUBSCRIBE = () => () => {}\n\n/** 卸载后延迟释放引用:StrictMode/路由抖动的立即重挂载会在窗口内重新\n * connect(引用计数 +1),延迟 dispose 只把计数减回去,实例得以复用;\n * 真正的卸载则在窗口结束后正常释放订阅。 */\nconst DISPOSE_DELAY_MS = 1000\n\n/**\n * 连接(或创建)一个会话并订阅其状态。\n *\n * - 优先级:sessionId(连接既有会话)> createOptions(按配置新建)> 默认新建。\n * - 同一 sessionId 重复调用复用同一 AgentSession 实例(SessionHub 引用计数)。\n * - 自动创建只发生一次(含 StrictMode 双跑);创建出的 id 经 onSessionCreated\n * 交还调用方。\n *\n * **连接与 sessionId 变更分离**:连接在挂载时建立、仅在卸载时释放;只有\n * 当 sessionId 明确切到一个「与当前已连接会话不同」的 id 时才重连。这样宿主\n * (如 Vue 把自动创建的 id 写回 `session-id` 属性)回填同一个 id 不会触发\n * 拆–重连——否则会把正在流式的会话换成新实例、当前渲染整个丢失。\n */\nexport function useAgentSession(\n sessionId?: string,\n options: UseAgentSessionOptions = {},\n): UseAgentSessionResult {\n const client = useBladeClient()\n const [session, setSession] = useState<AgentSession | null>(null)\n const [error, setError] = useState<Error | null>(null)\n\n // 当前连接:id + 实例 + 一个递增的「代」号,用于让过期的异步连接结果作废。\n const connRef = useRef<{\n id: string | null\n session: AgentSession | null\n cleanup: (() => void) | null\n gen: number\n }>({\n id: null,\n session: null,\n cleanup: null,\n gen: 0,\n })\n // 自动创建去重:StrictMode 双跑时同一组件实例只创建一次会话。\n const createdIdPromiseRef = useRef<Promise<string> | null>(null)\n const onCreatedRef = useRef(options.onSessionCreated)\n onCreatedRef.current = options.onSessionCreated\n const onConnectedRef = useRef(options.onSessionConnected)\n onConnectedRef.current = options.onSessionConnected\n const createOptionsRef = useRef(options.createOptions)\n createOptionsRef.current = options.createOptions\n // 最新的目标 sessionId,供挂载 effect 的异步链读取(不进依赖,避免重连)。\n const sessionIdRef = useRef(sessionId)\n sessionIdRef.current = sessionId\n\n // 把「连接到某个 id / 自动创建」封成一次操作;gen 作废过期结果。\n const connect = useMemo(() => {\n return (targetId: string | undefined) => {\n const gen = ++connRef.current.gen\n let pendingCleanup: (() => void) | null = null\n const idPromise = targetId\n ? Promise.resolve(targetId)\n : // biome-ignore lint/suspicious/noAssignInExpressions: ??= 挂 ref 是 StrictMode 下\"只创建一次\"的关键\n (createdIdPromiseRef.current ??= client.sessions\n .create(createOptionsRef.current ?? {})\n .then((created) => {\n // create() 的实例引用立即归还,由下面的 connect() 统一取,避免多挂一份计数\n const id = created.sessionId\n created.dispose()\n onCreatedRef.current?.(id)\n return id\n }))\n\n idPromise\n .then((id) =>\n client.hub.connect(id, {\n setup: (next) => {\n pendingCleanup = onConnectedRef.current?.(next) ?? null\n },\n }),\n )\n .then((next) => {\n if (connRef.current.gen !== gen) {\n pendingCleanup?.()\n next.dispose() // 已被更新的连接请求取代\n return\n }\n connRef.current.id = next.sessionId\n connRef.current.session = next\n connRef.current.cleanup = pendingCleanup\n setSession(next)\n setError(null)\n })\n .catch((err) => {\n pendingCleanup?.()\n if (connRef.current.gen !== gen) return\n createdIdPromiseRef.current = null // 创建失败允许下次重试\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n }\n }, [client])\n\n // 挂载时建立连接,仅在卸载(或 client 变化)时释放。\n useEffect(() => {\n connect(sessionIdRef.current)\n return () => {\n connRef.current.gen++ // 作废在途的连接结果\n const toRelease = connRef.current.session\n const cleanup = connRef.current.cleanup\n connRef.current.id = null\n connRef.current.session = null\n connRef.current.cleanup = null\n cleanup?.()\n setSession(null)\n if (toRelease) setTimeout(() => toRelease.dispose(), DISPOSE_DELAY_MS)\n }\n }, [connect])\n\n // 仅当 sessionId 明确切到「与已连接会话不同」的 id 时才重连。\n // 回填自动创建的 id(sessionId === 已连接 id)在此被跳过。\n useEffect(() => {\n if (!sessionId) return\n if (connRef.current.id === null) return // 首连仍在进行,交给挂载 effect\n if (sessionId === connRef.current.id) return\n const previous = connRef.current.session\n const cleanup = connRef.current.cleanup\n connRef.current.id = null\n connRef.current.session = null\n connRef.current.cleanup = null\n cleanup?.()\n if (previous) setTimeout(() => previous.dispose(), DISPOSE_DELAY_MS)\n connect(sessionId)\n }, [sessionId, connect])\n\n const store = useMemo(\n () => ({\n subscribe: session ? (listener: () => void) => session.subscribe(listener) : EMPTY_SUBSCRIBE,\n getSnapshot: () => (session ? session.getState() : null),\n }),\n [session],\n )\n const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n\n return { session, state, error }\n}\n","import { DEFAULT_REPLAY_SPEED } from \"@blade-hq/agent-client\"\nimport type { AgentSession, ReplaySpeed, SessionState } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\n\n/** 回放中用户说的话和录制内容对不上,需要当场决定往哪走。 */\nexport interface ReplayMismatch {\n /** 用户这次实际发出的内容。 */\n actualMessage: string\n /** 源会话里录制的下一句。 */\n expectedMessage: string\n /** keep_replay:按录制内容继续;continue_replay:从这里转为真实运行。 */\n resolve: (decision: \"keep_replay\" | \"continue_replay\") => void\n}\n\nexport interface UseReplayResult {\n /** 当前会话是不是正在回放(转自主运行后为 false)。 */\n isReplay: boolean\n /** 当前播放倍速,非回放会话固定为 1。 */\n speed: ReplaySpeed\n setSpeed: (speed: ReplaySpeed) => Promise<void>\n /** 退出回放,之后的对话交给真实模型运行。 */\n exitToAutonomous: () => Promise<void>\n /** 待决策的输入冲突;处理完自动清空。 */\n mismatch: ReplayMismatch | null\n /** 能不能改回放(改回放是 owner 专属)。只读身份下仍然显示\"在回放\",但不能操作。 */\n canControl: boolean\n\n /** 当前会话能否作为回放来源(并行子智能体等场景不支持)。 */\n canReplay: boolean\n /** canReplay 为 false 时的原因,可直接展示。 */\n unsupportedReason: string | null\n /** 从当前会话派生一个回放会话,返回新会话 id。 */\n startReplay: (speed?: ReplaySpeed) => Promise<string>\n isStarting: boolean\n /** 最近一次回放操作失败的原因。 */\n error: Error | null\n}\n\n/** 正在创建回放会话的源会话;按会话共享,多个 useReplay 实例也只会建出一个。 */\nconst replayCreating = new Set<string>()\n\n/**\n * 会话回放(演示 / 彩排模式)。\n *\n * 回放会话不会自动播放:用户照常发消息,只要这句和源会话录制的下一句意图一致,\n * 服务端就按倍速重放当时的回复和工具调用,完全不调用模型;对不上就抛出\n * `mismatch` 交给界面决定。\n *\n * 同一个 hook 覆盖两种身份——传入的会话既可能是回放会话(isReplay / setSpeed /\n * exitToAutonomous / mismatch),也可能是准备被回放的源会话(canReplay /\n * startReplay)。\n *\n * 回放状态本身住在 `SessionState.replay` 里(非 React 宿主直接订阅它即可),\n * 这里只做 React 绑定:订阅快照 + 接管冲突 + 拉一次 preview。\n *\n * **注册即接管**:底层的 `replayMismatch` 只交给最先注册的处理器。调用本 hook\n * 就意味着由它接管冲突;不调用则维持 SDK 默认行为——直接转为真实运行。\n */\nexport function useReplay(session: AgentSession | null): UseReplayResult {\n const client = useBladeClient()\n const sessionId = session?.sessionId ?? null\n\n const [replay, setReplay] = useState<SessionState[\"replay\"]>(null)\n const [viewerRole, setViewerRole] = useState<SessionState[\"viewerRole\"]>(null)\n // 运行中的会话不能当回放素材(服务端也这么判),跑完要重新问一次能不能回放。\n const [sourceRunning, setSourceRunning] = useState(false)\n const [mismatch, setMismatch] = useState<ReplayMismatch | null>(null)\n // 加载中不能当\"支持\"用:慢网络下一打开就点,会绕过这道检查发出必然失败的请求。\n const [preview, setPreview] = useState<\n { status: \"loading\" } | { status: \"ready\"; supported: boolean; reason: string | null }\n >({ status: \"loading\" })\n const [isStarting, setIsStarting] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n // 还没开始回放时倍速只是个本地选择,startReplay 时才带给服务端。\n const [draftSpeed, setDraftSpeed] = useState<ReplaySpeed>(DEFAULT_REPLAY_SPEED)\n\n // 回放状态跟着会话快照走;AgentSession 在 bootstrap 时就填好了初始值。\n useEffect(() => {\n if (!session) {\n setReplay(null)\n setViewerRole(null)\n setSourceRunning(false)\n return\n }\n const pull = () => {\n const state = session.getState()\n setReplay(state.replay)\n setViewerRole(state.viewerRole)\n setSourceRunning(state.isStreaming || state.status === \"running\")\n }\n pull()\n return session.subscribe(pull)\n }, [session])\n\n // 换会话才重置这些:倍速是跟着源会话走的选择,上一个会话选的 1x 不该带过来;\n // 冲突和错误同理。在渲染期比较而不是用 effect——effect 会先渲染一帧旧值,\n // 而且不能和下面的 preview 查询合并(那个还要跟着运行状态重跑,会把用户\n // 刚选的倍速冲掉)。\n const [lastSessionId, setLastSessionId] = useState(sessionId)\n if (sessionId !== lastSessionId) {\n setLastSessionId(sessionId)\n setMismatch(null)\n setError(null)\n setDraftSpeed(DEFAULT_REPLAY_SPEED)\n }\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: sourceRunning 跨过\"运行中\"这条线就是重新查询的触发条件——这一轮跑完了本该能回放,新一轮开始了就不该再放行\n useEffect(() => {\n setPreview({ status: \"loading\" })\n if (!sessionId) return\n let alive = true\n client.sessions\n .getReplayPreview(sessionId)\n .then((result) => {\n if (!alive) return\n setPreview({\n status: \"ready\",\n supported: result.supported,\n reason: result.reason ?? null,\n })\n })\n .catch(() => {\n // 查不到就放行,让服务端在真正创建时把关——但那是\"问过了没结论\",\n // 和\"还没问\"不是一回事,后者必须继续禁用。\n if (alive) setPreview({ status: \"ready\", supported: true, reason: null })\n })\n return () => {\n alive = false\n }\n }, [client, sessionId, sourceRunning])\n\n useEffect(() => {\n if (!session) return\n return session.on(\"replayMismatch\", ({ actualMessage, expectedMessage, respond }) => {\n setMismatch({\n actualMessage: actualMessage ?? \"\",\n expectedMessage: expectedMessage ?? \"\",\n resolve: (decision) => {\n setMismatch(null)\n respond(decision)\n },\n })\n })\n }, [session])\n\n const isReplay = replay?.isReplay ?? false\n\n const setSpeed = useCallback(\n async (next: ReplaySpeed) => {\n setDraftSpeed(next)\n if (!session) return\n setError(null)\n try {\n await session.setReplaySpeed(next)\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n },\n [session],\n )\n\n const exitToAutonomous = useCallback(async () => {\n if (!session) return\n setError(null)\n // 冲突还没决定就点退出:那条消息已经发出去、输入框锁着,只改回放状态的话\n // 卡片会留在屏幕上、对话卡死。\"不回放了\"对应的决定就是让它真的运行,\n // 而 continue_replay 本身就会转自主,不用再 PATCH 一次。\n const pending = mismatch\n if (pending) {\n pending.resolve(\"continue_replay\")\n return\n }\n try {\n await session.exitReplay()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n }, [session, mismatch])\n\n const startReplay = useCallback(\n async (speed?: ReplaySpeed) => {\n if (!sessionId) throw new Error(\"当前没有可回放的会话\")\n // 不能只看 isStarting:state 在同一次渲染里还没更新,多个组件各挂一个\n // hook 实例时也各有各的 state。\n if (replayCreating.has(sessionId)) throw new Error(\"正在创建回放会话,请稍候\")\n replayCreating.add(sessionId)\n setIsStarting(true)\n setError(null)\n try {\n const result = await client.sessions.startReplaySession(sessionId, speed ?? draftSpeed)\n return result.session_id\n } catch (err) {\n const failure = err instanceof Error ? err : new Error(String(err))\n setError(failure)\n throw failure\n } finally {\n replayCreating.delete(sessionId)\n setIsStarting(false)\n }\n },\n [client, sessionId, draftSpeed],\n )\n\n return {\n isReplay,\n speed: isReplay ? (replay?.speed ?? 1) : draftSpeed,\n setSpeed,\n exitToAutonomous,\n mismatch,\n canControl: viewerRole !== \"viewer\",\n canReplay:\n Boolean(sessionId) &&\n preview.status === \"ready\" &&\n preview.supported &&\n viewerRole !== \"viewer\",\n unsupportedReason:\n preview.status === \"ready\" && !preview.supported ? preview.reason : null,\n startReplay,\n isStarting,\n error,\n }\n}\n","import type { ChatMessage, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { useCallback, useMemo, useRef, useState } from \"react\"\n\n/** OpenAI 标准的工具声明,原样发给模型服务。 */\nexport interface ChatCompletionTool {\n type: \"function\"\n function: {\n name: string\n description?: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface LlmToolCall {\n id: string\n name: string\n /** 模型生成的 JSON 字符串,是不可信输入,用之前自己校验。 */\n arguments: string\n}\n\nexport interface LlmChatOptions {\n /** OpenAI 兼容根地址,如 `/api/llm`(自己后端的透传路径)或 `http://host:30000/v1`。 */\n baseURL: string\n model: string\n /**\n * 直连模型服务时才需要。默认形态是让 `baseURL` 指向自己的后端,由后端补密钥转发,\n * 浏览器不持有密钥。\n */\n apiKey?: string\n headers?: Record<string, string>\n /** 系统提示词,合成到消息最前面。 */\n system?: string\n /** 每次带上最近多少轮,默认 12。越长越贵,也越容易超上下文。 */\n historyTurns?: number\n temperature?: number\n /** 透传给模型服务的额外字段。 */\n extraBody?: Record<string, unknown>\n tools?: ChatCompletionTool[]\n /** 执行模型请求的工具,返回值会被序列化后回喂给模型。 */\n onToolCall?: (call: LlmToolCall) => Promise<unknown>\n /** 工具最多来回几轮,默认 4,防止模型绕圈烧 token。 */\n maxToolRounds?: number\n fetchImpl?: typeof fetch\n}\n\nexport interface UseLlmChatResult {\n messages: ChatMessage[]\n isStreaming: boolean\n /** 出错原因;下一次发送会清掉。 */\n error: string | null\n send: (text: string) => Promise<boolean>\n stop: () => void\n reset: () => void\n}\n\ninterface OpenAiToolCall {\n id: string\n type: \"function\"\n function: { name: string; arguments: string }\n}\n\ntype OpenAiMessage =\n | { role: \"user\" | \"assistant\"; content: string; tool_calls?: OpenAiToolCall[] }\n | { role: \"tool\"; tool_call_id: string; content: string }\n\ninterface AssistantTiming {\n startedAt: number\n durationMs?: number\n status?: \"failed\" | \"interrupted\"\n}\n\nconst DEFAULT_HISTORY_TURNS = 12\nconst DEFAULT_MAX_TOOL_ROUNDS = 4\n\n/** 补 id 用的序号。全局递增即可,只要同一段对话里不重复。 */\nlet syntheticIdSeq = 0\n\n/**\n * 纯 LLM 对话的状态机,协议就是 **OpenAI Chat Completions**——任何兼容实现都能直接接,\n * 不需要为这个组件专门写后端。\n *\n * 工具调用走 OpenAI 标准的多轮流程:模型返回 tool_calls → 调 onToolCall 执行 →\n * 结果作为 role:\"tool\" 消息回喂 → 再请求一次。\n */\nexport function useLlmChat(options: LlmChatOptions): UseLlmChatResult {\n const [history, setHistory] = useState<OpenAiMessage[]>([])\n const [streamingText, setStreamingText] = useState<string | null>(null)\n const [streamingCalls, setStreamingCalls] = useState<OpenAiToolCall[]>([])\n const [failedToolIds, setFailedToolIds] = useState<string[]>([])\n const [error, setError] = useState<string | null>(null)\n const [isStreaming, setIsStreaming] = useState(false)\n const abortRef = useRef<AbortController | null>(null)\n const activeStartedAtRef = useRef<number | null>(null)\n const assistantTimingsRef = useRef(new WeakMap<OpenAiMessage, AssistantTiming>())\n // reset 会把对话清空;正在跑的那轮不能事后再往里补消息\n const generationRef = useRef(0)\n // 用 ref 读:send 不该因为消息或配置变化而换引用,否则宿主的 useEffect 会连环重跑\n const historyRef = useRef<OpenAiMessage[]>([])\n const optionsRef = useRef(options)\n optionsRef.current = options\n historyRef.current = history\n\n const stop = useCallback(() => {\n abortRef.current?.abort()\n abortRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n generationRef.current += 1\n stop()\n setHistory([])\n setStreamingText(null)\n setStreamingCalls([])\n setFailedToolIds([])\n setError(null)\n setIsStreaming(false)\n activeStartedAtRef.current = null\n assistantTimingsRef.current = new WeakMap()\n }, [stop])\n\n const send = useCallback(async (text: string): Promise<boolean> => {\n const content = text.trim()\n if (!content || abortRef.current) return false\n const opts = optionsRef.current\n const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS\n const startedAt = Date.now()\n activeStartedAtRef.current = startedAt\n\n const commit = (message: OpenAiMessage) => {\n historyRef.current = [...historyRef.current, message]\n setHistory(historyRef.current)\n }\n const latestAssistantTiming = { current: null as AssistantTiming | null }\n const commitAssistant = (message: OpenAiMessage) => {\n const timing: AssistantTiming = { startedAt }\n assistantTimingsRef.current.set(message, timing)\n latestAssistantTiming.current = timing\n commit(message)\n return timing\n }\n const finishTiming = (timing: AssistantTiming) => {\n timing.durationMs = Math.max(0, Date.now() - timing.startedAt)\n }\n\n setError(null)\n setIsStreaming(true)\n setStreamingText(\"\")\n setStreamingCalls([])\n commit({ role: \"user\", content })\n // 停止时要把已经吐出来的部分留在对话里,所以本地也累一份——state 在闭包里读不到\n let partial = \"\"\n\n const controller = new AbortController()\n abortRef.current = controller\n const generation = generationRef.current\n try {\n for (let round = 0; ; round += 1) {\n const result = await streamOnce(opts, historyRef.current, controller.signal, {\n onText: (chunk) => {\n partial += chunk\n setStreamingText((prev) => `${prev ?? \"\"}${chunk}`)\n },\n onToolCalls: (calls) => setStreamingCalls(calls),\n })\n\n const assistant: OpenAiMessage = {\n role: \"assistant\",\n content: result.content,\n ...(result.toolCalls.length ? { tool_calls: result.toolCalls } : {}),\n }\n setStreamingText(null)\n setStreamingCalls([])\n const assistantTiming = commitAssistant(assistant)\n\n if (!result.toolCalls.length) {\n finishTiming(assistantTiming)\n return true\n }\n // 提交过的 assistant 带着 tool_calls,就必须有对应的 role:\"tool\" 回复,\n // 否则下一轮请求里是一组没人应答的工具调用,严格的 OpenAI 兼容服务直接 400,\n // 这段对话从此就废了。提前收尾时补上说明,让模型知道没执行。\n // 没执行成功的要标成失败,否则界面给个绿勾,等于骗人说查过了\n const bail = (reason: string) => {\n for (const call of result.toolCalls) {\n commit({ role: \"tool\", tool_call_id: call.id, content: JSON.stringify({ error: reason }) })\n }\n setFailedToolIds((prev) => [...prev, ...result.toolCalls.map((call) => call.id)])\n }\n if (!opts.onToolCall) {\n bail(\"这个应用没有提供工具执行入口\")\n finishTiming(assistantTiming)\n return true\n }\n if (round >= maxRounds) {\n bail(`工具调用已达上限 ${maxRounds} 轮,没有执行`)\n setError(`工具调用超过 ${maxRounds} 轮仍未给出结论,已停下。`)\n finishTiming(assistantTiming)\n return false\n }\n\n for (const call of result.toolCalls) {\n let output: string\n try {\n const value = await opts.onToolCall({\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n })\n output = typeof value === \"string\" ? value : JSON.stringify(value ?? null)\n } catch (err) {\n // 执行失败也要回喂给模型,让它自己决定换个方式还是告诉用户\n output = JSON.stringify({ error: err instanceof Error ? err.message : String(err) })\n setFailedToolIds((prev) => [...prev, call.id])\n }\n commit({ role: \"tool\", tool_call_id: call.id, content: output })\n }\n setStreamingText(\"\")\n }\n } catch (err) {\n setStreamingText(null)\n setStreamingCalls([])\n if (controller.signal.aborted) {\n // reset 也走 abort,但它是\"把这段对话清空\",不能再往里补消息\n if (generation !== generationRef.current) return false\n // 保留已经看到的内容:清成\"(已停止)\"等于把用户刚读到的答案抹掉,\n // 下一轮的上下文也跟着丢了\n const timing = commitAssistant({\n role: \"assistant\",\n content: partial ? `${partial}(已停止)` : \"(已停止)\",\n })\n timing.status = \"interrupted\"\n finishTiming(timing)\n return false\n }\n // 和\"停止\"一样保留已经吐出来的部分:网关在流中途下发 error(限流、上游断连)\n // 时把半屏答案抹掉,用户刚读的内容没了,重试时模型也看不到这段上下文\n if (partial && generation === generationRef.current) {\n const timing = commitAssistant({ role: \"assistant\", content: partial })\n timing.status = \"failed\"\n finishTiming(timing)\n } else if (latestAssistantTiming.current) {\n latestAssistantTiming.current.status = \"failed\"\n finishTiming(latestAssistantTiming.current)\n }\n setError(err instanceof Error && err.message ? err.message : \"对话失败,请重试\")\n return false\n } finally {\n if (abortRef.current === controller) abortRef.current = null\n activeStartedAtRef.current = null\n setIsStreaming(false)\n }\n }, [])\n\n const messages = useMemo(\n () =>\n toChatMessages(\n history,\n streamingText,\n streamingCalls,\n failedToolIds,\n assistantTimingsRef.current,\n activeStartedAtRef.current,\n ),\n [history, streamingText, streamingCalls, failedToolIds],\n )\n\n return { messages, isStreaming, error, send, stop, reset }\n}\n\ninterface StreamResult {\n content: string\n toolCalls: OpenAiToolCall[]\n}\n\n/** 发一次请求并消费流;返回这一轮模型给出的完整内容与工具调用。 */\nasync function streamOnce(\n options: LlmChatOptions,\n history: OpenAiMessage[],\n signal: AbortSignal,\n handlers: { onText: (chunk: string) => void; onToolCalls: (calls: OpenAiToolCall[]) => void },\n): Promise<StreamResult> {\n const {\n baseURL,\n model,\n apiKey,\n headers,\n system,\n historyTurns = DEFAULT_HISTORY_TURNS,\n temperature,\n extraBody,\n tools,\n fetchImpl,\n } = options\n\n const trimmed = trimHistory(history, historyTurns)\n const payload = {\n model,\n stream: true,\n messages: system ? [{ role: \"system\", content: system }, ...trimmed] : trimmed,\n ...(tools?.length ? { tools } : {}),\n ...(temperature === undefined ? {} : { temperature }),\n ...extraBody,\n }\n\n const doFetch = fetchImpl ?? globalThis.fetch\n const url = `${baseURL.replace(/\\/$/, \"\")}/chat/completions`\n let response: Response\n try {\n response = await doFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n ...headers,\n },\n body: JSON.stringify(payload),\n signal,\n })\n } catch (err) {\n if (signal.aborted) throw err\n // 浏览器给的原始信息是 \"Failed to fetch\",看不出是哪台服务连不上。\n // 地址填错是最常见的故障,必须把地址显示出来。\n throw new Error(`连不上模型服务 ${url}:${err instanceof Error ? err.message : String(err)}`)\n }\n if (!response.ok) {\n throw new Error(await describeHttpError(response))\n }\n if (!response.body) {\n throw new Error(\"模型服务没有返回流式响应\")\n }\n\n let content = \"\"\n const calls: OpenAiToolCall[] = []\n for await (const chunk of readSse(response.body, signal)) {\n if (chunk.error) {\n throw new Error(chunk.error)\n }\n if (chunk.text) {\n content += chunk.text\n handlers.onText(chunk.text)\n }\n if (chunk.toolCallDeltas) {\n for (const delta of chunk.toolCallDeltas) {\n // 工具调用按 index 分片下发:id 与函数名通常只在第一片,参数逐片拼\n const index = delta.index ?? 0\n const existing = calls[index] ?? { id: \"\", type: \"function\" as const, function: { name: \"\", arguments: \"\" } }\n calls[index] = {\n id: delta.id ?? existing.id,\n type: \"function\",\n function: {\n name: delta.function?.name ?? existing.function.name,\n arguments: `${existing.function.arguments}${delta.function?.arguments ?? \"\"}`,\n },\n }\n }\n handlers.onToolCalls(calls.filter(Boolean).map((call) => ({ ...call })))\n }\n }\n const normalized = calls\n .filter(Boolean)\n // 流里会出现只有 arguments、没有函数名的分片;累积后如果仍然没名字,说明它不是\n // 一次真的调用(网关分片的产物)。留着会让宿主的 onToolCall 收到空名字,\n // 只能抛\"没有这个函数\",整轮就废了。\n .filter((call) => call.function.name)\n // 有的实现不给 id,补一个,否则回喂时对不上。序号必须跨轮唯一:\n // 每轮都从 call_0 开始的话,工具结果和失败标记会串到上一轮的卡片上。\n .map((call) => {\n if (call.id) return call\n syntheticIdSeq += 1\n return { ...call, id: `call_auto_${syntheticIdSeq}` }\n })\n return { content, toolCalls: normalized }\n}\n\n/** 按轮数截断历史,但不能把工具消息与它所属的助手消息切散。 */\nfunction trimHistory(history: OpenAiMessage[], historyTurns: number): OpenAiMessage[] {\n const userIndexes = history.reduce<number[]>((acc, msg, i) => {\n if (msg.role === \"user\") acc.push(i)\n return acc\n }, [])\n if (userIndexes.length <= historyTurns) return history\n return history.slice(userIndexes[userIndexes.length - historyTurns])\n}\n\nasync function describeHttpError(response: Response): Promise<string> {\n const raw = await response.text().catch(() => \"\")\n try {\n const parsed = JSON.parse(raw) as { error?: { message?: string } | string; detail?: unknown }\n const message =\n typeof parsed.error === \"string\"\n ? parsed.error\n : (parsed.error?.message ??\n (typeof parsed.detail === \"string\" ? parsed.detail : undefined))\n if (message) return `模型服务返回 ${response.status}:${message}`\n } catch {\n // 不是 JSON 就用原文\n }\n const snippet = raw.trim().slice(0, 120)\n return snippet ? `模型服务返回 ${response.status}:${snippet}` : `模型服务返回 ${response.status}`\n}\n\ninterface StreamChunk {\n text?: string\n toolCallDeltas?: Array<{\n index?: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n error?: string\n}\n\n/**\n * 读 OpenAI 的流式响应。事件之间用空行分隔,`data: [DONE]` 收尾。\n * 必须按 `\\n\\n` 缓冲——一个事件可能横跨两个网络分片,这是实测踩过的。\n */\nasync function* readSse(\n stream: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n): AsyncGenerator<StreamChunk> {\n const reader = stream.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n try {\n while (!signal.aborted) {\n const { done, value } = await reader.read()\n if (done) {\n // 不少兼容网关最后一个 data: 后面不补空行、也不发 [DONE]。不收尾解析的话,\n // 回答的最后一段会静默丢失;丢的要是工具参数的最后一片,宿主拿到的就是\n // 截断的 JSON。\n const tail = parseSse(buffer)\n if (tail && tail !== \"done\") yield tail\n break\n }\n // SSE 规范允许 CRLF;只认 \\n\\n 的话,用 CRLF 的服务永远解析不出事件。\n // 归一必须在拼接之后做:网络分片可能正好切在 \\r 和 \\n 中间,先替换再拼会\n // 留下一个 \\n\\r\\n,边界就再也认不出来了。\n buffer = `${buffer}${decoder.decode(value, { stream: true })}`.replace(/\\r\\n/g, \"\\n\")\n let boundary = buffer.indexOf(\"\\n\\n\")\n while (boundary !== -1) {\n const chunk = parseSse(buffer.slice(0, boundary))\n buffer = buffer.slice(boundary + 2)\n if (chunk === \"done\") return\n if (chunk) yield chunk\n boundary = buffer.indexOf(\"\\n\\n\")\n }\n }\n } finally {\n reader.cancel().catch(() => {})\n }\n}\n\nfunction parseSse(raw: string): StreamChunk | \"done\" | null {\n const dataLines: string[] = []\n for (const line of raw.split(\"\\n\")) {\n if (line.startsWith(\"data:\")) dataLines.push(line.slice(5).trim())\n }\n if (!dataLines.length) return null\n const data = dataLines.join(\"\\n\")\n if (data === \"[DONE]\") return \"done\"\n let parsed: {\n choices?: Array<{ delta?: { content?: string; tool_calls?: StreamChunk[\"toolCallDeltas\"] } }>\n error?: { message?: string } | string\n }\n try {\n parsed = JSON.parse(data)\n } catch {\n return null\n }\n if (parsed.error) {\n const message = typeof parsed.error === \"string\" ? parsed.error : parsed.error.message\n return { error: message || \"模型服务返回了错误\" }\n }\n const delta = parsed.choices?.[0]?.delta\n if (!delta) return null\n return { text: delta.content, toolCallDeltas: delta.tool_calls }\n}\n\n/** OpenAI 形状 → SDK 的 ChatMessage:工具结果并进对应的 tool_calls,不单独成条。 */\nfunction toChatMessages(\n history: OpenAiMessage[],\n streamingText: string | null,\n streamingCalls: OpenAiToolCall[],\n failedToolIds: string[],\n assistantTimings: WeakMap<OpenAiMessage, AssistantTiming>,\n activeStartedAt: number | null,\n): ChatMessage[] {\n const results = new Map<string, string>()\n for (const msg of history) {\n if (msg.role === \"tool\") results.set(msg.tool_call_id, msg.content)\n }\n\n const messages: ChatMessage[] = []\n for (const msg of history) {\n if (msg.role === \"tool\") continue\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content, status: \"completed\" })\n continue\n }\n const timing = assistantTimings.get(msg)\n messages.push({\n role: \"assistant\",\n content: msg.content,\n status: timing?.status ?? \"completed\",\n ...(timing ? { timestamp: new Date(timing.startedAt).toISOString() } : {}),\n ...(timing?.durationMs === undefined ? {} : { duration_ms: timing.durationMs }),\n ...(msg.tool_calls?.length\n ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n\n if (streamingText !== null) {\n messages.push({\n role: \"assistant\",\n content: streamingText,\n status: \"streaming\",\n ...(activeStartedAt === null\n ? {}\n : { timestamp: new Date(activeStartedAt).toISOString() }),\n ...(streamingCalls.length\n ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n return messages\n}\n\nfunction toToolCallInfo(\n call: OpenAiToolCall,\n results: Map<string, string>,\n failedToolIds: string[],\n): ToolCallInfo {\n const result = results.get(call.id)\n const failed = failedToolIds.includes(call.id)\n return {\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n ...(result === undefined ? {} : { result }),\n status: result === undefined ? \"pending\" : failed ? \"error\" : \"done\",\n }\n}\n","import { useCallback, useEffect, useRef } from \"react\"\n\nconst DEFAULT_MARGIN_PX = 24\nconst MANUAL_SCROLL_TOLERANCE_PX = 2\n\nexport interface UseMessagePinOptions {\n targetKey: string | null\n pinTarget: boolean\n layoutKey?: string\n getScrollElement: () => HTMLElement | null\n getContentElement?: () => HTMLElement | null\n getTargetElement: () => HTMLElement | null\n getTargetScrollTop?: (scroll: HTMLElement) => number | null\n getSpacerHeight: () => number\n setSpacerHeight: (height: number) => void\n stopAutoScroll: () => void\n scrollToBottom: () => void\n margin?: number\n}\n\n/**\n * 用户发言顶置的共享状态机。SDK 和内置轻量列表只提供 DOM/虚拟列表适配器,\n * 触发、调度、resize、手动接管和跟随交还的决策只在这里维护。\n */\nexport function useMessagePin({\n targetKey,\n pinTarget,\n layoutKey,\n getScrollElement,\n getContentElement,\n getTargetElement,\n getTargetScrollTop,\n getSpacerHeight,\n setSpacerHeight,\n stopAutoScroll,\n scrollToBottom,\n margin = DEFAULT_MARGIN_PX,\n}: UseMessagePinOptions) {\n const pinActiveRef = useRef(false)\n const frameRef = useRef<number | null>(null)\n const retryTimeoutRef = useRef<number | null>(null)\n const manualScrollCheckTimeoutRef = useRef<number | null>(null)\n const repositionPendingRef = useRef(false)\n const pinnedTargetScrollTopRef = useRef<number | null>(null)\n const appliedTargetKeyRef = useRef<string | null>(null)\n const initialObservedTargetKeyRef = useRef<string | null>(pinTarget ? null : targetKey)\n const observedTargetKeyRef = useRef<string | null>(initialObservedTargetKeyRef.current)\n const setSpacerHeightRef = useRef(setSpacerHeight)\n setSpacerHeightRef.current = setSpacerHeight\n\n const release = useCallback(() => {\n if (!pinActiveRef.current) return\n pinActiveRef.current = false\n repositionPendingRef.current = false\n pinnedTargetScrollTopRef.current = null\n if (frameRef.current != null) cancelAnimationFrame(frameRef.current)\n frameRef.current = null\n if (manualScrollCheckTimeoutRef.current != null) {\n clearTimeout(manualScrollCheckTimeoutRef.current)\n manualScrollCheckTimeoutRef.current = null\n }\n setSpacerHeightRef.current(0)\n }, [])\n\n const reposition = useCallback(\n () => {\n const scroll = getScrollElement()\n const target = getTargetElement()\n if (!scroll || !pinActiveRef.current) return\n\n const targetScrollTop = target\n ? Math.max(\n 0,\n scroll.scrollTop +\n target.getBoundingClientRect().top -\n scroll.getBoundingClientRect().top -\n margin,\n )\n : getTargetScrollTop?.(scroll)\n if (targetScrollTop == null) return\n const previousSpacerHeight = getSpacerHeight()\n const baseScrollHeight = scroll.scrollHeight - previousSpacerHeight\n const nextSpacerHeight = Math.max(\n 0,\n targetScrollTop + scroll.clientHeight - baseScrollHeight,\n )\n setSpacerHeightRef.current(nextSpacerHeight)\n pinnedTargetScrollTopRef.current = targetScrollTop\n\n stopAutoScroll()\n scroll.scrollTop = targetScrollTop\n // A zero spacer on the first placement is valid: the target may already\n // fill the viewport, and releasing here would immediately scroll its\n // beginning away. Hand over only after an existing positive spacer has\n // been consumed by response growth.\n if (nextSpacerHeight === 0 && previousSpacerHeight > 0) {\n pinActiveRef.current = false\n scrollToBottom()\n }\n },\n [\n getScrollElement,\n getSpacerHeight,\n getTargetElement,\n getTargetScrollTop,\n margin,\n scrollToBottom,\n stopAutoScroll,\n ],\n )\n\n const scheduleReposition = useCallback(\n () => {\n if (!pinActiveRef.current) return\n repositionPendingRef.current = true\n if (frameRef.current != null) return\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null\n const shouldReposition = repositionPendingRef.current\n repositionPendingRef.current = false\n if (shouldReposition) reposition()\n })\n },\n [reposition],\n )\n\n useEffect(() => {\n if (!targetKey) {\n release()\n appliedTargetKeyRef.current = null\n observedTargetKeyRef.current = null\n return\n }\n if (!pinTarget) {\n if (\n pinActiveRef.current &&\n appliedTargetKeyRef.current != null &&\n appliedTargetKeyRef.current !== targetKey\n ) {\n release()\n }\n observedTargetKeyRef.current = targetKey\n return\n }\n if (appliedTargetKeyRef.current === targetKey) {\n observedTargetKeyRef.current = targetKey\n return\n }\n if (observedTargetKeyRef.current === targetKey) return\n\n appliedTargetKeyRef.current = targetKey\n observedTargetKeyRef.current = targetKey\n pinActiveRef.current = true\n stopAutoScroll()\n scheduleReposition()\n if (retryTimeoutRef.current != null) clearTimeout(retryTimeoutRef.current)\n retryTimeoutRef.current = window.setTimeout(() => {\n retryTimeoutRef.current = null\n scheduleReposition()\n }, 80)\n }, [pinTarget, release, scheduleReposition, stopAutoScroll, targetKey])\n\n useEffect(() => {\n if (layoutKey !== undefined) scheduleReposition()\n }, [layoutKey, scheduleReposition])\n\n useEffect(() => {\n const scroll = getScrollElement()\n if (!scroll) return\n const content = getContentElement?.()\n const observer = new ResizeObserver(scheduleReposition)\n observer.observe(scroll)\n if (content && content !== scroll) observer.observe(content)\n window.addEventListener(\"resize\", scheduleReposition)\n window.visualViewport?.addEventListener(\"resize\", scheduleReposition)\n\n const handleScroll = () => {\n if (!pinActiveRef.current) return\n if (manualScrollCheckTimeoutRef.current != null) {\n clearTimeout(manualScrollCheckTimeoutRef.current)\n }\n manualScrollCheckTimeoutRef.current = window.setTimeout(() => {\n manualScrollCheckTimeoutRef.current = null\n if (!pinActiveRef.current || repositionPendingRef.current) return\n const targetScrollTop = pinnedTargetScrollTopRef.current\n if (\n targetScrollTop != null &&\n Math.abs(scroll.scrollTop - targetScrollTop) > MANUAL_SCROLL_TOLERANCE_PX\n ) {\n release()\n }\n }, 0)\n }\n scroll.addEventListener(\"scroll\", handleScroll, { passive: true })\n return () => {\n observer.disconnect()\n window.removeEventListener(\"resize\", scheduleReposition)\n window.visualViewport?.removeEventListener(\"resize\", scheduleReposition)\n scroll.removeEventListener(\"scroll\", handleScroll)\n }\n }, [getContentElement, getScrollElement, release, scheduleReposition])\n\n useEffect(\n () => () => {\n if (frameRef.current != null) cancelAnimationFrame(frameRef.current)\n frameRef.current = null\n if (retryTimeoutRef.current != null) clearTimeout(retryTimeoutRef.current)\n retryTimeoutRef.current = null\n if (manualScrollCheckTimeoutRef.current != null) {\n clearTimeout(manualScrollCheckTimeoutRef.current)\n }\n manualScrollCheckTimeoutRef.current = null\n repositionPendingRef.current = false\n pinnedTargetScrollTopRef.current = null\n appliedTargetKeyRef.current = null\n observedTargetKeyRef.current = initialObservedTargetKeyRef.current\n pinActiveRef.current = false\n setSpacerHeightRef.current(0)\n },\n [],\n )\n\n const isActive = useCallback(() => pinActiveRef.current, [])\n return { release, isActive }\n}\n","import type {\n AgentSession,\n BladeClient,\n CreateSessionRequest,\n ResultFeedback,\n} from \"@blade-hq/agent-client\"\nimport { BladeApiError, latestPostChatFollowup } from \"@blade-hq/agent-client\"\nimport { LockKeyhole } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\nimport { useAgentSession } from \"../hooks/use-agent-session\"\nimport { useReplay } from \"../hooks/use-replay\"\nimport { ReplayBar } from \"./ReplayBar\"\nimport { ReplayMismatchPrompt } from \"./ReplayMismatchPrompt\"\nimport { isPlanUpdateTool, parsePlanUpdate } from \"./PlanUpdateBlock\"\nimport { cn } from \"../lib/utils\"\nimport { ChatSurface, themeAttr, type ChatPresentationProps } from \"./ChatSurface\"\nimport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\n\nexport interface AgentChatProps extends ChatPresentationProps {\n /** follow-up、成果和评分交互;SDK 不绑定任何分析厂商。 */\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n /** 连接既有会话;不传则按 createOptions 自动新建。 */\n sessionId?: string\n createOptions?: CreateSessionRequest\n /** 自动创建出的 sessionId 通过此回调交还调用方持有。 */\n onSessionCreated?: (sessionId: string) => void\n /** 会话连接完成后回调(页面协作场景拿 AgentSession 实例用)。 */\n onSessionReady?: (session: AgentSession) => void\n /** 智能体下发给宿主页面的指令处理器(action → handler)。 */\n commands?: Record<string, (data: unknown) => void>\n}\n\nfunction isUnauthorizedError(error: Error | null): boolean {\n return error instanceof BladeApiError && error.status === 401\n}\n\n/** 登录引导卡片:未持有令牌或令牌失效时展示,按钮走弹窗授权。 */\nfunction LoginCard({ client, onLoggedIn }: { client: BladeClient; onLoggedIn: () => void }) {\n const [loggingIn, setLoggingIn] = useState(false)\n const [loginError, setLoginError] = useState<string | null>(null)\n\n const handleLogin = async () => {\n setLoggingIn(true)\n setLoginError(null)\n try {\n await client.login()\n onLoggedIn()\n } catch (error) {\n setLoginError(error instanceof Error && error.message ? error.message : \"登录失败,请重试\")\n } finally {\n setLoggingIn(false)\n }\n }\n\n return (\n <div className=\"blade-chat-login flex flex-1 items-center justify-center p-6\">\n <div className=\"flex w-full max-w-sm flex-col items-center gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-6 py-8 text-center\">\n <LockKeyhole size={28} className=\"text-[hsl(var(--muted-foreground))]\" />\n <div className=\"text-base font-medium text-[hsl(var(--foreground))]\">需要登录后使用</div>\n <div className=\"text-sm text-[hsl(var(--muted-foreground))]\">\n 登录后即可与智能体对话,你的会话内容仅自己可见。\n </div>\n <button\n type=\"button\"\n onClick={handleLogin}\n disabled={loggingIn}\n className=\"w-full rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-semibold text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:opacity-60\"\n >\n {loggingIn ? \"登录中…\" : \"登录\"}\n </button>\n {loginError && <div className=\"text-xs text-[hsl(var(--destructive))]\">{loginError}</div>}\n </div>\n </div>\n )\n}\n\n/**\n * 智能体模式的聊天界面:连接(或新建)一个 Blade 会话,渲染消息流与输入框。\n * 状态全部来自 useAgentSession,不依赖任何全局 store。\n */\nexport function AgentChat(props: AgentChatProps) {\n const client = useBladeClient()\n // attempt 作为会话子树的 key:登录成功后整体重挂载,重新走连接/创建流程\n const [attempt, setAttempt] = useState(0)\n const [needLogin, setNeedLogin] = useState(() => !client.hasToken())\n\n if (needLogin) {\n return (\n <div\n data-theme={themeAttr(props.theme)}\n className={cn(\"blade-chat flex min-h-0 flex-1 flex-col\", props.classNames?.root)}\n >\n <LoginCard\n client={client}\n onLoggedIn={() => {\n setNeedLogin(false)\n setAttempt((value) => value + 1)\n }}\n />\n </div>\n )\n }\n\n return <ChatSessionView key={attempt} {...props} onUnauthorized={() => setNeedLogin(true)} />\n}\n\nfunction ChatSessionView({\n sessionId,\n createOptions,\n onSessionCreated,\n onSessionReady,\n commands,\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onFollowupInteraction,\n onUnauthorized,\n}: AgentChatProps & { onUnauthorized: () => void }) {\n const client = useBladeClient()\n const [planRevealRevisions, setPlanRevealRevisions] = useState(\n () => new Map<string, number>(),\n )\n const handleSessionConnected = useCallback((connectedSession: AgentSession) => {\n return connectedSession.on(\"toolResult\", ({ toolCall, turn, source }) => {\n if (\n source === \"reconnect_replay\" ||\n (turn.loop_id || \"root\") !== \"root\" ||\n toolCall.status !== \"done\" ||\n !isPlanUpdateTool(toolCall) ||\n !parsePlanUpdate(toolCall.arguments)\n ) {\n return\n }\n setPlanRevealRevisions((current) => {\n const next = new Map(current)\n next.set(connectedSession.sessionId, (current.get(connectedSession.sessionId) ?? 0) + 1)\n return next\n })\n })\n }, [])\n const { session, state, error } = useAgentSession(sessionId, {\n createOptions,\n onSessionCreated,\n onSessionConnected: handleSessionConnected,\n })\n // 开箱即用的聊天界面自带回放处理:不接的话,用它打开回放会话就是个普通聊天\n // ——看不出在回放、退不出去,说错话还会走\"无人监听\"兜底静默转成真实运行。\n const replay = useReplay(session)\n const [stopRequested, setStopRequested] = useState(false)\n const [inputText, setInputText] = useState(\"\")\n const [resultFeedback, setResultFeedback] = useState<ResultFeedback[]>([])\n const resolvedSessionId = session?.sessionId\n const isViewer = state?.viewerRole === \"viewer\"\n\n useEffect(() => {\n setResultFeedback([])\n if (!resolvedSessionId || isViewer) return\n let cancelled = false\n void client.sessions.listResultFeedback(resolvedSessionId).then(\n (items) => {\n if (cancelled) return\n setResultFeedback((current) => {\n const merged = new Map(items.map((item) => [item.assistant_entry_id, item]))\n for (const item of current) {\n const fetched = merged.get(item.assistant_entry_id)\n if (!fetched || fetched.updated_at < item.updated_at) {\n merged.set(item.assistant_entry_id, item)\n }\n }\n return [...merged.values()]\n })\n },\n () => {},\n )\n return () => {\n cancelled = true\n }\n }, [client, isViewer, resolvedSessionId])\n const resultFeedbackByEntry = useMemo(\n () => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),\n [resultFeedback],\n )\n const handleResultFeedbackSaved = useCallback((saved: ResultFeedback) => {\n setResultFeedback((current) => [\n ...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),\n saved,\n ])\n }, [])\n\n // 会话就绪后交还实例\n useEffect(() => {\n if (session) {\n onSessionReady?.(session)\n }\n }, [session, onSessionReady])\n\n // session.attach() / insertText() 注入到输入框:页面把业务数据(如地图选中\n // 的点位)塞进输入框,随下一条消息发给智能体。\n useEffect(() => {\n if (!session) return\n const offAttach = session.on(\"attachRequested\", ({ label, content }) => {\n setInputText((prev) => `${prev ? `${prev}\\n\\n` : \"\"}[${label}]\\n${content}`)\n })\n const offInsert = session.on(\"insertTextRequested\", ({ text }) => {\n setInputText((prev) => prev + text)\n })\n return () => {\n offAttach()\n offInsert()\n }\n }, [session])\n\n // 令牌失效(401)时回到登录引导\n useEffect(() => {\n if (isUnauthorizedError(error)) {\n onUnauthorized()\n }\n }, [error, onUnauthorized])\n\n // 注册宿主页面指令:卸载或 commands 变化时反注册\n useEffect(() => {\n if (!session || !commands) return\n const unsubscribes = Object.entries(commands).map(([action, handler]) =>\n session.onCommand(action, (payload) => handler(payload)),\n )\n return () => {\n for (const unsubscribe of unsubscribes) unsubscribe()\n }\n }, [session, commands])\n\n const isStreaming = state?.isStreaming ?? false\n const planRevealRevision = resolvedSessionId\n ? (planRevealRevisions.get(resolvedSessionId) ?? 0)\n : 0\n const isStopping = stopRequested && isStreaming\n const connectError =\n error && !isUnauthorizedError(error)\n ? error.message || \"连接失败,请稍后重试\"\n : null\n // 回放控制失败(改倍速 / 退出)也要让人看见,否则点了毫无反馈。\n const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null\n\n const handleSend = (text: string): Promise<boolean> => {\n setStopRequested(false)\n return session?.send(text, { mode: state?.mode ?? undefined }) ?? Promise.resolve(false)\n }\n\n const handleStop = () => {\n setStopRequested(true)\n void session?.stop()\n }\n\n return (\n <ChatSurface\n theme={theme}\n onFollowupInteraction={onFollowupInteraction}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n connection={state?.connection ?? \"connecting\"}\n banner={\n <>\n <ReplayBar\n isReplay={replay.isReplay}\n speed={replay.speed}\n canControl={replay.canControl}\n onSpeedChange={(next) => void replay.setSpeed(next)}\n onExit={() => void replay.exitToAutonomous()}\n />\n <ReplayMismatchPrompt mismatch={replay.mismatch} />\n </>\n }\n errorMessage={errorMessage}\n messages={state?.messages ?? []}\n postChatFollowup={latestPostChatFollowup(state?.turns ?? [])}\n resultFeedbackByEntry={resultFeedbackByEntry}\n onResultFeedbackSaved={handleResultFeedbackSaved}\n isStreaming={isStreaming}\n showPlanUpdates\n planRevealRevision={planRevealRevision}\n isStopping={isStopping}\n inputText={inputText}\n onInputChange={setInputText}\n onSuggestion={setInputText}\n onSend={handleSend}\n onAppend={(text) => {\n void session?.send(text, { mode: state?.mode ?? undefined })\n }}\n onStop={handleStop}\n sessionStatus={state?.status ?? undefined}\n askAnswers={state?.askAnswers}\n onAnswer={(answer, toolCallId, answerData) => {\n void session?.send(answer, {\n mode: state?.mode ?? undefined,\n askUserAnswer: { tool_call_id: toolCallId, ...answerData },\n })\n }}\n // 用 session 自己持有的 id,不是 sessionId prop——后者是可选的,\n // 自动建会话时一直是 undefined,文件卡片会永远不可用。\n sessionId={resolvedSessionId}\n isViewer={isViewer}\n />\n )\n}\n","import { CamelToPascal } from './utility-types';\n\n/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n\n/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","import { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses } from '@lucide/shared';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n {\n color = 'currentColor',\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = '',\n children,\n iconNode,\n ...rest\n },\n ref,\n ) => {\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,\n className: mergeClasses('lucide', className),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),\n ...props,\n }),\n );\n\n Component.displayName = `${iconName}`;\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowRight = createLucideIcon('ArrowRight', [\n ['path', { d: 'M5 12h14', key: '1ays0h' }],\n ['path', { d: 'm12 5 7 7-7 7', key: 'xquz4c' }],\n]);\n\nexport default ArrowRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowUpRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUpRight = createLucideIcon('ArrowUpRight', [\n ['path', { d: 'M7 7h10v10', key: '1tivn9' }],\n ['path', { d: 'M7 17 17 7', key: '1vkiza' }],\n]);\n\nexport default ArrowUpRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowUp\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUp = createLucideIcon('ArrowUp', [\n ['path', { d: 'm5 12 7-7 7 7', key: 'hav0vg' }],\n ['path', { d: 'M12 19V5', key: 'x0mq9r' }],\n]);\n\nexport default ArrowUp;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name BookOpen\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/book-open\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst BookOpen = createLucideIcon('BookOpen', [\n ['path', { d: 'M12 7v14', key: '1akyts' }],\n [\n 'path',\n {\n d: 'M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z',\n key: 'ruj8y',\n },\n ],\n]);\n\nexport default BookOpen;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Bot\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/bot\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Bot = createLucideIcon('Bot', [\n ['path', { d: 'M12 8V4H8', key: 'hb8ula' }],\n ['rect', { width: '16', height: '12', x: '4', y: '8', rx: '2', key: 'enze0r' }],\n ['path', { d: 'M2 14h2', key: 'vft8re' }],\n ['path', { d: 'M20 14h2', key: '4cs60a' }],\n ['path', { d: 'M15 13v2', key: '1xurst' }],\n ['path', { d: 'M9 13v2', key: 'rq6x2g' }],\n]);\n\nexport default Bot;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Check\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/check\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Check = createLucideIcon('Check', [['path', { d: 'M20 6 9 17l-5-5', key: '1gmf2c' }]]);\n\nexport default Check;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronDown\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-down\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronDown = createLucideIcon('ChevronDown', [\n ['path', { d: 'm6 9 6 6 6-6', key: 'qrunsl' }],\n]);\n\nexport default ChevronDown;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronRight = createLucideIcon('ChevronRight', [\n ['path', { d: 'm9 18 6-6-6-6', key: 'mthhwq' }],\n]);\n\nexport default ChevronRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name CircleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/circle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst CircleAlert = createLucideIcon('CircleAlert', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['line', { x1: '12', x2: '12', y1: '8', y2: '12', key: '1pkeuh' }],\n ['line', { x1: '12', x2: '12.01', y1: '16', y2: '16', key: '4dfq90' }],\n]);\n\nexport default CircleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name CircleDot\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/circle-dot\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst CircleDot = createLucideIcon('CircleDot', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['circle', { cx: '12', cy: '12', r: '1', key: '41hilf' }],\n]);\n\nexport default CircleDot;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Circle\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/circle\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Circle = createLucideIcon('Circle', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n]);\n\nexport default Circle;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Copy\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/copy\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Copy = createLucideIcon('Copy', [\n ['rect', { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2', key: '17jyea' }],\n ['path', { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2', key: 'zix9uf' }],\n]);\n\nexport default Copy;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Earth\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/earth\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Earth = createLucideIcon('Earth', [\n ['path', { d: 'M21.54 15H17a2 2 0 0 0-2 2v4.54', key: '1djwo0' }],\n [\n 'path',\n {\n d: 'M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17',\n key: '1tzkfa',\n },\n ],\n ['path', { d: 'M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05', key: '14pb5j' }],\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n]);\n\nexport default Earth;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name FilePenLine\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-pen-line\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FilePenLine = createLucideIcon('FilePenLine', [\n [\n 'path',\n {\n d: 'm18 5-2.414-2.414A2 2 0 0 0 14.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2',\n key: '142zxg',\n },\n ],\n [\n 'path',\n {\n d: 'M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z',\n key: '2t3380',\n },\n ],\n ['path', { d: 'M8 18h1', key: '13wk12' }],\n]);\n\nexport default FilePenLine;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name FileText\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-text\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FileText = createLucideIcon('FileText', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n ['path', { d: 'M10 9H8', key: 'b1mrlr' }],\n ['path', { d: 'M16 13H8', key: 't4e002' }],\n ['path', { d: 'M16 17H8', key: 'z1uh3a' }],\n]);\n\nexport default FileText;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Globe\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/globe\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Globe = createLucideIcon('Globe', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['path', { d: 'M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20', key: '13o1zl' }],\n ['path', { d: 'M2 12h20', key: '9i4pu4' }],\n]);\n\nexport default Globe;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Layers\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/layers\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Layers = createLucideIcon('Layers', [\n [\n 'path',\n {\n d: 'M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z',\n key: 'zw3jo',\n },\n ],\n [\n 'path',\n {\n d: 'M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12',\n key: '1wduqc',\n },\n ],\n [\n 'path',\n {\n d: 'M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17',\n key: 'kqbvx6',\n },\n ],\n]);\n\nexport default Layers;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Lightbulb\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lightbulb\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Lightbulb = createLucideIcon('Lightbulb', [\n [\n 'path',\n {\n d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5',\n key: '1gvzjb',\n },\n ],\n ['path', { d: 'M9 18h6', key: 'x1upvd' }],\n ['path', { d: 'M10 22h4', key: 'ceow96' }],\n]);\n\nexport default Lightbulb;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ListChecks\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/list-checks\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ListChecks = createLucideIcon('ListChecks', [\n ['path', { d: 'm3 17 2 2 4-4', key: '1jhpwq' }],\n ['path', { d: 'm3 7 2 2 4-4', key: '1obspn' }],\n ['path', { d: 'M13 6h8', key: '15sg57' }],\n ['path', { d: 'M13 12h8', key: 'h98zly' }],\n ['path', { d: 'M13 18h8', key: 'oe0vm4' }],\n]);\n\nexport default ListChecks;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LoaderCircle\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/loader-circle\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LoaderCircle = createLucideIcon('LoaderCircle', [\n ['path', { d: 'M21 12a9 9 0 1 1-6.219-8.56', key: '13zald' }],\n]);\n\nexport default LoaderCircle;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LockKeyhole\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lock-keyhole\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LockKeyhole = createLucideIcon('LockKeyhole', [\n ['circle', { cx: '12', cy: '16', r: '1', key: '1au0dj' }],\n ['rect', { x: '3', y: '10', width: '18', height: '12', rx: '2', key: '6s8ecr' }],\n ['path', { d: 'M7 10V7a5 5 0 0 1 10 0v3', key: '1pqi11' }],\n]);\n\nexport default LockKeyhole;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquareMore\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square-more\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquareMore = createLucideIcon('MessageSquareMore', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n ['path', { d: 'M8 10h.01', key: '19clt8' }],\n ['path', { d: 'M12 10h.01', key: '1nrarc' }],\n ['path', { d: 'M16 10h.01', key: '1m94wz' }],\n]);\n\nexport default MessageSquareMore;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquare\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquare = createLucideIcon('MessageSquare', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n]);\n\nexport default MessageSquare;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Play\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/play\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Play = createLucideIcon('Play', [\n ['polygon', { points: '6 3 20 12 6 21 6 3', key: '1oa8hb' }],\n]);\n\nexport default Play;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name RefreshCcw\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/refresh-ccw\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst RefreshCcw = createLucideIcon('RefreshCcw', [\n ['path', { d: 'M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8', key: '14sxne' }],\n ['path', { d: 'M3 3v5h5', key: '1xhq8a' }],\n ['path', { d: 'M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16', key: '1hlbsb' }],\n ['path', { d: 'M16 16h5v5', key: 'ccwih5' }],\n]);\n\nexport default RefreshCcw;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Search\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/search\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Search = createLucideIcon('Search', [\n ['circle', { cx: '11', cy: '11', r: '8', key: '4ej97u' }],\n ['path', { d: 'm21 21-4.3-4.3', key: '1qie3q' }],\n]);\n\nexport default Search;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Settings2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/settings-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Settings2 = createLucideIcon('Settings2', [\n ['path', { d: 'M20 7h-9', key: '3s1dr2' }],\n ['path', { d: 'M14 17H5', key: 'gfn3mx' }],\n ['circle', { cx: '17', cy: '17', r: '3', key: '18b49y' }],\n ['circle', { cx: '7', cy: '7', r: '3', key: 'dfmy0x' }],\n]);\n\nexport default Settings2;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Sparkles\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/sparkles\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Sparkles = createLucideIcon('Sparkles', [\n [\n 'path',\n {\n d: 'M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z',\n key: '4pj2yx',\n },\n ],\n ['path', { d: 'M20 3v4', key: '1olli1' }],\n ['path', { d: 'M22 5h-4', key: '1gvqau' }],\n ['path', { d: 'M4 17v2', key: 'vumght' }],\n ['path', { d: 'M5 18H3', key: 'zchphs' }],\n]);\n\nexport default Sparkles;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Square\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Square = createLucideIcon('Square', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n]);\n\nexport default Square;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Terminal\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/terminal\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Terminal = createLucideIcon('Terminal', [\n ['polyline', { points: '4 17 10 11 4 5', key: 'akl6gq' }],\n ['line', { x1: '12', x2: '20', y1: '19', y2: '19', key: 'q2wloq' }],\n]);\n\nexport default Terminal;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name TriangleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/triangle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst TriangleAlert = createLucideIcon('TriangleAlert', [\n [\n 'path',\n {\n d: 'm21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3',\n key: 'wmoenq',\n },\n ],\n ['path', { d: 'M12 9v4', key: 'juzpu7' }],\n ['path', { d: 'M12 17h.01', key: 'p32p05' }],\n]);\n\nexport default TriangleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Wrench\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/wrench\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Wrench = createLucideIcon('Wrench', [\n [\n 'path',\n {\n d: 'M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z',\n key: 'cbrjhi',\n },\n ],\n]);\n\nexport default Wrench;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name X\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/x\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst X = createLucideIcon('X', [\n ['path', { d: 'M18 6 6 18', key: '1bl5f8' }],\n ['path', { d: 'm6 6 12 12', key: 'd8bk6v' }],\n]);\n\nexport default X;\n","import { clsx, type ClassValue } from \"clsx\"\n\n/** 合并 className(SDK 内不引入 tailwind-merge,按序拼接即可)。 */\nexport function cn(...inputs: ClassValue[]): string {\n return clsx(inputs)\n}\n\n/** 复制文本到剪贴板,返回是否成功。 */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n return false\n }\n}\n","import type { ReplaySpeed } from \"@blade-hq/agent-client\"\nimport { Play } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst SPEED_OPTIONS: ReplaySpeed[] = [1, 2, 5]\n\nexport interface ReplayBarProps {\n /** 非回放会话传 false 时整条不渲染。 */\n isReplay: boolean\n speed: ReplaySpeed\n onSpeedChange: (speed: ReplaySpeed) => void\n onExit: () => void\n /** 传 useReplay().canControl。只读身份下仍然显示\"在回放\",但操作按钮禁用。 */\n canControl?: boolean\n className?: string\n}\n\n/** 回放会话的顶部状态条:说明当前在回放、切换倍速、随时退出回放。 */\nexport function ReplayBar({\n isReplay,\n speed,\n onSpeedChange,\n onExit,\n canControl = true,\n className,\n}: ReplayBarProps) {\n if (!isReplay) return null\n\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs\",\n className,\n )}\n >\n <span className=\"inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]\">\n <Play size={13} />\n 回放模式\n </span>\n <span className=\"text-[hsl(var(--muted-foreground))]\">\n 正在重现之前的对话\n </span>\n <div className=\"ml-auto flex items-center gap-2\">\n <div className=\"flex items-center gap-0.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-0.5\">\n {SPEED_OPTIONS.map((option) => (\n <button\n key={option}\n type=\"button\"\n onClick={() => onSpeedChange(option)}\n aria-pressed={speed === option}\n disabled={!canControl}\n className={cn(\n \"h-6 rounded px-2 font-medium transition-colors\",\n speed === option\n ? \"bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]\"\n : \"text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50\",\n )}\n >\n {option}x\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={onExit}\n disabled={!canControl}\n className={cn(\n \"h-7 rounded-md border border-[hsl(var(--border))] px-2.5 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n 退出回放\n </button>\n </div>\n </div>\n )\n}\n","import type { ReplayMismatch } from \"../hooks/use-replay\"\nimport { cn } from \"../lib/utils\"\n\nexport interface ReplayMismatchPromptProps {\n /** 来自 useReplay().mismatch;为 null 时不渲染。 */\n mismatch: ReplayMismatch | null\n className?: string\n}\n\n/** 回放中输入和录制内容对不上时的选择卡:按录制继续,还是从这里真跑。 */\nexport function ReplayMismatchPrompt({ mismatch, className }: ReplayMismatchPromptProps) {\n if (!mismatch) return null\n\n return (\n <div\n className={cn(\n \"mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm\",\n className,\n )}\n >\n <div className=\"font-medium text-[hsl(var(--foreground))]\">\n 这句话和之前录制的不一样\n </div>\n <dl className=\"mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]\">\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">录制的是</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.expectedMessage || \"(空)\"}\n </dd>\n </div>\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">你输入的</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.actualMessage || \"(空)\"}\n </dd>\n </div>\n </dl>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"keep_replay\")}\n className=\"h-8 rounded-md bg-[hsl(var(--primary))] px-3 text-xs font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90\"\n >\n 按录制内容继续\n </button>\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"continue_replay\")}\n className=\"h-8 rounded-md border border-[hsl(var(--border))] px-3 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\"\n >\n 从这里开始真的运行\n </button>\n </div>\n </div>\n )\n}\n","import type { ChatMessage, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { Check, ChevronDown, Circle, CircleDot, ListChecks, Loader2 } from \"lucide-react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { formatToolName } from \"./display-utils\"\n\nexport type PlanStepStatus = \"pending\" | \"in_progress\" | \"completed\"\n\nexport interface PlanUpdateData {\n plan: Array<{ step: string; status: PlanStepStatus }>\n}\n\nconst PLAN_STEP_STATUSES = new Set<PlanStepStatus>([\"pending\", \"in_progress\", \"completed\"])\n\nexport const PLAN_AUTO_COLLAPSE_MS = 5_000\n\nexport function isPlanUpdateTool(toolCall: Pick<ToolCallInfo, \"name\">): boolean {\n return formatToolName(toolCall.name) === \"UpdatePlan\"\n}\n\nexport interface PlanUpdateDisplayState {\n current: ToolCallInfo | null\n updating: boolean\n}\n\nexport function getPlanUpdateDisplayState(messages: ChatMessage[]): PlanUpdateDisplayState {\n let current: ToolCallInfo | null = null\n let latestAttempt: ToolCallInfo | null = null\n let latestAttemptStreaming = false\n for (const message of messages) {\n if ((message.loop_name ?? \"root\") !== \"root\") continue\n for (const toolCall of message.tool_calls ?? []) {\n if (!isPlanUpdateTool(toolCall)) continue\n latestAttempt = toolCall\n latestAttemptStreaming = message.status === \"streaming\"\n if (toolCall.status === \"done\" && parsePlanUpdate(toolCall.arguments)) current = toolCall\n }\n }\n return {\n current,\n updating: latestAttempt?.status === \"pending\" && latestAttemptStreaming,\n }\n}\n\nexport function parsePlanUpdate(argumentsJson: string): PlanUpdateData | null {\n try {\n const raw: unknown = JSON.parse(argumentsJson)\n if (!raw || typeof raw !== \"object\") return null\n const candidate = raw as { plan?: unknown }\n if (!Array.isArray(candidate.plan)) return null\n\n const plan = candidate.plan.map((item: unknown) => {\n if (!item || typeof item !== \"object\") return null\n const step = (item as { step?: unknown }).step\n const status = (item as { status?: unknown }).status\n if (\n typeof step !== \"string\" ||\n !step.trim() ||\n typeof status !== \"string\" ||\n !PLAN_STEP_STATUSES.has(status as PlanStepStatus)\n ) {\n return null\n }\n return { step: step.trim(), status: status as PlanStepStatus }\n })\n if (plan.some((item: unknown) => item === null)) return null\n if (plan.filter((item) => item?.status === \"in_progress\").length > 1) return null\n return { plan: plan as PlanUpdateData[\"plan\"] }\n } catch {\n return null\n }\n}\n\nexport function pickCurrentPlanStep(\n plan: PlanUpdateData[\"plan\"],\n): PlanUpdateData[\"plan\"][number] | null {\n return (\n plan.find((item) => item.status === \"in_progress\") ??\n plan.find((item) => item.status === \"pending\") ??\n plan[plan.length - 1] ??\n null\n )\n}\n\nfunction PlanStepIcon({\n status,\n size = 17,\n running = false,\n}: {\n status: PlanStepStatus\n size?: number\n running?: boolean\n}) {\n if (status === \"completed\") {\n return <Check size={size} strokeWidth={2} className=\"shrink-0 text-emerald-500\" />\n }\n if (status === \"in_progress\") {\n return running ? (\n <Loader2 size={size} className=\"shrink-0 animate-spin text-[hsl(var(--muted-foreground))]\" />\n ) : (\n <CircleDot size={size} className=\"shrink-0 text-amber-500\" />\n )\n }\n return <Circle size={size} className=\"shrink-0 text-[hsl(var(--muted-foreground))]/60\" />\n}\n\nexport function PlanUpdateBlock({\n toolCall,\n running = false,\n autoReveal = false,\n}: {\n toolCall: ToolCallInfo\n running?: boolean\n /** 实时收到的新进度短暂展开;加载历史时保持默认收起。 */\n autoReveal?: boolean\n}) {\n const updateKey = `${toolCall.id}:${toolCall.arguments}`\n const revealKey = autoReveal ? updateKey : null\n const [collapsed, setCollapsed] = useState(!autoReveal)\n const collapseTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const data = parsePlanUpdate(toolCall.arguments)\n\n useEffect(() => {\n if (!revealKey) return\n if (collapseTimerRef.current) clearTimeout(collapseTimerRef.current)\n setCollapsed(false)\n collapseTimerRef.current = setTimeout(() => {\n setCollapsed(true)\n collapseTimerRef.current = null\n }, PLAN_AUTO_COLLAPSE_MS)\n }, [revealKey])\n\n useEffect(\n () => () => {\n if (collapseTimerRef.current) clearTimeout(collapseTimerRef.current)\n },\n [],\n )\n\n if (!data) return null\n const completed = data.plan.filter((item) => item.status === \"completed\").length\n const currentStep = pickCurrentPlanStep(data.plan)\n const pausedAtCurrentStep = !running && currentStep?.status === \"in_progress\"\n\n return (\n <section className=\"overflow-hidden\">\n <button\n type=\"button\"\n aria-expanded={!collapsed}\n onClick={() => {\n if (collapseTimerRef.current) {\n clearTimeout(collapseTimerRef.current)\n collapseTimerRef.current = null\n }\n setCollapsed((value) => !value)\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-[hsl(var(--muted)/0.3)]\",\n !collapsed && \"border-b border-[hsl(var(--border))]\",\n )}\n >\n {collapsed && currentStep ? (\n <span className=\"flex min-w-0 flex-1 items-center gap-1.5 text-xs text-[hsl(var(--foreground))]\">\n <PlanStepIcon status={currentStep.status} size={14} running={running} />\n <span className=\"truncate\">{currentStep.step}</span>\n {pausedAtCurrentStep ? (\n <span className=\"shrink-0 text-[11px] text-amber-500\">已暂停</span>\n ) : null}\n </span>\n ) : (\n <span className=\"flex min-w-0 flex-1 items-center gap-1.5 text-[11px] text-[hsl(var(--muted-foreground))]\">\n <ListChecks size={14} className=\"shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">任务进度</span>\n {pausedAtCurrentStep ? <span className=\"shrink-0 text-amber-500\">已暂停</span> : null}\n </span>\n )}\n <span className=\"shrink-0 text-[11px] tabular-nums text-[hsl(var(--muted-foreground))]\">\n {completed}/{data.plan.length}\n </span>\n <ChevronDown\n size={14}\n className={cn(\n \"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform duration-300 ease-out motion-reduce:transition-none\",\n !collapsed && \"rotate-180\",\n )}\n />\n </button>\n\n <div\n aria-hidden={collapsed}\n className={cn(\n \"grid transition-[grid-template-rows,opacity] duration-300 ease-out motion-reduce:transition-none\",\n collapsed ? \"grid-rows-[0fr] opacity-0\" : \"grid-rows-[1fr] opacity-100\",\n )}\n >\n <div className=\"min-h-0 overflow-hidden\">\n <div className=\"flex max-h-40 flex-col gap-0.5 overflow-y-auto px-3 py-2\">\n {data.plan.length === 0 ? (\n <span className=\"text-xs text-[hsl(var(--muted-foreground))]\">暂无任务步骤</span>\n ) : (\n data.plan.map((item, index) => (\n <div key={`${index}-${item.step}`} className=\"flex items-start gap-2 py-0.5\">\n <span className=\"mt-[3px] flex shrink-0\">\n <PlanStepIcon status={item.status} size={14} running={running} />\n </span>\n <span\n className={cn(\n \"min-w-0 flex-1 break-words text-[13px] leading-5\",\n item.status === \"completed\"\n ? \"text-[hsl(var(--muted-foreground))]\"\n : item.status === \"in_progress\"\n ? \"font-medium text-[hsl(var(--foreground))]\"\n : \"text-[hsl(var(--muted-foreground))]\",\n )}\n >\n {item.step}\n </span>\n </div>\n ))\n )}\n </div>\n </div>\n </div>\n </section>\n )\n}\n\nexport function CurrentPlanPanel({\n messages,\n running = false,\n revealRevision = 0,\n sessionId,\n className,\n}: {\n messages: ChatMessage[]\n running?: boolean\n /** 仅实时成功的 update_plan 递增;历史 hydration 不得改变。 */\n revealRevision?: number\n sessionId?: string\n className?: string\n}) {\n const { current, updating } = getPlanUpdateDisplayState(messages)\n const revealBaselinesRef = useRef(new Map([[sessionId, revealRevision]]))\n const autoReveal =\n (revealBaselinesRef.current.get(sessionId) ?? 0) !== revealRevision\n\n useEffect(() => {\n // 会话实例可能先于它的状态快照公开;尚无计划时不能提前消费该会话在\n // bootstrap 窗口收到的展开信号。\n if (!current) return\n revealBaselinesRef.current.set(sessionId, revealRevision)\n }, [current, revealRevision, sessionId])\n\n if (!current && !updating) return null\n\n return (\n <div className={cn(\"blade-chat-plan mx-auto w-full max-w-[748px] px-4\", className)}>\n {updating ? (\n <div className=\"mb-2 flex items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={14} className=\"shrink-0 animate-spin\" />\n <span>正在更新任务进度…</span>\n </div>\n ) : null}\n {current ? (\n <PlanUpdateBlock\n key={sessionId ?? \"current-session\"}\n toolCall={current}\n running={running}\n autoReveal={autoReveal}\n />\n ) : null}\n </div>\n )\n}\n","import type { ToolCallInfo } from \"@blade-hq/agent-client\"\n\n/** 工具名归一化别名表:把后端各种命名风格统一为展示用的驼峰名。 */\nconst TOOL_NAME_ALIASES: Record<string, string> = {\n agent: \"Agent\",\n ask_user_question: \"AskUserQuestion\",\n bash: \"Bash\",\n bg_bash: \"BgBash\",\n edit: \"Edit\",\n exit_plan_mode: \"ExitPlanMode\",\n file_edit: \"Edit\",\n file_read: \"Read\",\n file_write: \"Write\",\n finish_task: \"FinishTask\",\n glob: \"Glob\",\n grep: \"Grep\",\n kb_search: \"KbSearch\",\n ls: \"Ls\",\n multi_edit: \"MultiEdit\",\n read: \"Read\",\n read_skill: \"ReadSkill\",\n update_plan: \"UpdatePlan\",\n web_fetch: \"WebFetch\",\n web_search: \"WebSearch\",\n write: \"Write\",\n}\n\n/** 系统工具的中文展示名。 */\nconst TOOL_DISPLAY_LABELS: Record<string, string> = {\n Bash: \"执行命令\",\n BgBash: \"后台执行命令\",\n Read: \"读取文件\",\n Write: \"写入文件\",\n Edit: \"编辑文件\",\n MultiEdit: \"编辑文件\",\n Ls: \"列出目录\",\n Glob: \"匹配文件\",\n Grep: \"搜索文本\",\n KbSearch: \"检索知识库\",\n WebSearch: \"搜索网页\",\n WebFetch: \"整理网页内容\",\n Agent: \"派生子智能体\",\n AskUserQuestion: \"向用户提问\",\n ReadSkill: \"读取技能\",\n FinishTask: \"任务完成\",\n ExitPlanMode: \"提交计划\",\n ListSessions: \"列出历史会话\",\n GetSessionHistory: \"读取会话历史\",\n}\n\nconst PRIORITY_FILE_KEYS = [\n \"file_path\",\n \"filePath\",\n \"output_file\",\n \"outputFile\",\n \"output_path\",\n \"outputPath\",\n \"path\",\n \"filepath\",\n \"target_file\",\n \"destination\",\n \"filename\",\n]\n\nexport type ToolTone = \"emerald\" | \"blue\" | \"amber\" | \"red\"\n\nfunction safeParseJson(value: string | null | undefined): unknown {\n if (!value) return null\n try {\n return JSON.parse(value)\n } catch {\n return null\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction getStringArgValue(args: Record<string, unknown> | null, key: string): string {\n const value = args?.[key]\n return typeof value === \"string\" ? value.trim() : \"\"\n}\n\n/** 技能的入口文件名(registry 认这两个)。 */\nconst SKILL_ENTRY_FILE_NAMES = new Set([\"skill.md\", \"command.md\"])\n\n/** 这些目录名只是容器,不是技能名。 */\nconst NON_SKILL_DIR_NAMES = new Set([\".\", \"..\", \".agent\", \".agents\", \".claude\", \"skill_data\", \"skills\"])\n\n/**\n * 技能入口文件的所在目录就是技能名(`.agent/skills/<技能名>/SKILL.md`)。\n * 只显示 \"SKILL.md\" 用户看不出读的是哪个技能。\n *\n * 改这里必须同步 apps/web/src/agent/components/chat/display-utils.ts 的同名函数。\n */\nexport function getSkillNameFromFilePath(filePath: string | null | undefined): string | null {\n if (!filePath) return null\n // Windows 原生 daemon 传的是反斜杠路径(C:\\\\Users\\\\...\\\\skills\\\\<技能名>\\\\SKILL.md)\n const segments = filePath.split(/[\\\\/]+/).filter(Boolean)\n const fileName = segments.pop()\n if (!fileName || !SKILL_ENTRY_FILE_NAMES.has(fileName.toLowerCase())) return null\n const dirName = segments.pop()\n if (!dirName || NON_SKILL_DIR_NAMES.has(dirName.toLowerCase())) return null\n return dirName\n}\n\n/** 归一化工具名:去掉命名空间前缀并映射到别名表(如 \"fork:Agent\" → \"Agent\")。 */\nexport function formatToolName(name: string): string {\n const trimmed = name.trim()\n if (!trimmed) return name\n\n const stripped = trimmed.split(\":\").pop()?.split(\"/\").pop()?.split(\".\").pop()?.trim() || trimmed\n const normalized = stripped\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .toLowerCase()\n\n return TOOL_NAME_ALIASES[normalized] ?? stripped\n}\n\n// 与 apps/web 的同名函数保持一致:只从文件读写工具提取用户可定位的路径。\nexport function extractToolFilePath(toolCall: ToolCallInfo): string | null {\n return extractToolFilePaths(toolCall)[0] ?? null\n}\n\n/** 从文件类工具参数中提取全部可定位的文件路径(按出现顺序去重)。 */\nexport function extractToolFilePaths(toolCall: ToolCallInfo): string[] {\n const formattedName = formatToolName(toolCall.name)\n if (![\"Read\", \"Write\", \"Edit\", \"MultiEdit\"].includes(formattedName)) return []\n const paths: string[] = []\n const collect = (value: unknown, depth = 0, allowPlainString = false) => {\n if (depth > 4) return\n if (typeof value === \"string\") {\n const path = value.trim()\n if (path && (allowPlainString || path.includes(\"/\") || path.includes(\"\\\\\") || /\\.[a-z0-9]{1,8}$/i.test(path)) && !path.split(/[\\\\/]/).pop()?.toLowerCase().includes(\"phase.json\") && !paths.includes(path)) paths.push(path)\n return\n }\n if (Array.isArray(value)) {\n for (const item of value) collect(item, depth + 1, allowPlainString)\n return\n }\n if (!isPlainObject(value)) return\n for (const key of PRIORITY_FILE_KEYS) collect(value[key], depth + 1, true)\n // Only explicit path-bearing keys are traversed; arbitrary content may\n // contain slashes and must not become a misleading file path.\n for (const nested of Object.values(value)) if (nested && typeof nested === \"object\") collect(nested, depth + 1, false)\n }\n collect(safeParseJson(toolCall.arguments))\n return paths\n}\n\n/**\n * 返回工具调用的中文展示标签:优先取参数里的 description,\n * 其次取后端下发的 display_name / 系统工具中文名。\n */\nexport function getToolDisplayLabel(toolCall: ToolCallInfo): string {\n const normalized = formatToolName(toolCall.name)\n const args = safeParseJson(toolCall.arguments) as Record<string, unknown> | null\n const displayName = toolCall.display_name?.trim() ?? \"\"\n const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized\n const metaDisplayName = getStringArgValue(args, \"_meta_display_name\")\n if (metaDisplayName) {\n return metaDisplayName\n }\n\n const description = getStringArgValue(args, \"description\")\n\n if (normalized === \"BgBash\") {\n return description ? `后台执行:${description}` : \"后台执行命令\"\n }\n if (normalized === \"ReadSkill\") {\n const skillName = getStringArgValue(args, \"skill\") || getStringArgValue(args, \"skill_name\")\n return skillName ? `${baseLabel}「${skillName}」` : baseLabel\n }\n if (normalized === \"Read\") {\n // Read 也接受 path 这个旧参数名,两个都要看\n const skillName = getSkillNameFromFilePath(\n getStringArgValue(args, \"file_path\") || getStringArgValue(args, \"path\"),\n )\n if (skillName) return `读取技能「${skillName}」`\n }\n if (normalized === \"FinishTask\") {\n const title = getStringArgValue(args, \"title\")\n return title ? `${baseLabel}:${title}` : baseLabel\n }\n\n return description || baseLabel\n}\n\nexport function getToolTone(status?: ToolCallInfo[\"status\"]): ToolTone {\n if (status === \"error\" || status === \"cancelled\") return \"red\"\n if (status === \"awaiting_answer\") return \"amber\"\n if (status === \"pending\") return \"blue\"\n return \"emerald\"\n}\n\nexport function getToolStatusLabel(status?: ToolCallInfo[\"status\"]): string {\n if (status === \"pending\") return \"运行中\"\n if (status === \"awaiting_answer\") return \"等待回答\"\n if (status === \"error\") return \"错误\"\n if (status === \"cancelled\") return \"已取消\"\n return \"完成\"\n}\n\nexport function formatToolDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`\n const seconds = ms / 1000\n if (seconds < 60) return `${seconds.toFixed(1)}s`\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.round(seconds % 60)\n return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`\n}\n\n/** 参数 JSON 美化:无法解析时按原文展示。 */\nexport function formatToolArgs(args: string): string {\n try {\n return JSON.stringify(JSON.parse(args), null, 2)\n } catch {\n return args\n }\n}\n\nconst RESULT_PREVIEW_LIMIT = 4000\n\n/** 工具结果的文本化 + 截断:长结果只在折叠面板里展示前若干字符。 */\nexport function formatToolResult(result: unknown): string {\n const text = typeof result === \"string\" ? result : JSON.stringify(result, null, 2)\n if (text == null) return \"\"\n if (text.length <= RESULT_PREVIEW_LIMIT) return text\n return `${text.slice(0, RESULT_PREVIEW_LIMIT)}\\n…(结果过长,已截断)`\n}\n","import type {\n AskUserAnswerData,\n ChatMessage,\n ConnectionStatus,\n PostChatFollowup,\n} from \"@blade-hq/agent-client\"\nimport { chatErrorForDisplay } from \"@blade-hq/agent-client\"\nimport { CircleAlert } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { ChatInput } from \"./ChatInput\"\nimport { ConnectionBanner } from \"./ConnectionBanner\"\nimport { MessageList } from \"./MessageList\"\nimport type { ToolCallRenderer } from \"./ToolCallBlock\"\nimport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\nimport type { ResultFeedback } from \"@blade-hq/agent-client\"\nimport { CurrentPlanPanel } from \"./PlanUpdateBlock\"\n\nexport interface ChatViewClassNames {\n root?: string\n banner?: string\n messageList?: string\n chatInput?: string\n}\n\nexport interface ChatViewRenderers {\n /** 自定义工具调用渲染;返回 null 走默认渲染。 */\n toolCall?: ToolCallRenderer\n}\n\nexport interface ChatViewSlots {\n /** 消息列表上方的自定义区域。 */\n header?: ReactNode\n /** 无消息时的空状态。 */\n emptyState?: ReactNode\n /** 输入框下方的自定义区域。 */\n footer?: ReactNode\n}\n\n/** 两种模式共用的外观属性。 */\nexport interface ChatPresentationProps {\n classNames?: ChatViewClassNames\n renderers?: ChatViewRenderers\n slots?: ChatViewSlots\n placeholder?: string\n /** 配色主题,默认浅色。宿主已经在祖先元素上写了 data-theme 时不用传。 */\n theme?: \"light\" | \"dark\"\n}\n\n/** 主题落到聊天根节点上:深色变量挂在 [data-theme=\"dark\"] 上,由它继承给整棵子树。 */\nexport function themeAttr(theme: ChatPresentationProps[\"theme\"]): \"dark\" | undefined {\n return theme === \"dark\" ? \"dark\" : undefined\n}\n\ninterface ChatSurfaceProps extends ChatPresentationProps {\n /** follow-up、成果和评分交互;SDK 不绑定任何分析厂商。 */\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n connection: ConnectionStatus\n errorMessage: string | null\n messages: ChatMessage[]\n postChatFollowup?: PostChatFollowup | null\n isStreaming: boolean\n isStopping: boolean\n inputText: string\n onInputChange: (value: string) => void\n onSuggestion?: (value: string) => void\n onSend: (text: string) => Promise<boolean>\n onAppend?: (text: string) => void\n onStop: () => void\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n sessionId?: string\n isViewer?: boolean\n resultFeedbackByEntry?: ReadonlyMap<string, ResultFeedback>\n onResultFeedbackSaved?: (feedback: ResultFeedback) => void\n /** 输入框上方的内部区域(纯 LLM 模式的高级设置用)。不传时不渲染任何节点。 */\n beforeInput?: ReactNode\n /** 仅 Agent 模式启用 Blade 的 update_plan 面板。 */\n showPlanUpdates?: boolean\n /** 仅实时成功的 update_plan 递增;历史 hydration 不得改变。 */\n planRevealRevision?: number\n /** 连接状态条下方的横幅(回放模式条用)。不传时不渲染任何节点。 */\n banner?: ReactNode\n}\n\n/**\n * 消息流 + 输入框:智能体模式与纯 LLM 模式共用同一套外观,差别只在谁喂数据。\n *\n * 这里的 DOM 结构受 tests/chat-view-compat.test.tsx 的快照保护——接入方拿\n * .blade-chat-* 类名写了自己的样式,结构一动他们的页面就歪。\n */\nexport function ChatSurface({\n theme,\n classNames,\n renderers,\n slots,\n placeholder,\n connection,\n errorMessage,\n messages,\n postChatFollowup,\n isStreaming,\n isStopping,\n inputText,\n onInputChange,\n onSuggestion,\n onSend,\n onAppend,\n onStop,\n sessionStatus,\n askAnswers,\n onAnswer,\n sessionId,\n isViewer,\n resultFeedbackByEntry,\n onResultFeedbackSaved,\n onFollowupInteraction,\n beforeInput,\n showPlanUpdates = false,\n planRevealRevision = 0,\n banner,\n}: ChatSurfaceProps) {\n return (\n <div\n data-theme={themeAttr(theme)}\n className={cn(\n \"blade-chat flex min-h-0 flex-1 flex-col overflow-hidden\",\n classNames?.root,\n )}\n >\n <ConnectionBanner connection={connection} className={classNames?.banner} />\n {banner}\n {/* 出错条的配色由 .blade-chat-error-bar 的 --destructive 变量给,别写死 red-*:\n Tailwind 的 dark: 变体跟的是系统偏好,会和 data-theme 主题打架。 */}\n {errorMessage && (\n <div className=\"blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm\">\n <CircleAlert size={16} className=\"mt-0.5 shrink-0\" />\n <span className=\"min-w-0 whitespace-pre-wrap break-words [overflow-wrap:anywhere]\">\n {chatErrorForDisplay(errorMessage)}\n </span>\n </div>\n )}\n {slots?.header}\n <MessageList\n messages={messages}\n postChatFollowup={postChatFollowup}\n onSuggestion={onSuggestion}\n isStreaming={isStreaming}\n sessionStatus={sessionStatus}\n askAnswers={askAnswers}\n onAnswer={onAnswer}\n toolCallRenderer={renderers?.toolCall}\n hidePlanUpdateTools={showPlanUpdates}\n emptyState={slots?.emptyState}\n className={classNames?.messageList}\n sessionId={sessionId}\n isViewer={isViewer}\n resultFeedbackByEntry={resultFeedbackByEntry}\n onResultFeedbackSaved={onResultFeedbackSaved}\n onFollowupInteraction={onFollowupInteraction}\n />\n {showPlanUpdates ? (\n <CurrentPlanPanel\n messages={messages}\n running={isStreaming}\n revealRevision={planRevealRevision}\n sessionId={sessionId}\n className=\"border-t border-[hsl(var(--border))]\"\n />\n ) : null}\n {beforeInput}\n <ChatInput\n value={inputText}\n onValueChange={onInputChange}\n onSend={onSend}\n onAppend={onAppend}\n onStop={onStop}\n isStreaming={isStreaming}\n isStopping={isStopping}\n placeholder={placeholder}\n queueKey={sessionId}\n className={classNames?.chatInput}\n />\n {slots?.footer}\n </div>\n )\n}\n","import { ArrowUp, Loader2, Square } from \"lucide-react\"\nimport type { KeyboardEvent } from \"react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\n\ninterface Props {\n /** 受控输入内容(由 ChatView 持有,便于 attach/insertText 注入)。 */\n value: string\n onValueChange: (value: string) => void\n onSend: (text: string) => boolean | Promise<boolean>\n /** Optional direct insertion path used while another response streams. */\n onAppend?: (text: string) => void\n onStop: () => void\n isStreaming: boolean\n isStopping?: boolean\n placeholder?: string\n className?: string\n queueKey?: string\n}\n\ninterface ChatInputKeyboardEvent {\n key: string\n shiftKey: boolean\n isComposing: boolean\n keyCode?: number\n}\n\n// Keep these predicates in sync with apps/web/src/agent/components/chat/input-keyboard.ts.\n// They intentionally stay local to this package so the SDK remains independently buildable.\nfunction isImeCompositionKey(event: ChatInputKeyboardEvent): boolean {\n return event.isComposing || event.keyCode === 229\n}\n\nfunction shouldSubmitChatInput(\n event: ChatInputKeyboardEvent,\n menuOpen: boolean,\n): boolean {\n return event.key === \"Enter\" && !event.shiftKey && !menuOpen && !isImeCompositionKey(event)\n}\n\n/**\n * 轻量聊天输入框:textarea(Enter 发送 / Shift+Enter 换行)+ 发送/停止按钮。\n * 不含第一方版本的富文本编辑、@ 补全、技能菜单、语音输入与模型选择。\n * 内容受控,session.attach() / insertText() 通过 ChatView 注入到这里。\n */\nexport function ChatInput({\n value,\n onValueChange,\n onSend,\n onAppend,\n onStop,\n isStreaming,\n isStopping = false,\n placeholder = \"输入消息…\",\n className,\n queueKey,\n}: Props) {\n const trimmed = value.trim()\n const [sendMode, setSendMode] = useState<\"direct\" | \"queue\">(\"direct\")\n const [promptQueue, setPromptQueue] = useState<string[]>([])\n const queueSendingRef = useRef(false)\n const queueBlockedRef = useRef(false)\n const previousQueueKeyRef = useRef(queueKey)\n useEffect(() => {\n if (previousQueueKeyRef.current === queueKey) return\n previousQueueKeyRef.current = queueKey\n setPromptQueue([])\n queueBlockedRef.current = false\n }, [queueKey])\n const canSend = trimmed.length > 0 && (!isStreaming || sendMode === \"queue\")\n\n // Keep this queue transition contract aligned with the built-in composer:\n // retain until accepted, pause on rejection, and resume on the next turn.\n useEffect(() => {\n if (isStreaming) { queueBlockedRef.current = false; return }\n if (queueBlockedRef.current || promptQueue.length === 0 || queueSendingRef.current) return\n const next = promptQueue[0]\n queueSendingRef.current = true\n Promise.resolve().then(() => onSend(next)).then((accepted) => {\n if (accepted) setPromptQueue((current) => current.slice(1))\n else queueBlockedRef.current = true\n }).finally(() => {\n queueSendingRef.current = false\n })\n }, [isStreaming, onSend, promptQueue])\n\n const handleSend = async () => {\n if (!canSend) return\n if (isStreaming && sendMode === \"queue\") {\n setPromptQueue((current) => [...current, trimmed])\n onValueChange(\"\")\n return\n }\n if (isStreaming && sendMode === \"direct\") {\n if (!onAppend) return\n onAppend(trimmed)\n onValueChange(\"\")\n return\n }\n const accepted = await onSend(trimmed)\n if (!accepted) return\n onValueChange(\"\")\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n // 输入法组合中回车用于选字,不能触发发送。\n if (shouldSubmitChatInput({\n key: event.key,\n shiftKey: event.shiftKey,\n isComposing: event.nativeEvent.isComposing,\n keyCode: event.nativeEvent.keyCode,\n }, false)) {\n event.preventDefault()\n void handleSend()\n }\n }\n\n return (\n <div className={cn(\"blade-chat-input border-t border-[hsl(var(--border))] py-3\", className)}>\n <div className=\"mx-auto mb-2 flex max-w-[748px] items-center justify-between px-1 text-xs text-[hsl(var(--muted-foreground))]\">\n <fieldset className=\"flex items-center gap-1 rounded-md border border-[hsl(var(--border))] p-0.5\">\n <legend className=\"sr-only\">发送方式</legend>\n <button type=\"button\" onClick={() => setSendMode(\"direct\")} aria-pressed={sendMode === \"direct\"} disabled={isStreaming && !onAppend} className={`rounded px-2 py-1 ${sendMode === \"direct\" ? \"bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]\" : \"\"}`}>直接插入</button>\n <button type=\"button\" onClick={() => setSendMode(\"queue\")} aria-pressed={sendMode === \"queue\"} className={`rounded px-2 py-1 ${sendMode === \"queue\" ? \"bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]\" : \"\"}`}>排队执行</button>\n </fieldset>\n {promptQueue.length > 0 ? <details className=\"relative\"><summary className=\"cursor-pointer list-none rounded px-2 py-1 hover:bg-[hsl(var(--accent))]\">待执行 {promptQueue.length} 条</summary><div className=\"absolute bottom-full right-0 z-20 mb-2 w-64 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-2 shadow-lg\">{promptQueue.map((item, index) => <div key={`${index}-${item}`} className=\"flex gap-2 border-b border-[hsl(var(--border))] py-2 last:border-0\"><span className=\"min-w-0 flex-1 truncate\">{item}</span><button type=\"button\" onClick={() => setPromptQueue((current) => current.filter((_, i) => i !== index))}>取消</button></div>)}</div></details> : null}\n </div>\n <div className=\"blade-chat-input-inner mx-auto flex max-w-[748px] items-end gap-2 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2\">\n <textarea\n value={value}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={handleKeyDown}\n onInput={(event) => {\n // 内容驱动的高度自适应:先复位再按 scrollHeight 撑开,上限约 8 行\n const el = event.currentTarget\n el.style.height = \"auto\"\n el.style.height = `${Math.min(el.scrollHeight, 192)}px`\n }}\n rows={1}\n placeholder={placeholder}\n aria-label=\"聊天输入\"\n className=\"blade-chat-textarea max-h-48 min-h-[28px] flex-1 resize-none bg-transparent py-1 text-sm leading-6 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.6)]\"\n />\n {isStreaming ? (\n <>\n {sendMode === \"queue\" ? <button type=\"button\" onClick={handleSend} disabled={!canSend} aria-label=\"加入排队\" title=\"加入排队\" className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] disabled:opacity-40\"><ArrowUp size={15} /></button> : null}\n <button type=\"button\" onClick={onStop} disabled={isStopping} aria-label={isStopping ? \"正在停止\" : \"停止回复\"} title={isStopping ? \"正在停止\" : \"停止回复\"} className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--muted))] text-[hsl(var(--foreground))] transition-opacity hover:opacity-90 disabled:opacity-60\">{isStopping ? <Loader2 size={14} className=\"animate-spin\" /> : <Square size={12} fill=\"currentColor\" />}</button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={handleSend}\n disabled={!canSend}\n aria-label=\"发送消息\"\n title=\"发送消息\"\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ArrowUp size={15} />\n </button>\n )}\n </div>\n </div>\n )\n}\n","import type { ConnectionStatus } from \"@blade-hq/agent-client\"\nimport { AlertTriangle, LoaderCircle } from \"lucide-react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\n\nconst CONNECTION_NOTICE_DELAY_MS = 3_000\nconst CONNECTION_ERROR_DELAY_MS = 15_000\n\ntype ConnectionNoticePhase = \"hidden\" | \"recovering\" | \"failed\"\n\ninterface Props {\n connection: ConnectionStatus\n className?: string\n}\n\n/** 与内置 Web 的 ConnectionBanner 保持同样的前台恢复容错时序。 */\nfunction useConnectionNoticePhase(connected: boolean): ConnectionNoticePhase {\n const [phase, setPhase] = useState<ConnectionNoticePhase>(\"hidden\")\n const connectedRef = useRef(connected)\n const timersRef = useRef<ReturnType<typeof setTimeout>[]>([])\n\n connectedRef.current = connected\n\n useEffect(() => {\n const clearTimers = () => {\n for (const timer of timersRef.current) clearTimeout(timer)\n timersRef.current = []\n }\n const startGracePeriod = () => {\n clearTimers()\n setPhase(\"hidden\")\n timersRef.current = [\n setTimeout(() => setPhase(\"recovering\"), CONNECTION_NOTICE_DELAY_MS),\n setTimeout(() => setPhase(\"failed\"), CONNECTION_ERROR_DELAY_MS),\n ]\n }\n\n if (connected) {\n clearTimers()\n setPhase(\"hidden\")\n } else {\n startGracePeriod()\n }\n\n const handleForeground = () => {\n if (!connectedRef.current) startGracePeriod()\n }\n const handleVisibilityChange = () => {\n if (document.visibilityState === \"visible\") handleForeground()\n }\n\n window.addEventListener(\"blade:app-active\", handleForeground)\n window.addEventListener(\"focus\", handleForeground)\n window.addEventListener(\"pageshow\", handleForeground)\n document.addEventListener(\"visibilitychange\", handleVisibilityChange)\n return () => {\n clearTimers()\n window.removeEventListener(\"blade:app-active\", handleForeground)\n window.removeEventListener(\"focus\", handleForeground)\n window.removeEventListener(\"pageshow\", handleForeground)\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange)\n }\n }, [connected])\n\n return phase\n}\n\n/** 连接状态提示条:短暂断线静默,超过容错时间后再逐级提示。 */\nexport function ConnectionBanner({ connection, className }: Props) {\n const hasConnectedRef = useRef(connection === \"connected\" || connection === \"reconnecting\")\n if (connection === \"connected\") hasConnectedRef.current = true\n const connected = connection === \"connected\"\n const phase = useConnectionNoticePhase(connected)\n if (connected || phase === \"hidden\") return null\n\n const recovering = phase === \"recovering\"\n const firstConnection = !hasConnectedRef.current\n\n return (\n <div className={cn(\"blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3\", className)}>\n <div\n className={cn(\n \"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3\",\n recovering\n ? \"border-amber-500/25 bg-amber-500/10 text-amber-100\"\n : \"border-rose-500/25 bg-rose-500/10 text-rose-100\",\n )}\n >\n <span className=\"mt-0.5 shrink-0\">\n {recovering ? (\n <LoaderCircle size={14} className=\"animate-spin\" />\n ) : (\n <AlertTriangle size={14} />\n )}\n </span>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium\">\n {recovering\n ? firstConnection\n ? \"正在连接…\"\n : \"正在恢复连接…\"\n : \"暂时无法连接\"}\n </div>\n <div className=\"text-xs opacity-80\">\n {recovering\n ? \"恢复后会自动同步最新消息,请稍候\"\n : \"请检查网络或服务状态,系统会继续自动重试\"}\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import type {\n AskUserAnswerData,\n ChatMessage,\n PostChatFollowup,\n ResultFeedback,\n} from \"@blade-hq/agent-client\"\nimport { isHiddenInternalMessage } from \"@blade-hq/agent-client\"\nimport { ChevronDown, Layers, Lightbulb, MessageSquare } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\"\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\"\nimport { cn } from \"../lib/utils\"\nimport { useMessagePin } from \"../hooks/use-message-pin\"\nimport { AssistantTurnBlock } from \"./AssistantTurnBlock\"\nimport { RenderErrorBoundary } from \"./RenderErrorBoundary\"\nimport {\n HistoricalResultFeedback,\n PostChatFollowupBlock,\n type FollowupInteractionEvent,\n} from \"./PostChatFollowupBlock\"\nimport type { ToolCallRenderer } from \"./ToolCallBlock\"\nimport {\n ErrorMessageBlock,\n UserMessageBubble,\n isErrorMessage,\n isUserMessage,\n} from \"./UserMessageBubble\"\n\ntype RenderBlock =\n | { type: \"message\"; message: ChatMessage; key: string }\n | { type: \"assistant_turn\"; messages: ChatMessage[]; key: string; isStreaming: boolean }\n | { type: \"compaction\"; key: string }\n | { type: \"planning_divider\"; kind: \"enter\" | \"exit\"; key: string }\n\nfunction parseModeChange(message: ChatMessage): { from: string; to: string } | null {\n if (message.kind !== \"mode_change\" || typeof message.content !== \"string\") {\n return null\n }\n try {\n const parsed = JSON.parse(message.content) as { from?: unknown; to?: unknown }\n if (typeof parsed.from === \"string\" && typeof parsed.to === \"string\") {\n return { from: parsed.from, to: parsed.to }\n }\n } catch {}\n return null\n}\n\nfunction getPlanningDividerKind(message: ChatMessage): \"enter\" | \"exit\" | null {\n const modeChange = parseModeChange(message)\n if (modeChange?.to === \"planning\") return \"enter\"\n if (modeChange?.from === \"planning\") return \"exit\"\n return null\n}\n\nfunction getMessageResetSignature(messages: ChatMessage[]): string {\n return messages\n .map((message) => {\n const contentSize =\n typeof message.content === \"string\" ? message.content.length : message.content.length\n const toolSignature =\n message.tool_calls\n ?.map((toolCall) => `${toolCall.id}:${toolCall.status ?? \"\"}`)\n .join(\",\") ?? \"\"\n return [\n message.entry_id ?? message.timestamp ?? message.role,\n message.status ?? \"\",\n contentSize,\n toolSignature,\n ].join(\":\")\n })\n .join(\"|\")\n}\n\ninterface Props {\n messages: ChatMessage[]\n postChatFollowup?: PostChatFollowup | null\n onSuggestion?: (value: string) => void\n isStreaming: boolean\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n toolCallRenderer?: ToolCallRenderer\n hidePlanUpdateTools?: boolean\n emptyState?: ReactNode\n className?: string\n /** 会话 id;传给助手消息的统一上下文。 */\n sessionId?: string\n isViewer?: boolean\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n resultFeedbackByEntry?: ReadonlyMap<string, ResultFeedback>\n onResultFeedbackSaved?: (feedback: ResultFeedback) => void\n}\n\n/**\n * 消息列表:use-stick-to-bottom 自动滚动 + 按轮次分组渲染。\n * 相比第一方版本去掉了轮次导航栏、吸顶状态条与规划摘要卡片。\n */\nexport function MessageList({\n messages,\n postChatFollowup,\n onSuggestion,\n isStreaming,\n sessionStatus,\n askAnswers,\n onAnswer,\n toolCallRenderer,\n hidePlanUpdateTools = false,\n emptyState,\n className,\n sessionId,\n isViewer = false,\n onFollowupInteraction,\n resultFeedbackByEntry = new Map(),\n onResultFeedbackSaved,\n}: Props) {\n const visibleRootMessages = messages.filter((message) => {\n if ((message.loop_name ?? \"root\") !== \"root\") return false\n if (isHiddenInternalMessage(message)) return false\n if (message.kind === \"context\") return false\n return message.role !== \"tool\" || getPlanningDividerKind(message) !== null\n })\n const userMessages = visibleRootMessages.filter((message) => isUserMessage(message))\n const latestUserMessage = userMessages.at(-1)\n const latestPromptText = latestUserMessage\n ? (typeof latestUserMessage.content === \"string\"\n ? latestUserMessage.content\n : latestUserMessage.content.filter((part) => part.type === \"text\").map((part) => part.text).join(\"\"))\n .replace(/\\s+/g, \" \").trim()\n : \"\"\n const latestPromptPreview = latestPromptText.length > 80 ? `${latestPromptText.slice(0, 80)}…` : latestPromptText\n // AgentSession 的真实发送会先插入 local-user 乐观消息;历史加载是服务端 ID,\n // 仍应落到底部。纯 LLM 消息没有 entry_id,也属于本地即时发送。\n const shouldPinLatestUser =\n latestUserMessage != null &&\n (latestUserMessage.entry_id == null || latestUserMessage.entry_id.startsWith(\"local-user-\"))\n const renderBlocks = useMemo(() => {\n const visible = messages.filter((message) => {\n if ((message.loop_name ?? \"root\") !== \"root\") return false\n if (isHiddenInternalMessage(message)) return false\n if (message.kind === \"context\") return false\n if (message.kind === \"compaction\") return true\n return message.role !== \"tool\" || getPlanningDividerKind(message) !== null\n })\n\n const blocks: RenderBlock[] = []\n let assistantBuffer: ChatMessage[] = []\n let assistantTurnCount = 0\n\n const flushAssistant = () => {\n if (assistantBuffer.length === 0) return\n assistantTurnCount += 1\n const first = assistantBuffer[0]\n blocks.push({\n type: \"assistant_turn\",\n messages: assistantBuffer,\n key: first.entry_id ?? `assistant-${assistantTurnCount}`,\n isStreaming: assistantBuffer.some((message) => message.status === \"streaming\"),\n })\n assistantBuffer = []\n }\n\n for (const message of visible) {\n const dividerKind = getPlanningDividerKind(message)\n if (dividerKind) {\n flushAssistant()\n blocks.push({\n type: \"planning_divider\",\n kind: dividerKind,\n key: message.entry_id ?? `${dividerKind}-${blocks.length}`,\n })\n continue\n }\n\n if (message.kind === \"compaction\") {\n flushAssistant()\n blocks.push({ type: \"compaction\", key: message.entry_id ?? `compaction-${blocks.length}` })\n continue\n }\n\n if (message.role === \"assistant\") {\n assistantBuffer.push(message)\n continue\n }\n\n flushAssistant()\n blocks.push({\n type: \"message\",\n message,\n key: message.render_id ?? message.entry_id ?? `${message.role}-${blocks.length}`,\n })\n }\n\n flushAssistant()\n\n // 流式中但最后一块还是用户消息:补一个空的助手轮,承载生成状态和后续输出。\n const last = blocks[blocks.length - 1]\n if (isStreaming) {\n if (last?.type === \"assistant_turn\") {\n last.isStreaming = true\n } else if (last?.type === \"message\" && isUserMessage(last.message)) {\n blocks.push({\n type: \"assistant_turn\",\n messages: [\n {\n role: \"assistant\",\n content: \"\",\n entry_id: `pending-assistant-${last.key}`,\n status: \"streaming\",\n loop_name: \"root\",\n },\n ],\n key: `pending-assistant-${last.key}`,\n isStreaming: true,\n })\n }\n }\n\n return blocks\n }, [messages, isStreaming])\n\n return (\n <div className={cn(\"blade-chat-messages relative min-h-0 flex-1\", className)}>\n {isStreaming ? <output className=\"sr-only\">正在生成回复</output> : null}\n <StickToBottom\n key={sessionId ?? \"no-session\"}\n className=\"h-full overflow-y-hidden\"\n initial=\"instant\"\n resize=\"instant\"\n >\n <StickToBottom.Content className=\"blade-chat-messages-scroll\">\n <div className=\"blade-chat-messages-content mx-auto max-w-[748px]\">\n {isStreaming && latestUserMessage && latestPromptPreview ? (\n <button\n type=\"button\"\n className=\"sticky top-0 z-20 mb-4 w-full truncate rounded-2xl border border-[hsl(var(--primary)/0.2)] bg-[hsl(var(--background)/0.92)] px-4 py-3 text-left text-sm font-medium shadow-sm backdrop-blur\"\n onClick={() => Array.from(document.querySelectorAll<HTMLElement>(\"[data-entry-id]\")).find((el) => el.dataset.entryId === latestUserMessage.entry_id)?.scrollIntoView({ behavior: \"smooth\", block: \"start\" })}\n aria-label=\"跳转到当前提问\"\n >\n {latestPromptPreview}\n </button>\n ) : null}\n <div className=\"flex min-w-0 flex-col\">\n {renderBlocks.length === 0\n ? (emptyState ?? (\n <div className=\"blade-chat-empty\">\n <MessageSquare size={40} strokeWidth={1.5} />\n <span className=\"text-base font-medium\">开始对话</span>\n <span className=\"text-sm opacity-60\">在下方输入消息开始聊天</span>\n </div>\n ))\n : renderBlocks.map((block) => {\n if (block.type === \"message\") {\n return (\n <div key={block.key} data-entry-id={block.message.entry_id}>\n {isUserMessage(block.message) ? (\n <UserMessageBubble message={block.message} />\n ) : isErrorMessage(block.message) ? (\n <ErrorMessageBlock message={block.message} />\n ) : null}\n </div>\n )\n }\n\n if (block.type === \"assistant_turn\") {\n const blockFeedback = block.messages\n .map((message) =>\n message.entry_id\n ? resultFeedbackByEntry.get(message.entry_id)\n : undefined,\n )\n .find((feedback): feedback is ResultFeedback => feedback != null)\n const hasActiveFollowup = Boolean(\n postChatFollowup &&\n block.messages.some(\n (message) => message.entry_id === postChatFollowup.assistant_entry_id,\n ),\n )\n return (\n <div key={block.key} data-entry-id={block.messages[0]?.entry_id}>\n <RenderErrorBoundary\n label=\"助手消息\"\n details={block.key}\n resetKey={getMessageResetSignature(block.messages)}\n >\n <AssistantTurnBlock\n messages={block.messages}\n isStreaming={block.isStreaming}\n askAnswers={askAnswers}\n onAnswer={onAnswer}\n sessionStatus={sessionStatus}\n toolCallRenderer={toolCallRenderer}\n hidePlanUpdateTools={hidePlanUpdateTools}\n sessionId={sessionId}\n />\n {blockFeedback && !hasActiveFollowup ? (\n <HistoricalResultFeedback feedback={blockFeedback} />\n ) : null}\n {hasActiveFollowup && postChatFollowup ? (\n <PostChatFollowupBlock\n followup={postChatFollowup}\n sessionId={sessionId}\n onSuggestion={onSuggestion}\n isViewer={isViewer}\n onInteraction={onFollowupInteraction}\n savedFeedback={blockFeedback}\n onFeedbackSaved={onResultFeedbackSaved}\n />\n ) : null}\n </RenderErrorBoundary>\n </div>\n )\n }\n\n if (block.type === \"compaction\") {\n return (\n <div\n key={block.key}\n className=\"flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <Layers size={12} />\n <span>上下文已压缩</span>\n </div>\n )\n }\n\n return <PlanningDivider key={block.key} kind={block.kind} />\n })}\n\n {sessionStatus === \"interrupted\" && !isStreaming ? (\n <div className=\"flex\">\n <div className=\"rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300\">\n 已中断\n </div>\n </div>\n ) : null}\n </div>\n </div>\n </StickToBottom.Content>\n <PinLatestUserMessage\n key={sessionId ?? \"no-session\"}\n userMessageCount={userMessages.length}\n shouldPinLatestUser={shouldPinLatestUser}\n targetKey={\n latestUserMessage?.render_id ??\n latestUserMessage?.entry_id ??\n (latestUserMessage ? `user:${userMessages.length}` : null)\n }\n />\n <ScrollToBottomButton />\n </StickToBottom>\n </div>\n )\n}\n\n/** SDK 与内置 Web 使用相同的新发言契约:把本轮用户消息放到视口顶部。 */\nfunction PinLatestUserMessage({\n userMessageCount,\n shouldPinLatestUser,\n targetKey,\n}: {\n userMessageCount: number\n shouldPinLatestUser: boolean\n targetKey: string | null\n}) {\n const { contentRef, scrollRef, scrollToBottom, stopScroll } = useStickToBottomContext()\n const previousCountRef = useRef(userMessageCount)\n const spacerHeightRef = useRef(0)\n const getScrollElement = useCallback(() => scrollRef.current, [scrollRef])\n const getContentElement = useCallback(() => contentRef.current, [contentRef])\n const getTargetElement = useCallback(() => {\n const rows = contentRef.current?.querySelectorAll<HTMLElement>(\".blade-chat-user-row\")\n return rows?.item((rows?.length ?? 0) - 1) ?? null\n }, [contentRef])\n const getSpacerHeight = useCallback(() => spacerHeightRef.current, [])\n const setSpacerHeight = useCallback(\n (height: number) => {\n spacerHeightRef.current = height\n const content = contentRef.current\n if (!content) return\n if (height > 0) content.style.setProperty(\"--blade-chat-pin-spacer\", `${height}px`)\n else content.style.removeProperty(\"--blade-chat-pin-spacer\")\n },\n [contentRef],\n )\n\n useMessagePin({\n targetKey,\n pinTarget: shouldPinLatestUser,\n getScrollElement,\n getContentElement,\n getTargetElement,\n getSpacerHeight,\n setSpacerHeight,\n stopAutoScroll: stopScroll,\n scrollToBottom,\n })\n\n useEffect(() => {\n if (\n userMessageCount > previousCountRef.current &&\n !shouldPinLatestUser\n ) {\n scrollToBottom(\"instant\")\n }\n previousCountRef.current = userMessageCount\n }, [scrollToBottom, shouldPinLatestUser, userMessageCount])\n return null\n}\n\nfunction ScrollToBottomButton() {\n const { isAtBottom, scrollToBottom } = useStickToBottomContext()\n const [visible, setVisible] = useState(false)\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // 防抖:isAtBottom 变为 true 时不立即隐藏,等 300ms 确认稳定后再隐藏\n // 避免流式内容高度变化导致 isAtBottom 在 true/false 之间快速切换闪烁\n useEffect(() => {\n if (isAtBottom) {\n // 启动延迟隐藏定时器\n if (!hideTimerRef.current) {\n hideTimerRef.current = setTimeout(() => {\n setVisible(false)\n hideTimerRef.current = null\n }, 300)\n }\n } else {\n // 不在底部,取消所有待执行的隐藏,立即显示\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(true)\n }\n\n return () => {\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n }, [isAtBottom])\n\n // hook 必须在任何早退之前调用,否则 visible 变化时 hook 顺序不一致\n const handleClick = useCallback(() => {\n // 点击滚动到底部时立即隐藏按钮,避免闪烁\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(false)\n scrollToBottom()\n }, [scrollToBottom])\n\n if (!visible) return null\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n aria-label=\"滚动到底部\"\n className=\"blade-chat-scroll-bottom absolute bottom-4 right-4 flex items-center gap-1 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-1.5 text-xs text-[hsl(var(--muted-foreground))] shadow-lg transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\"\n >\n <ChevronDown size={14} />\n {/* 窄容器里这行字会横跨近半个宽度,收起来只留图标;可访问名由 aria-label 保住。 */}\n <span className=\"blade-chat-scroll-bottom-label\">滚动到底部</span>\n </button>\n )\n}\n\nfunction PlanningDivider({ kind }: { kind: \"enter\" | \"exit\" }) {\n return (\n <div className=\"flex items-center gap-3 py-1\">\n <div className=\"h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent\" />\n <div className=\"inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300\">\n <Lightbulb size={12} />\n <span>{kind === \"enter\" ? \"进入规划模式\" : \"规划完成\"}</span>\n </div>\n <div className=\"h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent\" />\n </div>\n )\n}\n","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport { useCallback, useMemo, useRef, useState, } from \"react\";\nconst DEFAULT_SPRING_ANIMATION = {\n /**\n * A value from 0 to 1, on how much to damp the animation.\n * 0 means no damping, 1 means full damping.\n *\n * @default 0.7\n */\n damping: 0.7,\n /**\n * The stiffness of how fast/slow the animation gets up to speed.\n *\n * @default 0.05\n */\n stiffness: 0.05,\n /**\n * The inertial mass associated with the animation.\n * Higher numbers make the animation slower.\n *\n * @default 1.25\n */\n mass: 1.25,\n};\nconst STICK_TO_BOTTOM_OFFSET_PX = 70;\nconst SIXTY_FPS_INTERVAL_MS = 1000 / 60;\nconst RETAIN_ANIMATION_DURATION_MS = 350;\nlet mouseDown = false;\nglobalThis.document?.addEventListener(\"mousedown\", () => {\n mouseDown = true;\n});\nglobalThis.document?.addEventListener(\"mouseup\", () => {\n mouseDown = false;\n});\nglobalThis.document?.addEventListener(\"click\", () => {\n mouseDown = false;\n});\nexport const useStickToBottom = (options = {}) => {\n const [escapedFromLock, updateEscapedFromLock] = useState(false);\n const [isAtBottom, updateIsAtBottom] = useState(options.initial !== false);\n const [isNearBottom, setIsNearBottom] = useState(false);\n const optionsRef = useRef(null);\n optionsRef.current = options;\n const isSelecting = useCallback(() => {\n if (!mouseDown) {\n return false;\n }\n const selection = window.getSelection();\n if (!selection || !selection.rangeCount) {\n return false;\n }\n const range = selection.getRangeAt(0);\n return (range.commonAncestorContainer.contains(scrollRef.current) ||\n scrollRef.current?.contains(range.commonAncestorContainer));\n }, []);\n const setIsAtBottom = useCallback((isAtBottom) => {\n state.isAtBottom = isAtBottom;\n updateIsAtBottom(isAtBottom);\n }, []);\n const setEscapedFromLock = useCallback((escapedFromLock) => {\n state.escapedFromLock = escapedFromLock;\n updateEscapedFromLock(escapedFromLock);\n }, []);\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const state = useMemo(() => {\n let lastCalculation;\n return {\n escapedFromLock,\n isAtBottom,\n resizeDifference: 0,\n accumulated: 0,\n velocity: 0,\n listeners: new Set(),\n get scrollTop() {\n return scrollRef.current?.scrollTop ?? 0;\n },\n set scrollTop(scrollTop) {\n if (scrollRef.current) {\n scrollRef.current.scrollTop = scrollTop;\n state.ignoreScrollToTop = scrollRef.current.scrollTop;\n }\n },\n get targetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n return (scrollRef.current.scrollHeight - 1 - scrollRef.current.clientHeight);\n },\n get calculatedTargetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n const { targetScrollTop } = this;\n if (!options.targetScrollTop) {\n return targetScrollTop;\n }\n if (lastCalculation?.targetScrollTop === targetScrollTop) {\n return lastCalculation.calculatedScrollTop;\n }\n const calculatedScrollTop = Math.max(Math.min(options.targetScrollTop(targetScrollTop, {\n scrollElement: scrollRef.current,\n contentElement: contentRef.current,\n }), targetScrollTop), 0);\n lastCalculation = { targetScrollTop, calculatedScrollTop };\n requestAnimationFrame(() => {\n lastCalculation = undefined;\n });\n return calculatedScrollTop;\n },\n get scrollDifference() {\n return this.calculatedTargetScrollTop - this.scrollTop;\n },\n get isNearBottom() {\n return this.scrollDifference <= STICK_TO_BOTTOM_OFFSET_PX;\n },\n };\n }, []);\n const scrollToBottom = useCallback((scrollOptions = {}) => {\n if (typeof scrollOptions === \"string\") {\n scrollOptions = { animation: scrollOptions };\n }\n if (!scrollOptions.preserveScrollPosition) {\n setIsAtBottom(true);\n }\n const waitElapsed = Date.now() + (Number(scrollOptions.wait) || 0);\n const behavior = mergeAnimations(optionsRef.current, scrollOptions.animation);\n const { ignoreEscapes = false } = scrollOptions;\n let durationElapsed;\n let startTarget = state.calculatedTargetScrollTop;\n if (scrollOptions.duration instanceof Promise) {\n scrollOptions.duration.finally(() => {\n durationElapsed = Date.now();\n });\n }\n else {\n durationElapsed = waitElapsed + (scrollOptions.duration ?? 0);\n }\n const next = async () => {\n const promise = new Promise(requestAnimationFrame).then(() => {\n if (!state.isAtBottom) {\n state.animation = undefined;\n return false;\n }\n const { scrollTop } = state;\n const tick = performance.now();\n const tickDelta = (tick - (state.lastTick ?? tick)) / SIXTY_FPS_INTERVAL_MS;\n state.animation || (state.animation = { behavior, promise, ignoreEscapes });\n if (state.animation.behavior === behavior) {\n state.lastTick = tick;\n }\n if (isSelecting()) {\n return next();\n }\n if (waitElapsed > Date.now()) {\n return next();\n }\n if (scrollTop < Math.min(startTarget, state.calculatedTargetScrollTop)) {\n if (state.animation?.behavior === behavior) {\n if (behavior === \"instant\") {\n state.scrollTop = state.calculatedTargetScrollTop;\n return next();\n }\n state.velocity =\n (behavior.damping * state.velocity +\n behavior.stiffness * state.scrollDifference) /\n behavior.mass;\n state.accumulated += state.velocity * tickDelta;\n state.scrollTop += state.accumulated;\n if (state.scrollTop !== scrollTop) {\n state.accumulated = 0;\n }\n }\n return next();\n }\n if (durationElapsed > Date.now()) {\n startTarget = state.calculatedTargetScrollTop;\n return next();\n }\n state.animation = undefined;\n /**\n * If we're still below the target, then queue\n * up another scroll to the bottom with the last\n * requested animatino.\n */\n if (state.scrollTop < state.calculatedTargetScrollTop) {\n return scrollToBottom({\n animation: mergeAnimations(optionsRef.current, optionsRef.current.resize),\n ignoreEscapes,\n duration: Math.max(0, durationElapsed - Date.now()) || undefined,\n });\n }\n return state.isAtBottom;\n });\n return promise.then((isAtBottom) => {\n requestAnimationFrame(() => {\n if (!state.animation) {\n state.lastTick = undefined;\n state.velocity = 0;\n }\n });\n return isAtBottom;\n });\n };\n if (scrollOptions.wait !== true) {\n state.animation = undefined;\n }\n if (state.animation?.behavior === behavior) {\n return state.animation.promise;\n }\n return next();\n }, [setIsAtBottom, isSelecting, state]);\n const stopScroll = useCallback(() => {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }, [setEscapedFromLock, setIsAtBottom]);\n const handleScroll = useCallback(({ target }) => {\n if (target !== scrollRef.current) {\n return;\n }\n const { scrollTop, ignoreScrollToTop } = state;\n let { lastScrollTop = scrollTop } = state;\n state.lastScrollTop = scrollTop;\n state.ignoreScrollToTop = undefined;\n if (ignoreScrollToTop && ignoreScrollToTop > scrollTop) {\n /**\n * When the user scrolls up while the animation plays, the `scrollTop` may\n * not come in separate events; if this happens, to make sure `isScrollingUp`\n * is correct, set the lastScrollTop to the ignored event.\n */\n lastScrollTop = ignoreScrollToTop;\n }\n setIsNearBottom(state.isNearBottom);\n /**\n * Scroll events may come before a ResizeObserver event,\n * so in order to ignore resize events correctly we use a\n * timeout.\n *\n * @see https://github.com/WICG/resize-observer/issues/25#issuecomment-248757228\n */\n setTimeout(() => {\n /**\n * When theres a resize difference ignore the resize event.\n */\n if (state.resizeDifference || scrollTop === ignoreScrollToTop) {\n return;\n }\n if (isSelecting()) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n return;\n }\n const isScrollingDown = scrollTop > lastScrollTop;\n const isScrollingUp = scrollTop < lastScrollTop;\n if (state.animation?.ignoreEscapes) {\n state.scrollTop = lastScrollTop;\n return;\n }\n if (isScrollingUp) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n if (isScrollingDown) {\n setEscapedFromLock(false);\n }\n if (!state.escapedFromLock && state.isNearBottom) {\n setIsAtBottom(true);\n }\n }, 1);\n }, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);\n const handleWheel = useCallback(({ target, deltaY }) => {\n let element = target;\n while (![\"scroll\", \"auto\"].includes(getComputedStyle(element).overflow)) {\n if (!element.parentElement) {\n return;\n }\n element = element.parentElement;\n }\n /**\n * The browser may cancel the scrolling from the mouse wheel\n * if we update it from the animation in meantime.\n * To prevent this, always escape when the wheel is scrolled up.\n */\n if (element === scrollRef.current &&\n deltaY < 0 &&\n scrollRef.current.scrollHeight > scrollRef.current.clientHeight &&\n !state.animation?.ignoreEscapes) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n }, [setEscapedFromLock, setIsAtBottom, state]);\n const scrollRef = useRefCallback((scroll) => {\n scrollRef.current?.removeEventListener(\"scroll\", handleScroll);\n scrollRef.current?.removeEventListener(\"wheel\", handleWheel);\n scroll?.addEventListener(\"scroll\", handleScroll, { passive: true });\n scroll?.addEventListener(\"wheel\", handleWheel, { passive: true });\n }, []);\n const contentRef = useRefCallback((content) => {\n state.resizeObserver?.disconnect();\n if (!content) {\n return;\n }\n let previousHeight;\n state.resizeObserver = new ResizeObserver(([entry]) => {\n const { height } = entry.contentRect;\n const difference = height - (previousHeight ?? height);\n state.resizeDifference = difference;\n /**\n * Sometimes the browser can overscroll past the target,\n * so check for this and adjust appropriately.\n */\n if (state.scrollTop > state.targetScrollTop) {\n state.scrollTop = state.targetScrollTop;\n }\n setIsNearBottom(state.isNearBottom);\n if (difference >= 0) {\n /**\n * If it's a positive resize, scroll to the bottom when\n * we're already at the bottom.\n */\n const animation = mergeAnimations(optionsRef.current, previousHeight\n ? optionsRef.current.resize\n : optionsRef.current.initial);\n scrollToBottom({\n animation,\n wait: true,\n preserveScrollPosition: true,\n duration: animation === \"instant\" ? undefined : RETAIN_ANIMATION_DURATION_MS,\n });\n }\n else {\n /**\n * Else if it's a negative resize, check if we're near the bottom\n * if we are want to un-escape from the lock, because the resize\n * could have caused the container to be at the bottom.\n */\n if (state.isNearBottom) {\n setEscapedFromLock(false);\n setIsAtBottom(true);\n }\n }\n previousHeight = height;\n /**\n * Reset the resize difference after the scroll event\n * has fired. Requires a rAF to wait for the scroll event,\n * and a setTimeout to wait for the other timeout we have in\n * resizeObserver in case the scroll event happens after the\n * resize event.\n */\n requestAnimationFrame(() => {\n setTimeout(() => {\n if (state.resizeDifference === difference) {\n state.resizeDifference = 0;\n }\n }, 1);\n });\n });\n state.resizeObserver?.observe(content);\n }, []);\n return {\n contentRef,\n scrollRef,\n scrollToBottom,\n stopScroll,\n isAtBottom: isAtBottom || isNearBottom,\n isNearBottom,\n escapedFromLock,\n state,\n };\n};\nfunction useRefCallback(callback, deps) {\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const result = useCallback((ref) => {\n result.current = ref;\n return callback(ref);\n }, deps);\n return result;\n}\nconst animationCache = new Map();\nfunction mergeAnimations(...animations) {\n const result = { ...DEFAULT_SPRING_ANIMATION };\n let instant = false;\n for (const animation of animations) {\n if (animation === \"instant\") {\n instant = true;\n continue;\n }\n if (typeof animation !== \"object\") {\n continue;\n }\n instant = false;\n result.damping = animation.damping ?? result.damping;\n result.stiffness = animation.stiffness ?? result.stiffness;\n result.mass = animation.mass ?? result.mass;\n }\n const key = JSON.stringify(result);\n if (!animationCache.has(key)) {\n animationCache.set(key, Object.freeze(result));\n }\n return instant ? \"instant\" : animationCache.get(key);\n}\n","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport * as React from \"react\";\nimport { createContext, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, } from \"react\";\nimport { useStickToBottom, } from \"./useStickToBottom.js\";\nconst StickToBottomContext = createContext(null);\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\nexport function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {\n const customTargetScrollTop = useRef(null);\n const targetScrollTop = React.useCallback((target, elements) => {\n const get = context?.targetScrollTop ?? currentTargetScrollTop;\n return get?.(target, elements) ?? target;\n }, [currentTargetScrollTop]);\n const defaultInstance = useStickToBottom({\n mass,\n damping,\n stiffness,\n resize,\n initial,\n targetScrollTop,\n });\n const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state, } = instance ?? defaultInstance;\n const context = useMemo(() => ({\n scrollToBottom,\n stopScroll,\n scrollRef,\n isAtBottom,\n escapedFromLock,\n contentRef,\n state,\n get targetScrollTop() {\n return customTargetScrollTop.current;\n },\n set targetScrollTop(targetScrollTop) {\n customTargetScrollTop.current = targetScrollTop;\n },\n }), [\n scrollToBottom,\n isAtBottom,\n contentRef,\n scrollRef,\n stopScroll,\n escapedFromLock,\n state,\n ]);\n useImperativeHandle(contextRef, () => context, [context]);\n useIsomorphicLayoutEffect(() => {\n if (!scrollRef.current) {\n return;\n }\n if (getComputedStyle(scrollRef.current).overflow === \"visible\") {\n scrollRef.current.style.overflow = \"auto\";\n }\n }, []);\n return (React.createElement(StickToBottomContext.Provider, { value: context },\n React.createElement(\"div\", { ...props }, typeof children === \"function\" ? children(context) : children)));\n}\n(function (StickToBottom) {\n function Content({ children, scrollClassName, ...props }) {\n const context = useStickToBottomContext();\n return (React.createElement(\"div\", { ref: context.scrollRef, style: {\n height: \"100%\",\n width: \"100%\",\n scrollbarGutter: \"stable both-edges\",\n }, className: scrollClassName },\n React.createElement(\"div\", { ...props, ref: context.contentRef }, typeof children === \"function\" ? children(context) : children)));\n }\n StickToBottom.Content = Content;\n})(StickToBottom || (StickToBottom = {}));\n/**\n * Use this hook inside a <StickToBottom> component to gain access to whether the component is at the bottom of the scrollable area.\n */\nexport function useStickToBottomContext() {\n const context = useContext(StickToBottomContext);\n if (!context) {\n throw new Error(\"use-stick-to-bottom component context must be used within a StickToBottom component\");\n }\n return context;\n}\n","import type { AskUserAnswerData, ChatMessage, MemoryRefInfo, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport {\n getFileParts,\n getImageParts,\n getTextContent,\n normalizeMessageContent,\n} from \"@blade-hq/agent-client\"\nimport {\n BookOpen,\n Bot,\n ChevronRight,\n CircleAlert,\n FileText,\n FilePenLine,\n Globe2,\n Loader2,\n Search,\n Terminal,\n Wrench,\n X,\n} from \"lucide-react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { AgentLoopBlock } from \"./AgentLoopBlock\"\nimport { MarkdownContent } from \"./MarkdownContent\"\nimport { Shimmer } from \"./Shimmer\"\nimport { ToolCallBlock, type ToolCallRenderer } from \"./ToolCallBlock\"\nimport { isPlanUpdateTool, parsePlanUpdate } from \"./PlanUpdateBlock\"\nimport { formatToolName } from \"./display-utils\"\n\ninterface Props {\n messages: ChatMessage[]\n isStreaming?: boolean\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n sessionStatus?: string\n toolCallRenderer?: ToolCallRenderer\n /** Agent 模式把 update_plan 收口到输入框上方的任务面板;纯 LLM 同名工具照常展示。 */\n hidePlanUpdateTools?: boolean\n /** 会话 id;传给 Markdown 渲染的统一上下文。 */\n sessionId?: string\n}\n\nexport type TurnDisplayMode = \"compact\" | \"detail\"\n\n/** 流式期间显示思考过程;执行完成后不显示“已思考”。 */\nfunction ThinkingBlock({ reasoning, isStreaming }: { reasoning: string; isStreaming: boolean }) {\n const [open, setOpen] = useState(false)\n if (!isStreaming) return null\n\n return (\n <div className=\"blade-chat-thinking text-xs\">\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n className=\"group/thinking inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]\"\n >\n <Shimmer className=\"text-xs\">正在思考</Shimmer>\n <ChevronRight\n size={14}\n className={cn(\n \"shrink-0 opacity-0 transition-[opacity,transform] group-hover/thinking:opacity-100\",\n open && \"rotate-90 opacity-100\",\n )}\n />\n </button>\n {open ? (\n <div className=\"mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]\">\n {reasoning}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction getMessageText(message: ChatMessage): string {\n return getTextContent(normalizeMessageContent(message.content)).trim()\n}\n\nfunction hasRenderableMessageContent(message: ChatMessage) {\n return (\n Boolean(getMessageText(message)) ||\n getImageParts(message.content).length > 0 ||\n getFileParts(message.content).length > 0\n )\n}\n\nfunction getLastContentMessage(messages: readonly ChatMessage[]) {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (hasRenderableMessageContent(messages[index])) return messages[index]\n }\n return null\n}\n\ntype OrderedMessagePart =\n | { type: \"text\"; key: string; content: ChatMessage[\"content\"] }\n | { type: \"tools\"; key: string; toolCalls: ToolCallInfo[] }\n\n// 与 apps/web 的同名同签名函数保持一致:projection blocks 是单条消息内\n// part 顺序的唯一来源,不能把整条消息二分类成 text-first/tool-first。\nfunction getOrderedMessageParts(\n message: ChatMessage,\n toolCalls: ToolCallInfo[],\n): OrderedMessagePart[] {\n const blocks = message.blocks ?? []\n if (!blocks.some((block) => block.type === \"text\") ||\n !blocks.some((block) => block.type === \"tool_use\")) return []\n const toolsById = new Map(toolCalls.map((toolCall) => [toolCall.id, toolCall]))\n const seenToolIds = new Set<string>()\n const parts: OrderedMessagePart[] = []\n for (const [index, block] of blocks.entries()) {\n if (block.type === \"text\" && block.content != null && block.content !== \"\") {\n const content = Array.isArray(block.content)\n ? block.content as ChatMessage[\"content\"]\n : String(block.content)\n parts.push({ type: \"text\", key: `text-${index}`, content })\n }\n if (block.type !== \"tool_use\" || !block.tool_call_id) continue\n const toolCall = toolsById.get(block.tool_call_id)\n if (!toolCall) continue\n seenToolIds.add(toolCall.id)\n const previous = parts[parts.length - 1]\n if (previous?.type === \"tools\") previous.toolCalls.push(toolCall)\n else parts.push({ type: \"tools\", key: `tools-${index}`, toolCalls: [toolCall] })\n }\n const missingTools = toolCalls.filter((toolCall) => !seenToolIds.has(toolCall.id))\n if (seenToolIds.size === 0) return []\n if (missingTools.length > 0) {\n parts.push({ type: \"tools\", key: \"tools-missing\", toolCalls: missingTools })\n }\n return parts\n}\n\nfunction findLatestReasoningMessageIndex(messages: readonly ChatMessage[]): number {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (messages[index].reasoning) return index\n }\n return -1\n}\n\n// 与 apps/web 的同名函数保持一致:运行中也允许用户真实收起执行过程。\nexport function resolveTurnDisplayMode({\n isStreaming: _isStreaming,\n displayMode,\n}: {\n isStreaming: boolean\n displayMode: TurnDisplayMode\n}): TurnDisplayMode {\n return displayMode\n}\n\n// 与 apps/web 的同名函数保持一致,避免两套聊天界面显示不同耗时。\nexport function formatExecutionDuration(durationMs: number) {\n const totalSeconds = Math.max(0, Math.round(durationMs / 1000))\n const minutes = Math.floor(totalSeconds / 60)\n const seconds = totalSeconds % 60\n return minutes > 0 ? `${minutes}分${seconds}秒` : `${seconds}秒`\n}\n\nexport function getExecutionDurationMs({\n messages,\n isStreaming,\n now = Date.now(),\n}: {\n messages: ChatMessage[]\n isStreaming: boolean\n now?: number\n}) {\n const knownDuration = messages.reduce(\n (total, message) => {\n if (typeof message.duration_ms === \"number\" && message.duration_ms > 0) {\n return total + message.duration_ms\n }\n return (\n total +\n (message.tool_calls ?? []).reduce(\n (toolTotal, toolCall) =>\n toolTotal +\n (typeof toolCall.duration_ms === \"number\" && toolCall.duration_ms > 0\n ? toolCall.duration_ms\n : 0),\n 0,\n )\n )\n },\n 0,\n )\n if (!isStreaming) return knownDuration\n\n const startedAt = messages\n .map((message) => (message.timestamp ? Date.parse(message.timestamp) : Number.NaN))\n .filter((value) => Number.isFinite(value))\n .sort((a, b) => a - b)[0]\n if (startedAt === undefined) return knownDuration\n return Math.max(knownDuration, now - startedAt)\n}\n\n// 与 apps/web 的同名函数保持一致。\nfunction findLastExceptionalEvent(messages: ChatMessage[]) {\n for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {\n const messageStatus = messages[messageIndex].status\n if (messageStatus === \"failed\") return { messageIndex, status: \"error\" as const }\n if (messageStatus === \"interrupted\") return { messageIndex, status: \"cancelled\" as const }\n const toolCalls = messages[messageIndex].tool_calls ?? []\n for (let toolIndex = toolCalls.length - 1; toolIndex >= 0; toolIndex -= 1) {\n const status = toolCalls[toolIndex].status\n if (status === \"error\" || status === \"cancelled\") {\n return { messageIndex, status }\n }\n }\n }\n return null\n}\n\n// 与 apps/web 的同名函数保持一致:只把当前未回答的问题标成等待输入。\nfunction executionSummaryLabel({\n messages,\n isStreaming,\n durationMs,\n sessionStatus,\n askAnswers,\n}: {\n messages: ChatMessage[]\n isStreaming: boolean\n durationMs: number\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n}) {\n if (isStreaming) {\n return durationMs > 0\n ? `正在执行 ${formatExecutionDuration(durationMs)}`\n : \"正在执行\"\n }\n if (\n sessionStatus === \"waiting_for_input\" &&\n messages.some((message) =>\n (message.tool_calls ?? []).some(\n (toolCall) =>\n formatToolName(toolCall.name) === \"AskUserQuestion\" &&\n toolCall.status === \"awaiting_answer\" &&\n !askAnswers?.[toolCall.id],\n ),\n )\n ) {\n return \"等待输入\"\n }\n const completedLabel = durationMs > 0\n ? `执行完成 ${formatExecutionDuration(durationMs)}`\n : \"执行完成\"\n const lastExceptionalEvent = findLastExceptionalEvent(messages)\n if (lastExceptionalEvent) {\n const recovered = messages\n .slice(lastExceptionalEvent.messageIndex + 1)\n .some(hasRenderableMessageContent)\n if (lastExceptionalEvent.status === \"error\") {\n return recovered ? `${completedLabel} · 部分步骤失败` : \"执行失败\"\n }\n return recovered ? `${completedLabel} · 部分步骤未完成` : \"执行已中断\"\n }\n return completedLabel\n}\n// 开发者模式之外不需要暴露固定工具类别;常规界面只由行为图标表达差异。\n// 与 apps/web 的同名同签名函数保持一致:原始运行时名称不是面向用户的展示名。\nfunction businessToolDisplayName(toolCall: ToolCallInfo) {\n const displayName = toolCall.display_name?.trim() ?? \"\"\n if (!displayName) return \"\"\n const rawName = toolCall.name.trim()\n return displayName !== rawName && formatToolName(displayName) !== formatToolName(rawName)\n ? displayName\n : \"\"\n}\n\nfunction executionToolTypeLabel(toolCall: ToolCallInfo) {\n switch (formatToolName(toolCall.name)) {\n case \"WebSearch\":\n case \"WebFetch\":\n return \"网络检索\"\n case \"Bash\":\n case \"BgBash\":\n return \"命令执行\"\n case \"Read\":\n case \"ReadSkill\":\n return \"内容读取\"\n case \"Write\":\n case \"Edit\":\n case \"MultiEdit\":\n return \"文件处理\"\n case \"Grep\":\n case \"Glob\":\n return \"内容搜索\"\n case \"Agent\":\n return \"子任务\"\n case \"search_skills\":\n return \"技能检索\"\n case \"get_skill_content\":\n return \"读取技能\"\n case \"run_skill_tool\":\n return \"执行技能\"\n default:\n return businessToolDisplayName(toolCall) || \"执行步骤\"\n }\n}\n\n// 与 apps/web 的同名同签名函数保持一致:只展示业务意图,不暴露工具名和 JSON。\nfunction executionToolIntent(toolCall: ToolCallInfo) {\n const normalizedName = formatToolName(toolCall.name)\n let args: Record<string, unknown> | null = null\n try {\n const parsed = JSON.parse(toolCall.arguments)\n args = parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? parsed : null\n } catch {\n args = null\n }\n const getString = (key: string) => {\n const value = args?.[key]\n return typeof value === \"string\" ? value.trim() : \"\"\n }\n const explicitIntent =\n getString(\"description\") ||\n getString(\"_meta_display_name\") ||\n getString(\"display_name\") ||\n \"\"\n if (explicitIntent) return explicitIntent\n if (normalizedName === \"search_skills\") return getString(\"query\")\n if (normalizedName === \"get_skill_content\" || normalizedName === \"ReadSkill\") {\n return getString(\"skill_name\") || getString(\"skill\")\n }\n if (normalizedName === \"FinishTask\") return getString(\"title\")\n return \"\"\n}\nfunction ExecutionToolRow({ toolCall }: { toolCall: ToolCallInfo }) {\n const normalizedName = formatToolName(toolCall.name)\n const typeLabel = executionToolTypeLabel(toolCall)\n const intent = executionToolIntent(toolCall)\n const label = intent ? `${typeLabel}:${intent}` : typeLabel\n const failed = toolCall.status === \"error\" || toolCall.status === \"cancelled\"\n const iconClass = cn(\n \"size-3.5 shrink-0\",\n failed ? \"text-red-500\" : \"text-[hsl(var(--muted-foreground))]\",\n )\n const icon = toolCall.status === \"pending\"\n ? <Loader2 className={cn(iconClass, \"animate-spin\")} aria-hidden=\"true\" />\n : toolCall.status === \"error\"\n ? <CircleAlert className={iconClass} aria-hidden=\"true\" />\n : toolCall.status === \"cancelled\"\n ? <X className={iconClass} aria-hidden=\"true\" />\n : normalizedName === \"WebSearch\" || normalizedName === \"WebFetch\"\n ? <Globe2 className={iconClass} aria-hidden=\"true\" />\n : normalizedName === \"Bash\" || normalizedName === \"BgBash\"\n ? <Terminal className={iconClass} aria-hidden=\"true\" />\n : normalizedName === \"Write\" || normalizedName === \"Edit\" || normalizedName === \"MultiEdit\"\n ? <FilePenLine className={iconClass} aria-hidden=\"true\" />\n : normalizedName === \"Read\" || normalizedName === \"ReadSkill\" || normalizedName === \"get_skill_content\"\n ? <BookOpen className={iconClass} aria-hidden=\"true\" />\n : normalizedName === \"Grep\" || normalizedName === \"Glob\" || normalizedName === \"search_skills\"\n ? <Search className={iconClass} aria-hidden=\"true\" />\n : normalizedName === \"Agent\"\n ? <Bot className={iconClass} aria-hidden=\"true\" />\n : <Wrench className={iconClass} aria-hidden=\"true\" />\n const rowClassName = cn(\n \"flex min-w-0 items-center gap-1 py-1.5 text-xs leading-[22px]\",\n failed ? \"text-red-500\" : \"text-[hsl(var(--muted-foreground))]\",\n )\n return (\n <div data-testid=\"execution-tool-intent\" className={rowClassName} title={label}>\n {icon}\n <span className=\"min-w-0 truncate\">{label}</span>\n </div>\n )\n}\n\n/** 一轮助手回复:执行过程默认折叠,最终正文始终显示在摘要下方。 */\nexport function AssistantTurnBlock({\n messages,\n isStreaming = false,\n askAnswers,\n onAnswer,\n sessionStatus,\n toolCallRenderer,\n hidePlanUpdateTools = false,\n sessionId,\n}: Props) {\n const shouldHideToolCall = (message: ChatMessage, toolCall: ToolCallInfo) => {\n if (\n !hidePlanUpdateTools ||\n !isPlanUpdateTool(toolCall) ||\n parsePlanUpdate(toolCall.arguments) === null\n ) {\n return false\n }\n return toolCall.status === \"done\" ||\n (toolCall.status === \"pending\" && message.status === \"streaming\")\n }\n const hasInterrupted = messages.some((message) => message.status === \"interrupted\")\n const hasFailedWithoutContent = messages.some(\n (message) => message.status === \"failed\" && !hasRenderableMessageContent(message),\n )\n const finalMessage = getLastContentMessage(messages)\n const turnToolCalls = messages.flatMap((message) => message.tool_calls ?? [])\n const finalOrderedParts = finalMessage\n ? getOrderedMessageParts(\n finalMessage,\n (finalMessage.tool_calls ?? []).filter(\n (toolCall) =>\n formatToolName(toolCall.name) !== \"AskUserQuestion\" &&\n !shouldHideToolCall(finalMessage, toolCall),\n ),\n )\n : []\n const hasExecutionProcess = messages.some(\n (message) =>\n message.reasoning ||\n (message.tool_calls ?? []).some((toolCall) => !shouldHideToolCall(message, toolCall)),\n )\n const latestReasoningIndex = isStreaming\n ? findLatestReasoningMessageIndex(messages)\n : -1\n const hasActionableToolCall = messages.some(\n (message) =>\n message.status === \"failed\" ||\n message.status === \"interrupted\" ||\n (message.tool_calls ?? []).some(\n (toolCall) =>\n !shouldHideToolCall(message, toolCall) &&\n (toolCall.status === \"error\" || toolCall.status === \"cancelled\"),\n ),\n )\n const questionToolCalls = messages.flatMap((message) =>\n (message.tool_calls ?? []).filter(\n (toolCall) => formatToolName(toolCall.name) === \"AskUserQuestion\",\n ),\n )\n const activeQuestionId = questionToolCalls.filter((toolCall) => toolCall.status === \"pending\").at(-1)?.id\n const [displayMode, setDisplayMode] = useState<TurnDisplayMode>(\n () => (isStreaming || hasActionableToolCall ? \"detail\" : \"compact\"),\n )\n const userSelectedDisplayModeRef = useRef(false)\n const wasStreamingRef = useRef(isStreaming)\n useEffect(() => {\n if (\n wasStreamingRef.current &&\n !isStreaming &&\n !userSelectedDisplayModeRef.current\n ) {\n setDisplayMode(hasActionableToolCall ? \"detail\" : \"compact\")\n }\n wasStreamingRef.current = isStreaming\n }, [hasActionableToolCall, isStreaming])\n const effectiveMode = resolveTurnDisplayMode({ isStreaming, displayMode })\n const executionDurationMs = getExecutionDurationMs({ messages, isStreaming })\n const [clock, setClock] = useState(() => Date.now())\n const hasLiveStartTime = messages.some(\n (message) => message.timestamp != null && Number.isFinite(Date.parse(message.timestamp)),\n )\n useEffect(() => {\n // Without a timestamp there is no live duration to update. Avoid keeping an\n // interval alive for synthetic/optimistic rows, which also makes fake-timer\n // tests and short-lived streaming placeholders deterministic.\n if (!isStreaming || !hasLiveStartTime) return\n const timer = window.setInterval(() => setClock(Date.now()), 1000)\n return () => window.clearInterval(timer)\n }, [hasLiveStartTime, isStreaming])\n const liveExecutionDurationMs = isStreaming\n ? getExecutionDurationMs({ messages, isStreaming, now: clock })\n : executionDurationMs\n const memoryRefs = collectMemoryRefs(messages)\n\n if (!hasExecutionProcess) {\n return (\n <div\n aria-busy={isStreaming || undefined}\n className=\"blade-chat-assistant-turn flex flex-col gap-3\"\n >\n {memoryRefs.length > 0 ? <MemoryRefsHint refs={memoryRefs} /> : null}\n {hasInterrupted && (\n <div className=\"ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300\">\n 已中断\n </div>\n )}\n {hasFailedWithoutContent && (\n <div className=\"ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400\">\n 生成失败\n </div>\n )}\n {messages.map((message, index) => {\n return hasRenderableMessageContent(message) ? (\n <div\n key={message.entry_id ?? `${message.timestamp ?? \"assistant\"}-${index}`}\n className=\"flex flex-col gap-3\"\n >\n <AssistantMessageContent\n message={message}\n sessionId={sessionId}\n streaming={isStreaming && index === messages.length - 1}\n />\n </div>\n ) : null\n })}\n </div>\n )\n }\n\n return (\n <div\n aria-busy={isStreaming || undefined}\n className=\"blade-chat-assistant-turn flex flex-col gap-3\"\n >\n {memoryRefs.length > 0 ? <MemoryRefsHint refs={memoryRefs} /> : null}\n {hasInterrupted && (\n <div className=\"ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300\">\n 已中断\n </div>\n )}\n {hasFailedWithoutContent && (\n <div className=\"ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400\">\n 生成失败\n </div>\n )}\n <div className=\"flex w-full items-start gap-2.5\">\n <span\n className=\"grid size-[30px] shrink-0 place-items-center rounded-full bg-[hsl(var(--muted)/0.55)] text-[hsl(var(--foreground))]\"\n aria-hidden=\"true\"\n >\n <Bot size={16} />\n </span>\n <div className=\"min-w-0 flex-1 pt-0.5\">\n <button\n type=\"button\"\n onClick={() => {\n userSelectedDisplayModeRef.current = true\n setDisplayMode(displayMode === \"detail\" ? \"compact\" : \"detail\")\n }}\n aria-expanded={effectiveMode === \"detail\"}\n aria-label={effectiveMode === \"detail\" ? \"收起执行过程\" : \"展开执行过程\"}\n data-testid=\"assistant-execution-summary\"\n className=\"inline-flex min-w-0 max-w-full select-none items-center gap-1 bg-transparent p-0 text-left text-xs leading-[22px] text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))] focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none\"\n >\n <span className=\"min-w-0 truncate\">\n {executionSummaryLabel({\n messages,\n isStreaming,\n durationMs: liveExecutionDurationMs,\n sessionStatus,\n askAnswers,\n })}\n </span>\n <ChevronRight\n size={14}\n // 260ms + cubic-bezier(0.25,0.1,0.25,1) 是 apps/web 那份同名组件的动效 token\n // 数值(design-tokens.css 的 --motion-duration-base / --motion-ease-enter)。\n // 这个包是独立发布的 SDK,没有内部 app 的 CSS 变量可用,只能保持数值同步、\n // 各自写字面量——两处实现要一起改,不能只改一边。\n style={{ transitionDuration: \"260ms\", transitionTimingFunction: \"cubic-bezier(0.25, 0.1, 0.25, 1)\" }}\n className={cn(\n \"shrink-0 transition-transform\",\n effectiveMode === \"detail\" && \"rotate-90\",\n )}\n aria-hidden=\"true\"\n />\n </button>\n <div className=\"mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]\" />\n </div>\n </div>\n {effectiveMode === \"detail\" ? (\n <div className=\"ml-10 flex flex-col gap-3 pt-1\">\n {messages.map((message, index) => {\n const isLast = index === messages.length - 1\n const streamingThis = isStreaming && isLast\n const text = getMessageText(message)\n const toolCalls = (message.tool_calls ?? []).filter(\n (toolCall) =>\n formatToolName(toolCall.name) !== \"AskUserQuestion\" &&\n !shouldHideToolCall(message, toolCall),\n )\n const orderedParts = getOrderedMessageParts(message, toolCalls)\n const showReasoning =\n !!message.reasoning && isStreaming && index === latestReasoningIndex\n\n return (\n <div\n key={message.entry_id ?? `${message.timestamp ?? \"assistant\"}-${index}`}\n className=\"flex flex-col gap-3\"\n >\n {showReasoning && message.reasoning ? (\n <ThinkingBlock reasoning={message.reasoning} isStreaming={streamingThis && !text} />\n ) : null}\n {orderedParts.length > 0 ? orderedParts.map((part) =>\n part.type === \"text\" ? (\n <AssistantMessageContent\n key={part.key}\n message={{ ...message, content: part.content, tool_calls: turnToolCalls }}\n sessionId={sessionId}\n streaming={streamingThis}\n compact\n />\n ) : (\n <div key={part.key} className=\"flex flex-col gap-0.5\">\n {part.toolCalls.map((toolCall) => {\n const custom = toolCallRenderer?.(toolCall)\n return custom !== null && custom !== undefined ? (\n <div key={toolCall.id}>{custom}</div>\n ) : formatToolName(toolCall.name) === \"Agent\" ? (\n <AgentLoopBlock key={toolCall.id} toolCall={toolCall} />\n ) : (\n <ExecutionToolRow key={toolCall.id} toolCall={toolCall} />\n )\n })}\n </div>\n ),\n ) : hasRenderableMessageContent(message) && message !== finalMessage ? (\n <AssistantMessageContent\n message={message}\n sessionId={sessionId}\n streaming={streamingThis}\n compact\n />\n ) : null}\n {orderedParts.length === 0 && toolCalls.length > 0 ? (\n <div className=\"flex flex-col gap-0.5\">\n {toolCalls.map((toolCall) => {\n const custom = toolCallRenderer?.(toolCall)\n return custom !== null && custom !== undefined ? (\n <div key={toolCall.id}>{custom}</div>\n ) : formatToolName(toolCall.name) === \"Agent\" ? (\n <AgentLoopBlock key={toolCall.id} toolCall={toolCall} />\n ) : (\n <ExecutionToolRow key={toolCall.id} toolCall={toolCall} />\n )\n })}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n ) : null}\n {finalMessage &&\n (effectiveMode === \"compact\" || finalOrderedParts.length === 0) ? (\n <div className=\"ml-10\">\n <AssistantMessageContent\n message={finalMessage}\n sessionId={sessionId}\n streaming={isStreaming && finalMessage === messages[messages.length - 1]}\n />\n </div>\n ) : null}\n {questionToolCalls.map((toolCall) => (\n <ToolCallBlock\n key={toolCall.id}\n toolCall={toolCall}\n answerData={askAnswers?.[toolCall.id]}\n onAnswer={onAnswer}\n answered={sessionStatus !== \"waiting_for_input\"}\n sessionStatus={sessionStatus}\n isActiveQuestion={toolCall.id === activeQuestionId}\n renderer={toolCallRenderer}\n />\n ))}\n </div>\n )\n}\n\nexport function collectMemoryRefs(messages: ChatMessage[]): MemoryRefInfo[] {\n const refs = new Map<number, MemoryRefInfo>()\n for (const message of messages) {\n for (const ref of message.memory_refs ?? []) if (!refs.has(ref.id)) refs.set(ref.id, ref)\n }\n return [...refs.values()]\n}\n\nexport function MemoryRefsHint({ refs }: { refs: MemoryRefInfo[] }) {\n const [expanded, setExpanded] = useState(false)\n const label = refs.some((ref) => ref.skill_name) ? \"参考了该技能的历史经验\" : \"参考了历史经验\"\n return (\n <div className=\"blade-chat-memory-refs ml-1 w-full max-w-[680px]\">\n <button type=\"button\" onClick={() => setExpanded((value) => !value)} className=\"inline-flex h-8 items-center gap-1.5 rounded-lg border border-[hsl(var(--primary)/0.22)] bg-[hsl(var(--primary)/0.07)] px-3 text-xs font-medium text-[hsl(var(--primary))]\">\n <BookOpen size={12} /><span>{label}({refs.length})</span><ChevronRight size={10} className={cn(\"transition-transform\", expanded && \"rotate-90\")} />\n </button>\n {expanded ? <div className=\"mt-2 flex flex-col gap-2 rounded-xl border border-[hsl(var(--border)/0.8)] bg-[hsl(var(--muted)/0.28)] p-2.5\">\n {refs.map((ref) => <div key={ref.id} className=\"rounded-lg border border-[hsl(var(--border)/0.55)] bg-[hsl(var(--background)/0.72)] px-3 py-2.5 text-xs\"><p className=\"line-clamp-2 break-words leading-5\">{ref.content_preview}</p>{ref.skill_name ? <span className=\"mt-1 inline-flex text-[10px] text-[hsl(var(--primary))]\">{ref.skill_name}</span> : null}</div>)}\n </div> : null}\n </div>\n )\n}\n\nfunction AssistantMessageContent({\n message,\n sessionId,\n streaming,\n compact = false,\n}: {\n message: ChatMessage\n sessionId?: string\n streaming: boolean\n compact?: boolean\n}) {\n const text = getMessageText(message)\n const imageParts = getImageParts(message.content)\n const fileParts = getFileParts(message.content)\n const failed = message.status === \"failed\"\n const failedBadge = failed ? (\n <div className=\"w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400\">\n 生成失败\n </div>\n ) : null\n const textContent = text ? (\n <div\n className={cn(\n \"blade-chat-assistant-text\",\n compact\n ? \"text-xs leading-[22px] text-[hsl(var(--foreground))]\"\n : \"text-[15px] leading-8 text-[hsl(var(--foreground))]\",\n )}\n >\n <MarkdownContent\n mode={streaming ? \"streaming\" : \"static\"}\n className=\"blade-chat-prose\"\n sessionId={sessionId}\n >\n {text}\n </MarkdownContent>\n </div>\n ) : null\n\n if (imageParts.length === 0 && fileParts.length === 0) {\n if (!failed) return textContent\n return failedBadge || textContent ? (\n <div className=\"flex flex-col gap-2\">\n {failedBadge}\n {textContent}\n </div>\n ) : null\n }\n\n return (\n <div className=\"flex flex-col gap-3\">\n {failedBadge}\n {imageParts.length > 0 ? (\n <div className=\"grid gap-2\">\n {imageParts.map((part) => (\n <img\n key={part.image_url.url}\n src={part.image_url.url}\n alt=\"消息附件\"\n className=\"max-h-72 rounded-xl border border-[hsl(var(--border))] object-cover\"\n />\n ))}\n </div>\n ) : null}\n {fileParts.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {fileParts.map((part) => (\n <div\n key={`${part.name}-${part.data.slice(0, 32)}`}\n className=\"flex min-w-0 items-center gap-1.5 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]\"\n title={part.name}\n >\n <FileText size={12} className=\"shrink-0\" />\n <span className=\"max-w-56 truncate\">{part.name}</span>\n </div>\n ))}\n </div>\n ) : null}\n {textContent}\n </div>\n )\n}\n","import type { ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { Bot, ChevronRight, CircleAlert, Loader2, X } from \"lucide-react\"\nimport { useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { formatToolResult } from \"./display-utils\"\n\ninterface Props {\n toolCall: ToolCallInfo\n}\n\nfunction parseAgentDescription(argumentsJson: string): string {\n try {\n const parsed = JSON.parse(argumentsJson)\n return typeof parsed?.description === \"string\" && parsed.description\n ? parsed.description\n : \"子智能体\"\n } catch {\n return \"子智能体\"\n }\n}\n\n/** SDK 没有第一方子循环消息流,只保留轻量意图行和可展开的最终结果。 */\nexport function AgentLoopBlock({ toolCall }: Props) {\n const [expanded, setExpanded] = useState(false)\n const description = parseAgentDescription(toolCall.arguments)\n const running = toolCall.status === \"pending\" || toolCall.status === \"awaiting_answer\"\n const failed = toolCall.status === \"error\" || toolCall.status === \"cancelled\"\n const hasResult = toolCall.result != null\n const iconClass = cn(\n \"size-3.5 shrink-0\",\n failed ? \"text-red-500\" : \"text-[hsl(var(--muted-foreground))]\",\n )\n\n return (\n <div className=\"blade-chat-agent-loop text-xs leading-[22px]\">\n <button\n type=\"button\"\n onClick={() => hasResult && setExpanded(!expanded)}\n disabled={!hasResult}\n aria-expanded={hasResult ? expanded : undefined}\n data-testid=\"execution-tool-intent\"\n className={cn(\n \"flex min-w-0 items-center gap-1 py-1.5 text-left\",\n hasResult && \"cursor-pointer hover:text-[hsl(var(--foreground))]\",\n failed ? \"text-red-500\" : \"text-[hsl(var(--muted-foreground))]\",\n )}\n title={`子任务:${description}`}\n >\n {running ? (\n <Loader2 className={cn(iconClass, \"animate-spin\")} aria-hidden=\"true\" />\n ) : toolCall.status === \"error\" ? (\n <CircleAlert className={iconClass} aria-hidden=\"true\" />\n ) : toolCall.status === \"cancelled\" ? (\n <X className={iconClass} aria-hidden=\"true\" />\n ) : (\n <Bot className={iconClass} aria-hidden=\"true\" />\n )}\n <span className=\"min-w-0 truncate\">子任务:{description}</span>\n {hasResult ? (\n <ChevronRight\n size={14}\n style={{ transitionDuration: \"260ms\", transitionTimingFunction: \"cubic-bezier(0.25, 0.1, 0.25, 1)\" }}\n className={cn(\n \"shrink-0 transition-transform\",\n expanded && \"rotate-90\",\n )}\n aria-hidden=\"true\"\n />\n ) : null}\n </button>\n {expanded && hasResult ? (\n <div className=\"ml-[18px] mt-1.5 max-h-[400px] overflow-auto whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]\">\n {formatToolResult(toolCall.result)}\n </div>\n ) : null}\n </div>\n )\n}\n","import { Check, Copy } from \"lucide-react\"\nimport {\n type ComponentProps,\n type ComponentPropsWithRef,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\"\nimport { type ExtraProps, Streamdown } from \"streamdown\"\nimport { cn, copyToClipboard } from \"../lib/utils\"\n\ntype StreamdownProps = ComponentProps<typeof Streamdown>\n\n/** 平台内部提醒标记:不属于给用户看的内容,渲染前整段剥掉。 */\nconst SYSTEM_REMINDER_RE = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi\n\n/** Keep inline formatting markers from being consumed by URL autolinking. */\nexport function normalizeAdjacentUrlFormatting(value: string): string {\n const protectedSegments: string[] = []\n const protectedValue = value.replace(/(`{1,3}[\\s\\S]*?`{1,3}|\\[[^\\]]*\\]\\([^)]*\\))/g, (segment) => {\n const index = protectedSegments.push(segment) - 1\n return `blade-url-protected-${index}-marker`\n })\n const normalized = protectedValue.replace(\n /(\\*\\*|__|~~|\\*|_)(https?:\\/\\/[^\\s<>]+?)\\1(?=[\\s。,、!?;:,.!?;:]|$)/g,\n (_, marker: string, url: string) => `${marker}[${url}](<${url}>)${marker}`,\n )\n return normalized.replace(/blade-url-protected-(\\d+)-marker/g, (_, index: string) => protectedSegments[Number(index)])\n}\n\ntype PreProps = ComponentPropsWithRef<\"pre\"> & ExtraProps\n\n/** 代码块外壳:常驻头部栏(语言标签 + 复制按钮),代码区浅底。 */\nfunction CodeBlockPre({ children, node: _node, ...props }: PreProps) {\n const preRef = useRef<HTMLPreElement>(null)\n const [copied, setCopied] = useState(false)\n const [language, setLanguage] = useState(\"\")\n\n // DOM 测量:语言标签写在内层 code 的 className 上,只能挂载后读取。\n useEffect(() => {\n const codeEl = preRef.current?.querySelector(\"code\")\n setLanguage(codeEl?.className.match(/language-(\\S+)/)?.[1] ?? \"\")\n }, [])\n\n const handleCopy = async () => {\n const ok = await copyToClipboard(preRef.current?.querySelector(\"code\")?.textContent ?? \"\")\n if (ok) {\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }\n\n return (\n <div className=\"blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]\">\n <div className=\"blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5\">\n <span className=\"font-mono text-[12px] text-[hsl(var(--muted-foreground))]\">\n {language || \"code\"}\n </span>\n <button\n type=\"button\"\n onClick={handleCopy}\n className={cn(\n \"flex h-6 items-center gap-1 rounded-md px-1.5 text-[11px] transition-colors\",\n copied\n ? \"text-[hsl(var(--primary))]\"\n : \"text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n )}\n >\n {copied ? <Check size={12} /> : <Copy size={12} />}\n <span>{copied ? \"已复制\" : \"复制\"}</span>\n </button>\n </div>\n <pre\n ref={preRef}\n {...props}\n className={cn(props.className, \"blade-chat-codeblock-body m-0 overflow-x-auto\")}\n >\n {children}\n </pre>\n </div>\n )\n}\n\ntype AnchorProps = ComponentPropsWithRef<\"a\"> & ExtraProps\n\n/** 链接一律新窗口打开并断开 opener,SDK 不接管站内跳转。 */\nfunction ExternalAnchor({ node: _node, children, ...props }: AnchorProps) {\n return (\n <a {...props} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n )\n}\n\nconst MARKDOWN_COMPONENTS = {\n pre: CodeBlockPre,\n a: ExternalAnchor,\n} as NonNullable<StreamdownProps[\"components\"]>\n\nexport interface MarkdownContentProps {\n children: string\n className?: string\n /** 流式渲染传 \"streaming\",历史静态内容默认 \"static\"。 */\n mode?: \"streaming\" | \"static\"\n /** 会话 id;保留给聊天组件统一透传上下文。 */\n sessionId?: string\n}\n\n/**\n * SDK 版 Markdown 渲染:附带代码块复制按钮;相比第一方版本去掉了\n * mermaid、外链确认弹窗与文件预览。\n */\nexport function MarkdownContent({ children, className, mode, sessionId }: MarkdownContentProps) {\n const resolvedChildren = useMemo(() => {\n return normalizeAdjacentUrlFormatting(children.replace(SYSTEM_REMINDER_RE, \"\"))\n }, [children])\n\n return (\n <Streamdown\n // Streamdown 外层 memo 不比较 components,光换 sessionId 可能留着旧闭包,\n // 卡片就会拿旧会话去下载。挂 key 强制重建,切会话本来也要换内容。\n key={sessionId ?? \"no-session\"}\n className={cn(\"blade-chat-markdown break-words\", className)}\n mode={mode ?? \"static\"}\n components={MARKDOWN_COMPONENTS}\n >\n {resolvedChildren}\n </Streamdown>\n )\n}\n","import { cn } from \"../lib/utils\"\n\ninterface ShimmerProps {\n children?: string\n className?: string\n}\n\n/** 流式等待时的呼吸文字效果(样式见 style.css 的 .blade-shimmer-text)。 */\nexport function Shimmer({ children = \"正在思考...\", className }: ShimmerProps) {\n return (\n <span className={cn(\"blade-shimmer-text text-sm font-medium\", className)}>{children}</span>\n )\n}\n","import type { AskUserAnswerData, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { Check, ChevronRight, Loader2, MessageSquareMore, X } from \"lucide-react\"\nimport { useState, type ReactNode } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport {\n AskUserQuestionBlock,\n parseAskUserQuestion,\n parseAskUserQuestionError,\n} from \"./AskUserQuestionBlock\"\nimport {\n formatToolArgs,\n formatToolDuration,\n formatToolName,\n formatToolResult,\n getToolDisplayLabel,\n getToolStatusLabel,\n getToolTone,\n} from \"./display-utils\"\n\n/** 接入方自定义渲染器:返回 null 时回落到默认渲染。 */\nexport type ToolCallRenderer = (info: ToolCallInfo) => ReactNode | null\n\ninterface Props {\n toolCall: ToolCallInfo\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n answered?: boolean\n answerData?: AskUserAnswerData\n sessionStatus?: string\n isActiveQuestion?: boolean\n renderer?: ToolCallRenderer\n}\n\n/**\n * 判断提问卡片该等待作答还是展示成已作答,只认工具状态。\n * 与 apps/web 的 ToolCallBlock 里同名函数保持一致:两处算出不同结果时,\n * 同一份会话在内置页面能点、在 SDK 里点不动。\n *\n * 不要再拿 sessionStatus 兜底。历史投影算错时用 waiting_for_input 补救,\n * 会同时把已经回答过的旧提问重新变成可作答(历史加载不会带回 answerData),\n * 而且会盖住投影层的错误——这个 bug 就是这么藏住的。\n */\nexport function resolveAskQuestionState({\n toolStatus,\n hasAnswerData,\n fallbackAnswered,\n fallbackAwaiting,\n}: {\n toolStatus: ToolCallInfo[\"status\"]\n hasAnswerData: boolean\n fallbackAnswered?: boolean\n fallbackAwaiting?: boolean\n}) {\n const awaitingAnswer = !hasAnswerData && (\n toolStatus === \"awaiting_answer\" ||\n (toolStatus === \"pending\" && fallbackAwaiting === true)\n )\n return {\n awaitingAnswer,\n answered:\n hasAnswerData ||\n (!awaitingAnswer &&\n (Boolean(fallbackAnswered) ||\n toolStatus === \"done\" ||\n toolStatus === \"cancelled\" ||\n toolStatus === \"error\")),\n }\n}\n\n/**\n * 极简工具调用块:状态 + 展示名一行,点开后是参数/结果的 <pre> 折叠展示。\n * AskUserQuestion 特殊处理为提问选项卡片。\n */\nexport function ToolCallBlock({\n toolCall,\n onAnswer,\n answered,\n answerData,\n sessionStatus,\n isActiveQuestion,\n renderer,\n}: Props) {\n const [expanded, setExpanded] = useState(false)\n const normalizedName = formatToolName(toolCall.name)\n\n if (renderer) {\n const custom = renderer(toolCall)\n if (custom !== null && custom !== undefined) {\n return <>{custom}</>\n }\n }\n\n // AskUserQuestion:渲染成可作答的提问卡片\n if (normalizedName === \"AskUserQuestion\") {\n const askData =\n parseAskUserQuestion(typeof toolCall.result === \"string\" ? toolCall.result : null) ??\n parseAskUserQuestion(buildAskUserPayload(toolCall.arguments))\n const questionState = resolveAskQuestionState({\n toolStatus: toolCall.status,\n hasAnswerData: Boolean(answerData),\n fallbackAnswered: answered,\n fallbackAwaiting:\n isActiveQuestion === true &&\n (sessionStatus === \"paused\" || sessionStatus === \"waiting_for_input\"),\n })\n const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer)\n if (askData) {\n return (\n <AskUserQuestionBlock\n data={askData}\n answered={questionState.answered}\n toolCallId={toolCall.id}\n sessionStatus={sessionStatus ?? \"\"}\n answerData={answerData}\n onAnswer={canAnswer ? onAnswer : undefined}\n />\n )\n }\n if (toolCall.status === \"pending\") {\n return (\n <div className=\"ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={14} className=\"animate-spin\" />\n <span>正在准备问题…</span>\n </div>\n )\n }\n const errorDetail = parseAskUserQuestionError(\n typeof toolCall.result === \"string\" ? toolCall.result : null,\n )\n return (\n <div className=\"ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]\">\n <div className=\"font-semibold\">选择题内容暂时无法显示</div>\n <div className=\"mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]\">\n {errorDetail?.message ?? \"收到的交互数据不完整。请让智能体重新提问。\"}\n </div>\n {errorDetail?.detail ? (\n <details className=\"mt-2 text-xs text-[hsl(var(--muted-foreground))]\">\n <summary className=\"cursor-pointer\">查看具体原因</summary>\n <div className=\"mt-1 break-words font-mono\">{errorDetail.detail}</div>\n </details>\n ) : null}\n </div>\n )\n }\n\n const tone = getToolTone(toolCall.status)\n const displayName = getToolDisplayLabel(toolCall)\n const toneClass =\n tone === \"red\"\n ? \"border-l-[hsl(var(--muted-foreground)/0.5)]\"\n : tone === \"amber\"\n ? \"border-l-amber-400\"\n : tone === \"blue\"\n ? \"border-l-blue-500\"\n : \"border-l-[hsl(var(--primary))]\"\n\n const statusIcon =\n toolCall.status === \"pending\" ? (\n <Loader2 size={11} className=\"animate-spin\" />\n ) : toolCall.status === \"awaiting_answer\" ? (\n <MessageSquareMore size={11} />\n ) : toolCall.status === \"cancelled\" || toolCall.status === \"error\" ? (\n <X size={11} />\n ) : (\n <Check size={11} />\n )\n\n const statusTextClass =\n tone === \"red\"\n ? \"text-[hsl(var(--muted-foreground))]\"\n : tone === \"amber\"\n ? \"text-amber-300\"\n : tone === \"blue\"\n ? \"text-blue-300\"\n : \"text-[hsl(var(--primary))]\"\n\n return (\n <div className=\"blade-chat-tool ml-4 text-xs\">\n <div className={cn(\"border-l-[3px] flex items-center gap-2 px-3 py-2\", toneClass)}>\n <button\n type=\"button\"\n onClick={() => setExpanded(!expanded)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none\"\n aria-expanded={expanded}\n >\n <ChevronRight\n size={11}\n className={cn(\n \"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform\",\n expanded && \"rotate-90\",\n )}\n />\n <span className={cn(\"flex shrink-0 items-center gap-1 text-[10px]\", statusTextClass)}>\n {statusIcon}\n <span>{getToolStatusLabel(toolCall.status)}</span>\n </span>\n <span className=\"min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]\">\n {displayName}\n </span>\n </button>\n {typeof toolCall.duration_ms === \"number\" && toolCall.duration_ms > 0 && (\n <span className=\"shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {formatToolDuration(toolCall.duration_ms)}\n </span>\n )}\n </div>\n {expanded && (\n <div className=\"blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3\">\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 工具\n </div>\n <div className=\"mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {normalizedName}\n </div>\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 参数\n </div>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolArgs(toolCall.arguments)}\n </pre>\n {toolCall.result != null && (\n <>\n <div className=\"mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 结果\n </div>\n <pre className=\"max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolResult(toolCall.result)}\n </pre>\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction buildAskUserPayload(argumentsJson: string): string | null {\n try {\n const parsed = JSON.parse(argumentsJson)\n if (\n parsed &&\n typeof parsed === \"object\" &&\n Array.isArray((parsed as { questions?: unknown }).questions)\n ) {\n return JSON.stringify({ questions: (parsed as { questions: unknown[] }).questions })\n }\n } catch {\n return null\n }\n return null\n}\n","import type { AskUserAnswerData } from \"@blade-hq/agent-client\"\nimport { Check, Loader2, MessageSquareMore } from \"lucide-react\"\nimport { useEffect, useMemo, useRef, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { MarkdownContent } from \"./MarkdownContent\"\n\ninterface OptionItem {\n label: string\n description: string\n}\n\ninterface QuestionItem {\n question: string\n options: OptionItem[]\n multiSelect?: boolean\n}\n\ninterface SourceLoopInfo {\n loop_name: string\n description: string\n}\n\nconst CUSTOM_TEXTAREA_MAX_HEIGHT = 160\n\n// Keep this behavior aligned with apps/web's AskUserQuestionBlock.\nfunction resizeCustomTextarea(textarea: HTMLTextAreaElement) {\n textarea.style.height = \"auto\"\n textarea.style.height = `${Math.min(textarea.scrollHeight, CUSTOM_TEXTAREA_MAX_HEIGHT)}px`\n textarea.style.overflowY =\n textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? \"auto\" : \"hidden\"\n}\n\n// Keep this hook's name, signature, and behavior aligned with apps/web.\nfunction useAutoResizeTextarea(value: string) {\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n useEffect(() => {\n const textarea = textareaRef.current\n if (textarea?.value === value) resizeCustomTextarea(textarea)\n }, [value])\n\n useEffect(() => {\n const textarea = textareaRef.current\n if (!textarea || typeof ResizeObserver === \"undefined\") return\n\n let previousWidth = textarea.clientWidth\n const observer = new ResizeObserver(([entry]) => {\n if (!entry || entry.contentRect.width === previousWidth) return\n previousWidth = entry.contentRect.width\n resizeCustomTextarea(textarea)\n })\n observer.observe(textarea)\n return () => observer.disconnect()\n }, [])\n\n return textareaRef\n}\n\n// Keep this formatter's name, signature, and behavior aligned with apps/web.\nfunction indentAnswerContinuationLines(answer: string) {\n return answer.replaceAll(\"\\n\", \"\\n \")\n}\n\nexport interface AskUserQuestionData {\n questions: QuestionItem[]\n source_loop?: SourceLoopInfo | null\n}\n\ninterface Props {\n data: AskUserQuestionData\n answered: boolean\n toolCallId: string\n sessionStatus: string\n answerData?: AskUserAnswerData | undefined\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n}\n\n/** 提问选项卡片:单选/多选 + 自定义输入,回答通过 onAnswer 回调交还 ChatView。 */\nexport function AskUserQuestionBlock({\n data,\n answered,\n toolCallId,\n sessionStatus,\n answerData,\n onAnswer,\n}: Props) {\n // 每个问题独立的选择状态:选项下标集合或自定义文本\n const [selections, setSelections] = useState<Map<number, Set<number>>>(new Map())\n const [customTexts, setCustomTexts] = useState<Map<number, string>>(new Map())\n const [usingCustom, setUsingCustom] = useState<Set<number>>(new Set())\n const [note, setNote] = useState(\"\")\n const [submitted, setSubmitted] = useState(false)\n\n useEffect(() => {\n if (sessionStatus === \"failed\" || sessionStatus === \"interrupted\") {\n setSubmitted(false)\n }\n }, [sessionStatus])\n\n // 已作答时按历史答案展示,未作答时展示本地编辑状态\n const displayAnswerState = useMemo(() => {\n if (!(answered && answerData)) {\n return { selections, customTexts, usingCustom, note }\n }\n\n const nextSelections = new Map<number, Set<number>>()\n const nextCustomTexts = new Map<number, string>()\n const nextUsingCustom = new Set<number>()\n\n for (const [questionKey, optionIndexes] of Object.entries(answerData.selections)) {\n nextSelections.set(Number(questionKey), new Set(optionIndexes))\n }\n\n for (const [questionKey, text] of Object.entries(answerData.custom)) {\n const qIdx = Number(questionKey)\n nextCustomTexts.set(qIdx, text)\n nextUsingCustom.add(qIdx)\n }\n\n return {\n selections: nextSelections,\n customTexts: nextCustomTexts,\n usingCustom: nextUsingCustom,\n note: answerData.note ?? \"\",\n }\n }, [answerData, answered, customTexts, note, selections, usingCustom])\n\n const toggleOption = (qIdx: number, optIdx: number, multi: boolean) => {\n if (answered || submitted) return\n setSelections((prev) => {\n const next = new Map(prev)\n const current = new Set(next.get(qIdx) ?? [])\n if (multi) {\n if (current.has(optIdx)) current.delete(optIdx)\n else current.add(optIdx)\n } else {\n current.clear()\n current.add(optIdx)\n }\n next.set(qIdx, current)\n return next\n })\n setUsingCustom((prev) => {\n const next = new Set(prev)\n next.delete(qIdx)\n return next\n })\n }\n\n const handleCustomFocus = (qIdx: number) => {\n if (answered || submitted) return\n setSelections((prev) => {\n const next = new Map(prev)\n next.delete(qIdx)\n return next\n })\n setUsingCustom((prev) => new Set(prev).add(qIdx))\n }\n\n const setCustomText = (qIdx: number, text: string) => {\n if (answered || submitted) return\n setCustomTexts((prev) => new Map(prev).set(qIdx, text))\n }\n\n const getAnswer = (qIdx: number): string | null => {\n const q = data.questions[qIdx]\n if (usingCustom.has(qIdx)) {\n const text = (customTexts.get(qIdx) ?? \"\").trim()\n return text || null\n }\n const sel = selections.get(qIdx)\n if (!sel || sel.size === 0) return null\n return [...sel]\n .sort()\n .map((i) => q.options[i].label)\n .join(\", \")\n }\n\n const allAnswered = data.questions.every((_, i) => getAnswer(i) !== null)\n\n const handleSubmit = () => {\n if (answered || submitted || !allAnswered || !onAnswer) return\n\n const trimmedNote = note.trim()\n const nextAnswerData: AskUserAnswerData = {\n selections: Object.fromEntries(\n Array.from(selections.entries()).map(([qIdx, optionIndexes]) => [\n qIdx,\n Array.from(optionIndexes).sort((a, b) => a - b),\n ]),\n ) as Record<number, number[]>,\n custom: Object.fromEntries(\n Array.from(usingCustom)\n .map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? \"\").trim()] as const)\n .filter(([, text]) => text.length > 0),\n ) as Record<number, string>,\n ...(trimmedNote ? { note: trimmedNote } : {}),\n }\n\n const parts = data.questions.map(\n (q, i) => `- ${q.question} -> ${indentAnswerContinuationLines(getAnswer(i) ?? \"\")}`,\n )\n const text = [\n `关于需要确认的问题,用户的回答如下:\\n${parts.join(\"\\n\")}`,\n trimmedNote ? `补充说明:${indentAnswerContinuationLines(trimmedNote)}` : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\")\n setSubmitted(true)\n onAnswer(text, toolCallId, nextAnswerData)\n }\n\n return (\n <div\n className={cn(\n \"blade-chat-ask-card ml-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]\",\n answered\n ? \"max-w-2xl space-y-3 p-3 text-xs text-[hsl(var(--muted-foreground))] opacity-80\"\n : \"max-w-lg space-y-5 p-4 text-sm\",\n )}\n >\n {data.source_loop?.description && (\n <div className=\"rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]\">\n 子智能体「{data.source_loop.description}」在等待你的回答\n </div>\n )}\n {data.questions.map((q, qIdx) => (\n <QuestionCard\n key={q.question}\n question={q}\n qIdx={qIdx}\n answered={answered}\n selected={displayAnswerState.selections.get(qIdx) ?? new Set()}\n isCustom={displayAnswerState.usingCustom.has(qIdx)}\n customText={displayAnswerState.customTexts.get(qIdx) ?? \"\"}\n onToggle={toggleOption}\n onCustomFocus={handleCustomFocus}\n onCustomChange={setCustomText}\n />\n ))}\n\n <NoteField\n answered={answered}\n submitted={submitted}\n note={displayAnswerState.note}\n onChange={setNote}\n />\n\n {!answered && !submitted && onAnswer && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!allAnswered}\n className=\"w-full rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {allAnswered ? \"确认\" : \"请先选择一个选项\"}\n </button>\n )}\n\n {submitted && !answered && (\n <button\n type=\"button\"\n disabled\n className=\"flex w-full items-center justify-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] opacity-80\"\n >\n <Loader2 size={14} className=\"animate-spin\" />\n 确认中\n </button>\n )}\n </div>\n )\n}\n\nfunction QuestionCard({\n question,\n qIdx,\n answered,\n selected,\n isCustom,\n customText,\n onToggle,\n onCustomFocus,\n onCustomChange,\n}: {\n question: QuestionItem\n qIdx: number\n answered: boolean\n selected: Set<number>\n isCustom: boolean\n customText: string\n onToggle: (qIdx: number, optIdx: number, multi: boolean) => void\n onCustomFocus: (qIdx: number) => void\n onCustomChange: (qIdx: number, text: string) => void\n}) {\n const multi = question.multiSelect ?? false\n const customTextareaRef = useAutoResizeTextarea(customText)\n\n return (\n <div>\n <div className={cn(\"flex items-start gap-2\", answered ? \"mb-2\" : \"mb-3\")}>\n <MessageSquareMore\n size={answered ? 12 : 13}\n className=\"mt-0.5 shrink-0 text-[hsl(var(--primary))]\"\n />\n <div\n className={cn(\n \"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]\",\n answered ? \"text-xs\" : \"text-sm\",\n )}\n >\n <MarkdownContent className=\"blade-chat-prose\">{question.question}</MarkdownContent>\n </div>\n </div>\n\n <div className={cn(\"flex flex-col pl-5\", answered ? \"gap-1\" : \"gap-1.5\")}>\n {question.options.map((opt, optIdx) => {\n const isSel = selected.has(optIdx)\n return (\n <button\n key={opt.label}\n type=\"button\"\n disabled={answered}\n onClick={() => onToggle(qIdx, optIdx, multi)}\n className={cn(\n \"flex items-start gap-2.5 rounded-lg border text-left transition-all\",\n answered ? \"px-2.5 py-1.5\" : \"px-3 py-2.5\",\n isSel && !answered\n ? \"border-[hsl(var(--primary)/0.8)] bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]\"\n : isSel\n ? \"border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.08)] text-[hsl(var(--foreground))]\"\n : \"border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)]\",\n !answered &&\n !isSel &&\n \"hover:border-[hsl(var(--ring)/0.4)] hover:bg-[hsl(var(--accent))]\",\n answered && \"cursor-default opacity-70\",\n )}\n >\n {multi && (\n <div\n className={cn(\n \"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors\",\n isSel && !answered\n ? \"border-[hsl(var(--primary-foreground)/0.6)] bg-[hsl(var(--primary-foreground)/0.2)]\"\n : isSel\n ? \"border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.12)]\"\n : \"border-[hsl(var(--border))]\",\n )}\n >\n {isSel && (\n <Check\n size={9}\n className={\n answered\n ? \"text-[hsl(var(--primary))]\"\n : \"text-[hsl(var(--primary-foreground))]\"\n }\n />\n )}\n </div>\n )}\n <div className=\"min-w-0\">\n <div className={cn(\"font-medium\", answered ? \"text-xs\" : \"text-[13px]\")}>\n {opt.label}\n </div>\n {opt.description && (\n <div\n className={cn(\n \"mt-0.5\",\n answered ? \"text-[11px]\" : \"text-xs\",\n isSel && !answered ? \"opacity-75\" : \"text-[hsl(var(--muted-foreground))]\",\n )}\n >\n {opt.description}\n </div>\n )}\n </div>\n </button>\n )\n })}\n\n {/* 「其他」多行输入 */}\n {answered && !isCustom ? null : (\n <div\n className={cn(\n \"flex items-start gap-2 rounded-lg border transition-all focus-within:ring-2 focus-within:ring-[hsl(var(--ring)/0.35)]\",\n answered ? \"px-2.5 py-1.5\" : \"px-3 py-2.5\",\n isCustom\n ? \"border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]\"\n : \"border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]\",\n answered && \"cursor-default opacity-70\",\n )}\n >\n <span className=\"shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]\">\n 其他:\n </span>\n <textarea\n ref={customTextareaRef}\n rows={2}\n value={customText}\n readOnly={answered}\n onChange={(e) => onCustomChange(qIdx, e.target.value)}\n onFocus={() => onCustomFocus(qIdx)}\n aria-label=\"自定义回答\"\n placeholder=\"输入你的答案...\"\n className={cn(\n \"min-h-10 min-w-0 flex-1 resize-none bg-transparent leading-5 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]\",\n answered ? \"text-xs\" : \"text-sm\",\n )}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n\nfunction NoteField({\n answered,\n submitted,\n note,\n onChange,\n}: {\n answered: boolean\n submitted: boolean\n note: string\n onChange: (text: string) => void\n}) {\n const textareaRef = useAutoResizeTextarea(note)\n const readOnly = answered || submitted\n if (answered && !note.trim()) return null\n\n return (\n <label\n className={cn(\n \"block rounded-lg border transition-all focus-within:ring-2 focus-within:ring-[hsl(var(--ring)/0.35)]\",\n answered ? \"px-2.5 py-1.5\" : \"px-3 py-2.5\",\n note.trim()\n ? \"border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]\"\n : \"border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]\",\n readOnly && \"cursor-default opacity-70\",\n )}\n >\n <span className=\"mb-1.5 block text-xs text-[hsl(var(--muted-foreground))]\">\n 补充说明(可选)\n </span>\n <textarea\n ref={textareaRef}\n rows={2}\n value={note}\n readOnly={readOnly}\n onChange={(event) => {\n if (readOnly) return\n onChange(event.target.value)\n }}\n aria-label=\"补充说明\"\n placeholder=\"选完还可以再讲两句,空着就当没有\"\n className={cn(\n \"min-h-10 w-full resize-none bg-transparent leading-5 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]\",\n answered ? \"text-xs\" : \"text-sm\",\n )}\n />\n </label>\n )\n}\n\n/** 解析 AskUserQuestion 的工具结果/参数,容错 LLM 把 questions 输出为字符串的情况。 */\nexport function parseAskUserQuestion(\n toolResult: string | null | undefined,\n): AskUserQuestionData | null {\n if (!toolResult) return null\n try {\n const parsed = JSON.parse(toolResult)\n let questions = parsed?.questions\n if (typeof questions === \"string\") {\n try {\n questions = JSON.parse(questions)\n } catch {\n return null\n }\n }\n if (Array.isArray(questions) && questions.length > 0) {\n const normalized = questions.map(normalizeQuestionItem)\n if (normalized.every((question): question is QuestionItem => question !== null)) {\n return { ...parsed, questions: normalized } as AskUserQuestionData\n }\n }\n } catch {\n // 非 JSON,忽略\n }\n return null\n}\n\nexport interface AskUserQuestionError {\n message: string\n detail: string | null\n}\n\n/**\n * Keep this parser synchronized with apps/web/AskUserQuestionBlock.tsx.\n * The built-in Web cannot import agent-react, and this is not a public SDK protocol API.\n */\nexport function parseAskUserQuestionError(\n toolResult: string | null | undefined,\n): AskUserQuestionError | null {\n if (!toolResult) return null\n try {\n const parsed = JSON.parse(toolResult) as {\n error?: unknown\n error_code?: unknown\n message?: unknown\n }\n let detail: string | null = null\n if (typeof parsed.error === \"string\") detail = parsed.error\n if (parsed.error && typeof parsed.error === \"object\") {\n const message = (parsed.error as { message?: unknown }).message\n if (typeof message === \"string\") detail = message\n }\n if (!detail && typeof parsed.message === \"string\") detail = parsed.message\n if (!detail) return null\n return {\n message:\n parsed.error_code === \"invalid_questions\"\n ? \"提问内容不完整,当前没有等待你回答。请让智能体重新提问。\"\n : \"这次提问没有成功,当前没有等待你回答。请让智能体重新提问。\",\n detail,\n }\n } catch {\n return null\n }\n}\n\nfunction normalizeQuestionItem(value: unknown): QuestionItem | null {\n if (!value || typeof value !== \"object\") return null\n const item = value as {\n question?: unknown\n options?: unknown\n multiSelect?: unknown\n multi_select?: unknown\n }\n if (typeof item.question !== \"string\" || !Array.isArray(item.options)) return null\n const options = item.options.map(normalizeOptionItem)\n if (!options.every((option): option is OptionItem => option !== null)) return null\n return {\n question: item.question,\n options,\n multiSelect: item.multiSelect === true || item.multi_select === true,\n }\n}\n\nfunction normalizeOptionItem(value: unknown): OptionItem | null {\n if (typeof value === \"string\") return { label: value, description: \"\" }\n if (!value || typeof value !== \"object\") return null\n const option = value as { label?: unknown; description?: unknown }\n if (typeof option.label !== \"string\") return null\n return {\n label: option.label,\n description: typeof option.description === \"string\" ? option.description : \"\",\n }\n}\n","import { AlertTriangle } from \"lucide-react\"\nimport { Component, type ErrorInfo, type ReactNode } from \"react\"\n\ninterface Props {\n label: string\n details?: string\n resetKey?: string\n children: ReactNode\n}\n\ninterface State {\n componentStack: string\n error: Error | null\n}\n\nfunction getFirstComponentName(componentStack: string): string | null {\n const match = componentStack.match(/\\n\\s+at\\s+([^\\s(]+)/)\n return match?.[1] ?? null\n}\n\n/** 消息级渲染错误边界:单条消息渲染崩溃时降级为提示卡片,不拖垮整个列表。 */\nexport class RenderErrorBoundary extends Component<Props, State> {\n state: State = {\n componentStack: \"\",\n error: null,\n }\n\n static getDerivedStateFromError(error: Error): State {\n return {\n componentStack: \"\",\n error,\n }\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n const componentStack = info.componentStack ?? \"\"\n console.error(`${this.props.label} render error:`, error, info)\n this.setState({ componentStack })\n }\n\n componentDidUpdate(prevProps: Props) {\n if (this.state.error && prevProps.resetKey !== this.props.resetKey) {\n this.setState({ componentStack: \"\", error: null })\n }\n }\n\n render() {\n const { children, details, label } = this.props\n const { componentStack, error } = this.state\n\n if (!error) {\n return children\n }\n\n const componentName = getFirstComponentName(componentStack)\n\n return (\n <div className=\"blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100\">\n <div className=\"flex items-start gap-2\">\n <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-amber-300\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"font-medium\">{label}渲染失败</div>\n <div className=\"mt-1 break-words text-xs leading-5 text-amber-100/75\">\n {componentName ? `组件:${componentName}。` : null}\n {error.message || \"发生了未预期的渲染错误。\"}\n </div>\n {details ? (\n <div className=\"mt-1 truncate text-xs text-amber-100/55\">{details}</div>\n ) : null}\n </div>\n </div>\n </div>\n )\n }\n}\n","import type { FinalArtifact, PostChatFollowup, ResultFeedback as SavedResultFeedback, ResultFeedbackReason } from \"@blade-hq/agent-client\"\nimport { ArrowRight, ArrowUpRight, ChevronDown, Globe, Sparkles } from \"lucide-react\"\nimport { useCallback, useEffect, useRef, useState, type MouseEvent } from \"react\"\nimport { useBladeClient } from \"../context\"\n\nexport type FollowupInteractionEvent =\n | {\n type: \"suggestions_shown\"\n sessionId?: string\n assistantEntryId: string\n count: number\n }\n | {\n type: \"suggestion_adopted\"\n sessionId?: string\n assistantEntryId: string\n suggestionIndex: number\n }\n | {\n type: \"artifact_shown\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: FinalArtifact[\"kind\"]\n }\n | {\n type: \"artifact_opened\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: FinalArtifact[\"kind\"]\n }\n | {\n type: \"artifact_download_started\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: \"file\"\n }\n | {\n type: \"artifact_download_succeeded\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: \"file\"\n }\n | {\n type: \"result_feedback_shown\"\n sessionId?: string\n assistantEntryId: string\n }\n | {\n type: \"result_feedback_submitted\"\n sessionId?: string\n assistantEntryId: string\n helpful: boolean\n reason: ResultFeedbackReason | null\n updatedAt: string\n }\n\nfunction emitInteraction(\n callback: ((event: FollowupInteractionEvent) => void) | undefined,\n event: FollowupInteractionEvent,\n) {\n try {\n callback?.(event)\n } catch {\n // 宿主的分析回调不得阻断 SDK 交互。\n }\n}\n\nfunction basename(path: string): string {\n return path.split(/[\\\\/]/).filter(Boolean).pop() || path\n}\n\nfunction ArtifactCard({\n artifact,\n sessionId,\n assistantEntryId,\n artifactIndex,\n onInteraction,\n onArtifactOpened,\n}: {\n artifact: FinalArtifact\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n onInteraction?: (event: FollowupInteractionEvent) => void\n onArtifactOpened: (artifactIndex: number, artifactKind: FinalArtifact[\"kind\"]) => void\n}) {\n const client = useBladeClient()\n const [downloading, setDownloading] = useState(false)\n const name = artifact.label || basename(artifact.target)\n\n if (artifact.kind === \"link\") {\n return (\n <a\n href={artifact.target}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={() => onArtifactOpened(artifactIndex, \"link\")}\n title={`${name}\\n${artifact.target}`}\n className=\"group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))]\"\n >\n <Globe size={15} className=\"shrink-0 text-[hsl(var(--primary))]\" />\n <span className=\"min-w-0 flex-1 truncate font-medium\">{name}</span>\n <ArrowUpRight size={13} className=\"absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100\" />\n </a>\n )\n }\n\n const fileName = basename(artifact.target)\n const downloadUrl = sessionId\n ? client.buildAuthedUrl(\n `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(artifact.target)}`,\n )\n : undefined\n\n const handleDownload = async (event: MouseEvent<HTMLAnchorElement>) => {\n // 修饰键点击保留标准链接语义;普通点击走 SDK,保证跨域和 token 刷新可用。\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return\n event.preventDefault()\n if (!sessionId || downloading) return\n setDownloading(true)\n emitInteraction(onInteraction, {\n type: \"artifact_download_started\",\n sessionId,\n assistantEntryId,\n artifactIndex,\n artifactKind: \"file\",\n })\n try {\n await client.sessions.downloadFile(sessionId, artifact.target, fileName)\n emitInteraction(onInteraction, {\n type: \"artifact_download_succeeded\",\n sessionId,\n assistantEntryId,\n artifactIndex,\n artifactKind: \"file\",\n })\n } catch {\n // 下载失败不能误报成功,也不影响其余聊天交互。\n } finally {\n setDownloading(false)\n }\n }\n\n return (\n <a\n href={downloadUrl}\n download={fileName}\n onClick={handleDownload}\n title={fileName}\n aria-label={`下载文件:${fileName}`}\n aria-disabled={!sessionId || undefined}\n aria-busy={downloading || undefined}\n className=\"min-w-0 cursor-pointer break-all text-xs text-[hsl(var(--primary))] underline aria-disabled:cursor-not-allowed aria-disabled:opacity-60 aria-busy:cursor-wait\"\n >\n {fileName}\n </a>\n )\n}\n\nconst FEEDBACK_REASONS: Array<{ value: ResultFeedbackReason; label: string }> = [\n { value: \"not_solved\", label: \"没解决问题\" },\n { value: \"inaccurate\", label: \"内容不准确\" },\n { value: \"incomplete\", label: \"内容不完整\" },\n { value: \"needs_major_changes\", label: \"需要大量修改\" },\n { value: \"too_slow\", label: \"太慢\" },\n { value: \"other\", label: \"其他\" },\n]\n\nfunction feedbackReasonLabel(reason: ResultFeedbackReason | null): string | null {\n return FEEDBACK_REASONS.find((item) => item.value === reason)?.label ?? null\n}\n\nexport function HistoricalResultFeedback({ feedback }: { feedback: SavedResultFeedback }) {\n const label = feedbackReasonLabel(feedback.reason)\n return (\n <section\n aria-label=\"历史结果反馈\"\n className=\"mt-3 w-fit max-w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <span>你对此轮结果的评价:{feedback.helpful ? \"有帮助\" : \"没帮助\"}</span>\n {label ? <span> · {label}</span> : null}\n </section>\n )\n}\n\nfunction ResultFeedback({\n followup,\n sessionId,\n isViewer,\n onInteraction,\n savedFeedback,\n onFeedbackSaved,\n}: {\n followup: PostChatFollowup\n sessionId?: string\n isViewer: boolean\n onInteraction?: (event: FollowupInteractionEvent) => void\n savedFeedback?: SavedResultFeedback\n onFeedbackSaved?: (feedback: SavedResultFeedback) => void\n}) {\n const client = useBladeClient()\n const [saved, setSaved] = useState<SavedResultFeedback | null>(savedFeedback ?? null)\n const [helpful, setHelpful] = useState<boolean | null>(savedFeedback?.helpful ?? null)\n const [reason, setReason] = useState<ResultFeedbackReason | null>(savedFeedback?.reason ?? null)\n const [saving, setSaving] = useState(false)\n const [saveError, setSaveError] = useState(false)\n const reportedShown = useRef(false)\n const latestChoice = useRef<{\n helpful: boolean\n reason: ResultFeedbackReason | null\n } | null>(null)\n const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer\n\n useEffect(() => {\n if (!eligible || reportedShown.current) return\n reportedShown.current = true\n emitInteraction(onInteraction, {\n type: \"result_feedback_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n })\n }, [eligible, followup.assistant_entry_id, onInteraction, sessionId])\n\n useEffect(() => {\n if (!savedFeedback || latestChoice.current) return\n setSaved(savedFeedback)\n setHelpful(savedFeedback.helpful)\n setReason(savedFeedback.reason)\n }, [savedFeedback])\n\n const submit = useCallback(\n async (nextHelpful: boolean, nextReason: ResultFeedbackReason | null) => {\n if (!sessionId) return\n const choice = { helpful: nextHelpful, reason: nextReason }\n latestChoice.current = choice\n setHelpful(nextHelpful)\n setReason(nextReason)\n setSaving(true)\n setSaveError(false)\n try {\n const result = await client.sessions.putResultFeedback(\n sessionId,\n followup.assistant_entry_id,\n choice,\n )\n if (latestChoice.current !== choice) return\n setSaved(result)\n onFeedbackSaved?.(result)\n emitInteraction(onInteraction, {\n type: \"result_feedback_submitted\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n helpful: result.helpful,\n reason: result.reason,\n updatedAt: result.updated_at,\n })\n } catch {\n if (latestChoice.current === choice) setSaveError(true)\n } finally {\n if (latestChoice.current === choice) setSaving(false)\n }\n },\n [client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId],\n )\n\n if (!eligible) return null\n return (\n <section\n aria-label=\"结果反馈\"\n className=\"flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3\"\n >\n <div className=\"text-xs font-medium text-[hsl(var(--muted-foreground))]\">\n 目前的整体结果有帮助吗?\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n <button\n type=\"button\"\n aria-pressed={helpful === true}\n disabled={saving}\n onClick={() => void submit(true, null)}\n className=\"rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60\"\n >\n 有帮助\n </button>\n <button\n type=\"button\"\n aria-pressed={helpful === false}\n disabled={saving}\n onClick={() => void submit(false, reason)}\n className=\"rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60\"\n >\n 没帮助\n </button>\n </div>\n {helpful === false ? (\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"没帮助的主要原因\">\n {FEEDBACK_REASONS.map((item) => (\n <button\n key={item.value}\n type=\"button\"\n aria-pressed={reason === item.value}\n disabled={saving}\n onClick={() => void submit(false, item.value)}\n className=\"rounded-full bg-[hsl(var(--muted)/0.62)] px-2.5 py-1 text-[11px] aria-pressed:bg-[hsl(var(--primary)/0.14)] disabled:opacity-60\"\n >\n {item.label}\n </button>\n ))}\n </div>\n ) : null}\n {saveError ? (\n <div className=\"flex items-center gap-2 text-xs text-[hsl(var(--destructive))]\">\n <span>反馈暂未保存,可重试</span>\n <button\n type=\"button\"\n className=\"underline\"\n onClick={() => {\n const choice = latestChoice.current\n if (choice) void submit(choice.helpful, choice.reason)\n }}\n >\n 重试\n </button>\n </div>\n ) : saved ? (\n <div className=\"text-[11px] text-[hsl(var(--muted-foreground))]\">已保存,可随时修改</div>\n ) : null}\n </section>\n )\n}\n\nexport function PostChatFollowupBlock({\n followup,\n sessionId,\n onSuggestion,\n isViewer = false,\n onInteraction,\n savedFeedback,\n onFeedbackSaved,\n}: {\n followup: PostChatFollowup\n sessionId?: string\n onSuggestion?: (value: string) => void\n isViewer?: boolean\n onInteraction?: (event: FollowupInteractionEvent) => void\n savedFeedback?: SavedResultFeedback\n onFeedbackSaved?: (feedback: SavedResultFeedback) => void\n}) {\n const [expanded, setExpanded] = useState(false)\n const adopted = useRef(new Set<number>())\n const reportedSuggestions = useRef(false)\n const reportedArtifacts = useRef(new Set<number>())\n const openedArtifacts = useRef(new Set<number>())\n const artifacts = followup.final_artifacts ?? []\n const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3)\n useEffect(() => {\n if (!reportedSuggestions.current && followup.suggestions.length > 0) {\n reportedSuggestions.current = true\n emitInteraction(onInteraction, {\n type: \"suggestions_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n count: followup.suggestions.length,\n })\n }\n for (let artifactIndex = 0; artifactIndex < visibleArtifacts.length; artifactIndex += 1) {\n if (reportedArtifacts.current.has(artifactIndex)) continue\n const artifact = visibleArtifacts[artifactIndex]\n if (!artifact) continue\n reportedArtifacts.current.add(artifactIndex)\n emitInteraction(onInteraction, {\n type: \"artifact_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n artifactIndex,\n artifactKind: artifact.kind,\n })\n }\n }, [\n followup.assistant_entry_id,\n followup.suggestions.length,\n onInteraction,\n sessionId,\n visibleArtifacts,\n ])\n const reportArtifactOpened = useCallback(\n (artifactIndex: number, artifactKind: FinalArtifact[\"kind\"]) => {\n if (openedArtifacts.current.has(artifactIndex)) return\n openedArtifacts.current.add(artifactIndex)\n emitInteraction(onInteraction, {\n type: \"artifact_opened\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n artifactIndex,\n artifactKind,\n })\n },\n [followup.assistant_entry_id, onInteraction, sessionId],\n )\n if (\n !followup.recaption &&\n artifacts.length === 0 &&\n followup.suggestions.length === 0 &&\n !followup.feedback_eligible\n )\n return null\n\n return (\n <div className=\"mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]\">\n {followup.recaption || artifacts.length > 0 ? (\n <section aria-label=\"本轮小结\" className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]\">\n <Sparkles size={14} />\n 本轮小结\n </div>\n {followup.recaption ? <p className=\"text-[13px] leading-5\">{followup.recaption}</p> : null}\n {artifacts.length > 0 ? (\n <>\n <div className=\"grid max-w-full grid-cols-3 gap-1.5\">\n {visibleArtifacts.map((artifact, artifactIndex) => (\n <ArtifactCard\n key={`${artifact.kind}:${artifactIndex}`}\n artifact={artifact}\n sessionId={sessionId}\n assistantEntryId={followup.assistant_entry_id}\n artifactIndex={artifactIndex}\n onInteraction={onInteraction}\n onArtifactOpened={reportArtifactOpened}\n />\n ))}\n </div>\n {artifacts.length > 3 ? (\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n aria-expanded={expanded}\n className=\"flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]\"\n >\n {expanded ? \"收起\" : `展开 ${artifacts.length - 3} 个`}\n <ChevronDown size={13} className={expanded ? \"rotate-180\" : undefined} />\n </button>\n ) : null}\n </>\n ) : null}\n </section>\n ) : null}\n {followup.suggestions.length > 0 ? (\n <section aria-label=\"下一步建议\" className=\"flex flex-col gap-1.5\">\n <div className=\"text-xs font-medium text-[hsl(var(--muted-foreground))]\">下一步可以</div>\n {followup.suggestions.map((suggestion, suggestionIndex) => (\n <button\n key={suggestion}\n type=\"button\"\n disabled={isViewer}\n onClick={() => {\n if (!adopted.current.has(suggestionIndex)) {\n adopted.current.add(suggestionIndex)\n emitInteraction(onInteraction, {\n type: \"suggestion_adopted\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n suggestionIndex,\n })\n }\n onSuggestion?.(suggestion)\n }}\n className=\"group flex items-center gap-2 rounded-xl bg-[hsl(var(--muted)/0.62)] px-3 py-2 text-left text-[13px] disabled:cursor-default disabled:opacity-60\"\n >\n <span>{suggestion}</span>\n <ArrowRight size={14} className=\"ml-auto shrink-0\" />\n </button>\n ))}\n </section>\n ) : null}\n <ResultFeedback\n followup={followup}\n sessionId={sessionId}\n isViewer={isViewer}\n onInteraction={onInteraction}\n savedFeedback={savedFeedback}\n onFeedbackSaved={onFeedbackSaved}\n />\n </div>\n )\n}\n","import type { ChatMessage } from \"@blade-hq/agent-client\"\nimport {\n chatErrorForDisplay,\n getFileParts,\n getImageParts,\n getTextContent,\n} from \"@blade-hq/agent-client\"\nimport { FileText, Loader2 } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { parseWhatIfPrompt } from \"../lib/whatif-prompt\"\nimport { MarkdownContent } from \"./MarkdownContent\"\nimport { WhatIfUserBubble } from \"./WhatIfUserBubble\"\n\nexport type UserChatMessage = ChatMessage & { role: \"user\" }\nexport type ErrorChatMessage = ChatMessage & { role: \"error\" }\n\nexport function isUserMessage(message: ChatMessage): message is UserChatMessage {\n return message.role === \"user\"\n}\n\nexport function isErrorMessage(message: ChatMessage): message is ErrorChatMessage {\n return message.role === \"error\"\n}\n\ninterface UserMessageProps {\n message: UserChatMessage\n className?: string\n}\n\nconst isSending = (message: ChatMessage) => message.status === \"streaming\"\n\n/** 用户消息气泡:文本、canonical 引用消息与图片/文件附件。 */\nexport function UserMessageBubble({ message, className }: UserMessageProps) {\n const text = getTextContent(message.content).trim()\n const fileParts = getFileParts(message.content)\n const imageParts = getImageParts(message.content)\n const whatifParsed =\n text && imageParts.length === 0 && fileParts.length === 0 ? parseWhatIfPrompt(text) : null\n\n if (whatifParsed) {\n return (\n <div className={cn(\"blade-chat-user-row flex justify-end\", className)}>\n <div className=\"blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3\">\n <WhatIfUserBubble parsed={whatifParsed} />\n </div>\n </div>\n )\n }\n\n return (\n <div className={cn(\"blade-chat-user-row flex justify-end\", className)}>\n <div className=\"blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3\">\n {imageParts.length > 0 && (\n <div className=\"grid gap-2\">\n {imageParts.map((part) => (\n <img\n key={part.image_url.url}\n src={part.image_url.url}\n alt=\"用户上传的图片\"\n className=\"max-h-64 rounded-xl border border-[hsl(var(--user-msg-border))] object-cover\"\n />\n ))}\n </div>\n )}\n {fileParts.length > 0 && (\n <div className=\"flex flex-col items-end gap-1.5\">\n {fileParts.map((part) => (\n <div\n key={`${part.name}-${part.data.length}`}\n className=\"flex items-center gap-1.5 rounded-lg border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <FileText size={12} className=\"shrink-0\" />\n <span className=\"max-w-56 truncate\" title={part.name}>\n {part.name}\n </span>\n </div>\n ))}\n </div>\n )}\n {text && (\n <div className=\"blade-chat-user-bubble max-w-full rounded-[20px] rounded-br-[6px] border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-[18px] py-[13px] text-sm leading-[1.65] text-[hsl(var(--user-msg-fg))]\">\n <MarkdownContent className=\"blade-chat-prose\">{text}</MarkdownContent>\n </div>\n )}\n {text && isSending(message) && (\n <div className=\"flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]\">\n <Loader2 size={11} className=\"animate-spin\" aria-hidden=\"true\" />\n <span>发送中</span>\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/** 错误消息:居中红色提示条。 */\nexport function ErrorMessageBlock({\n message,\n className,\n}: {\n message: ErrorChatMessage\n className?: string\n}) {\n const text = chatErrorForDisplay(getTextContent(message.content))\n return (\n <div className={cn(\"blade-chat-error-row flex min-w-0 justify-start\", className)}>\n {/* 与内置 Web 的 ErrorMessageBlock 保持一致:消息流里的错误不用红色。 */}\n <div className=\"blade-chat-error-block min-w-0 max-w-full whitespace-pre-wrap break-words border-l-[3px] border-[hsl(var(--border))] px-3 py-1 text-left text-sm leading-7 text-[hsl(var(--muted-foreground))] [overflow-wrap:anywhere]\">\n {text}\n </div>\n </div>\n )\n}\n","/** 一条 canonical what-if 消息中的引用快照。 */\nexport interface WhatIfQuote {\n stepNumber: number | null\n label: string\n snapshot: string\n}\n\nexport interface ParsedWhatIfPrompt {\n fromStep: number | null\n quotes: WhatIfQuote[]\n userText: string\n}\n\nconst HEADER_RE =\n /^以下消息和 step 产物标记为 deprecated_by_rerun,请基于最新用户假设从 step(\\d+) 开始完整重新推演,不要复用旧结论。$/\nconst QUOTE_HEADER_RE = /^\\[步骤(\\d+)\\s*·\\s*(.+?)\\]$/\nconst USER_INPUT_TAG = \"[用户输入]\"\n\n/**\n * 解析 Server 历史与流式消息共用的 canonical what-if 文本。\n *\n * 无 `[用户输入]` 标签的历史消息仍按最后一条快照尾部的空行切分,保持内置\n * Web 原有兼容行为。\n */\nexport function parseWhatIfPrompt(text: string): ParsedWhatIfPrompt | null {\n const lines = text.replace(/\\r\\n/g, \"\\n\").trimEnd().split(\"\\n\")\n const headerMatch = lines[0]?.match(HEADER_RE)\n if (!headerMatch) return null\n\n const userTagIdx = lines.indexOf(USER_INPUT_TAG)\n const hasUserTag = userTagIdx >= 0\n const quoteBlockEndExclusive = hasUserTag ? userTagIdx : lines.length\n const quoteHeaderIdxs: number[] = []\n let quoteBlockFound = false\n for (let i = 1; i < quoteBlockEndExclusive; i++) {\n if (!quoteBlockFound && lines[i].trim() === \"[引用]\") {\n quoteBlockFound = true\n } else if (quoteBlockFound && QUOTE_HEADER_RE.test(lines[i])) {\n quoteHeaderIdxs.push(i)\n }\n }\n\n let legacyUserTextStart = -1\n if (!hasUserTag && quoteHeaderIdxs.length > 0) {\n const lastSnapshotStart = quoteHeaderIdxs.at(-1)! + 1\n let i = lines.length - 1\n while (i >= lastSnapshotStart && lines[i].trim() === \"\") i -= 1\n while (i >= lastSnapshotStart && lines[i].trim() !== \"\") i -= 1\n if (i >= lastSnapshotStart) legacyUserTextStart = i + 1\n }\n\n const quotes = quoteHeaderIdxs.map((headerIdx, index) => {\n const match = lines[headerIdx].match(QUOTE_HEADER_RE)!\n const nextHeader = quoteHeaderIdxs[index + 1]\n const end = nextHeader ?? (legacyUserTextStart >= 0 ? legacyUserTextStart : quoteBlockEndExclusive)\n const snapshotLines = lines.slice(headerIdx + 1, end)\n while (snapshotLines.at(-1)?.trim() === \"\") snapshotLines.pop()\n return {\n stepNumber: Number.parseInt(match[1], 10),\n label: match[2].trim(),\n snapshot: snapshotLines.join(\"\\n\"),\n }\n })\n\n const userTextStart = hasUserTag ? userTagIdx + 1 : legacyUserTextStart\n const userLines = userTextStart >= 0 ? lines.slice(userTextStart) : []\n while (userLines[0]?.trim() === \"\") userLines.shift()\n while (userLines.at(-1)?.trim() === \"\") userLines.pop()\n\n const fromStep = Number.parseInt(headerMatch[1], 10)\n return {\n fromStep: Number.isFinite(fromStep) ? fromStep : null,\n quotes,\n userText: userLines.join(\"\\n\"),\n }\n}\n","import { RefreshCcw } from \"lucide-react\"\nimport type { ParsedWhatIfPrompt } from \"../lib/whatif-prompt\"\nimport { MarkdownContent } from \"./MarkdownContent\"\n\nexport interface WhatIfUserBubbleProps {\n parsed: ParsedWhatIfPrompt\n onQuoteClick?: (stepNumber: number) => void\n}\n\n/** SDK 与内置 Web 共用的 canonical 引用消息渲染。 */\nexport function WhatIfUserBubble({ parsed, onQuoteClick }: WhatIfUserBubbleProps) {\n const { fromStep, quotes, userText } = parsed\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex items-center gap-1.5 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.5)] px-2.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]\">\n <RefreshCcw size={10} />\n <span>{fromStep != null ? `重跑自 step ${fromStep}` : \"重跑\"}</span>\n </div>\n {quotes.length > 0 && (\n <div className=\"flex max-w-[min(72vw,42rem)] flex-col items-stretch gap-2\">\n {quotes.map((quote, index) => {\n const clickable = quote.stepNumber != null && !!onQuoteClick\n const label = quote.stepNumber != null ? `步骤${quote.stepNumber} · ${quote.label}` : quote.label\n return (\n <div key={`${quote.stepNumber ?? \"x\"}-${index}`} className=\"rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.8)] px-3 py-2 text-left\">\n <button type=\"button\" disabled={!clickable} onClick={() => clickable && onQuoteClick(quote.stepNumber!)} className=\"inline-flex max-w-full items-center gap-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] disabled:cursor-default\" title={clickable ? \"跳转到对应步骤卡片\" : undefined}>\n <span>↳</span><span className=\"truncate\">{label}</span>\n </button>\n {quote.snapshot ? <div className=\"mt-1 border-l-2 border-[hsl(var(--accent-foreground)/0.35)] pl-2 text-xs leading-relaxed text-[hsl(var(--foreground)/0.8)]\"><MarkdownContent className=\"blade-chat-prose\">{quote.snapshot}</MarkdownContent></div> : null}\n </div>\n )\n })}\n </div>\n )}\n {userText && <div className=\"rounded-2xl border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-4 py-2.5 text-sm leading-relaxed text-[hsl(var(--user-msg-fg))]\"><MarkdownContent className=\"blade-chat-prose\">{userText}</MarkdownContent></div>}\n </div>\n )\n}\n","import { useEffect, useMemo, useState } from \"react\"\nimport { useLlmChat, type LlmChatOptions } from \"../hooks/use-llm-chat\"\nimport { ChatSurface, type ChatPresentationProps } from \"./ChatSurface\"\nimport {\n LlmAdvancedSettingsBar,\n readOverride,\n writeOverride,\n type LlmAdvancedSettings,\n type LlmOverride,\n} from \"./LlmAdvancedSettings\"\n\n/** 纯 LLM 模式的操作句柄,对应智能体模式的 session.insertText() / send()。 */\nexport interface LlmChatHandle {\n /** 把文本放进输入框交给用户确认,不直接发出去。 */\n insertText: (text: string) => void\n send: (text: string) => Promise<boolean>\n /** 清空这段对话。 */\n reset: () => void\n}\n\nexport interface LlmChatProps extends LlmChatOptions, ChatPresentationProps {\n /** 拿到操作句柄:业务界面把选中的数据塞进输入框,或直接发一句。 */\n onReady?: (handle: LlmChatHandle) => void\n /**\n * 打开输入框上方的「高级设置」,让用户自己改模型服务地址(默认还能改模型名)。\n * 不传就一个节点都不渲染。传 `true` 用默认配置,或用对象细调。\n */\n advanced?: boolean | LlmAdvancedSettings\n /** 用户改了覆盖值时回调,方便宿主自己也记一份。 */\n onOverrideChange?: (override: LlmOverride) => void\n}\n\n/**\n * 纯 LLM 模式的聊天界面:不建会话、不连实时通道、也不要求登录 Blade,\n * 只按 **OpenAI Chat Completions** 标准协议跟模型服务一问一答。\n *\n * 默认形态是把 `baseURL` 指向自己后端的透传路径,由后端补密钥再转发,浏览器不持有密钥。\n */\nexport function LlmChat({\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onReady,\n advanced,\n onOverrideChange,\n ...options\n}: LlmChatProps) {\n // 用户在高级设置里填的值盖住调用方传进来的默认值;本轮请求用哪套由这里决定\n const [override, setOverride] = useState<LlmOverride>(() => readOverride(advanced, options.baseURL))\n // 每次渲染新建对象也没关系:useLlmChat 每轮都把配置写进 ref,send 用的是调用那一刻的值\n const effective = { ...options, ...override }\n const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective)\n const [inputText, setInputText] = useState(\"\")\n const [stopRequested, setStopRequested] = useState(false)\n\n // 句柄引用要稳定:随渲染换新对象的话,宿主放进 useEffect 依赖里会连环重跑\n const handle = useMemo<LlmChatHandle>(\n () => ({\n insertText: (text) => setInputText((prev) => (prev ? `${prev}\\n${text}` : text)),\n send,\n reset,\n }),\n [send, reset],\n )\n useEffect(() => {\n onReady?.(handle)\n }, [handle, onReady])\n\n return (\n <ChatSurface\n theme={theme}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n // 没有\"连接\"这回事:不建会话也不连实时通道,状态条恒为已连接\n connection=\"connected\"\n errorMessage={error}\n messages={messages}\n isStreaming={isStreaming}\n isStopping={stopRequested && isStreaming}\n inputText={inputText}\n onInputChange={setInputText}\n onSend={async (text) => {\n setStopRequested(false)\n if (!text.trim() || isStreaming) return false\n // 只等\"这条已经收下\",不等整段流跑完:等下去的话输入框在整个生成期间都清不掉,\n // 用户中途敲的下一句还会被清掉。失败通过出错条反馈,不靠输入框。\n void send(text)\n return true\n }}\n onStop={() => {\n setStopRequested(true)\n stop()\n }}\n beforeInput={\n advanced ? (\n <LlmAdvancedSettingsBar\n settings={advanced}\n defaults={{ baseURL: options.baseURL, model: options.model }}\n override={override}\n onChange={(next) => {\n setOverride(next)\n writeOverride(advanced, options.baseURL, next)\n onOverrideChange?.(next)\n }}\n />\n ) : undefined\n }\n />\n )\n}\n","import { Settings2 } from \"lucide-react\"\nimport { useState } from \"react\"\n\n/** 纯 LLM 模式的高级设置:让用户自己改模型服务地址。 */\nexport interface LlmAdvancedSettings {\n /** 允许改模型服务地址,默认允许。 */\n baseURL?: boolean\n /** 允许改模型名,默认允许。 */\n model?: boolean\n /**\n * 允许在界面上填密钥,默认**不允许**。\n * 打开意味着密钥存在浏览器里,只适合受控内网、演示,或密钥本身就属于当前用户的场景。\n */\n apiKey?: boolean\n /** 覆盖值存哪:`local` 记住用户的选择(默认),`memory` 只在本次会话有效。 */\n storage?: \"local\" | \"memory\"\n /** localStorage 的键,默认按组件拿到的 baseURL 生成。 */\n storageKey?: string\n}\n\nexport interface LlmOverride {\n baseURL?: string\n model?: string\n apiKey?: string\n}\n\nconst FIELDS: Array<{ id: keyof LlmOverride; label: string; placeholder: string; secret?: boolean }> = [\n { id: \"baseURL\", label: \"模型服务地址\", placeholder: \"http://内网地址/v1\" },\n { id: \"model\", label: \"模型\", placeholder: \"模型名称\" },\n { id: \"apiKey\", label: \"密钥\", placeholder: \"留空表示不改\", secret: true },\n]\n\nexport function normalizeAdvanced(\n value: boolean | LlmAdvancedSettings | undefined,\n): Required<Pick<LlmAdvancedSettings, \"baseURL\" | \"model\" | \"apiKey\" | \"storage\">> | null {\n if (!value) return null\n const config = value === true ? {} : value\n return {\n baseURL: config.baseURL ?? true,\n model: config.model ?? true,\n apiKey: config.apiKey ?? false,\n storage: config.storage ?? \"local\",\n }\n}\n\nfunction storageKeyFor(settings: boolean | LlmAdvancedSettings | undefined, baseURL: string): string {\n const explicit = typeof settings === \"object\" ? settings.storageKey : undefined\n return `blade-llm-override:${explicit ?? baseURL}`\n}\n\nexport function readOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n): LlmOverride {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return {}\n try {\n const raw = localStorage.getItem(storageKeyFor(settings, baseURL))\n if (!raw) return {}\n const stored = JSON.parse(raw) as LlmOverride\n // 只认当前还允许改的字段:宿主把密钥字段关掉之后,之前存下的密钥不能继续\n // 被悄悄发出去(同一个 storageKey 被别处写过也一样)。\n return Object.fromEntries(\n Object.entries(stored).filter(([key]) => normalized[key as keyof LlmOverride]),\n ) as LlmOverride\n } catch {\n // 隐私模式等场景读不到就当没设过\n return {}\n }\n}\n\nfunction writeOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n override: LlmOverride,\n): void {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return\n try {\n const key = storageKeyFor(settings, baseURL)\n if (Object.keys(override).length === 0) localStorage.removeItem(key)\n else localStorage.setItem(key, JSON.stringify(override))\n } catch {\n // 写不进去就退化成只在本次会话有效\n }\n}\n\ninterface Props {\n settings: boolean | LlmAdvancedSettings\n /** 组件拿到的默认值,用来做占位提示与\"恢复默认\" */\n defaults: { baseURL: string; model: string }\n override: LlmOverride\n onChange: (next: LlmOverride) => void\n}\n\n/**\n * 输入框上方的一行「高级设置」:默认收起,展开后可以改模型服务地址与模型。\n * 只有调用方显式打开才渲染——不传 advanced 时这里一个节点都不会出现。\n */\nexport function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }: Props) {\n const normalized = normalizeAdvanced(settings)\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState<LlmOverride>(override)\n if (!normalized) return null\n\n const fields = FIELDS.filter((field) => normalized[field.id])\n const dirty = Object.keys(override).length > 0\n\n return (\n <div className=\"blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs\">\n <button\n type=\"button\"\n onClick={() => {\n setDraft(override)\n setOpen((value) => !value)\n }}\n className=\"flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]\"\n >\n <Settings2 size={13} />\n 高级设置\n {dirty && <span className=\"text-[hsl(var(--primary))]\">(已自定义)</span>}\n </button>\n {open && (\n <div className=\"mt-2 flex flex-col gap-2\">\n {fields.map((field) => (\n <label key={field.id} className=\"flex flex-col gap-1\">\n <span className=\"text-[hsl(var(--muted-foreground))]\">{field.label}</span>\n <input\n type={field.secret ? \"password\" : \"text\"}\n value={draft[field.id] ?? \"\"}\n placeholder={\n field.id === \"apiKey\"\n ? field.placeholder\n : (defaults[field.id as \"baseURL\" | \"model\"] || field.placeholder)\n }\n onChange={(event) => setDraft({ ...draft, [field.id]: event.target.value })}\n className=\"rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none\"\n />\n </label>\n ))}\n {normalized.apiKey && (\n <p className=\"text-[hsl(var(--muted-foreground))]\">\n 密钥会存在这台浏览器里。只在你信得过这台机器时填。\n </p>\n )}\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n // 空字符串等于没改,不要把空值当成覆盖发出去\n const next = Object.fromEntries(\n Object.entries(draft).filter(([, value]) => String(value ?? \"\").trim() !== \"\"),\n ) as LlmOverride\n onChange(next)\n setOpen(false)\n }}\n className=\"rounded-md bg-[hsl(var(--primary))] px-2.5 py-1 font-medium text-[hsl(var(--primary-foreground))]\"\n >\n 保存\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setDraft({})\n onChange({})\n setOpen(false)\n }}\n className=\"rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-[hsl(var(--foreground))]\"\n >\n 恢复默认\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport { writeOverride }\n","import { AgentChat, type AgentChatProps } from \"./AgentChat\"\nimport { LlmChat, type LlmChatHandle } from \"./LlmChat\"\nimport type { LlmAdvancedSettings, LlmOverride } from \"./LlmAdvancedSettings\"\nimport type { LlmChatOptions } from \"../hooks/use-llm-chat\"\n\nexport type {\n ChatViewClassNames,\n ChatViewRenderers,\n ChatViewSlots,\n} from \"./ChatSurface\"\nexport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\n\nexport interface ChatViewProps extends AgentChatProps {\n /** 挂哪个下层组件。默认 `agent`——不传就是接 Blade 智能体会话的原有行为。 */\n mode?: \"agent\" | \"llm\"\n /** `mode=\"llm\"` 时的模型服务配置;`advanced` 打开输入框上方的「高级设置」。 */\n llm?: LlmChatOptions & {\n advanced?: boolean | LlmAdvancedSettings\n onOverrideChange?: (override: LlmOverride) => void\n }\n /** `mode=\"llm\"` 时拿到操作句柄;智能体模式请用 `onSessionReady`。 */\n onLlmReady?: (handle: LlmChatHandle) => void\n}\n\n/**\n * 聊天界面。默认接 Blade 智能体会话,`mode=\"llm\"` 时改接标准 OpenAI 对话接口。\n *\n * 这一层只做分流:两种模式各自是独立组件,已经确定要哪一种的可以直接用\n * `<AgentChat>` 或 `<LlmChat>`。\n */\nexport function ChatView(props: ChatViewProps) {\n // 参数保持单个 props、在函数体里解构:公共面报告会把参数列表原样印出来,\n // 在参数位置解构会让签名行发生变化,而这一行是接入方看兼容性的依据。\n const { mode, llm, onLlmReady, ...rest } = props\n if (mode === \"llm\") {\n if (!llm) {\n throw new Error('ChatView: mode=\"llm\" 需要同时传 llm={{ baseURL, model }}')\n }\n return (\n <LlmChat\n {...llm}\n classNames={rest.classNames}\n renderers={rest.renderers}\n slots={rest.slots}\n placeholder={rest.placeholder}\n theme={rest.theme}\n onReady={onLlmReady}\n />\n )\n }\n return <AgentChat {...rest} />\n}\n","import {\n getContextDisplayState,\n getContextGroupDisplayState,\n type ContextProjectionData,\n} from \"@blade-hq/agent-client\"\nimport { ChevronDown, Layers3 } from \"lucide-react\"\n\nexport interface ContextCardProps {\n context: ContextProjectionData\n className?: string\n}\n\n/** Business-facing Context summary. Native details keeps every card collapsed by default. */\nexport function ContextCard({ context, className }: ContextCardProps) {\n const display = getContextDisplayState(context)\n return (\n <details\n className={`blade-chat-context-card group/context-card rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? \"\"}`}\n >\n <summary className=\"blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden\">\n <Layers3\n size={15}\n className=\"blade-chat-context-icon shrink-0 text-[hsl(var(--muted-foreground))]\"\n aria-hidden=\"true\"\n />\n <span className=\"blade-chat-context-copy min-w-0 flex-1\">\n <span className=\"blade-chat-context-title block font-medium text-[hsl(var(--foreground))]\">\n {display.title}\n </span>\n <span className=\"blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]\">\n {display.summary}\n </span>\n </span>\n <ChevronDown\n size={14}\n className=\"blade-chat-context-chevron shrink-0 text-[hsl(var(--muted-foreground))] transition-transform group-open/context-card:rotate-180\"\n aria-hidden=\"true\"\n />\n </summary>\n <div className=\"blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]\">\n {display.detail}\n </div>\n </details>\n )\n}\n\nexport interface ContextGroupCardProps {\n contexts: readonly ContextProjectionData[]\n className?: string\n}\n\n/**\n * 一批上下文注入折叠成一行。会话首轮会一次注入十几个原子,一条一张卡片会把\n * 真正的对话挤出屏幕。\n *\n * 单条和多条**共用同一个 details 外壳**,不在数量变化时切换组件:流式时第二条\n * 到达会让这一行从单条变成多条,组件类型一换 React 就重挂 <details>,\n * 用户刚展开的行会被收起来。同理,key 由调用方按组内第一条给,不能带数量。\n */\nexport function ContextGroupCard({ contexts, className }: ContextGroupCardProps) {\n if (contexts.length === 0) return null\n const single = contexts.length === 1 ? getContextDisplayState(contexts[0]) : null\n const group = single ? null : getContextGroupDisplayState(contexts)\n return (\n <details className={`blade-chat-context-card group/context-group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? \"\"}`}>\n <summary className=\"blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden\">\n <Layers3\n size={15}\n className=\"blade-chat-context-icon shrink-0 text-[hsl(var(--muted-foreground))]\"\n aria-hidden=\"true\"\n />\n <span className=\"blade-chat-context-copy min-w-0 flex-1\">\n <span className=\"blade-chat-context-title block font-medium text-[hsl(var(--foreground))]\">\n {single ? single.title : `${group?.title} · ${group?.count} 项`}\n </span>\n <span className=\"blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]\">\n {single ? single.summary : group?.summary}\n </span>\n </span>\n <ChevronDown\n size={14}\n className=\"blade-chat-context-chevron shrink-0 text-[hsl(var(--muted-foreground))] transition-transform group-open/context-group:rotate-180\"\n aria-hidden=\"true\"\n />\n </summary>\n {single ? (\n <div className=\"blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]\">\n {single.detail}\n </div>\n ) : (\n <div className=\"blade-chat-context-group-items flex flex-col gap-1.5 border-t border-[hsl(var(--border))] p-2\">\n {contexts.map((context) => (\n <ContextCard\n key={`${context.context_kind}:${context.context_key}`}\n context={context}\n />\n ))}\n </div>\n )}\n </details>\n )\n}\n","import type { BladeClient } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore } from \"react\"\nimport { useOptionalBladeClient } from \"../context\"\nimport { cn } from \"../lib/utils\"\n\ninterface SaveState {\n saving: boolean\n listeners: Set<() => void>\n}\n\nconst saveStates = new WeakMap<BladeClient, Map<string, SaveState>>()\n\nfunction getSaveState(client: BladeClient, sessionId: string): SaveState {\n let clientStates = saveStates.get(client)\n if (!clientStates) {\n clientStates = new Map()\n saveStates.set(client, clientStates)\n }\n let state = clientStates.get(sessionId)\n if (!state) {\n state = { saving: false, listeners: new Set() }\n clientStates.set(sessionId, state)\n }\n return state\n}\n\nfunction notify(state: SaveState) {\n for (const listener of state.listeners) listener()\n}\n\nfunction cleanupSaveState(client: BladeClient, sessionId: string, state: SaveState) {\n if (state.saving || state.listeners.size > 0) return\n const clientStates = saveStates.get(client)\n if (clientStates?.get(sessionId) === state) clientStates.delete(sessionId)\n}\n\nexport interface SessionMemoryToggleProps {\n sessionId: string\n enabled: boolean\n /** 不在 BladeProvider 内使用时显式传入,与宿主现有 BladeClient 复用连接和鉴权。 */\n client?: BladeClient\n disabled?: boolean\n label?: string\n className?: string\n labelClassName?: string\n inputClassName?: string\n onSaved?: (sessionId: string, enabled: boolean) => void\n onError?: (error: unknown) => void\n}\n\n/**\n * 当前会话记忆开关。同一 client + sessionId 的所有实例共享保存锁,避免多个聊天入口\n * 或会话切换时发出乱序更新;服务端成功后才通过 onSaved 交还权威值。\n */\nexport function SessionMemoryToggle({\n sessionId,\n enabled,\n client: clientProp,\n disabled = false,\n label = \"当前会话使用记忆\",\n className,\n labelClassName,\n inputClassName,\n onSaved,\n onError,\n}: SessionMemoryToggleProps) {\n const contextClient = useOptionalBladeClient()\n const client = clientProp ?? contextClient\n if (!client) {\n throw new Error(\"SessionMemoryToggle 必须在 <BladeProvider> 内使用或显式传入 client\")\n }\n\n const saveState = getSaveState(client, sessionId)\n const subscribe = useCallback(\n (listener: () => void) => {\n saveState.listeners.add(listener)\n return () => {\n saveState.listeners.delete(listener)\n cleanupSaveState(client, sessionId, saveState)\n }\n },\n [client, saveState, sessionId],\n )\n const getSaving = useCallback(() => saveState.saving, [saveState])\n const saving = useSyncExternalStore(\n subscribe,\n getSaving,\n getSaving,\n )\n const [draftEnabled, setDraftEnabled] = useState(enabled)\n const activeSessionIdRef = useRef(sessionId)\n activeSessionIdRef.current = sessionId\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new session must discard this component instance's optimistic value even when both sessions share the same enabled prop\n useEffect(() => {\n setDraftEnabled(enabled)\n }, [enabled, sessionId])\n\n const update = useCallback(\n (nextEnabled: boolean) => {\n const currentSaveState = getSaveState(client, sessionId)\n if (currentSaveState.saving) return\n currentSaveState.saving = true\n notify(currentSaveState)\n setDraftEnabled(nextEnabled)\n\n void client.sessions.updateSessionMemory(sessionId, nextEnabled).then(\n (updated) => {\n if (activeSessionIdRef.current === sessionId) {\n setDraftEnabled(updated.memory_enabled)\n }\n onSaved?.(sessionId, updated.memory_enabled)\n },\n (error) => {\n if (activeSessionIdRef.current === sessionId) setDraftEnabled(enabled)\n onError?.(error)\n },\n ).finally(() => {\n currentSaveState.saving = false\n notify(currentSaveState)\n cleanupSaveState(client, sessionId, currentSaveState)\n })\n },\n [client, enabled, onError, onSaved, sessionId],\n )\n\n return (\n <label className={cn(\"flex items-center justify-between\", className)}>\n <span className={labelClassName}>{label}</span>\n <input\n type=\"checkbox\"\n checked={draftEnabled}\n onChange={(event) => update(event.target.checked)}\n disabled={disabled || saving}\n className={inputClassName}\n />\n </label>\n )\n}\n","/** 智能体是否在沙盒里启动了通用应用镜像(computer launch)。\n *\n * 只认「命令位置」上的 `computer launch`(行首 / `;` / `&&` / `|` / 换行 / 子 shell\n * 之后),避免路径、参数里恰好含 computer 的误判。\n *\n * 只认 `launch` 子命令,不认 `status`/`screenshot`/`click` 等——判据是「agent 启动过\n * 一个目标」,不是「agent 调用过任意 computer 子命令」。\n *\n * 与 apps/web/src/agent/lib/agent-computer-command.ts 保持同名同签名(判定逻辑不许\n * 各写一份,见仓库 CLAUDE.md),从 src/index.ts 导出给 SDK 接入方直接用。\n *\n * 注意:`isAgentComputerCommand`/`isAgentComputerToolCall` 只回答\"这条命令是不是\n * 一次 launch 尝试\",不回答\"这次 launch 最终成不成功\"——待处理/已失败的 launch\n * 也会命中这两个判定。SDK 接入方如果只用这两个函数就决定要不要展示自己的「电脑\n * 镜像」UI,会在启动失败/还没跑完时也展示出来,跟内置 Web 的实际行为不一致\n * (Codex review 指出:内置 Web 用下面的 `classifyAgentComputerLaunchOutcome`\n * 做完整判定,SDK 侧之前只导出了弱判据,等于把这道判断题的一半留给接入方自己\n * 猜)。下面把完整判定也一并导出,接入方要自己实现「电脑镜像」面板时不用重新\n * 猜一遍这条规则。\n */\nconst COMPUTER_LAUNCH_COMMAND_PATTERN = /(?:^|[\\n;&|(]\\s*)computer\\s+launch(?:\\s|$)/\n\nexport function isAgentComputerCommand(command: string): boolean {\n return COMPUTER_LAUNCH_COMMAND_PATTERN.test(command)\n}\n\n/** 工具调用的 JSON 参数里是不是一条 computer launch 命令。 */\nexport function isAgentComputerToolCall(argumentsJson: string | null | undefined): boolean {\n if (!argumentsJson) return false\n let command: unknown\n try {\n const parsed: unknown = JSON.parse(argumentsJson)\n if (typeof parsed !== \"object\" || parsed === null) return false\n command = (parsed as { command?: unknown }).command\n } catch {\n return false\n }\n return typeof command === \"string\" && isAgentComputerCommand(command)\n}\n\nexport type AgentComputerLaunchOutcome = \"pending\" | \"succeeded\" | \"failed\" | \"unknown\"\n\n// computer launch 自己成功时唯一打印的这行文本(见 appmirrord/cmd/computer/\n// launch.go 的 cmdLaunch),不会被任何其它 computer 子命令打印。判定\"launch\n// 是不是真的成功了\"首选看这个标记有没有出现在输出里,而不是看整条 Bash\n// 命令的最终退出码——同一次 Bash 调用可能是 `computer launch ... && computer\n// screenshot` 这种复合命令,launch 自己成功、后面的 screenshot 失败时,整条\n// 命令的退出码是非零,但 launch 明明是成功的。这个标记不受复合命令后半段成\n// 不成功的影响,直接对应 launch 自己有没有跑到成功打印那一步。\n//\n// 与 apps/web/src/agent/lib/agent-computer-command.ts 保持同名同签名——两边\n// 内容目前完全一致,改一处记得同步另一处。\nconst LAUNCH_SUCCESS_MARKER = \"已启动 \"\n\nfunction resultContainsLaunchSuccessMarker(result: unknown, depth = 0): boolean {\n if (depth > 2) return false // 防止极端嵌套结构把这段扫描拖成性能问题,正常数据不会嵌这么深\n if (typeof result === \"string\") {\n if (result.includes(LAUNCH_SUCCESS_MARKER)) return true\n try {\n return resultContainsLaunchSuccessMarker(JSON.parse(result), depth + 1)\n } catch {\n return false\n }\n }\n if (typeof result === \"object\" && result !== null) {\n for (const value of Object.values(result as Record<string, unknown>)) {\n if (typeof value === \"string\" && value.includes(LAUNCH_SUCCESS_MARKER)) return true\n }\n }\n return false\n}\n\n/** `computer launch` 这条 Bash 工具调用最终是不是真的启动成功了——不能只看\n * `isAgentComputerToolCall` 命中参数就当作成功:可执行文件缺失/不可执行、\n * 启动后随即退出这些 appmirrord 侧的失败,Bash 工具调用本身仍然会走完\n * `status: \"done\"`(Bash 工具的职责是\"跑完这条命令\",命令自己返回非零退出码\n * 不算工具调用失败)。\n *\n * 唯一的判据是输出里有没有出现 `LAUNCH_SUCCESS_MARKER`——launch.go 的\n * cmdLaunch 只要走到成功分支就一定会打印这行文本,不存在\"成功了但没打印\"\n * 的路径。标记缺失就等价于没有走到那条成功路径,不再退回看 `result.exit_code`\n * 当兜底:那样会给 `computer launch ... || echo failed` 这类复合命令开口子,\n * 让真实失败的 launch 被误判成 succeeded。\n *\n * \"pending\":还没跑完(status 既不是终态失败也不是 done),调用方不该现在\n * 就下结论,也不该把这次调用标记成\"已处理\"——等下一次状态更新再重新判断。\n *\n * \"unknown\":`status: \"done\"` 但 `result` 本身缺失(不是\"有 result、没有标记\",\n * 是根本没有 result 这个字段)——历史会话被整理(compaction)之后,归档的\n * 工具调用只保留 `arguments`/`status` 这类摘要字段,完整 `result` payload 不\n * 会重新序列化进历史投影,这是已知且刻意的压缩形状,不是数据丢失异常。这种\n * 情况下\"没看到成功标记\"不能说明\"这次 launch 失败了\",只能说明\"证据被裁剪\n * 了\"——跟\"result 确实回来了、里面就是没有这行文本\"是两种不同性质的证据,\n * 前者不能定性为 failed,接入方要按自己的场景(标签页展示可以偏宽松,自动\n * 跳转/替用户做决定的场景应该偏保守)自己决定怎么用。\n *\n * 与 apps/web/src/agent/lib/agent-computer-command.ts 保持同名同签名——两边\n * 内容目前完全一致,改一处记得同步另一处。 */\nexport function classifyAgentComputerLaunchOutcome(toolCall: {\n status?: string\n result?: unknown\n}): AgentComputerLaunchOutcome {\n if (toolCall.status === \"error\" || toolCall.status === \"cancelled\") return \"failed\"\n if (toolCall.status !== \"done\") return \"pending\"\n if (toolCall.result === undefined || toolCall.result === null) return \"unknown\"\n return resultContainsLaunchSuccessMarker(toolCall.result) ? \"succeeded\" : \"failed\"\n}\n","// ============================== React 绑定 ==============================\nexport { BladeProvider, useBladeClient } from \"./context\"\nexport type { BladeProviderProps } from \"./context\"\nexport { useAgentSession } from \"./hooks/use-agent-session\"\nexport type { UseAgentSessionOptions, UseAgentSessionResult } from \"./hooks/use-agent-session\"\nexport { useReplay } from \"./hooks/use-replay\"\nexport type { ReplayMismatch, UseReplayResult } from \"./hooks/use-replay\"\nexport { useLlmChat } from \"./hooks/use-llm-chat\"\nexport { useMessagePin } from \"./hooks/use-message-pin\"\nexport type { UseMessagePinOptions } from \"./hooks/use-message-pin\"\nexport type {\n ChatCompletionTool,\n LlmChatOptions,\n LlmToolCall,\n UseLlmChatResult,\n} from \"./hooks/use-llm-chat\"\n\n// ============================== 聊天界面 ==============================\nexport { ChatView } from \"./components/ChatView\"\nexport type {\n ChatViewClassNames,\n ChatViewProps,\n ChatViewRenderers,\n ChatViewSlots,\n FollowupInteractionEvent,\n} from \"./components/ChatView\"\n// 下层组件:已经确定要哪种模式的可以直接用,不必经 ChatView 分流\nexport { AgentChat } from \"./components/AgentChat\"\nexport type { AgentChatProps } from \"./components/AgentChat\"\nexport { LlmChat } from \"./components/LlmChat\"\nexport type { LlmChatHandle, LlmChatProps } from \"./components/LlmChat\"\nexport type { LlmAdvancedSettings, LlmOverride } from \"./components/LlmAdvancedSettings\"\nexport { MarkdownContent } from \"./components/MarkdownContent\"\nexport { normalizeAdjacentUrlFormatting } from \"./components/MarkdownContent\"\nexport type { MarkdownContentProps } from \"./components/MarkdownContent\"\nexport { WhatIfUserBubble } from \"./components/WhatIfUserBubble\"\nexport type { WhatIfUserBubbleProps } from \"./components/WhatIfUserBubble\"\nexport { parseWhatIfPrompt } from \"./lib/whatif-prompt\"\nexport type { ParsedWhatIfPrompt, WhatIfQuote } from \"./lib/whatif-prompt\"\nexport { ReplayBar } from \"./components/ReplayBar\"\nexport type { ReplayBarProps } from \"./components/ReplayBar\"\nexport { ReplayMismatchPrompt } from \"./components/ReplayMismatchPrompt\"\nexport type { ReplayMismatchPromptProps } from \"./components/ReplayMismatchPrompt\"\nexport type { ToolCallRenderer } from \"./components/ToolCallBlock\"\nexport {\n CurrentPlanPanel,\n getPlanUpdateDisplayState,\n isPlanUpdateTool,\n PLAN_AUTO_COLLAPSE_MS,\n parsePlanUpdate,\n pickCurrentPlanStep,\n PlanUpdateBlock,\n} from \"./components/PlanUpdateBlock\"\nexport type {\n PlanStepStatus,\n PlanUpdateData,\n PlanUpdateDisplayState,\n} from \"./components/PlanUpdateBlock\"\nexport { ContextCard, ContextGroupCard } from \"./components/ContextCard\"\nexport type { ContextCardProps, ContextGroupCardProps } from \"./components/ContextCard\"\nexport { collectMemoryRefs, MemoryRefsHint } from \"./components/AssistantTurnBlock\"\nexport { SessionMemoryToggle } from \"./components/SessionMemoryToggle\"\nexport type { SessionMemoryToggleProps } from \"./components/SessionMemoryToggle\"\n\n// ============================== 判定逻辑(不许各写一份) ==============================\n// apps/web 已开始按能力逐步消费这个包(见仓库 CLAUDE.md「聊天界面的能力先进\n// SDK」一节),但两端 UI 尚未整体收敛。这里先把电脑镜像的判定规则导出,让它可以被\n// SDK 接入方真正拿到、而不是完全不可达的死代码;接入方要自己实现「电脑镜像」\n// 标签页 UI 时可以直接复用这份判定,不用重新猜一遍规则。\n//\n// `isAgentComputerCommand`/`isAgentComputerToolCall` 只回答\"是不是一次 launch\n// 尝试\",`classifyAgentComputerLaunchOutcome` 才回答\"这次 launch 最终成不成功\"\n// ——只导出前者会让接入方把\"尝试过\"当成\"已经展示\",对失败/待定的 launch 也\n// 展示出电脑镜像面板,跟内置 Web 的实际行为不一致(Codex review 指出),所以\n// 完整判定也要一并导出。\nexport {\n isAgentComputerCommand,\n isAgentComputerToolCall,\n classifyAgentComputerLaunchOutcome,\n} from \"./lib/agent-computer-command\"\nexport type { AgentComputerLaunchOutcome } from \"./lib/agent-computer-command\"\n\n// ============================== 客户端全量转出 ==============================\n// 方便接入方只装一个包:agent-client 的全部公共面从这里原样可用。\nexport * from \"@blade-hq/agent-client\"\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","useCallback","useMemo","useRef","useState","useCallback","useEffect","useRef","Component","forwardRef","createElement","useCallback","useEffect","useMemo","useState","jsx","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","chatErrorForDisplay","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useCallback","useEffect","useMemo","useRef","useState","useCallback","useMemo","useRef","useState","isAtBottom","escapedFromLock","createContext","useContext","useEffect","useMemo","useRef","createContext","useEffect","useRef","useMemo","targetScrollTop","StickToBottom","useContext","useEffect","useRef","useState","useState","jsx","jsxs","useState","useEffect","useMemo","useRef","useState","jsx","jsxs","useRef","useState","useEffect","useMemo","jsx","useState","useEffect","useMemo","useRef","useState","jsx","jsxs","useRef","useEffect","useState","useMemo","text","Fragment","jsx","jsxs","useState","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","getFileParts","getImageParts","getTextContent","jsx","jsxs","jsx","jsxs","getTextContent","getFileParts","getImageParts","jsx","jsxs","useMemo","useRef","useCallback","useEffect","useState","jsx","jsxs","chatErrorForDisplay","Fragment","jsx","jsxs","useState","useCallback","useEffect","useMemo","useEffect","useMemo","useState","useState","jsx","jsxs","jsx","useState","useMemo","useEffect","jsx","jsx","jsxs","useCallback","useEffect","useRef","useState","useSyncExternalStore","jsx","jsxs","useCallback","useSyncExternalStore","useState","useRef","useEffect"]}
|