@blade-hq/agent-react 2612.0.0-beta.77 → 2612.0.0-beta.78

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.
Files changed (56) hide show
  1. package/README.md +12 -2
  2. package/dist/SessionPluginConfigForm-7LJ5SUNY.js +9 -0
  3. package/dist/SessionPluginConfigForm-7LJ5SUNY.js.map +1 -0
  4. package/dist/chat/LiteMessageList.d.ts +4 -0
  5. package/dist/{chunk-IVGTMWRV.js → chunk-66JXSDLB.js} +284 -289
  6. package/dist/chunk-66JXSDLB.js.map +1 -0
  7. package/dist/{chunk-VBHHFIII.js → chunk-C3LBNHVM.js} +12 -10
  8. package/dist/{chunk-VBHHFIII.js.map → chunk-C3LBNHVM.js.map} +1 -1
  9. package/dist/chunk-F4MWGSKX.js +1308 -0
  10. package/dist/chunk-F4MWGSKX.js.map +1 -0
  11. package/dist/{chunk-ZIECHNZA.js → chunk-FBGCDXRK.js} +110 -115
  12. package/dist/chunk-FBGCDXRK.js.map +1 -0
  13. package/dist/{chunk-26NE55SJ.js → chunk-FDB3J5QF.js} +8 -6
  14. package/dist/{chunk-26NE55SJ.js.map → chunk-FDB3J5QF.js.map} +1 -1
  15. package/dist/{chunk-WO2VCQ5N.js → chunk-FH5HA3GW.js} +551 -455
  16. package/dist/chunk-FH5HA3GW.js.map +1 -0
  17. package/dist/chunk-KHJEIHMJ.js +973 -0
  18. package/dist/chunk-KHJEIHMJ.js.map +1 -0
  19. package/dist/{chunk-XBEN2ASY.js → chunk-MWILWSPG.js} +4262 -3186
  20. package/dist/chunk-MWILWSPG.js.map +1 -0
  21. package/dist/{chunk-EQOKPZI2.js → chunk-NRYLAO3X.js} +87 -154
  22. package/dist/chunk-NRYLAO3X.js.map +1 -0
  23. package/dist/{chunk-F2IJZX2Z.js → chunk-SK43YNRX.js} +151 -1442
  24. package/dist/chunk-SK43YNRX.js.map +1 -0
  25. package/dist/components/BrowserConnectionDialog.d.ts +3 -2
  26. package/dist/components/ConfigConnection.d.ts +16 -0
  27. package/dist/components/ConnectorDetail.d.ts +46 -0
  28. package/dist/components/ConnectorRulesPanel.d.ts +2 -1
  29. package/dist/components/ConnectorRuntimeDiagnostics.d.ts +2 -2
  30. package/dist/components/DeviceConnection.d.ts +3 -1
  31. package/dist/components/FeishuPermissionStatus.d.ts +3 -1
  32. package/dist/components/ManagedConnection.d.ts +5 -2
  33. package/dist/components/PackEventNotice.d.ts +3 -0
  34. package/dist/components/SessionPluginConfigForm.d.ts +2 -1
  35. package/dist/connectors.d.ts +3 -0
  36. package/dist/connectors.js +7 -2
  37. package/dist/embed/entry.d.ts +4 -0
  38. package/dist/index.d.ts +3 -0
  39. package/dist/index.js +13 -7
  40. package/dist/internal.js +16 -9
  41. package/dist/internal.js.map +1 -1
  42. package/dist/questions.js +8 -6
  43. package/dist/questions.js.map +1 -1
  44. package/dist/rich-input.js +16 -13
  45. package/dist/rich-input.js.map +1 -1
  46. package/dist/style.full.css +1 -1
  47. package/dist/tool-details.js +5 -4
  48. package/dist/tool-details.js.map +1 -1
  49. package/package.json +2 -2
  50. package/public-api.md +41 -3
  51. package/dist/chunk-EQOKPZI2.js.map +0 -1
  52. package/dist/chunk-F2IJZX2Z.js.map +0 -1
  53. package/dist/chunk-IVGTMWRV.js.map +0 -1
  54. package/dist/chunk-WO2VCQ5N.js.map +0 -1
  55. package/dist/chunk-XBEN2ASY.js.map +0 -1
  56. package/dist/chunk-ZIECHNZA.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chat/runtime/input-history-storage.ts","../src/chat/runtime/input-cache-reset.ts","../src/rich-input/lib/plugin-suggestions.ts","../src/chat/runtime/input-services.tsx","../src/rich-input/lib/client-query.ts","../src/rich-input/lib/session-data.ts","../src/rich-input/model/model-data.ts","../src/rich-input/services.tsx","../src/components/SessionPluginIcon.tsx","../src/components/SessionPluginConfigDialog.tsx","../src/components/PluginConnectorList.tsx","../src/components/plugin-connector.ts","../src/components/use-session-plugin-activation.ts","../src/lib/message-shortcuts.ts","../src/chat/runtime/actions.ts","../src/chat/chat-theme.tsx","../src/lib/use-effect-event.ts"],"sourcesContent":["let generation = 0\n\nexport function getInputHistoryGeneration(): number {\n return generation\n}\n\nexport function resetInputHistoryStorage(): void {\n generation += 1\n if (typeof window === \"undefined\") return\n try {\n const keys = Object.keys(window.sessionStorage)\n for (const key of keys) {\n if (key.startsWith(\"input-history:\")) window.sessionStorage.removeItem(key)\n }\n } catch {\n }\n}\n","import type { BladeClient } from \"@blade-hq/agent-client\"\nimport { resetInputHistoryStorage } from \"./input-history-storage\"\n\nconst resetters = new WeakMap<BladeClient, Set<() => void>>()\n\nexport function onInputCachesReset(client: BladeClient, reset: () => void): () => void {\n let set = resetters.get(client)\n if (!set) {\n set = new Set()\n resetters.set(client, set)\n }\n set.add(reset)\n return () => { set.delete(reset) }\n}\n\nexport function resetInputCaches(client: BladeClient): void {\n resetInputHistoryStorage()\n for (const reset of resetters.get(client) ?? []) reset()\n}\n","import type { BladeClient, MessageRef } from \"@blade-hq/agent-client\"\nimport { onInputCachesReset } from \"../../chat/runtime/input-cache-reset\"\nimport type { PluginSuggestionItem, PluginSuggestionError } from \"../extensions/plugin-mention\"\n\nconst accountPreparation = new WeakMap<BladeClient, { token: string | null | undefined; ready: Promise<unknown> }>()\nconst registeredClients = new WeakSet<BladeClient>()\n\nexport function pluginRefsForSend(refs: MessageRef[], turns: readonly { role: string; refs?: MessageRef[] | null }[] = []): MessageRef[] | undefined {\n const latest = [...turns].reverse().find((turn) => turn.role === \"user\")\n return refs.length || latest?.refs?.length ? refs : undefined\n}\n\nfunction prepareAccount(client: BladeClient): Promise<unknown> {\n if (!registeredClients.has(client)) {\n registeredClients.add(client)\n onInputCachesReset(client, () => accountPreparation.delete(client))\n }\n const current = accountPreparation.get(client)\n if (current && current.token === client.token) return current.ready\n const ready = client.plugins.catalog().catch((error) => {\n accountPreparation.delete(client)\n throw error\n })\n accountPreparation.set(client, { token: client.token, ready })\n return ready\n}\n\nexport async function getPluginSuggestionItems(client: BladeClient, session: string | null | undefined, query: string, pendingPlugins: readonly string[] = []): Promise<Array<PluginSuggestionItem | PluginSuggestionError>> {\n if (!session && pendingPlugins.length === 0) return []\n const path = session ? `/api/sessions/${encodeURIComponent(session)}/plugin-tools/mentions` : \"/api/plugin-mentions/mentions\"\n try {\n if (!session) await prepareAccount(client)\n const data = await client.json<{ items: Omit<PluginSuggestionItem, \"kind\" | \"name\">[]; errors: string[] }>(\"POST\", path, { arguments: { query, cursor: \"\" }, ...(!session ? { plugin_selection: pendingPlugins } : {}) })\n return [...data.items.map((item) => ({ ...item, kind: \"plugin\" as const, name: item.label })), ...data.errors.map((message) => ({ kind: \"pluginError\" as const, message }))]\n } catch { return [{ kind: \"pluginError\", message: \"插件条目暂时无法加载,请重新输入 @ 重试\" }] }\n}\n","/**\n * 富输入(`@blade-hq/agent-react/rich-input`)可替换的数据来源与宿主部件。富输入对每一项都有基于\n * BladeClient 的默认实现(插件侧栏直接用默认);内置 Web 只在要和自己的 React Query 缓存、\n * 全局 store 打通的地方覆盖,或补上只有它有的部件(远程电脑、外部账号)。\n *\n * 这个上下文放在 internal 入口:内置 Web 在应用根部提供它,不必为此引入富输入的依赖。\n * hook 形式的注入点必须是稳定引用(模块常量),输入框无条件调用。\n */\nimport type { BackgroundTask, ConnectionStatus, SessionContextStats } from \"@blade-hq/agent-client/internal\"\nimport { type ComponentType, createContext, type ReactNode, useContext } from \"react\"\n\n/** 模型目录(`GET /api/config/models`)。 */\nexport interface InputModelOption {\n id: string\n label: string\n inputModalities?: string[]\n supportsImage?: boolean\n thinkingAvailable?: boolean\n reasoningLevels?: string[]\n defaultReasoningLevel?: string\n}\n\nexport interface InputModelCatalog {\n models: InputModelOption[]\n defaultModel: string\n thinkingAvailable: boolean\n unavailableProviderIds: string[]\n unavailableModelIds: string[]\n isLoading: boolean\n isFetching: boolean\n refresh: () => Promise<void>\n}\n\nexport interface InputModelPreferences {\n /** 用户上次选的模型(新建会话时优先用)。 */\n preferredModel: string | null\n setPreferredModel: (modelId: string) => void\n /** 各模型上次选的推理强度(模型 id → 档位)。 */\n reasoningLevels: Record<string, string>\n setReasoningLevel: (modelId: string, level: string) => void\n}\n\n/** 偏好里存的是 JSON 字符串(模型 id → 档位);坏数据当作没有。 */\nexport function parseModelReasoningLevels(value: string | null | undefined): Record<string, string> {\n if (!value) return {}\n try {\n const parsed: unknown = JSON.parse(value)\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return {}\n return Object.fromEntries(\n Object.entries(parsed).filter(\n (entry): entry is [string, string] => Boolean(entry[0]) && typeof entry[1] === \"string\" && Boolean(entry[1]),\n ),\n )\n } catch {\n return {}\n }\n}\n\nexport interface InputSessionSkill {\n skill_id: string\n display_name?: string | null\n description: string\n local: boolean\n builtin?: boolean\n}\n\nexport interface InputSkillStats {\n loaded_skill_count: number\n loaded_skill_names: string[]\n remote_configured: boolean\n remote_connected: boolean\n installed_skill_count: number\n}\n\nexport interface InputConnection {\n status: ConnectionStatus\n /** 正在第几次重连(0 表示没有在重连)。 */\n reconnectAttempt: number\n hasEverConnected: boolean\n}\n\nexport interface InputDataState<T> {\n data: T | undefined\n loading: boolean\n error: unknown\n}\n\nexport interface ConnectorComputerSectionProps {\n sessionId: string | null\n query: string\n excludedSelection?: string[]\n onExcludedSelectionChange?: (ids: string[]) => void\n /** 收起连接器面板(打开设置之类的动作前调用)。 */\n closePanel: () => void\n}\n\nexport interface ConnectorPhoneSectionProps {\n sessionId: string | null\n query: string\n pendingSelection?: string[]\n onPendingSelectionChange?: (ids: string[]) => void\n /** 收起连接器面板(打开设置之类的动作前调用)。 */\n closePanel: () => void\n}\n\nexport interface RichInputServices {\n useModelCatalog?: () => InputModelCatalog\n useModelPreferences?: () => InputModelPreferences\n /** 知识库清单(`@` 菜单打开时才调用)。 */\n /** 语音是否可用(部署可以关掉);默认不可用,不显示麦克风。 */\n voiceEnabled?: boolean\n /** 语音的 AudioWorklet 脚本地址。 */\n voiceWorkletUrl?: string\n /** 连接状态;默认读当前会话的连接状态(没有会话时视为已连接)。 */\n useConnection?: (sessionId: string | null) => InputConnection\n useSessionSkills?: (sessionId: string | null) => InputDataState<InputSessionSkill[]>\n useSkillStats?: (sessionId: string | null) => InputDataState<InputSkillStats>\n useContextStats?: (sessionId: string | null) => InputDataState<SessionContextStats>\n useBackgroundTasks?: (sessionId: string | null) => InputDataState<BackgroundTask[]>\n /** 宿主在这些变化后刷新自己的缓存(文件树、技能状态)。 */\n useSessionDataInvalidation?: () => {\n filesUploaded: (sessionId: string) => void\n pluginsChanged: (sessionId: string) => void\n }\n /** 连接器面板里的「电脑」分区;不提供时不显示。 */\n ConnectorComputerSection?: ComponentType<ConnectorComputerSectionProps>\n /** 连接器面板里的「手机」分区,渲染在「电脑」下面;不提供时不显示。 */\n ConnectorPhoneSection?: ComponentType<ConnectorPhoneSectionProps>\n /** 连接器面板里的「外部账号」分区;不提供时不显示。 */\n ConnectorAccountsSection?: ComponentType<{ query: string; closePanel: () => void }>\n}\n\nconst RichInputServicesContext = createContext<RichInputServices>({})\n\nexport function RichInputServicesProvider({ value, children }: { value: RichInputServices; children: ReactNode }) {\n return <RichInputServicesContext.Provider value={value}>{children}</RichInputServicesContext.Provider>\n}\n\nexport function useRichInputServices(): RichInputServices {\n return useContext(RichInputServicesContext)\n}\n","/**\n * 富输入默认实现用的小型请求缓存(按 BladeClient 隔离):同一个键在有效期内只请求一次,\n * 并发请求合并成一个,多个组件共享结果。内置 Web 在需要和 React Query 缓存打通的地方\n * 用自己的实现覆盖(见 `RichInputServices`),插件等其他宿主直接用这里。\n */\nimport type { BladeClient } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useSyncExternalStore } from \"react\"\nimport { onInputCachesReset } from \"../../chat/runtime/input-cache-reset\"\nimport { useBladeClient } from \"../../context\"\n\nexport interface ClientQueryState<T> {\n data: T | undefined\n error: unknown\n /** 还没有任何结果(首次加载中)。 */\n isLoading: boolean\n /** 正在请求(含已有结果时的刷新)。 */\n isFetching: boolean\n}\n\ninterface Entry<T> {\n snapshot: ClientQueryState<T>\n fetchedAt: number\n request: Promise<T> | null\n listeners: Set<() => void>\n}\n\nconst EMPTY: ClientQueryState<never> = { data: undefined, error: null, isLoading: false, isFetching: false }\nconst caches = new WeakMap<BladeClient, Map<string, Entry<unknown>>>()\n\nfunction entryFor<T>(client: BladeClient, key: string): Entry<T> {\n let cache = caches.get(client)\n if (!cache) {\n const created = new Map<string, Entry<unknown>>()\n cache = created\n caches.set(client, created)\n onInputCachesReset(client, () => {\n for (const [key, entry] of created) {\n entry.request = null\n entry.fetchedAt = 0\n update(entry, EMPTY)\n const load = loadersFor(client).get(key)\n if (load && entry.listeners.size > 0) void fetchClientQuery(client, key, load, { force: true }).catch(() => {})\n }\n })\n }\n let entry = cache.get(key) as Entry<T> | undefined\n if (!entry) {\n entry = { snapshot: EMPTY, fetchedAt: 0, request: null, listeners: new Set() }\n cache.set(key, entry as Entry<unknown>)\n }\n return entry\n}\n\nfunction update<T>(entry: Entry<T>, patch: Partial<ClientQueryState<T>>) {\n entry.snapshot = { ...entry.snapshot, ...patch }\n for (const listener of [...entry.listeners]) listener()\n}\n\n/** 请求(有效期内且已有结果时直接复用);失败不留结果,下次重试。 */\nexport function fetchClientQuery<T>(\n client: BladeClient,\n key: string,\n load: (client: BladeClient) => Promise<T>,\n { staleMs = 30_000, force = false }: { staleMs?: number; force?: boolean } = {},\n): Promise<T> {\n const entry = entryFor<T>(client, key)\n if (entry.request) return entry.request\n if (!force && entry.snapshot.data !== undefined && Date.now() - entry.fetchedAt < staleMs) {\n return Promise.resolve(entry.snapshot.data)\n }\n const request = Promise.resolve().then(() => load(client))\n entry.request = request\n update(entry, { isFetching: true, isLoading: entry.snapshot.data === undefined })\n request.then(\n (data) => {\n if (entry.request !== request) return\n entry.request = null\n entry.fetchedAt = Date.now()\n update(entry, { data, error: null, isFetching: false, isLoading: false })\n },\n (error: unknown) => {\n if (entry.request !== request) return\n entry.request = null\n update(entry, { error, isFetching: false, isLoading: false })\n },\n )\n return request\n}\n\n/** 直接改缓存里的值(乐观更新);还没有结果时不凭空造一份。 */\nexport function setClientQueryData<T>(client: BladeClient, key: string, updater: (current: T) => T): void {\n const entry = entryFor<T>(client, key)\n if (entry.snapshot.data === undefined) return\n update(entry, { data: updater(entry.snapshot.data) })\n}\n\n/**\n * 读一个键的缓存,挂载(或键变化、过期)时自动请求。`key` 为 null 时不请求。\n * `load` 只在真正发请求时调用,不必是稳定引用。\n */\nexport function useClientQuery<T>(\n key: string | null,\n load: (client: BladeClient) => Promise<T>,\n { staleMs = 30_000 }: { staleMs?: number } = {},\n): ClientQueryState<T> & { refetch: () => Promise<T | undefined> } {\n const client = useBladeClient()\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!key) return () => {}\n const entry = entryFor<T>(client, key)\n entry.listeners.add(listener)\n return () => {\n entry.listeners.delete(listener)\n }\n },\n [client, key],\n )\n const snapshot = useSyncExternalStore(\n subscribe,\n () => (key ? entryFor<T>(client, key).snapshot : EMPTY),\n () => (key ? entryFor<T>(client, key).snapshot : EMPTY),\n )\n if (key) loadersFor(client).set(key, load as (client: BladeClient) => Promise<unknown>)\n useEffect(() => {\n if (!key) return\n const registered = loadersFor(client).get(key) as ((client: BladeClient) => Promise<T>) | undefined\n if (registered) void fetchClientQuery(client, key, registered, { staleMs }).catch(() => {})\n }, [client, key, staleMs])\n const refetch = useCallback(async () => {\n if (!key) return undefined\n const registered = loadersFor(client).get(key) as ((client: BladeClient) => Promise<T>) | undefined\n if (!registered) return undefined\n return fetchClientQuery(client, key, registered, { force: true }).catch(() => undefined)\n }, [client, key])\n return { ...snapshot, refetch }\n}\n\nconst loaderRegistry = new WeakMap<BladeClient, Map<string, (client: BladeClient) => Promise<unknown>>>()\nfunction loadersFor(client: BladeClient) {\n let loaders = loaderRegistry.get(client)\n if (!loaders) {\n loaders = new Map()\n loaderRegistry.set(client, loaders)\n }\n return loaders\n}\n\n/** 让缓存过期并用登记的加载函数重新请求(有组件在用时)。 */\nexport function refreshClientQuery(client: BladeClient, key: string): void {\n const entry = caches.get(client)?.get(key)\n if (!entry) return\n entry.fetchedAt = 0\n const load = loadersFor(client).get(key)\n if (load && entry.listeners.size > 0) void fetchClientQuery(client, key, load, { force: true }).catch(() => {})\n}\n","/**\n * 输入框工具条要读的会话数据:本会话已加载的技能、技能统计、上下文用量、后台任务。\n * 默认实现直接用 BladeClient 请求,一轮回复结束(或收到后台任务事件)时刷新;内置 Web 用\n * React Query 覆盖(由它的事件桥统一失效),见 `RichInputServices`。\n */\nimport type { BackgroundTask, SessionContextStats } from \"@blade-hq/agent-client/internal\"\nimport { useEffect, useRef } from \"react\"\nimport { useBladeClient } from \"../../context\"\nimport { useSessionHandle, useSessionState } from \"../../chat/runtime/session-source\"\nimport type { InputDataState, InputSessionSkill, InputSkillStats } from \"../../chat/runtime/input-services\"\nimport { refreshClientQuery, useClientQuery } from \"./client-query\"\n\nexport type {\n InputDataState as SessionDataState,\n InputSessionSkill as SessionSkillSummary,\n InputSkillStats as SkillStats,\n} from \"../../chat/runtime/input-services\"\nexport type { SessionContextStats }\n\nconst sessionPath = (sessionId: string, rest: string) => `/api/sessions/${encodeURIComponent(sessionId)}/${rest}`\n\n/** 回复结束时让这些键过期重拉(有组件在用时)。 */\nexport function useRefreshWhenRunEnds(sessionId: string | null, keys: string[]) {\n const client = useBladeClient()\n const streaming = useSessionState(sessionId, (state) => state.isStreaming)\n const wasStreaming = useRef(streaming)\n useEffect(() => {\n if (wasStreaming.current && !streaming) {\n for (const key of keys) refreshClientQuery(client, key)\n }\n wasStreaming.current = streaming\n })\n}\n\nexport function useClientSessionSkills(sessionId: string | null): InputDataState<InputSessionSkill[]> {\n const key = sessionId ? `session-skills:${sessionId}` : null\n const query = useClientQuery<InputSessionSkill[]>(key, (client) =>\n client.json(\"GET\", sessionPath(sessionId ?? \"\", \"skills\")),\n )\n useRefreshWhenRunEnds(sessionId, key ? [key] : [])\n return { data: query.data, loading: query.isLoading, error: query.error }\n}\n\nexport function useClientSkillStats(sessionId: string | null): InputDataState<InputSkillStats> {\n const key = sessionId ? `skill-stats:${sessionId}` : null\n const query = useClientQuery<InputSkillStats>(key, (client) => client.json(\"GET\", sessionPath(sessionId ?? \"\", \"skill-stats\")), {\n staleMs: 300_000,\n })\n useRefreshWhenRunEnds(sessionId, key ? [key] : [])\n return { data: query.data, loading: query.isLoading, error: query.error }\n}\n\nexport function useClientContextStats(sessionId: string | null): InputDataState<SessionContextStats> {\n const key = sessionId ? `context-stats:${sessionId}` : null\n const query = useClientQuery<SessionContextStats>(key, (client) =>\n client.json(\"GET\", sessionPath(sessionId ?? \"\", \"context-stats\")),\n )\n useRefreshWhenRunEnds(sessionId, key ? [key] : [])\n return { data: query.data, loading: query.isLoading, error: query.error }\n}\n\nexport function useClientBackgroundTasks(sessionId: string | null): InputDataState<BackgroundTask[]> {\n const client = useBladeClient()\n const key = sessionId ? `background-tasks:${sessionId}` : null\n const query = useClientQuery<BackgroundTask[]>(key, (c) => c.sessions.listBackgroundTasks(sessionId ?? \"\"))\n const session = useSessionHandle(sessionId)\n useEffect(() => {\n if (!session || !key) return\n return session.on(\"backgroundTask\", () => refreshClientQuery(client, key))\n }, [client, key, session])\n return { data: query.data, loading: query.isLoading, error: query.error }\n}\n\n/** 会话的技能变了(连接器开关插件等):让默认实现的技能数据重拉。 */\nexport function refreshClientSessionSkills(client: ReturnType<typeof useBladeClient>, sessionId: string): void {\n refreshClientQuery(client, `session-skills:${sessionId}`)\n refreshClientQuery(client, `skill-stats:${sessionId}`)\n}\n\n/** 会话保存的模型(默认实现:请求会话详情,回复结束时刷新)。 */\nexport function useClientSessionModel(sessionId: string | null): string | null {\n const key = sessionId ? `session-detail:${sessionId}` : null\n const query = useClientQuery(key, (client) => client.sessions.getSession(sessionId ?? \"\"))\n useRefreshWhenRunEnds(sessionId, key ? [key] : [])\n return query.data?.model ?? null\n}\n","/**\n * 模型目录与用户的模型偏好(默认模型、各模型的推理强度)。默认实现直接用 BladeClient 请求;\n * 内置 Web 用自己的 React Query 缓存覆盖(与设置页、启动期偏好共用一份),见 `RichInputServices`。\n */\nimport type { BladeClient } from \"@blade-hq/agent-client\"\nimport { useBladeClient } from \"../../context\"\nimport {\n type InputModelCatalog,\n type InputModelOption,\n type InputModelPreferences,\n parseModelReasoningLevels,\n} from \"../../chat/runtime/input-services\"\nimport { fetchClientQuery, setClientQueryData, useClientQuery } from \"../lib/client-query\"\n\nexport type {\n InputModelCatalog as ModelCatalogState,\n InputModelOption as ModelOption,\n InputModelPreferences as ModelPreferencesState,\n} from \"../../chat/runtime/input-services\"\n\ninterface ModelCatalogResponse {\n default: string\n thinkingAvailable?: boolean\n unavailableProviderIds?: string[]\n unavailableModelIds?: string[]\n models: InputModelOption[]\n}\n\nconst EMPTY_MODELS: InputModelOption[] = []\nconst EMPTY_IDS: string[] = []\nconst CATALOG_KEY = \"model-catalog\"\n\nexport function useClientModelCatalog(): InputModelCatalog {\n const { data, isLoading, isFetching, refetch } = useClientQuery<ModelCatalogResponse>(\n CATALOG_KEY,\n (client) => client.json(\"GET\", \"/api/config/models\"),\n { staleMs: 0 },\n )\n return {\n models: data?.models ?? EMPTY_MODELS,\n defaultModel: data?.default ?? \"\",\n thinkingAvailable: data?.thinkingAvailable ?? false,\n unavailableProviderIds: data?.unavailableProviderIds ?? EMPTY_IDS,\n unavailableModelIds: data?.unavailableModelIds ?? EMPTY_IDS,\n isLoading,\n isFetching,\n refresh: async () => {\n await refetch()\n },\n }\n}\n\nexport const PREFERRED_MODEL_KEY = \"preferred_model\"\nexport const MODEL_REASONING_LEVELS_KEY = \"llm.model_thinking_preferences\"\nconst PREFERENCES_KEY = \"model-preferences\"\n\ntype PreferenceValues = Record<string, string | null>\n\nfunction loadPreferences(client: BladeClient): Promise<PreferenceValues> {\n const query = new URLSearchParams()\n query.append(\"key\", PREFERRED_MODEL_KEY)\n query.append(\"key\", MODEL_REASONING_LEVELS_KEY)\n return client\n .json<{ values: PreferenceValues }>(\"GET\", `/api/user/preferences?${query.toString()}`)\n .then((response) => response.values ?? {})\n}\n\nasync function writePreference(client: BladeClient, key: string, value: string) {\n const previous = await fetchClientQuery(client, PREFERENCES_KEY, loadPreferences, { staleMs: 60_000 }).catch(\n () => ({}) as PreferenceValues,\n )\n setClientQueryData<PreferenceValues>(client, PREFERENCES_KEY, (current) => ({ ...current, [key]: value }))\n try {\n await client.json(\"PUT\", `/api/user/preferences/${encodeURIComponent(key)}`, { value })\n } catch {\n setClientQueryData<PreferenceValues>(client, PREFERENCES_KEY, (current) => ({ ...current, [key]: previous[key] ?? null }))\n }\n}\n\nconst EMPTY_LEVELS: Record<string, string> = {}\n\nexport function useClientModelPreferences(): InputModelPreferences {\n const client = useBladeClient()\n const { data } = useClientQuery<PreferenceValues>(PREFERENCES_KEY, loadPreferences, { staleMs: 60_000 })\n const rawLevels = data?.[MODEL_REASONING_LEVELS_KEY] ?? null\n const reasoningLevels = rawLevels ? parseModelReasoningLevels(rawLevels) : EMPTY_LEVELS\n return {\n preferredModel: data?.[PREFERRED_MODEL_KEY] ?? null,\n setPreferredModel: (modelId) => void writePreference(client, PREFERRED_MODEL_KEY, modelId),\n reasoningLevels,\n setReasoningLevel: (modelId, level) => {\n if (!modelId) return\n void writePreference(\n client,\n MODEL_REASONING_LEVELS_KEY,\n JSON.stringify({ ...reasoningLevels, [modelId]: level }),\n )\n },\n }\n}\n","/**\n * 富输入的数据来源:宿主覆盖了就用宿主的(内置 Web 的 React Query 缓存、全局 store),\n * 否则用基于 BladeClient 的默认实现。覆盖点见 `RichInputServices`(internal 入口)。\n */\nimport type { BackgroundTask, ConnectionStatus, SessionContextStats } from \"@blade-hq/agent-client/internal\"\nimport { useMemo, useRef } from \"react\"\nimport {\n type InputConnection,\n type InputDataState,\n type InputModelCatalog,\n type InputModelPreferences,\n type InputSessionSkill,\n type InputSkillStats,\n useRichInputServices,\n} from \"../chat/runtime/input-services\"\nimport { useSessionState } from \"../chat/runtime/session-source\"\nimport { useBladeClient } from \"../context\"\nimport {\n refreshClientSessionSkills,\n useClientBackgroundTasks,\n useClientContextStats,\n useClientSessionSkills,\n useClientSkillStats,\n} from \"./lib/session-data\"\nimport { useClientModelCatalog, useClientModelPreferences } from \"./model/model-data\"\n\nexport {\n type ConnectorComputerSectionProps,\n type ConnectorPhoneSectionProps,\n type InputConnection,\n type InputDataState,\n type InputModelCatalog,\n type InputModelOption,\n type InputModelPreferences,\n type InputSessionSkill,\n type InputSkillStats,\n type RichInputServices,\n RichInputServicesProvider,\n useRichInputServices,\n} from \"../chat/runtime/input-services\"\n\nexport function useModelCatalog(): InputModelCatalog {\n const use = useRichInputServices().useModelCatalog ?? useClientModelCatalog\n return use()\n}\n\nexport function useModelPreferences(): InputModelPreferences {\n const use = useRichInputServices().useModelPreferences ?? useClientModelPreferences\n return use()\n}\n\nfunction useSessionConnection(sessionId: string | null): InputConnection {\n const connection = useSessionState(sessionId, (state) => state.connection)\n const status: ConnectionStatus = sessionId ? connection : \"connected\"\n const everConnected = useRef(false)\n if (status === \"connected\") everConnected.current = true\n return { status, reconnectAttempt: 0, hasEverConnected: everConnected.current }\n}\n\n/** 连接情况:宿主给了就用宿主的(内置 Web 看全局连接),否则看当前会话。 */\nexport function useComposerConnectionInfo(sessionId: string | null): InputConnection {\n const use = useRichInputServices().useConnection ?? useSessionConnection\n return use(sessionId)\n}\n\nexport function useComposerConnection(sessionId: string | null): ConnectionStatus {\n return useComposerConnectionInfo(sessionId).status\n}\n\nexport function useSessionSkills(sessionId: string | null): InputDataState<InputSessionSkill[]> {\n const use = useRichInputServices().useSessionSkills ?? useClientSessionSkills\n return use(sessionId)\n}\n\nexport function useSkillStats(sessionId: string | null): InputDataState<InputSkillStats> {\n const use = useRichInputServices().useSkillStats ?? useClientSkillStats\n return use(sessionId)\n}\n\nexport function useContextStats(sessionId: string | null): InputDataState<SessionContextStats> {\n const use = useRichInputServices().useContextStats ?? useClientContextStats\n return use(sessionId)\n}\n\nexport function useBackgroundTaskList(sessionId: string | null): InputDataState<BackgroundTask[]> {\n const use = useRichInputServices().useBackgroundTasks ?? useClientBackgroundTasks\n return use(sessionId)\n}\n\nfunction useClientSessionDataInvalidation() {\n const client = useBladeClient()\n return useMemo(\n () => ({\n filesUploaded: (_sessionId: string) => {},\n pluginsChanged: (sessionId: string) => refreshClientSessionSkills(client, sessionId),\n }),\n [client],\n )\n}\n\n/** 附件上传、插件开关之后让相关数据刷新。 */\nexport function useSessionDataInvalidation() {\n const use = useRichInputServices().useSessionDataInvalidation ?? useClientSessionDataInvalidation\n return use()\n}\n","import type { BladeClient, SessionPlugin } from \"@blade-hq/agent-client/internal\"\nimport { Puzzle } from \"lucide-react\"\nimport { useEffect, useState } from \"react\"\n\nexport interface SessionPluginIconProps {\n client: BladeClient\n /**\n * 会话内已准备的包。传 `null` 时改用账号级目录图标(`/api/plugins/{name}/icon`),\n * 这样首页这种还没有会话的地方也能显示包内图标。\n */\n sessionId: string | null\n /** Also accepts the package data image returned by the connector catalog. */\n plugin: Pick<SessionPlugin, \"name\" | \"icon\">\n /** 边长,默认 20。 */\n size?: number\n /**\n * 外框形状:`rounded`(默认,面板列表里的小方角)或 `circle`(触发器上的头像堆叠)。\n * 默认值不动,避免改到列表与第三方接入方的观感。\n */\n shape?: \"rounded\" | \"circle\"\n className?: string\n}\n\nexport interface SessionPluginLabelProps {\n plugin: Pick<SessionPlugin, \"name\" | \"display_name\">\n}\n\n/**\n * Business name for a plugin, falling back to the technical ID.\n *\n * The ID stays authoritative for paths, authorization, caching and calls; this\n * helper only decides what a user reads. Keep it shared — one place decides.\n */\nexport function sessionPluginLabel(plugin: Pick<SessionPlugin, \"name\" | \"display_name\">): string {\n const display = typeof plugin.display_name === \"string\" ? plugin.display_name.trim() : \"\"\n return display || plugin.name\n}\n\nconst clientCaches = new WeakMap<BladeClient, Map<string, Promise<Blob | null>>>()\nconst BLOB_CACHE_LIMIT = 64\n\nfunction iconBlob(client: BladeClient, sessionId: string | null, name: string, token: string): Promise<Blob | null> {\n let blobCache = clientCaches.get(client)\n if (!blobCache) {\n blobCache = new Map()\n clientCaches.set(client, blobCache)\n }\n const key = sessionId ? `${sessionId}:${name}:${token}` : `account:${name}:${token}`\n const cached = blobCache.get(key)\n if (cached) return cached\n const pending = (sessionId\n ? client.sessions.fetchSessionPluginIcon(sessionId, name)\n : client.plugins.fetchIcon(name)\n ).catch(() => {\n blobCache?.delete(key)\n return null\n })\n if (blobCache.size >= BLOB_CACHE_LIMIT) blobCache.clear()\n blobCache.set(key, pending)\n return pending\n}\n\n/** Package-local plugin icon, with a stable generic placeholder. */\nexport function SessionPluginIcon({ client, sessionId, plugin, size = 20, shape = \"rounded\", className }: SessionPluginIconProps) {\n const [url, setUrl] = useState<string | null>(null)\n const token = plugin.icon\n const inlineIcon = token && /^data:image\\/(?:png|jpeg|gif|webp);base64,/.test(token) ? token : null\n\n useEffect(() => {\n setUrl(null)\n if (!token || inlineIcon) return\n let alive = true\n let objectUrl: string | null = null\n void iconBlob(client, sessionId, plugin.name, token).then((blob) => {\n if (!alive || !blob) return\n objectUrl = URL.createObjectURL(blob)\n setUrl(objectUrl)\n })\n return () => {\n alive = false\n if (objectUrl) URL.revokeObjectURL(objectUrl)\n }\n }, [client, sessionId, plugin.name, token, inlineIcon])\n\n const frame = `flex shrink-0 items-center justify-center overflow-hidden ${shape === \"circle\" ? \"rounded-full\" : \"rounded\"} ${className ?? \"\"}`\n const frameStyle = { width: size, height: size }\n const source = url === \"\" ? null : inlineIcon ?? url\n if (!source) {\n return (\n <span className={`${frame} bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]`} style={frameStyle} data-plugin-icon=\"placeholder\">\n <Puzzle aria-hidden size={Math.round(size * 0.7)} />\n </span>\n )\n }\n return (\n <span className={frame} style={frameStyle} data-plugin-icon=\"image\">\n <img src={source} alt=\"\" aria-hidden className=\"h-full w-full object-cover\" onError={() => setUrl(\"\")} />\n </span>\n )\n}\n","import type {\n BladeClient,\n SessionPlugin,\n SessionPluginConfigError,\n SessionPluginConfigState,\n} from \"@blade-hq/agent-client/internal\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { SessionPluginConfigForm, type SessionPluginConfigDraft } from \"./SessionPluginConfigForm\"\nimport { SessionPluginIcon, sessionPluginLabel } from \"./SessionPluginIcon\"\n\nexport interface SessionPluginConfigDialogProps {\n client: BladeClient\n sessionId: string\n plugin: Pick<SessionPlugin, \"name\" | \"display_name\" | \"icon\">\n /** Called after every successful save with the freshly published state. */\n onSaved?: (state: SessionPluginConfigState) => void\n onClose: () => void\n}\n\ninterface FailureDetail {\n message?: string\n errors?: SessionPluginConfigError[]\n code?: string\n}\n\nasync function readFailure(error: unknown): Promise<FailureDetail> {\n const response = (error as { response?: Response } | undefined)?.response\n if (!response || typeof response.json !== \"function\") {\n return { message: \"配置未保存,请稍后重试\" }\n }\n try {\n const body = (await response.json()) as { detail?: string | FailureDetail }\n const detail = body?.detail\n if (typeof detail === \"string\") return { message: detail }\n if (detail && typeof detail === \"object\") {\n return {\n message: detail.message ?? \"配置未保存,请检查填写内容\",\n errors: Array.isArray(detail.errors) ? detail.errors : undefined,\n code: detail.code,\n }\n }\n } catch {\n }\n return { message: \"配置未保存,请稍后重试\" }\n}\n\nexport function SessionPluginConfigDialog({\n client,\n sessionId,\n plugin,\n ...props\n}: SessionPluginConfigDialogProps) {\n return (\n <ConfigDialogState\n key={`${sessionId}:${plugin.name}`}\n client={client}\n sessionId={sessionId}\n plugin={plugin}\n {...props}\n />\n )\n}\n\nfunction ConfigDialogState({\n client,\n sessionId,\n plugin,\n onSaved,\n onClose,\n}: SessionPluginConfigDialogProps) {\n const [host, setHost] = useState<{ state: string; digest: string; revision: string; reason: string; restarts: number; recent?: string[] } | null>(null)\n const [restarting, setRestarting] = useState(false)\n const [scope, setScope] = useState<\"personal\" | \"global\">(\"personal\")\n const [config, setConfig] = useState<SessionPluginConfigState | null>(null)\n const pendingRevision = scope === \"global\" && host?.state === \"ready\" && config?.revision != null && host.revision !== config.revision\n const waitingForHost = host?.state === \"starting\" || host?.state === \"updating\" || pendingRevision\n useEffect(() => {\n if (!waitingForHost) return\n const abort = new AbortController()\n let timer: ReturnType<typeof setTimeout>\n const refresh = async () => {\n try {\n const next = await client.sessions.getSessionPluginConfig(sessionId, plugin.name, { signal: abort.signal }, scope)\n if (!abort.signal.aborted) setHost(next.host ?? null)\n } catch {\n }\n if (!abort.signal.aborted) timer = setTimeout(refresh, 2000)\n }\n timer = setTimeout(refresh, 2000)\n return () => { abort.abort(); clearTimeout(timer) }\n }, [client, sessionId, plugin.name, scope, waitingForHost])\n const [canConfigureGlobal, setCanConfigureGlobal] = useState(false)\n const [loading, setLoading] = useState(true)\n const [loadFailure, setLoadFailure] = useState<string | null>(null)\n const [saving, setSaving] = useState(false)\n const [failure, setFailure] = useState<string | null>(null)\n const [fieldErrors, setFieldErrors] = useState<SessionPluginConfigError[]>([])\n const [formVersion, setFormVersion] = useState(0)\n const alive = useRef(true)\n const generation = useRef(0)\n const dialog = useRef<HTMLDialogElement>(null)\n\n useEffect(() => {\n const element = dialog.current\n if (!element || element.open) return\n if (typeof element.showModal === \"function\") element.showModal()\n else element.setAttribute(\"open\", \"\")\n }, [])\n\n const load = async () => {\n const current = ++generation.current\n setLoading(true)\n setLoadFailure(null)\n try {\n const { config: state, can_configure_global,host: hostState } = await client.sessions.getSessionPluginConfig(sessionId, plugin.name, undefined, scope)\n if (!alive.current || generation.current !== current) return\n setCanConfigureGlobal(can_configure_global ?? false)\n setHost(hostState ?? null)\n setConfig(state)\n setFormVersion((value) => value + 1)\n } catch (error) {\n if (!alive.current || generation.current !== current) return\n const detail = await readFailure(error)\n if (!alive.current || generation.current !== current) return\n setLoadFailure(detail.message ?? \"配置暂不可用\")\n } finally {\n if (alive.current && generation.current === current) setLoading(false)\n }\n }\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: 会话或插件变化就是新的读取生命周期。\n useEffect(() => {\n alive.current = true\n setConfig(null)\n setFailure(null)\n setFieldErrors([])\n void load()\n return () => {\n alive.current = false\n generation.current++\n }\n }, [client, sessionId, plugin.name, scope])\n\n const save = async (draft: SessionPluginConfigDraft) => {\n if (!config) return\n const current = generation.current\n setSaving(true)\n setFailure(null)\n setFieldErrors([])\n try {\n const { config: state } = await client.sessions.updateSessionPluginConfig(\n sessionId,\n plugin.name,\n {\n expected_revision: config.revision,\n set: draft.set,\n remove: draft.remove,\n },\n undefined,\n scope,\n )\n if (!alive.current || generation.current !== current) return\n setConfig(state)\n setFormVersion((value) => value + 1)\n if (scope === \"personal\") onSaved?.(state)\n } catch (error) {\n if (!alive.current || generation.current !== current) return\n const detail = await readFailure(error)\n if (!alive.current || generation.current !== current) return\n setFailure(detail.message ?? \"配置未保存,请稍后重试\")\n setFieldErrors(detail.errors ?? [])\n if (detail.code === \"plugin_config_conflict\") await load()\n } finally {\n if (alive.current) setSaving(false)\n }\n }\n\n return (\n <>\n <style>{\".blade-plugin-dialog::backdrop { background: rgb(0 0 0 / 40%); }\"}</style>\n <dialog\n ref={dialog}\n aria-modal=\"true\"\n aria-label={`${sessionPluginLabel(plugin)} 配置`}\n onCancel={(event) => {\n event.preventDefault()\n onClose()\n }}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") return\n event.preventDefault()\n event.stopPropagation()\n onClose()\n }}\n style={{\n position: \"fixed\",\n inset: 0,\n margin: \"auto\",\n boxSizing: \"border-box\",\n width: \"min(448px, calc(100% - 32px))\",\n }}\n className=\"blade-plugin-dialog max-h-[80vh] overflow-y-auto rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--popover))] p-4 text-[hsl(var(--popover-foreground))] shadow-lg\"\n >\n <div className=\"mb-3 flex items-center gap-2\">\n <SessionPluginIcon client={client} sessionId={sessionId} plugin={plugin} />\n <span\n className=\"min-w-0 flex-1 truncate text-sm font-medium\"\n title={sessionPluginLabel(plugin)}\n >\n {sessionPluginLabel(plugin)}\n </span>\n <button\n type=\"button\"\n aria-label=\"关闭\"\n className=\"shrink-0 text-xs underline\"\n onClick={onClose}\n >\n 关闭\n </button>\n </div>\n {canConfigureGlobal ? (\n <div className=\"mb-3 flex gap-2 text-xs\">\n <button type=\"button\" disabled={saving} aria-pressed={scope === \"personal\"} onClick={() => setScope(\"personal\")}>我的登录</button>\n <button type=\"button\" disabled={saving} aria-pressed={scope === \"global\"} onClick={() => setScope(\"global\")}>组织共享配置</button>\n </div>\n ) : null}\n {host ? <div className=\"mb-3 space-y-1 text-xs\">\n <p>服务:{pendingRevision ? \"正在更新\" : host.reason} · 已恢复 {host.restarts} 次</p>\n {canConfigureGlobal ? <>\n <p className=\"break-all text-[hsl(var(--muted-foreground))]\">包摘要:{host.digest} · 配置:{host.revision}</p>\n <button type=\"button\" disabled={restarting || saving} onClick={() => {\n setRestarting(true)\n void client.sessions.restartSessionPluginHost(sessionId, plugin.name)\n .then(() => load()).catch(() => setFailure(\"服务暂时不可用,请稍后重试\"))\n .finally(() => { if (alive.current) setRestarting(false) })\n }}>{restarting ? \"正在重启…\" : \"重启服务\"}</button>\n {host.recent?.length ? <details><summary>最近活动</summary>{host.recent.map((line, index) => <p key={`${index}-${line}`}>{line}</p>)}</details> : null}\n </> : null}\n </div> : null}\n {scope === \"personal\" && config?.auth_status === \"expired\" ? <p className=\"mb-2 text-xs\">登录已过期,请重新填写</p> : null}\n {scope === \"personal\" && config?.global_ready === false ? <p className=\"mb-2 text-xs\">请联系管理员填写共享配置</p> : null}\n {scope === \"global\" ? <p className=\"mb-2 text-xs\">共享密钥用于插件服务。命令类插件的沙盒代码可以读取它;需保密时请使用托管服务。</p> : null}\n {loading ? (\n <p className=\"text-xs text-[hsl(var(--muted-foreground))]\">正在读取配置…</p>\n ) : null}\n {loadFailure ? (\n <div className=\"space-y-2 text-xs\">\n <p>{loadFailure}</p>\n <button\n type=\"button\"\n className=\"rounded-full border border-[hsl(var(--border))] px-2.5 py-1\"\n onClick={() => void load()}\n >\n 重试\n </button>\n </div>\n ) : null}\n {config && !loading ? (\n <SessionPluginConfigForm\n key={formVersion}\n config={config}\n submitting={saving}\n errors={fieldErrors}\n failure={failure}\n onSubmit={(draft) => void save(draft)}\n onCancel={onClose}\n />\n ) : null}\n </dialog>\n </>\n )\n}\n","import type { BladeClient, SessionPluginConfigState } from \"@blade-hq/agent-client/internal\"\nimport { Loader2 } from \"lucide-react\"\nimport { SessionPluginConfigDialog } from \"./SessionPluginConfigDialog\"\nimport { SessionPluginIcon, sessionPluginLabel } from \"./SessionPluginIcon\"\nimport type { PluginConnectorItem } from \"./plugin-connector\"\n\nexport interface ConfiguringPlugin {\n name: string\n /**\n * 这次打开表单是不是「为了启用它」。\n *\n * 只有用户在开关上表达了启用意图时才为 true:已经跑起来的插件再保存配置\n * 不应该顺手重启 / 重新激活(见 SessionPluginSelector 的历史约定)。\n */\n pendingActivation: boolean\n}\n\nexport interface PluginConnectorListProps {\n client: BladeClient\n /** 会话内插件用会话接口取图标;首页候选(null)用账号级目录。 */\n sessionId: string | null\n items: PluginConnectorItem[]\n /** 正在提交意图的行;开关转圈,但仍然可点(点是取消)。 */\n inFlight: Record<string, true>\n /** 有启用/停用请求在飞,会写服务端的行先锁住次要操作。 */\n busy: boolean\n /** 上一次操作失败,给出重试入口。 */\n mutationError: boolean\n onToggle: (item: PluginConnectorItem, next: boolean) => void\n onRetry: (item: PluginConnectorItem) => void\n onEditConfig: (item: PluginConnectorItem) => void\n configuring: ConfiguringPlugin | null\n onConfigSaved: (state: SessionPluginConfigState) => void\n onConfigClose: () => void\n /** 开关不可用时说明原因,而不是静默禁用。 */\n disabledReason?: string | null\n /**\n * 列表本身不可用(读取失败 / 搜索无结果)时的说明;有值时不再叠加「还没装插件」,\n * 否则会把一次故障或一次没搜到说成「你这个账号没装东西」。\n */\n unavailableText?: string | null\n emptyText: string\n /** 首页待选模式:开关的语义是\"待选\",不是\"已经启用\"。 */\n draft?: boolean\n}\n\nfunction PluginSwitch({\n label,\n checked,\n pending,\n disabled,\n draft = false,\n onToggle,\n}: {\n label: string\n checked: boolean\n pending: boolean\n disabled: boolean\n draft?: boolean\n onToggle: (next: boolean) => void\n}) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={draft ? `${label} 待选` : `${label} 启用`}\n disabled={disabled}\n onClick={() => onToggle(!checked)}\n className={`relative inline-flex h-[18px] w-8 shrink-0 cursor-pointer items-center rounded-full border transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${\n checked\n ? \"border-transparent bg-[hsl(213_94%_55%)]\"\n : \"border-[hsl(var(--border))] bg-[hsl(var(--muted))]\"\n }`}\n >\n <span\n aria-hidden=\"true\"\n className={`pointer-events-none flex h-3.5 w-3.5 items-center justify-center rounded-full bg-white shadow transition-transform ${\n checked ? \"translate-x-[16px]\" : \"translate-x-[2px]\"\n }`}\n >\n {pending ? <Loader2 size={9} className=\"animate-spin text-[hsl(var(--muted-foreground))]\" /> : null}\n </span>\n </button>\n )\n}\n\n/**\n * 连接器面板里的插件列表。\n *\n * 首页与会话共用同一份渲染:开关是开关,候选是候选,行的存在与状态无关——\n * 没启用、没图标也要显示名称和占位,不隐藏整行。\n */\nexport function PluginConnectorList({\n client,\n sessionId,\n items,\n inFlight,\n busy,\n mutationError,\n onToggle,\n onRetry,\n onEditConfig,\n configuring,\n onConfigSaved,\n onConfigClose,\n disabledReason,\n unavailableText,\n emptyText,\n draft = false,\n}: PluginConnectorListProps) {\n const configuringItem = configuring\n ? items.find((item) => item.name === configuring.name) ?? null\n : null\n\n return (\n <div className=\"py-0.5\">\n {disabledReason ? (\n <div className=\"px-3 py-1.5 text-[11px] text-[hsl(var(--muted-foreground))]\">\n {disabledReason}\n </div>\n ) : null}\n {items.length === 0 ? (\n <div className=\"px-3 py-1.5 text-[11px] text-[hsl(var(--muted-foreground))]\">\n {unavailableText ?? emptyText}\n </div>\n ) : null}\n {items.map((item) => {\n const label = sessionPluginLabel(item)\n const actionable = (!disabledReason && item.installed !== false) || item.active\n const pending = Boolean(inFlight[item.name])\n const checked = item.active\n return (\n <div key={item.name} className=\"group px-3 py-1\">\n <div className=\"flex items-center gap-2\">\n <SessionPluginIcon\n client={client}\n sessionId={item.iconSource === \"account\" ? null : sessionId}\n plugin={item}\n />\n <span className=\"min-w-0 flex-1 truncate text-[11px]\" title={label}>\n {label}\n </span>\n {item.auth_status === \"expired\" || item.auth_status === \"login_required\" || item.auth_status === \"unavailable\" ? (\n <button type=\"button\" disabled={!sessionId || busy} onClick={() => onEditConfig(item)} className=\"shrink-0 text-[10px] underline\">\n {item.auth_status === \"expired\" ? \"登录已过期\" : item.auth_status === \"unavailable\" ? \"暂时不可用\" : \"未登录\"}\n </button>\n ) : null}\n {!draft && item.config === \"configured\" ? (\n <button\n type=\"button\"\n title=\"更新该插件的配置\"\n disabled={busy || !sessionId}\n onClick={() => onEditConfig(item)}\n className=\"shrink-0 text-[10px] text-[hsl(var(--muted-foreground))] underline opacity-0 [@media(hover:none)]:opacity-100 transition-opacity focus-visible:opacity-100 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n 更新配置\n </button>\n ) : null}\n {!draft && (item.config === \"required\" || item.config === \"error\") ? (\n <button\n type=\"button\"\n title=\"该插件需要填写配置\"\n disabled={busy || !sessionId}\n onClick={() => onEditConfig(item)}\n className=\"shrink-0 text-[10px] text-[hsl(var(--muted-foreground))] underline disabled:cursor-not-allowed disabled:opacity-50\"\n >\n 填写配置\n </button>\n ) : null}\n <PluginSwitch\n label={label}\n checked={checked}\n pending={pending}\n draft={draft}\n disabled={!actionable}\n onToggle={(next) => onToggle(item, next)}\n />\n </div>\n {!draft && item.active && item.status !== \"valid\" && item.status !== \"required\" ? (\n <div className=\"mt-0.5 flex items-center gap-2 pl-7 text-[10px] text-[hsl(var(--muted-foreground))]\">\n <span>{item.reason || \"插件暂不可用\"}</span>\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => onRetry(item)}\n className=\"underline disabled:cursor-not-allowed disabled:opacity-50\"\n >\n 重试\n </button>\n </div>\n ) : null}\n </div>\n )\n })}\n {mutationError ? (\n <div className=\"px-3 pt-1 text-[10px] text-[hsl(var(--destructive))]\">\n 操作未完成,请重试\n </div>\n ) : null}\n {configuring && configuringItem && sessionId ? (\n <SessionPluginConfigDialog\n client={client}\n sessionId={sessionId}\n plugin={configuringItem}\n onSaved={onConfigSaved}\n onClose={onConfigClose}\n />\n ) : null}\n </div>\n )\n}\n","import type { PluginCatalogEntry, SessionPlugin, SessionPluginConfigSummary } from \"@blade-hq/agent-client/internal\"\n\n/**\n * 连接器面板里的一行插件。\n *\n * 展示字段(名称、图标)和启用状态分开表达:目录接口在没有会话时也能给出名称与\n * 图标,而「这次会话是否启用」只有会话接口知道。首页候选行恒为 `active: false`,\n * 因为它只是待选,不代表已经激活。\n */\nexport interface PluginConnectorItem {\n name: string\n display_name: string | null\n /** 图标 token;null 时显示统一占位,不隐藏整行。 */\n icon: string | null\n /** 图标从哪个接口取:账号级目录,还是会话内已准备的包。 */\n iconSource: \"account\" | \"session\"\n auth_status?: PluginCatalogEntry[\"auth_status\"]\n installed: boolean | null\n connector?: boolean\n /** 会话里已提交的选择;首页候选恒为 false。 */\n active: boolean\n status: string\n reason?: string | null\n config?: SessionPluginConfigSummary\n projected: boolean\n}\n\n/** 首页候选:只有账号级目录,没有会话状态。 */\nexport function catalogToConnectorItems(catalog: PluginCatalogEntry[]): PluginConnectorItem[] {\n return catalog.map((entry) => ({\n name: entry.name,\n display_name: entry.display_name,\n icon: entry.icon,\n iconSource: \"account\",\n installed: entry.installed,\n connector: entry.connector,\n auth_status: entry.auth_status,\n active: false,\n status: \"not_prepared\",\n projected: false,\n }))\n}\n\n/**\n * 会话列表叠加账号目录。\n *\n * 目录可用时展示字段以目录为准(包可能还没准备,会话列表拿不到名称和图标),\n * 目录不可用时退回会话行自己的字段,这样已选插件仍然可以取消。\n */\nexport function mergeConnectorItems(\n sessionPlugins: SessionPlugin[],\n catalog: PluginCatalogEntry[] | null,\n): PluginConnectorItem[] {\n const byName = new Map((catalog ?? []).map((entry) => [entry.name, entry]))\n return sessionPlugins.map((plugin) => {\n const entry = byName.get(plugin.name)\n if (!entry) {\n return {\n name: plugin.name,\n display_name: plugin.display_name ?? null,\n icon: plugin.icon ?? null,\n iconSource: \"session\" as const,\n installed: plugin.installed,\n active: plugin.active,\n status: plugin.status,\n reason: plugin.reason,\n config: plugin.config,\n projected: plugin.projected,\n }\n }\n return {\n name: plugin.name,\n display_name: entry.display_name ?? plugin.display_name ?? null,\n icon: entry.icon,\n iconSource: \"account\" as const,\n installed: entry.installed,\n connector: entry.connector,\n auth_status: entry.auth_status,\n active: plugin.active,\n status: plugin.status,\n reason: plugin.reason,\n config: plugin.config,\n projected: plugin.projected,\n }\n })\n}\n\n/**\n * 一行的配置说明文案;权威状态来自配置接口。\n *\n * `unchecked` 刻意没有文案:列表没有读过用户的 Home,说任何话都是噪音,\n * 入口是开关本身。\n */\n/** 按名称和显示名做本地过滤;空查询返回全部。 */\nexport function filterConnectorItems(\n items: PluginConnectorItem[],\n query: string,\n): PluginConnectorItem[] {\n const needle = query.trim().toLowerCase()\n if (!needle) return items\n return items.filter((item) =>\n `${item.display_name ?? \"\"} ${item.name}`.toLowerCase().includes(needle),\n )\n}\n","import { isLocalConnectorName } from \"@blade-hq/agent-client/internal\"\nimport type {\n BladeClient,\n PluginCatalogEntry,\n SessionPlugin,\n SessionPluginActivation,\n SessionPluginConfigState,\n} from \"@blade-hq/agent-client/internal\"\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport type { ConfiguringPlugin } from \"./PluginConnectorList\"\nimport {\n mergeConnectorItems,\n type PluginConnectorItem,\n} from \"./plugin-connector\"\n\nexport interface UseSessionPluginActivationResult {\n items: PluginConnectorItem[]\n loading: boolean\n /** 插件列表本身读不到(Hub 不可用等),已有选择仍然可取消。 */\n listError: boolean\n /** 某次启用/停用/准备失败了。 */\n mutationError: boolean\n /** 正在为哪些插件提交意图;对应的开关在转圈,但仍然可点(点是取消)。 */\n inFlight: Record<string, true>\n busy: boolean\n configuring: ConfiguringPlugin | null\n reload: () => void\n toggle: (item: PluginConnectorItem, next: boolean) => void\n retry: (item: PluginConnectorItem) => void\n editConfig: (item: PluginConnectorItem) => void\n onConfigSaved: (state: SessionPluginConfigState) => void\n onConfigClose: () => void\n}\n\n/**\n * 会话内插件启用的唯一实现。\n *\n * 顺序是「先准备配置,再提交 activation」:缺配置时开关不落到已启用、立刻弹表单,\n * 保存成功才提交;取消、校验失败、配置读取失败都不提交。绝不先持久化 active=true\n * 再靠补偿关掉——那样用户下次刷新会看到一个从没被成功打开的插件。\n *\n * 停用仍然直接提交:在服务端它只是取消一条数据库选择,不准备包、不连 MCP。但如果\n * 这次「启用」只是还在准备、压根没提交过 activation,取消就只是放弃本地意图。\n *\n * 所有异步响应都按 (lifetime, mutation) 校验:迟到的准备或激活结果不会把已经取消 /\n * 已经切走会话的插件重新打开;反过来,旧响应发现服务端落库顺序与用户最新意图不同时,\n * 会重新提交最新意图。\n */\nexport function useSessionPluginActivation({\n client,\n sessionId,\n onChange,\n}: {\n client: BladeClient\n sessionId: string\n onChange?: (plugin: SessionPluginActivation) => void\n}): UseSessionPluginActivationResult {\n const [plugins, setPlugins] = useState<SessionPlugin[]>([])\n const [catalog, setCatalog] = useState<PluginCatalogEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [listError, setListError] = useState(false)\n const [mutationError, setMutationError] = useState(false)\n const [inFlight, setInFlight] = useState<Record<string, true>>({})\n const [configuring, setConfiguring] = useState<ConfiguringPlugin | null>(null)\n const alive = useRef(true)\n const generation = useRef(0)\n const mutationGenerations = useRef(new Map<string, number>())\n const desiredActivations = useRef(new Map<string, boolean>())\n const pendingMutations = useRef(new Map<string, number>())\n const submittedActivations = useRef(new Set<string>())\n const serverActive = useRef(new Set<string>())\n const lifetime = useRef(0)\n const request = useRef<AbortController | null>(null)\n const [desiredVersion, setDesiredVersion] = useState(0)\n\n const setDesired = useCallback((name: string, value: boolean | null) => {\n if (value === null) {\n if (!desiredActivations.current.delete(name)) return\n } else {\n if (desiredActivations.current.get(name) === value) return\n desiredActivations.current.set(name, value)\n }\n setDesiredVersion((version) => version + 1)\n }, [])\n\n const items = useMemo(() => {\n void desiredVersion\n const merged = mergeConnectorItems(plugins, catalog)\n return merged.map((item) => {\n if (configuring?.name === item.name && configuring.pendingActivation) {\n return { ...item, active: false }\n }\n const desired = desiredActivations.current.get(item.name)\n return desired === undefined ? item : { ...item, active: desired }\n })\n }, [catalog, configuring, desiredVersion, plugins])\n\n const markInFlight = useCallback((name: string, on: boolean) => {\n setInFlight((current) => {\n if (on) return current[name] ? current : { ...current, [name]: true }\n if (!current[name]) return current\n const { [name]: removed, ...rest } = current\n void removed\n return rest\n })\n }, [])\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: A different client starts a new ownership scope.\n useEffect(() => {\n alive.current = true\n setPlugins([])\n setCatalog(null)\n setLoading(false)\n setListError(false)\n setMutationError(false)\n setInFlight({})\n setConfiguring(null)\n desiredActivations.current.clear()\n mutationGenerations.current.clear()\n pendingMutations.current.clear()\n submittedActivations.current.clear()\n return () => {\n alive.current = false\n lifetime.current++\n generation.current++\n request.current?.abort()\n }\n }, [client])\n\n const load = useCallback(async () => {\n if (!alive.current) return\n request.current?.abort()\n const controller = new AbortController()\n request.current = controller\n const current = ++generation.current\n setLoading(true)\n setListError(false)\n try {\n const result = await client.sessions.listSessionPlugins(sessionId, { signal: controller.signal })\n const withConfig = await Promise.all(\n result.plugins.map(async (item) => {\n if (isLocalConnectorName(item.name) || item.config !== undefined && item.config !== \"unchecked\") return item\n try {\n const { config } = await client.sessions.getSessionPluginConfig(sessionId, item.name, {\n signal: controller.signal,\n })\n return { ...item, config: config.state }\n } catch {\n return item\n }\n }),\n )\n if (alive.current && current === generation.current) {\n serverActive.current = new Set(withConfig.filter((item) => item.active).map((item) => item.name))\n setPlugins(withConfig.map((item) => {\n const desired = desiredActivations.current.get(item.name)\n return desired === undefined ? item : { ...item, active: desired }\n }))\n }\n } catch {\n if (alive.current && current === generation.current && !controller.signal.aborted) setListError(true)\n } finally {\n if (alive.current && current === generation.current) setLoading(false)\n }\n }, [client, sessionId])\n\n const loadCatalog = useCallback(async () => {\n try {\n const { plugins: entries } = await client.plugins.catalog()\n if (alive.current) setCatalog(entries)\n } catch {\n }\n }, [client])\n\n const reload = useCallback(() => {\n void load()\n void loadCatalog()\n }, [load, loadCatalog])\n\n const abandon = useCallback((name: string) => {\n setDesired(name, null)\n pendingMutations.current.delete(name)\n mutationGenerations.current.set(name, (mutationGenerations.current.get(name) ?? 0) + 1)\n markInFlight(name, false)\n void load()\n }, [load, markInFlight, setDesired])\n\n const finish = useCallback((name: string, mutation: number) => {\n pendingMutations.current.delete(name)\n setDesired(name, null)\n if (mutation === mutationGenerations.current.get(name)) markInFlight(name, false)\n }, [markInFlight, setDesired])\n\n const commitActivation = useCallback(\n async (name: string, active: boolean, mutation: number, startedAt: number) => {\n submittedActivations.current.add(name)\n try {\n const { plugin } = await client.sessions.setSessionPluginActivation(sessionId, name, active)\n if (!alive.current || startedAt !== lifetime.current) return\n if (mutation !== mutationGenerations.current.get(name)) {\n const desired = desiredActivations.current.get(name)\n if (desired !== undefined && desired !== plugin.active) {\n const next = (mutationGenerations.current.get(name) ?? 0) + 1\n mutationGenerations.current.set(name, next)\n pendingMutations.current.set(name, next)\n markInFlight(name, true)\n await commitActivation(name, desired, next, startedAt)\n }\n return\n }\n if (active) serverActive.current.add(name)\n else serverActive.current.delete(name)\n finish(name, mutation)\n setPlugins((current) => current.map((item) => item.name === plugin.name ? { ...item, ...plugin } : item))\n onChange?.(plugin)\n if (plugin.active && plugin.status === \"required\") {\n setConfiguring({ name: plugin.name, pendingActivation: true })\n }\n await load()\n } catch {\n if (alive.current && startedAt === lifetime.current && mutation === mutationGenerations.current.get(name)) {\n pendingMutations.current.delete(name)\n submittedActivations.current.delete(name)\n setDesired(name, null)\n markInFlight(name, false)\n setMutationError(true)\n void load()\n }\n }\n },\n [client, finish, load, markInFlight, onChange, sessionId, setDesired],\n )\n\n const enable = useCallback(async (name: string) => {\n if (!alive.current) return\n const startedAt = lifetime.current\n const mutation = (mutationGenerations.current.get(name) ?? 0) + 1\n mutationGenerations.current.set(name, mutation)\n pendingMutations.current.set(name, mutation)\n setDesired(name, true)\n setMutationError(false)\n markInFlight(name, true)\n request.current?.abort()\n generation.current++\n setLoading(false)\n let state: SessionPluginConfigState\n try {\n const result = await client.sessions.prepareSessionPluginConfig(sessionId, name)\n if (!alive.current || startedAt !== lifetime.current || mutation !== mutationGenerations.current.get(name)) return\n state = result.config\n } catch {\n if (!alive.current || startedAt !== lifetime.current || mutation !== mutationGenerations.current.get(name)) return\n abandon(name)\n setMutationError(true)\n return\n }\n if (state.state === \"required\") {\n pendingMutations.current.delete(name)\n markInFlight(name, false)\n setConfiguring({ name, pendingActivation: true })\n return\n }\n if (state.state !== \"configured\" && state.state !== \"not_required\") {\n abandon(name)\n setMutationError(true)\n return\n }\n setPlugins((current) => current.map((item) => item.name === name ? { ...item, config: state.state } : item))\n await commitActivation(name, true, mutation, startedAt)\n }, [abandon, client, commitActivation, markInFlight, sessionId, setDesired])\n\n const disable = useCallback((name: string) => {\n const startedAt = lifetime.current\n request.current?.abort()\n generation.current++\n setLoading(false)\n if (!submittedActivations.current.has(name) && !serverActive.current.has(name)) {\n abandon(name)\n return\n }\n const mutation = (mutationGenerations.current.get(name) ?? 0) + 1\n mutationGenerations.current.set(name, mutation)\n pendingMutations.current.set(name, mutation)\n setDesired(name, false)\n setMutationError(false)\n markInFlight(name, true)\n void commitActivation(name, false, mutation, startedAt)\n }, [abandon, commitActivation, markInFlight, setDesired])\n\n const toggle = useCallback((item: PluginConnectorItem, next: boolean) => {\n if (next) void enable(item.name)\n else disable(item.name)\n }, [disable, enable])\n\n const retry = useCallback((item: PluginConnectorItem) => {\n void enable(item.name)\n }, [enable])\n\n const editConfig = useCallback((item: PluginConnectorItem) => {\n setConfiguring({ name: item.name, pendingActivation: false })\n }, [])\n\n const onConfigSaved = useCallback((state: SessionPluginConfigState) => {\n const target = configuring\n if (!target) return\n setConfiguring(null)\n void loadCatalog()\n setPlugins((current) => current.map((item) => item.name === target.name ? { ...item, config: state.state } : item))\n if (!target.pendingActivation || state.state !== \"configured\") {\n void load()\n return\n }\n const startedAt = lifetime.current\n const mutation = (mutationGenerations.current.get(target.name) ?? 0) + 1\n mutationGenerations.current.set(target.name, mutation)\n pendingMutations.current.set(target.name, mutation)\n setDesired(target.name, true)\n markInFlight(target.name, true)\n void commitActivation(target.name, true, mutation, startedAt)\n }, [commitActivation, configuring, load, loadCatalog, markInFlight, setDesired])\n\n const onConfigClose = useCallback(() => {\n const target = configuring\n if (!target) return\n setConfiguring(null)\n if (target.pendingActivation) abandon(target.name)\n else void load()\n }, [abandon, configuring, load])\n\n return {\n items,\n loading,\n listError,\n mutationError,\n inFlight,\n busy: Object.keys(inFlight).length > 0,\n configuring,\n reload,\n toggle,\n retry,\n editConfig,\n onConfigSaved,\n onConfigClose,\n }\n}\n","function usesCommandKey(): boolean {\n if (typeof navigator === \"undefined\") return false\n return /Mac|iPhone|iPad|iPod/i.test(navigator.platform || navigator.userAgent)\n}\n\nexport function queueShortcutLabel(): string {\n return usesCommandKey() ? \"⌘ Enter\" : \"Ctrl Enter\"\n}\n\nexport function isQueueShortcut(event: { metaKey: boolean; ctrlKey: boolean }): boolean {\n return usesCommandKey() ? event.metaKey : event.ctrlKey\n}\n","import type { AgentSession, MessageContent, SendOptions, SessionState } from \"@blade-hq/agent-client/internal\"\n\n/**\n * 等待回答时直接在输入框里打字:把这段文字当作最近一个子回路 AskUserQuestion 的自由作答,\n * 服务端据此恢复运行,而不是另起一轮。\n */\nexport function buildPendingAskUserAnswer(\n state: SessionState,\n message: MessageContent,\n): Record<string, unknown> | undefined {\n if (state.status !== \"waiting_for_input\") return undefined\n const text = extractMessageText(message)\n if (!text) return undefined\n for (let turnIndex = state.turns.length - 1; turnIndex >= 0; turnIndex -= 1) {\n const turn = state.turns[turnIndex]\n if (turn.loop_id === \"root\" || turn.role !== \"assistant\") continue\n for (let toolIndex = turn.tool_calls.length - 1; toolIndex >= 0; toolIndex -= 1) {\n const toolCall = turn.tool_calls[toolIndex]\n if (toolCall.status !== \"awaiting_answer\") continue\n if (!toolCall.tool_name.includes(\"AskUserQuestion\")) continue\n return { tool_call_id: toolCall.id, selections: {}, custom: { \"0\": text } }\n }\n }\n return undefined\n}\n\nfunction extractMessageText(message: MessageContent): string {\n if (typeof message === \"string\") return message.trim()\n return message\n .filter((part) => part.type === \"text\")\n .map((part) => part.text.trim())\n .filter(Boolean)\n .join(\"\\n\")\n .trim()\n}\n\n/** 发送附加项(模型、推理档位、知识库等由富输入给出)。 */\nexport interface ChatSendExtras {\n model?: string\n thinkingOverride?: boolean\n reasoningLevel?: string\n contextAttachments?: SendOptions[\"contextAttachments\"]\n refs?: SendOptions[\"refs\"]\n topicAssetRefs?: SendOptions[\"topicAssetRefs\"]\n whatif?: SendOptions[\"whatif\"]\n replayDecision?: SendOptions[\"replayDecision\"]\n /**\n * 默认发送引导消息;显式为 true 时,等待当前运行结束后处理。\n */\n queueWhenRunning?: boolean\n}\n\n/** 向会话发送一条消息。返回服务端是否接受(或已改道进等待队列)。 */\nexport function sendToSession(\n session: AgentSession,\n message: MessageContent,\n mode?: string,\n askUserAnswer?: Record<string, unknown>,\n extras: ChatSendExtras = {},\n): Promise<boolean> {\n return session.send(message, {\n mode,\n askUserAnswer: askUserAnswer ?? buildPendingAskUserAnswer(session.getState(), message),\n model: extras.model,\n thinkingOverride: extras.thinkingOverride,\n reasoningLevel: extras.reasoningLevel,\n refs: extras.refs,\n contextAttachments: extras.contextAttachments,\n topicAssetRefs: extras.topicAssetRefs,\n whatif: extras.whatif,\n replayDecision: extras.replayDecision,\n queueWhenRunning: extras.queueWhenRunning,\n })\n}\n","/**\n * 聊天区的语义配色。\n *\n * 全站共用一套令牌(见仓库根 DESIGN.md):中性灰负责层级,颜色只表达状态。\n * 所以「进行中」「思考中」这类过程态走中性色,只有成功 / 警告 / 失败三种结果态\n * 才允许带颜色。\n *\n * 不要在组件里直接写调色板类(text-blue-500 这种)——它们不走 CSS 变量,\n * 切主题时不会变。需要新的语义时加到下面的 TONE_CLASSES 里。\n */\n\n/** 颜色表达的是什么,而不是它长什么样。 */\nexport type ChatTone =\n | \"running\"\n | \"reasoning\"\n | \"success\"\n | \"warning\"\n | \"danger\"\n\ninterface ToneStyle {\n text: string\n border: string\n surface: string\n}\n\nconst NEUTRAL: ToneStyle = {\n text: \"text-[hsl(var(--muted-foreground))]\",\n border: \"border-l-[hsl(var(--swf-border-strong))]\",\n surface: \"bg-[hsl(var(--muted))]\",\n}\n\nconst TONE_CLASSES: Record<ChatTone, ToneStyle> = {\n running: NEUTRAL,\n reasoning: NEUTRAL,\n success: {\n text: \"text-[hsl(var(--swf-success))]\",\n border: \"border-l-[hsl(var(--swf-success))]\",\n surface: \"bg-[hsl(var(--swf-success-soft))]\",\n },\n warning: {\n text: \"text-[hsl(var(--swf-warning))]\",\n border: \"border-l-[hsl(var(--swf-warning))]\",\n surface: \"bg-[hsl(var(--swf-warning-soft))]\",\n },\n danger: {\n text: \"text-[hsl(var(--swf-danger))]\",\n border: \"border-l-[hsl(var(--swf-danger))]\",\n surface: \"bg-[hsl(var(--swf-danger-soft))]\",\n },\n}\n\nexport function chatTone(tone: ChatTone): ToneStyle {\n return TONE_CLASSES[tone]\n}\n","import * as React from \"react\"\n\ntype AnyFunction = (...args: never[]) => unknown\n\n/**\n * `useEffectEvent`:React 19.2 起内置;本包还要支持 React 18,缺失时用 ref 实现同样的语义——\n * 返回的函数总是调用最新一次渲染的回调,只能在 effect / 事件回调里调用,不能写进依赖数组。\n */\nfunction useEffectEventShim<T extends AnyFunction>(callback: T): T {\n const ref = React.useRef(callback)\n React.useLayoutEffect(() => {\n ref.current = callback\n })\n return React.useCallback(((...args) => ref.current(...args)) as T, [])\n}\n\nconst builtin = (React as unknown as { useEffectEvent?: <T extends AnyFunction>(callback: T) => T }).useEffectEvent\n\nexport const useEffectEvent: <T extends AnyFunction>(callback: T) => T = builtin ?? useEffectEventShim\n"],"mappings":"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","names":["key","entry","useEffect","useEffect","useRef","useRef","useEffect","jsx","useEffect","useRef","useState","jsx","useState","useEffect","useRef","generation","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","useState","useRef","generation","useCallback","useMemo","useEffect","useEffectEvent"]}
