@gendive/chatllm 0.21.5 → 0.21.7

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.
@@ -1819,6 +1819,46 @@ interface UseFloatingWidgetReturn {
1819
1819
  toggle: () => void;
1820
1820
  setTab: (tabKey: string) => void;
1821
1821
  }
1822
+ /**
1823
+ * @description 대화 검색 결과 스니펫
1824
+ * @Todo vibecode - DB에서 반환되는 과거 대화 조각
1825
+ */
1826
+ interface ConversationSnippet {
1827
+ /** 세션 ID */
1828
+ sessionId: string;
1829
+ /** 세션 제목 */
1830
+ sessionTitle?: string;
1831
+ /** 발화자 */
1832
+ role: 'user' | 'assistant';
1833
+ /** 메시지 내용 (또는 발췌) */
1834
+ content: string;
1835
+ /** 메시지 타임스탬프 */
1836
+ timestamp?: number;
1837
+ /** 검색 관련도 점수 (0-1) */
1838
+ relevance?: number;
1839
+ }
1840
+ /**
1841
+ * @description 대화 검색 옵션
1842
+ * @Todo vibecode - 검색 범위 제한용
1843
+ */
1844
+ interface ConversationSearchOptions {
1845
+ /** 최대 결과 수 (기본: 5) */
1846
+ limit?: number;
1847
+ /** 특정 세션으로 제한 */
1848
+ sessionId?: string;
1849
+ }
1850
+ /**
1851
+ * @description createConversationSearchSkill 팩토리 옵션
1852
+ * @Todo vibecode - 소비 앱이 DB 검색 구현을 제공
1853
+ */
1854
+ interface ConversationSearchSkillOptions {
1855
+ /** 검색 구현 (호스트 DB 쿼리) */
1856
+ onSearch: (query: string, options?: ConversationSearchOptions) => Promise<ConversationSnippet[]>;
1857
+ /** 스킬 라벨 (기본: '대화 검색') */
1858
+ label?: string;
1859
+ /** 비활성화 여부 */
1860
+ disabled?: boolean;
1861
+ }
1822
1862
 
1823
1863
  /**
1824
1864
  * @description ChatUI 메인 컴포넌트
@@ -2401,6 +2441,30 @@ declare const createAdvancedResearchSkill: (options: AdvancedResearchOptions) =>
2401
2441
  */
2402
2442
  declare const createDeepResearchSkill: (callbacks: DeepResearchCallbacks) => SkillConfig;
2403
2443
 
2444
+ /**
2445
+ * @description 대화 검색 스킬 팩토리
2446
+ * @Todo vibecode - 호스트 DB 검색을 SkillConfig로 래핑하여 LLM tool use로 과거 대화 검색
2447
+ *
2448
+ * 사용 예시:
2449
+ * ```ts
2450
+ * import { createConversationSearchSkill } from '@gendive/chatllm/react';
2451
+ *
2452
+ * const searchSkill = createConversationSearchSkill({
2453
+ * onSearch: async (query, options) => {
2454
+ * return await db.conversations.search(query, options?.limit);
2455
+ * },
2456
+ * });
2457
+ *
2458
+ * <ChatUI skills={{ conversation_search: searchSkill }} />
2459
+ * ```
2460
+ */
2461
+
2462
+ /**
2463
+ * @description 대화 검색 스킬 생성 팩토리
2464
+ * @Todo vibecode - LLM이 tool use로 과거 대화를 검색할 수 있는 스킬 생성
2465
+ */
2466
+ declare const createConversationSearchSkill: (options: ConversationSearchSkillOptions) => SkillConfig;
2467
+
2404
2468
  /**
2405
2469
  * @description Remix Icons wrapper component
2406
2470
  * @see https://remixicon.com/
@@ -3018,4 +3082,4 @@ declare const DEFAULT_PROJECT_TITLE = "\uAE30\uBCF8 \uD504\uB85C\uC81D\uD2B8";
3018
3082
  */
3019
3083
  declare const migrateSessionsToProjects: (storageKey: string) => void;
3020
3084
 
3021
- export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
3085
+ export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, type ConversationSearchOptions, type ConversationSearchSkillOptions, type ConversationSnippet, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createConversationSearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
@@ -1819,6 +1819,46 @@ interface UseFloatingWidgetReturn {
1819
1819
  toggle: () => void;
1820
1820
  setTab: (tabKey: string) => void;
1821
1821
  }