@@ -1,18 +1,23 @@
1
1
  import {
2
2
  PluginConnectorList,
3
- SessionPluginConfigForm,
4
3
  SessionPluginIcon,
5
4
  catalogToConnectorItems,
6
5
  filterConnectorItems,
7
6
  useSessionPluginActivation
8
- } from "./chunk-F2IJZX2Z.js";
7
+ } from "./chunk-F4MWGSKX.js";
9
8
  import {
10
9
  ConnectorConnection
11
- } from "./chunk-WO2VCQ5N.js";
10
+ } from "./chunk-FH5HA3GW.js";
12
11
  import {
13
12
  AccountConnectorRules,
14
13
  BrowserConnectionDialog
15
- } from "./chunk-EQOKPZI2.js";
14
+ } from "./chunk-NRYLAO3X.js";
15
+ import {
16
+ ConfigConnection,
17
+ Dialog,
18
+ DialogContent,
19
+ DialogTitle
20
+ } from "./chunk-MWILWSPG.js";
16
21
  import {
17
22
  Check,
18
23
  LoaderCircle,
@@ -20,9 +25,8 @@ import {
20
25
  Plug,
21
26
  RefreshCw,
22
27
  Search,
23
- Trash2,
24
- Unplug
25
- } from "./chunk-XBEN2ASY.js";
28
+ Trash2
29
+ } from "./chunk-KHJEIHMJ.js";
26
30
 
27
31
  // src/components/connector-panel-types.ts
28
32
  function selectionFromItem(item) {
@@ -46,7 +50,9 @@ function triggerSummary(items) {
46
50
  // src/components/AccountConnectors.tsx
47
51
  import { isLocalConnectorName } from "@blade-hq/agent-client/internal";
48
52
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
53
+ import { Suspense, lazy } from "react";
49
54
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
55
+ var SessionPluginConfigForm = lazy(() => import("./SessionPluginConfigForm-7LJ5SUNY.js").then((module) => ({ default: module.SessionPluginConfigForm })));
50
56
  var actionBaseClass = "inline-flex h-8 shrink-0 items-center justify-center gap-1.5 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] text-xs font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--accent))] focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50";
51
57
  var actionClass = `${actionBaseClass} px-3`;
52
58
  var iconActionClass = `${actionBaseClass} w-8 p-0`;
@@ -64,38 +70,30 @@ async function failureMessage(error) {
64
70
  }
65
71
  function DeviceDialog({ client, connector, onChanged, onClose }) {
66
72
  const [rulesRefresh, setRulesRefresh] = useState(0);
67
- const dialog = useRef(null);
68
- useEffect(() => {
69
- const element = dialog.current;
70
- element?.showModal();
71
- return () => element?.close();
72
- }, []);
73
- return /* @__PURE__ */ jsxs("dialog", { ref: dialog, "aria-label": `\u8FDE\u63A5 ${connector.display_name || connector.name}`, onCancel: (event) => {
74
- event.preventDefault();
75
- onClose();
76
- }, className: "fixed inset-0 m-auto max-h-[85vh] w-[min(640px,90vw)] overflow-y-auto rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--background))] p-6 text-[hsl(var(--foreground))] backdrop:bg-black/40", children: [
77
- /* @__PURE__ */ jsx(ConnectorConnection, { type: connector.authorization?.type === "managed" ? "managed" : "device", read: () => client.connectors.authorization(connector.name), act: (action, options) => client.connectors.authorize(connector.name, action, void 0, options), plan: (capabilities) => client.connectors.authorizationPlan(connector.name, capabilities), diagnostics: () => client.connectors.diagnostics(connector.name), onChanged: async () => {
73
+ return /* @__PURE__ */ jsx(Dialog, { open: true, onOpenChange: (open) => {
74
+ if (!open) onClose();
75
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { "aria-describedby": void 0, className: "max-h-[85dvh] overflow-y-auto sm:max-w-[640px]", children: [
76
+ /* @__PURE__ */ jsxs(DialogTitle, { className: "sr-only", children: [
77
+ "\u8FDE\u63A5 ",
78
+ connector.display_name || connector.name
79
+ ] }),
80
+ /* @__PURE__ */ jsx(ConnectorConnection, { detail: { name: connector.name, title: connector.display_name || connector.name, icon: /* @__PURE__ */ jsx(SessionPluginIcon, { client, sessionId: null, plugin: connector, size: 44 }), rules: /* @__PURE__ */ jsx(AccountConnectorRules, { client, name: connector.name, refresh: rulesRefresh }) }, type: connector.authorization?.type === "managed" ? "managed" : "device", read: () => client.connectors.authorization(connector.name), act: (action, options) => client.connectors.authorize(connector.name, action, void 0, options), plan: (capabilities) => client.connectors.authorizationPlan(connector.name, capabilities), diagnostics: () => client.connectors.diagnostics(connector.name), onChanged: async () => {
78
81
  await onChanged();
79
82
  setRulesRefresh((value) => value + 1);
80
83
  }, onDisable: () => client.connectors.disconnect(connector.name) }),
81
- /* @__PURE__ */ jsx(AccountConnectorRules, { client, name: connector.name, refresh: rulesRefresh }),
82
84
  /* @__PURE__ */ jsx("button", { type: "button", className: actionClass, onClick: onClose, children: "\u8FD4\u56DE\u8FDE\u63A5\u5668" })
83
- ] });
85
+ ] }) });
84
86
  }
85
- function ConnectionDialog({ client, connector, onConnected, onClose }) {
87
+ function ConnectionDialog({ client, connector, onConnected, onChanged, onClose }) {
86
88
  const [view, setView] = useState(null);
87
89
  const [failure, setFailure] = useState(null);
88
90
  const [submitting, setSubmitting] = useState(false);
89
- const [connected, setConnected] = useState(false);
90
- const dialog = useRef(null);
91
+ const [editing, setEditing] = useState(false);
92
+ const [rulesRefresh, setRulesRefresh] = useState(0);
91
93
  const connectionRequest = useRef(null);
92
- useEffect(() => () => connectionRequest.current?.abort(), []);
93
- useEffect(() => {
94
- const element = dialog.current;
95
- element?.showModal();
96
- return () => element?.close();
97
- }, []);
94
+ const alive = useRef(false);
98
95
  useEffect(() => {
96
+ alive.current = true;
99
97
  const abort = new AbortController();
100
98
  client.connectors.read(connector.name, { signal: abort.signal }).then((value) => {
101
99
  if (!abort.signal.aborted) setView(value);
@@ -103,74 +101,84 @@ function ConnectionDialog({ client, connector, onConnected, onClose }) {
103
101
  const message = await failureMessage(error);
104
102
  if (!abort.signal.aborted) setFailure(message);
105
103
  });
106
- return () => abort.abort();
104
+ return () => {
105
+ alive.current = false;
106
+ abort.abort();
107
+ connectionRequest.current?.abort();
108
+ };
107
109
  }, [client, connector.name]);
108
- return /* @__PURE__ */ jsx(
109
- "dialog",
110
- {
111
- ref: dialog,
112
- "aria-label": `\u8FDE\u63A5 ${connector.display_name || connector.name}`,
113
- className: "fixed inset-0 m-0 h-full max-h-none w-full max-w-none items-center justify-center border-0 bg-black/40 p-0 open:flex",
114
- onCancel: (event) => {
115
- event.preventDefault();
116
- if (!submitting) onClose();
117
- },
118
- children: /* @__PURE__ */ jsxs("div", { className: "max-h-[80vh] w-[min(448px,calc(100%_-_32px))] overflow-y-auto rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--popover))] p-4 text-[hsl(var(--popover-foreground))] shadow-lg", children: [
119
- /* @__PURE__ */ jsxs("div", { className: "mb-3 text-sm font-medium", children: [
120
- "\u8FDE\u63A5 ",
121
- connector.display_name || connector.name
122
- ] }),
123
- /* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-[hsl(var(--muted-foreground))]", children: view?.config.state === "not_required" ? "\u65E0\u9700\u586B\u5199\u8D26\u53F7\uFF0C\u9A8C\u8BC1\u8FDE\u63A5\u540E\u5373\u53EF\u4F7F\u7528\u3002" : "\u586B\u5199\u4F60\u7684\u8D26\u53F7\u4FE1\u606F\uFF0C\u9A8C\u8BC1\u901A\u8FC7\u540E\u53EF\u5728\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u3002" }),
124
- !view ? /* @__PURE__ */ jsx("div", { className: "text-xs", children: failure || "\u6B63\u5728\u83B7\u53D6\u8BA4\u8BC1\u8868\u5355\u2026" }) : !connected ? /* @__PURE__ */ jsx(
125
- SessionPluginConfigForm,
126
- {
127
- config: view.config,
128
- submitting,
129
- errors: [],
130
- failure,
131
- submitLabel: "\u8FDE\u63A5\u5E76\u9A8C\u8BC1",
132
- submittingLabel: "\u6B63\u5728\u8FDE\u63A5\u2026",
133
- onCancel: onClose,
134
- onSubmit: async (draft) => {
135
- setSubmitting(true);
136
- setFailure(null);
137
- const request = new AbortController();
138
- connectionRequest.current = request;
139
- try {
140
- const connected2 = await client.connectors.connect(connector.name, {
141
- expected_revision: view.config.revision,
142
- set: draft.set,
143
- remove: draft.remove
144
- }, { signal: request.signal });
145
- if (request.signal.aborted) return;
146
- setView(connected2);
147
- setConnected(true);
148
- try {
149
- await onConnected();
150
- } catch {
151
- if (!request.signal.aborted) setFailure("\u5DF2\u8FDE\u63A5\uFF0C\u4F46\u5217\u8868\u6216\u5BF9\u8BDD\u72B6\u6001\u672A\u80FD\u5237\u65B0\u3002\u8BF7\u8FD4\u56DE\u540E\u91CD\u8BD5\u5237\u65B0\u6216\u542F\u7528\u3002");
152
- return;
153
- }
154
- if (!request.signal.aborted) onClose();
155
- } catch (error) {
156
- if (request.signal.aborted) return;
157
- setFailure(await failureMessage(error));
158
- try {
159
- setView(await client.connectors.read(connector.name));
160
- } catch {
161
- }
162
- } finally {
163
- if (!request.signal.aborted) setSubmitting(false);
164
- }
165
- }
166
- }
167
- ) : null,
168
- /* @__PURE__ */ jsx(AccountConnectorRules, { client, name: connector.name }),
169
- connected ? /* @__PURE__ */ jsx("output", { className: "block text-sm", children: failure || "\u8D26\u53F7\u5DF2\u8FDE\u63A5\u3002" }) : null,
170
- !view || connected ? /* @__PURE__ */ jsx("button", { type: "button", className: `${actionClass} mt-3`, onClick: onClose, children: "\u5173\u95ED" }) : null
171
- ] })
110
+ async function submit(draft) {
111
+ if (!view || submitting) return;
112
+ setSubmitting(true);
113
+ setFailure(null);
114
+ const request = new AbortController();
115
+ connectionRequest.current = request;
116
+ try {
117
+ const next = await client.connectors.connect(connector.name, { expected_revision: view.config.revision, ...draft }, { signal: request.signal });
118
+ if (request.signal.aborted) return;
119
+ setView(next);
120
+ setEditing(false);
121
+ setRulesRefresh((value) => value + 1);
122
+ try {
123
+ await onConnected();
124
+ } catch {
125
+ if (!request.signal.aborted) setFailure("\u5DF2\u8FDE\u63A5\uFF0C\u4F46\u5217\u8868\u6216\u5BF9\u8BDD\u72B6\u6001\u672A\u80FD\u5237\u65B0\u3002\u8BF7\u8FD4\u56DE\u540E\u91CD\u8BD5\u5237\u65B0\u6216\u542F\u7528\u3002");
126
+ }
127
+ } catch (error) {
128
+ if (request.signal.aborted) return;
129
+ setFailure(await failureMessage(error));
130
+ try {
131
+ const next = await client.connectors.read(connector.name);
132
+ if (!request.signal.aborted) setView(next);
133
+ } catch {
134
+ }
135
+ } finally {
136
+ if (!request.signal.aborted) setSubmitting(false);
172
137
  }
173
- );
138
+ }
139
+ const form = view && /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("output", { children: "\u6B63\u5728\u8BFB\u53D6\u8FDE\u63A5\u8868\u5355\u2026" }), children: /* @__PURE__ */ jsx(SessionPluginConfigForm, { collapseHelp: true, config: view.config, submitting, errors: [], failure, submitLabel: editing ? "\u4FDD\u5B58\u5E76\u8FDE\u63A5" : "\u8FDE\u63A5\u5E76\u9A8C\u8BC1", submittingLabel: "\u6B63\u5728\u8FDE\u63A5\u2026", onCancel: () => editing ? setEditing(false) : onClose(), onSubmit: (draft) => void submit(draft) }) });
140
+ const current = view?.connection ?? connector.connection;
141
+ return /* @__PURE__ */ jsx(Dialog, { open: true, onOpenChange: (open) => {
142
+ if (!open && !submitting) onClose();
143
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { "aria-describedby": void 0, className: "max-h-[85dvh] overflow-y-auto sm:max-w-[640px]", onEscapeKeyDown: (event) => {
144
+ if (submitting) event.preventDefault();
145
+ }, onInteractOutside: (event) => {
146
+ if (submitting) event.preventDefault();
147
+ }, children: [
148
+ /* @__PURE__ */ jsxs(DialogTitle, { className: "sr-only", children: [
149
+ "\u8FDE\u63A5 ",
150
+ connector.display_name || connector.name
151
+ ] }),
152
+ /* @__PURE__ */ jsx(ConfigConnection, { detail: { title: connector.display_name || connector.name, icon: /* @__PURE__ */ jsx(SessionPluginIcon, { client, sessionId: null, plugin: connector, size: 44 }), rules: /* @__PURE__ */ jsx(AccountConnectorRules, { client, name: connector.name, refresh: rulesRefresh }) }, status: current.status, account: current.account_label, verifiedAt: current.verified_at, busy: submitting, connection: view ? form : /* @__PURE__ */ jsx("output", { children: failure || "\u6B63\u5728\u83B7\u53D6\u8BA4\u8BC1\u8868\u5355\u2026" }), onEdit: () => setEditing(true), onDisconnect: async () => {
153
+ setSubmitting(true);
154
+ try {
155
+ const next = await client.connectors.disconnect(connector.name);
156
+ if (alive.current) {
157
+ setView(next);
158
+ setRulesRefresh((value) => value + 1);
159
+ }
160
+ await onChanged();
161
+ } finally {
162
+ if (alive.current) setSubmitting(false);
163
+ }
164
+ }, onVerify: async () => {
165
+ const next = await client.connectors.verify(connector.name);
166
+ if (alive.current) setView(next);
167
+ } }),
168
+ failure && current.status === "connected" && /* @__PURE__ */ jsx("p", { role: "alert", children: failure }),
169
+ editing && /* @__PURE__ */ jsx(Dialog, { open: true, onOpenChange: (open) => {
170
+ if (!open && !submitting) setEditing(false);
171
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { "aria-describedby": void 0, className: "max-h-[85dvh] overflow-y-auto", onEscapeKeyDown: (event) => {
172
+ if (submitting) event.preventDefault();
173
+ }, onInteractOutside: (event) => {
174
+ if (submitting) event.preventDefault();
175
+ }, children: [
176
+ /* @__PURE__ */ jsx(DialogTitle, { children: "\u66F4\u6362\u8D26\u53F7" }),
177
+ /* @__PURE__ */ jsx("p", { children: "\u4FDD\u5B58\u540E\u65E7\u8D26\u53F7\u8FDE\u63A5\u4F1A\u88AB\u66FF\u6362\uFF0C\u4F7F\u7528\u89C4\u5219\u4FDD\u7559\u3002" }),
178
+ form
179
+ ] }) }),
180
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: submitting, className: actionClass, onClick: onClose, children: "\u8FD4\u56DE\u8FDE\u63A5\u5668" })
181
+ ] }) });
174
182
  }
175
183
  function AccountConnectors({ client, query = "", sessionId = null, pending, onUse, onSelectedChange, disabledReason, catalog = false, manage = false, onEdit }) {
176
184
  const [items, setItems] = useState([]);
@@ -277,23 +285,10 @@ function AccountConnectors({ client, query = "", sessionId = null, pending, onUs
277
285
  catalog ? /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2 border-t border-[hsl(var(--border))] pt-4", children: [
278
286
  connectAction,
279
287
  useAction,
280
- manage ? /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [
281
- /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": ["device", "managed"].includes(item.authorization?.type ?? "") ? "\u505C\u7528 BA \u8FDE\u63A5" : "\u65AD\u5F00", title: ["device", "managed"].includes(item.authorization?.type ?? "") ? "\u4EC5\u505C\u7528 BA\uFF0CSuperWork \u8D26\u53F7\u548C\u6D88\u606F\u8BA2\u9605\u4FDD\u7559" : "\u65AD\u5F00\u8FDE\u63A5", className: iconActionClass, onClick: async () => {
282
- setBusy(true);
283
- try {
284
- await client.connectors.disconnect(item.name);
285
- await reload();
286
- } catch (error) {
287
- setFailure(await failureMessage(error));
288
- } finally {
289
- setBusy(false);
290
- }
291
- }, children: /* @__PURE__ */ jsx(Unplug, { size: 15, "aria-hidden": "true" }) }),
292
- item.source === "personal" ? /* @__PURE__ */ jsxs(Fragment, { children: [
293
- /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": "\u7F16\u8F91", title: "\u7F16\u8F91\u8FDE\u63A5\u5668", className: iconActionClass, onClick: () => onEdit?.(item), children: /* @__PURE__ */ jsx(Pencil, { size: 15, "aria-hidden": "true" }) }),
294
- /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": "\u5220\u9664", title: "\u5220\u9664\u8FDE\u63A5\u5668", className: `${iconActionClass} text-[hsl(var(--destructive))] hover:border-[hsl(var(--destructive)/0.3)] hover:bg-[hsl(var(--destructive)/0.08)]`, onClick: () => setDeleting(item), children: /* @__PURE__ */ jsx(Trash2, { size: 15, "aria-hidden": "true" }) })
295
- ] }) : null
296
- ] }) : null
288
+ manage ? /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1.5", children: item.source === "personal" ? /* @__PURE__ */ jsxs(Fragment, { children: [
289
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": "\u7F16\u8F91", title: "\u7F16\u8F91\u8FDE\u63A5\u5668", className: iconActionClass, onClick: () => onEdit?.(item), children: /* @__PURE__ */ jsx(Pencil, { size: 15, "aria-hidden": "true" }) }),
290
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": "\u5220\u9664", title: "\u5220\u9664\u8FDE\u63A5\u5668", className: `${iconActionClass} text-[hsl(var(--destructive))] hover:border-[hsl(var(--destructive)/0.3)] hover:bg-[hsl(var(--destructive)/0.08)]`, onClick: () => setDeleting(item), children: /* @__PURE__ */ jsx(Trash2, { size: 15, "aria-hidden": "true" }) })
291
+ ] }) : null }) : null
297
292
  ] }) : null