1822
+ /**
1823
+ * @description 대화 검색 결과 스니펫
1824
+ * @Todo vibecode - DB에서 반환되는 과거 대화 조각
1825
+ */
1826
+ interface ConversationSnippet {
1827
+ /** 세션 ID */
1828
+ sessionId: string;
1829
+ /** 세션 제목 */
1830
+ sessionTitle?: string;
1831
+ /** 발화자 */
1832
+ role: 'user' | 'assistant';
1833
+ /** 메시지 내용 (또는 발췌) */
1834
+ content: string;
1835
+ /** 메시지 타임스탬프 */
1836
+ timestamp?: number;
1837
+ /** 검색 관련도 점수 (0-1) */
1838
+ relevance?: number;
1839
+ }
1840
+ /**
1841
+ * @description 대화 검색 옵션
1842
+ * @Todo vibecode - 검색 범위 제한용
1843
+ */
1844
+ interface ConversationSearchOptions {
1845
+ /** 최대 결과 수 (기본: 5) */
1846
+ limit?: number;
1847
+ /** 특정 세션으로 제한 */
1848
+ sessionId?: string;
1849
+ }
1850
+ /**
1851
+ * @description createConversationSearchSkill 팩토리 옵션
1852
+ * @Todo vibecode - 소비 앱이 DB 검색 구현을 제공
1853
+ */
1854
+ interface ConversationSearchSkillOptions {
1855
+ /** 검색 구현 (호스트 DB 쿼리) */
1856
+ onSearch: (query: string, options?: ConversationSearchOptions) => Promise<ConversationSnippet[]>;
1857
+ /** 스킬 라벨 (기본: '대화 검색') */
1858
+ label?: string;
1859
+ /** 비활성화 여부 */
1860
+ disabled?: boolean;
1861
+ }
1822
1862
 
1823
1863
  /**
1824
1864
  * @description ChatUI 메인 컴포넌트
@@ -2401,6 +2441,30 @@ declare const createAdvancedResearchSkill: (options: AdvancedResearchOptions) =>
2401
2441
  */
2402
2442
  declare const createDeepResearchSkill: (callbacks: DeepResearchCallbacks) => SkillConfig;
2403
2443
 
2444
+ /**
2445
+ * @description 대화 검색 스킬 팩토리
2446
+ * @Todo vibecode - 호스트 DB 검색을 SkillConfig로 래핑하여 LLM tool use로 과거 대화 검색
2447
+ *
2448
+ * 사용 예시:
2449
+ * ```ts
2450
+ * import { createConversationSearchSkill } from '@gendive/chatllm/react';
2451
+ *
2452
+ * const searchSkill = createConversationSearchSkill({
2453
+ * onSearch: async (query, options) => {
2454
+ * return await db.conversations.search(query, options?.limit);
2455
+ * },
2456
+ * });
2457
+ *
2458
+ * <ChatUI skills={{ conversation_search: searchSkill }} />
2459
+ * ```
2460
+ */
2461
+
2462
+ /**
2463
+ * @description 대화 검색 스킬 생성 팩토리
2464
+ * @Todo vibecode - LLM이 tool use로 과거 대화를 검색할 수 있는 스킬 생성
2465
+ */
2466
+ declare const createConversationSearchSkill: (options: ConversationSearchSkillOptions) => SkillConfig;
2467
+
2404
2468
  /**
2405
2469
  * @description Remix Icons wrapper component
2406
2470
  * @see https://remixicon.com/
@@ -3018,4 +3082,4 @@ declare const DEFAULT_PROJECT_TITLE = "\uAE30\uBCF8 \uD504\uB85C\uC81D\uD2B8";
3018
3082
  */
3019
3083
  declare const migrateSessionsToProjects: (storageKey: string) => void;
3020
3084
 
3021
- export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
3085
+ export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, type ConversationSearchOptions, type ConversationSearchSkillOptions, type ConversationSnippet, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createConversationSearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
@@ -68,6 +68,7 @@ __export(index_exports, {
68
68
  convertSkillsToOpenAITools: () => convertSkillsToOpenAITools,
69
69
  convertToolsToSkills: () => convertToolsToSkills,
70
70
  createAdvancedResearchSkill: () => createAdvancedResearchSkill,
71
+ createConversationSearchSkill: () => createConversationSearchSkill,
71
72
  createDeepResearchSkill: () => createDeepResearchSkill,
72
73
  migrateSessionsToProjects: () => migrateSessionsToProjects,
73
74
  useChatUI: () => useChatUI,
@@ -2843,6 +2844,11 @@ ${projectMemoryContext}`);
2843
2844
  - \uB370\uC774\uD130 \uBE44\uAD50/\uCD94\uC774 \u2192 html (Chart.js CDN \uC0AC\uC6A9, <artifact> \uD0DC\uADF8)
2844
2845
  - \uC544\uC774\uCF58/\uB85C\uACE0/\uC77C\uB7EC\uC2A4\uD2B8 \u2192 svg
2845
2846
 
2847
+ **mermaid \uC8FC\uC758\uC0AC\uD56D:**
2848
+ - \uB178\uB4DC \uB77C\uBCA8\uC5D0 \uAD04\uD638 () \uC0AC\uC6A9 \uAE08\uC9C0 \u2192 \uB300\uAD04\uD638 [] \uC0AC\uC6A9: B[\uC0C1\uD0DC \uAD00\uB9AC useState] \u2705 B[\uC0C1\uD0DC \uAD00\uB9AC (useState)] \u274C
2849
+ - \uB178\uB4DC \uB77C\uBCA8\uC5D0 \uD2B9\uC218\uBB38\uC790\uAC00 \uC788\uC73C\uBA74 \uD070\uB530\uC634\uD45C\uB85C \uAC10\uC2F8\uAE30: B["API \uD638\uCD9C \u2192 \uC751\uB2F5"]
2850
+ - \uC18C\uAD04\uD638 ()\uB294 mermaid \uBB38\uBC95\uC5D0\uC11C \uB465\uADFC \uB178\uB4DC\uB97C \uC758\uBBF8\uD558\uBBC0\uB85C \uB77C\uBCA8 \uD14D\uC2A4\uD2B8\uC5D0 \uC808\uB300 \uD3EC\uD568\uD558\uC9C0 \uB9C8\uC138\uC694
2851
+
2846
2852
  **mermaid \uC608\uC2DC:**
2847
2853
  \`\`\`mermaid
2848
2854
  graph TD
@@ -2867,7 +2873,7 @@ new Chart(document.getElementById('chart'), {
2867
2873
  - html \uC2DC\uAC01\uD654\uB294 \uBC18\uB4DC\uC2DC <artifact language="html"> \uD0DC\uADF8\uB85C \uAC10\uC2F8\uC57C \uD568 (\`\`\`html \uCF54\uB4DC\uBE14\uB85D \uC0AC\uC6A9 \uAE08\uC9C0)
2868
2874
  - \uC0AC\uC6A9\uC790\uAC00 \uC2DC\uAC01\uD654\uB97C \uC694\uCCAD\uD558\uC9C0 \uC54A\uC544\uB3C4 \uC801\uC808\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uD3EC\uD568
2869
2875
  - \uC2DC\uAC01\uD654 \uC55E\uB4A4\uC5D0 \uAC04\uACB0\uD55C \uD14D\uC2A4\uD2B8 \uC124\uBA85 \uD3EC\uD568
2870
- - mermaid \uBB38\uBC95 \uC624\uB958 \uC8FC\uC758 (\uD2B9\uD788 \uAD04\uD638, \uB530\uC634\uD45C \uC774\uC2A4\uCF00\uC774\uD504)`);
2876
+ - mermaid \uB178\uB4DC \uB77C\uBCA8\uC5D0 \uC18C\uAD04\uD638 () \uC808\uB300 \uC0AC\uC6A9 \uAE08\uC9C0 \u2014 \uD30C\uC2F1 \uC5D0\uB7EC \uC6D0\uC778`);
2871
2877
  if (shouldIncludeSkills) {
2872
2878
  const skillsPrompt = buildSkillsPrompt();
2873
2879
  if (skillsPrompt) {
@@ -3200,18 +3206,29 @@ ${newConversation}
3200
3206
  setIsSessionLoading(true);
3201
3207
  try {
3202
3208
  const sessionDetail = await onLoadSessionRef.current(id);
3203
- let loadedMessages = sessionDetail.messages.map((m, idx) => ({
3204
- id: m.id || generateId5("msg"),
3205
- role: typeof m.role === "string" ? m.role.toLowerCase() : m.role,
3206
- // API는 message 필드, 내부는 content 필드 사용
3207
- content: m.content || m.message || "",
3208
- timestamp: m.created_at ? new Date(m.created_at).getTime() : Date.now() - (sessionDetail.messages.length - idx) * 1e3,
3209
- model: m.model,
3210
- alternatives: m.alternatives,
3211
- sources: m.sources,
3212
- /** @Todo vibecode - 백엔드에서 전달받은 contentParts 복원 (이미지/파일 등) */
3213
- ...m.contentParts && { contentParts: m.contentParts }
3214
- }));
3209
+ let loadedMessages = sessionDetail.messages.map((m, idx) => {
3210
+ const msg = {
3211
+ id: m.id || generateId5("msg"),
3212
+ role: typeof m.role === "string" ? m.role.toLowerCase() : m.role,
3213
+ // API는 message 필드, 내부는 content 필드 사용
3214
+ content: m.content || m.message || "",
3215
+ timestamp: m.created_at ? new Date(m.created_at).getTime() : Date.now() - (sessionDetail.messages.length - idx) * 1e3,
3216
+ model: m.model,
3217
+ alternatives: m.alternatives,
3218
+ sources: m.sources,
3219
+ /** @Todo vibecode - 백엔드에서 전달받은 contentParts 복원 (이미지/파일 등) */
3220
+ ...m.contentParts && { contentParts: m.contentParts }
3221
+ };
3222
+ if (!msg.contentParts && msg.role === "assistant" && hasArtifactTag(msg.content)) {
3223
+ const { artifacts, cleanContent } = parseArtifactsFromContent(msg.content);
3224
+ if (artifacts.length > 0) {
3225
+ const textPart = cleanContent.trim() ? [{ type: "text", content: cleanContent }] : [];
3226
+ msg.content = cleanContent;
3227
+ msg.contentParts = [...textPart, ...artifacts];
3228
+ }
3229
+ }
3230
+ return msg;
3231
+ });
3215
3232
  let resolvedTitle = sessionDetail.title;
3216
3233
  let resolvedSessionContext = sessionDetail.sessionContext;
3217
3234
  if (loadedMessages.length === 0) {
@@ -3586,13 +3603,17 @@ ${finalContent}`;
3586
3603
  for (const [skillName, skillConfig] of attachmentSkills) {
3587
3604
  let matchedFiles = currentAttachments.filter((att) => {
3588
3605
  if (!skillConfig.acceptedTypes || skillConfig.acceptedTypes.length === 0) return true;
3606
+ const fileName = (att.name || "").toLowerCase();
3607
+ const fileMime = (att.mimeType || "").toLowerCase();
3608
+ const fileExt = fileName.includes(".") ? fileName.slice(fileName.lastIndexOf(".")) : "";
3589
3609
  return skillConfig.acceptedTypes.some((type) => {
3590
- if (type.startsWith(".")) return att.name.toLowerCase().endsWith(type.toLowerCase());
3591
- if (type.includes("*")) {
3592
- const regex = new RegExp("^" + type.replace("*", ".*") + "$");
3593
- return regex.test(att.mimeType);
3610
+ const t = type.toLowerCase();
3611
+ if (t.startsWith(".")) return fileExt === t || fileName.endsWith(t);
3612
+ if (t.includes("*")) {
3613
+ const regex = new RegExp("^" + t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\*", ".*") + "$");
3614
+ return regex.test(fileMime);
3594
3615
  }
3595
- return att.mimeType === type;
3616
+ return fileMime === t;
3596
3617
  });
3597
3618
  });
3598
3619
  if (skipImageAttachmentSkill) {
@@ -4047,25 +4068,26 @@ ${attachmentContext}
4047
4068
  }
4048
4069
  }
4049
4070
  }
4050
- const saveMessagesOnEarlyReturn = () => {
4071
+ const saveMessagesOnEarlyReturn = (overrideContentParts) => {
4051
4072
  if (!useExternalStorage || !capturedSessionId) return;
4052
- queueMicrotask(() => {
4073
+ setTimeout(() => {
4053
4074
  const latestSession = sessionsRef.current.find((s) => s.id === capturedSessionId);
4054
4075
  if (!latestSession) return;
4055
4076
  const latestMessages = latestSession.messages;
4056
4077
  const assistantMsg = [...latestMessages].reverse().find((m) => m.role === "assistant");
4057
4078
  const userMsg = latestMessages.find((m) => m.role === "user" && m.content === finalContent);
4058
4079
  const assistantContent = assistantMsg?.content || "";
4059
- if (assistantContent && onSaveMessagesRef.current) {
4080
+ const finalContentParts = overrideContentParts || assistantMsg?.contentParts;
4081
+ if ((assistantContent || finalContentParts) && onSaveMessagesRef.current) {
4060
4082
  onSaveMessagesRef.current(capturedSessionId, [
4061
4083
  { role: "user", message: finalContent, ...userMsg?.contentParts && { contentParts: userMsg.contentParts } },
4062
- { role: "assistant", message: assistantContent, ...assistantMsg?.contentParts && { contentParts: assistantMsg.contentParts } }
4084
+ { role: "assistant", message: assistantContent, ...finalContentParts && { contentParts: finalContentParts } }
4063
4085
  ]).catch((e) => console.error("[useChatUI] Failed to save messages:", e));
4064
4086
  }
4065
4087
  if (latestSession.messages.length > 0) {
4066
4088
  writeSessionCache(storageKey, latestSession);
4067
4089
  }
4068
- });
4090
+ }, 0);
4069
4091
  };
4070
4092
  if (!shouldSkipSkillParsing) {
4071
4093
  const assistantContent = accumulatedContent;
@@ -4255,7 +4277,7 @@ ${attachmentContext}
4255
4277
  promoteToSessionContext(capturedSessionId, toolName, result.content, result.metadata, toolLabel || toolName);
4256
4278
  }
4257
4279
  if (resultType === "image" || resultType === "file") {
4258
- saveMessagesOnEarlyReturn();
4280
+ saveMessagesOnEarlyReturn(parts);
4259
4281
  removeLoadingSession(capturedSessionId);
4260
4282
  abortControllersRef.current.delete(capturedSessionId);
4261
4283
  return;
@@ -4266,7 +4288,7 @@ ${attachmentContext}
4266
4288
  shouldContinue = decision === "continue";
4267
4289
  }
4268
4290
  if (!shouldContinue) {
4269
- saveMessagesOnEarlyReturn();
4291
+ saveMessagesOnEarlyReturn(parts);
4270
4292
  removeLoadingSession(capturedSessionId);
4271
4293
  abortControllersRef.current.delete(capturedSessionId);
4272
4294
  return;
@@ -4341,13 +4363,13 @@ ${result.content}
4341
4363
  shouldContinueImgFile = decision === "continue";
4342
4364
  }
4343
4365
  if (!shouldContinueImgFile) {
4344
- saveMessagesOnEarlyReturn();
4366
+ saveMessagesOnEarlyReturn(imgFileParts);
4345
4367
  removeLoadingSession(capturedSessionId);
4346
4368
  abortControllersRef.current.delete(capturedSessionId);
4347
4369
  return;
4348
4370
  }
4349
4371
  skipNextSkillParsingRef.current = true;
4350
- saveMessagesOnEarlyReturn();
4372
+ saveMessagesOnEarlyReturn(imgFileParts);
4351
4373
  const imgFilePrompt = skillResultType === "image" ? `"${detectedSkill.name}" \uC2A4\uD0AC\uB85C \uC774\uBBF8\uC9C0\uAC00 \uC0DD\uC131\uB418\uC5B4 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD45C\uC2DC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC774\uBBF8\uC9C0 URL\uC744 \uD14D\uC2A4\uD2B8\uC5D0 \uD3EC\uD568\uD558\uC9C0 \uB9D0\uACE0, \uC0DD\uC131\uB41C \uC774\uBBF8\uC9C0\uC5D0 \uB300\uD574 \uAC04\uB2E8\uD788 \uC548\uB0B4\uD574\uC8FC\uC138\uC694. skill_use \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.` : `"${detectedSkill.name}" \uC2A4\uD0AC\uB85C \uD30C\uC77C\uC774 \uC0DD\uC131\uB418\uC5B4 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD45C\uC2DC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uD30C\uC77C URL\uC744 \uD14D\uC2A4\uD2B8\uC5D0 \uD3EC\uD568\uD558\uC9C0 \uB9D0\uACE0, \uACB0\uACFC\uC5D0 \uB300\uD574 \uAC04\uB2E8\uD788 \uC548\uB0B4\uD574\uC8FC\uC138\uC694. skill_use \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`;
4352
4374
  setTimeout(() => {
4353
4375
  sendMessage(imgFilePrompt, { hiddenUserMessage: true });
@@ -10285,9 +10307,39 @@ var MermaidArtifact = ({ code }) => {
10285
10307
  }
10286
10308
  );
10287
10309
  };
10288
- var ArtifactActions = ({ code, language }) => {
10310
+ var svgToPngBlob = (svgString, scale = 2) => {
10311
+ return new Promise((resolve) => {
10312
+ const parser = new DOMParser();
10313
+ const doc = parser.parseFromString(svgString, "image/svg+xml");
10314
+ const svgEl = doc.querySelector("svg");
10315
+ if (!svgEl) return resolve(null);
10316
+ const w = parseInt(svgEl.getAttribute("width") || "800", 10);
10317
+ const h = parseInt(svgEl.getAttribute("height") || "600", 10);
10318
+ const canvas = document.createElement("canvas");
10319
+ canvas.width = w * scale;
10320
+ canvas.height = h * scale;
10321
+ const ctx = canvas.getContext("2d");
10322
+ if (!ctx) return resolve(null);
10323
+ ctx.scale(scale, scale);
10324
+ const blob = new Blob([new XMLSerializer().serializeToString(svgEl)], { type: "image/svg+xml;charset=utf-8" });
10325
+ const url = URL.createObjectURL(blob);
10326
+ const img = new Image();
10327
+ img.onload = () => {
10328
+ ctx.drawImage(img, 0, 0, w, h);
10329
+ URL.revokeObjectURL(url);
10330
+ canvas.toBlob((pngBlob) => resolve(pngBlob), "image/png");
10331
+ };
10332
+ img.onerror = () => {
10333
+ URL.revokeObjectURL(url);
10334
+ resolve(null);
10335
+ };
10336
+ img.src = url;
10337
+ });
10338
+ };
10339
+ var ArtifactActions = ({ code, language, containerRef }) => {
10289
10340
  const [showCode, setShowCode] = (0, import_react17.useState)(false);
10290
10341
  const [copied, setCopied] = (0, import_react17.useState)(false);
10342
+ const [saving, setSaving] = (0, import_react17.useState)(false);
10291
10343
  const handleCopy = async () => {
10292
10344
  try {
10293
10345
  await navigator.clipboard.writeText(code);
@@ -10296,6 +10348,39 @@ var ArtifactActions = ({ code, language }) => {
10296
10348
  } catch {
10297
10349
  }
10298
10350
  };
10351
+ const handleSaveImage = async () => {
10352
+ setSaving(true);
10353
+ try {
10354
+ let blob = null;
10355
+ if (language === "svg" || language === "mermaid") {
10356
+ const svgEl = containerRef?.current?.querySelector("svg");
10357
+ if (svgEl) {
10358
+ blob = await svgToPngBlob(svgEl.outerHTML);
10359
+ }
10360
+ } else if (language === "html") {
10361
+ const htmlBlob = new Blob([code], { type: "text/html;charset=utf-8" });
10362
+ const url = URL.createObjectURL(htmlBlob);
10363
+ const a = document.createElement("a");
10364
+ a.href = url;
10365
+ a.download = `artifact.html`;
10366
+ a.click();
10367
+ URL.revokeObjectURL(url);
10368
+ setSaving(false);
10369
+ return;
10370
+ }
10371
+ if (blob) {
10372
+ const url = URL.createObjectURL(blob);
10373
+ const a = document.createElement("a");
10374
+ a.href = url;
10375
+ a.download = `artifact.png`;
10376
+ a.click();
10377
+ URL.revokeObjectURL(url);
10378
+ }
10379
+ } catch {
10380
+ } finally {
10381
+ setSaving(false);
10382
+ }
10383
+ };
10299
10384
  const langLabel = language === "mermaid" ? "Mermaid" : language === "svg" ? "SVG" : "HTML";
10300
10385
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
10301
10386
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
@@ -10361,6 +10446,30 @@ var ArtifactActions = ({ code, language }) => {
10361
10446
  copied ? "\uBCF5\uC0AC\uB428" : "\uBCF5\uC0AC"
10362
10447
  ]
10363
10448
  }
10449
+ ),
10450
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
10451
+ "button",
10452
+ {
10453
+ onClick: handleSaveImage,
10454
+ disabled: saving,
10455
+ style: {
10456
+ display: "flex",
10457
+ alignItems: "center",
10458
+ gap: "4px",
10459
+ padding: "4px 8px",
10460
+ fontSize: "12px",
10461
+ color: "var(--chatllm-text-muted, #64748b)",
10462
+ backgroundColor: "transparent",
10463
+ border: "none",
10464
+ borderRadius: "4px",
10465
+ cursor: saving ? "wait" : "pointer",
10466
+ opacity: saving ? 0.5 : 1
10467
+ },
10468
+ children: [
10469
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSvg, { name: "download-line", size: 12, color: "var(--chatllm-text-muted, #64748b)" }),
10470
+ language === "html" ? "HTML \uC800\uC7A5" : "\uC774\uBBF8\uC9C0 \uC800\uC7A5"
10471
+ ]
10472
+ }
10364
10473
  )
10365
10474
  ]
10366
10475
  }
@@ -10395,6 +10504,7 @@ var ArtifactActions = ({ code, language }) => {
10395
10504
  ] });