298
293
  ] }, item.name);
299
294
  }),
@@ -332,10 +327,10 @@ function AccountConnectors({ client, query = "", sessionId = null, pending, onUs
332
327
  editing && ["device", "managed"].includes(editing.authorization?.type ?? "") ? /* @__PURE__ */ jsx(DeviceDialog, { client, connector: editing, onChanged: reload, onClose: () => {
333
328
  setEditing(null);
334
329
  void reload();
335
- } }, editing.name) : editing?.authorization?.type === "browser" ? /* @__PURE__ */ jsx(BrowserConnectionDialog, { client, name: editing.name, label: editing.display_name || editing.name, onClose: () => {
330
+ } }, editing.name) : editing?.authorization?.type === "browser" ? /* @__PURE__ */ jsx(BrowserConnectionDialog, { connector: editing, client, name: editing.name, label: editing.display_name || editing.name, onClose: () => {
336
331
  setEditing(null);
337
332
  void reload().catch(() => setFailure("\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"));
338
- } }, editing.name) : editing ? /* @__PURE__ */ jsx(ConnectionDialog, { client, connector: editing, onClose: () => setEditing(null), onConnected: async () => {
333
+ } }, editing.name) : editing ? /* @__PURE__ */ jsx(ConnectionDialog, { client, onChanged: reload, connector: editing, onClose: () => setEditing(null), onConnected: async () => {
339
334
  const scope = lifetime.current;
340
335
  await reload();
341
336
  if (scope !== lifetime.current || scope?.signal.aborted) return;
@@ -609,4 +604,4 @@ export {
609
604
  ConnectorPanel,
610
605
  PluginTriggerStack
611
606
  };
612
- //# sourceMappingURL=chunk-ZIECHNZA.js.map
607
+ //# sourceMappingURL=chunk-FBGCDXRK.js.map