10396
10505
  };
10397
10506
  var ArtifactCard = ({ part, index = 0 }) => {
10507
+ const contentRef = (0, import_react17.useRef)(null);
10398
10508
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
10399
10509
  "div",
10400
10510
  {
@@ -10434,12 +10544,12 @@ var ArtifactCard = ({ part, index = 0 }) => {
10434
10544
  ]
10435
10545
  }
10436
10546
  ),
10437
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { padding: part.language === "html" ? 0 : "12px" }, children: [
10547
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { ref: contentRef, style: { padding: part.language === "html" ? 0 : "12px" }, children: [
10438
10548
  part.language === "html" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HtmlArtifact, { code: part.code }),
10439
10549
  part.language === "svg" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SvgArtifact, { code: part.code }),
10440
10550
  part.language === "mermaid" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MermaidArtifact, { code: part.code })
10441
10551
  ] }),
10442
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ArtifactActions, { code: part.code, language: part.language })
10552
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ArtifactActions, { code: part.code, language: part.language, containerRef: contentRef })
10443
10553
  ]
10444
10554
  }
10445
10555
  );
@@ -15278,6 +15388,7 @@ var useDragResize = (options) => {
15278
15388
  const dragStartRef = (0, import_react24.useRef)(null);
15279
15389
  const isDraggingRef = (0, import_react24.useRef)(false);
15280
15390
  const wasDragRef = (0, import_react24.useRef)(false);
15391
+ const userDraggedRef = (0, import_react24.useRef)(false);
15281
15392
  const dragDeltaRef = (0, import_react24.useRef)({ dx: 0, dy: 0 });
15282
15393
  const fabElRef = (0, import_react24.useRef)(null);
15283
15394
  const dragCleanupRef = (0, import_react24.useRef)(null);
@@ -15344,6 +15455,7 @@ var useDragResize = (options) => {
15344
15455
  if (!isDraggingRef.current && distance > DRAG_THRESHOLD) {
15345
15456
  isDraggingRef.current = true;
15346
15457
  wasDragRef.current = true;
15458
+ userDraggedRef.current = true;
15347
15459
  setIsDragging(true);
15348
15460
  fabElRef.current?.classList.add("chatllm-fab-dragging");
15349
15461
  }
@@ -15583,7 +15695,11 @@ var useDragResize = (options) => {
15583
15695
  const newVw = window.innerWidth;
15584
15696
  const newVh = window.innerHeight;
15585
15697
  setViewport({ width: newVw, height: newVh });
15586
- setFabPos((prev) => clampToViewport(prev.x, prev.y, newVw, newVh));
15698
+ if (!userDraggedRef.current) {
15699
+ setFabPos(getInitialFabPosition(initialPosition));
15700
+ } else {
15701
+ setFabPos((prev) => clampToViewport(prev.x, prev.y, newVw, newVh));
15702
+ }
15587
15703
  };
15588
15704
  window.addEventListener("resize", handleResize);
15589
15705
  return () => window.removeEventListener("resize", handleResize);
@@ -17183,23 +17299,39 @@ var ChatFloatingWidget = ({
17183
17299
  return [chatTab, ...customTabs];
17184
17300
  }, [tabs]);
17185
17301
  const [unreadBadge, setUnreadBadge] = (0, import_react31.useState)(0);
17186
- const seenMessageIdsRef = (0, import_react31.useRef)(new Set(chatState.messages.map((m) => m.id)));
17302
+ const seenMessageIdsRef = (0, import_react31.useRef)(/* @__PURE__ */ new Set());
17303
+ (0, import_react31.useEffect)(() => {
17304
+ if (seenMessageIdsRef.current.size === 0 && chatState.messages.length > 0) {
17305
+ for (const m of chatState.messages) {
17306
+ seenMessageIdsRef.current.add(m.id);
17307
+ }
17308
+ }
17309
+ }, [chatState.messages]);
17187
17310
  (0, import_react31.useEffect)(() => {
17188
- if (!isOpen) {
17311
+ if (isOpen) {
17312
+ for (const m of chatState.messages) {
17313
+ seenMessageIdsRef.current.add(m.id);
17314
+ }
17315
+ } else {
17189
17316
  const newAssistant = chatState.messages.filter(
17190
17317
  (m) => m.role === "assistant" && !seenMessageIdsRef.current.has(m.id)
17191
17318
  );
17192
17319
  if (newAssistant.length > 0) {
17193
17320
  setUnreadBadge((prev) => prev + newAssistant.length);
17321
+ for (const m of newAssistant) {
17322
+ seenMessageIdsRef.current.add(m.id);
17323
+ }
17194
17324
  }
17195
17325
  }
17196
- for (const m of chatState.messages) {
17197
- seenMessageIdsRef.current.add(m.id);
17198
- }
17199
17326
  }, [chatState.messages, isOpen]);
17200
17327
  (0, import_react31.useEffect)(() => {
17201
- if (isOpen) setUnreadBadge(0);
17202
- }, [isOpen]);
17328
+ if (isOpen) {
17329
+ setUnreadBadge(0);
17330
+ for (const m of chatState.messages) {
17331
+ seenMessageIdsRef.current.add(m.id);
17332
+ }
17333
+ }
17334
+ }, [isOpen, chatState.messages]);
17203
17335
  const totalBadge = (0, import_react31.useMemo)(() => {
17204
17336
  return tabs.reduce((sum, t) => sum + (t.badge || 0), 0) + unreadBadge;
17205
17337
  }, [tabs, unreadBadge]);
@@ -17635,6 +17767,57 @@ var useDeepResearch = (options) => {
17635
17767
  };
17636
17768
  };
17637
17769
 
17770
+ // src/react/utils/conversationSearchAdapter.ts
17771
+ var formatSearchResults = (results) => {
17772
+ if (results.length === 0) {
17773
+ return "\uAD00\uB828 \uB300\uD654\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
17774
+ }
17775
+ const formatted = results.map((r, i) => {
17776
+ const header = r.sessionTitle ? `[${r.sessionTitle}]` : "";
17777
+ const time = r.timestamp ? ` (${new Date(r.timestamp).toLocaleDateString("ko-KR")})` : "";
17778
+ return `[${i + 1}] ${header}${time} (${r.role}): ${r.content}`;
17779
+ }).join("\n\n");
17780
+ return `\uAC80\uC0C9 \uACB0\uACFC (${results.length}\uAC74):
17781
+
17782
+ ${formatted}`;
17783
+ };
17784
+ var createConversationSearchSkill = (options) => {
17785
+ const { onSearch, label, disabled } = options;
17786
+ return {
17787
+ description: '\uC774\uC804 \uB300\uD654 \uB0B4\uC6A9\uC744 \uAC80\uC0C9\uD569\uB2C8\uB2E4. \uBC18\uB4DC\uC2DC \uD638\uCD9C\uD574\uC57C \uD558\uB294 \uC0C1\uD669: \uC0AC\uC6A9\uC790\uAC00 "\uC804\uC5D0", "\uC9C0\uB09C\uBC88\uC5D0", "\uC608\uC804\uC5D0", "\uC544\uAE4C", "\uC774\uC804\uC5D0", "\uB2E4\uC2DC \uC54C\uB824\uC918", "\uB610", "\uADF8\uB54C" \uB4F1 \uACFC\uAC70 \uB300\uD654\uB97C \uCC38\uC870\uD558\uB294 \uD45C\uD604\uC744 \uC0AC\uC6A9\uD560 \uB54C. \uACFC\uAC70\uC5D0 \uB17C\uC758\uD588\uB358 \uC8FC\uC81C\uB97C \uB2E4\uC2DC \uBB3C\uC5B4\uBCFC \uB54C\uB3C4 \uD638\uCD9C\uD558\uC138\uC694.',
17788
+ trigger: "auto",
17789
+ label: label || "\uB300\uD654 \uAC80\uC0C9",
17790
+ icon: "search-line",
17791
+ disabled,
17792
+ persist: false,
17793
+ parameters: {
17794
+ type: "object",
17795
+ properties: {
17796
+ query: {
17797
+ type: "string",
17798
+ description: "\uAC80\uC0C9 \uD0A4\uC6CC\uB4DC \uB610\uB294 \uBB38\uC7A5 (\uC0AC\uC6A9\uC790 \uC758\uB3C4\uB97C \uBC18\uC601\uD55C \uAC80\uC0C9\uC5B4)"
17799
+ },
17800
+ limit: {
17801
+ type: "number",
17802
+ description: "\uCD5C\uB300 \uACB0\uACFC \uC218 (\uAE30\uBCF8: 5)"
17803
+ }
17804
+ },
17805
+ required: ["query"]
17806
+ },
17807
+ execute: async (params) => {
17808
+ const { query, limit } = params;
17809
+ if (!query) {
17810
+ return { content: "\uAC80\uC0C9\uC5B4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4." };
17811
+ }
17812
+ const results = await onSearch(query, { limit: limit || 5 });
17813
+ return {
17814
+ content: formatSearchResults(results),
17815
+ metadata: { resultCount: results.length }
17816
+ };
17817
+ }
17818
+ };
17819
+ };
17820
+
17638
17821
  // src/react/components/EmptyState.tsx
17639
17822
  var import_jsx_runtime32 = require("react/jsx-runtime");
17640
17823
  var EmptyState = ({
@@ -18151,6 +18334,7 @@ var MemoryPanel = ({
18151
18334
  convertSkillsToOpenAITools,
18152
18335
  convertToolsToSkills,
18153
18336
  createAdvancedResearchSkill,
18337
+ createConversationSearchSkill,
18154
18338
  createDeepResearchSkill,
18155
18339
  migrateSessionsToProjects,
18156
18340
  useChatUI,