@lobehub/lobehub 2.0.0-next.277 → 2.0.0-next.279
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/db-migrations.mdc +1 -1
- package/.cursor/rules/debug-usage.mdc +7 -5
- package/.cursor/rules/desktop-controller-tests.mdc +2 -1
- package/.cursor/rules/desktop-feature-implementation.mdc +9 -5
- package/.cursor/rules/desktop-local-tools-implement.mdc +67 -66
- package/.cursor/rules/desktop-menu-configuration.mdc +21 -9
- package/.cursor/rules/desktop-window-management.mdc +17 -2
- package/.cursor/rules/drizzle-schema-style-guide.mdc +6 -6
- package/.cursor/rules/hotkey.mdc +1 -0
- package/.cursor/rules/i18n.mdc +1 -0
- package/.cursor/rules/project-structure.mdc +16 -3
- package/.cursor/rules/react.mdc +17 -5
- package/.cursor/rules/recent-data-usage.mdc +2 -1
- package/.cursor/rules/testing-guide/testing-guide.mdc +262 -238
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +1 -1
- package/.cursor/rules/zustand-action-patterns.mdc +1 -1
- package/.cursor/rules/zustand-slice-organization.mdc +4 -4
- package/CHANGELOG.md +51 -0
- package/CLAUDE.md +1 -1
- package/GEMINI.md +1 -1
- package/changelog/v1.json +14 -0
- package/docs/development/database-schema.dbml +16 -0
- package/locales/en-US/chat.json +24 -0
- package/locales/zh-CN/chat.json +24 -0
- package/package.json +1 -1
- package/packages/business/const/src/index.ts +3 -0
- package/packages/database/migrations/0069_add_topic_shares_table.sql +22 -0
- package/packages/database/migrations/meta/0069_snapshot.json +9704 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/models/__tests__/topicShare.test.ts +318 -0
- package/packages/database/src/models/topicShare.ts +177 -0
- package/packages/database/src/schemas/topic.ts +44 -2
- package/packages/types/src/conversation.ts +5 -0
- package/packages/types/src/topic/topic.ts +46 -0
- package/src/app/[variants]/(main)/agent/features/Conversation/Header/ShareButton/index.tsx +24 -9
- package/src/app/[variants]/(main)/agent/features/Conversation/ThreadHydration.tsx +2 -1
- package/src/app/[variants]/(main)/agent/features/Portal/_layout/Mobile.tsx +3 -3
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +3 -2
- package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +26 -9
- package/src/app/[variants]/(main)/group/features/Conversation/ThreadHydration.tsx +2 -1
- package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +3 -3
- package/src/app/[variants]/(main)/group/profile/features/MemberProfile/AgentTool.tsx +4 -1
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/AspectRatioSelect/index.tsx +1 -2
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ImageNum.tsx +54 -173
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ResolutionSelect.tsx +22 -67
- package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +18 -0
- package/src/app/[variants]/router/desktopRouter.config.tsx +18 -0
- package/src/app/[variants]/share/t/[id]/SharedMessageList.tsx +54 -0
- package/src/app/[variants]/share/t/[id]/_layout/index.tsx +170 -0
- package/src/app/[variants]/share/t/[id]/features/Portal/index.tsx +66 -0
- package/src/app/[variants]/share/t/[id]/index.tsx +112 -0
- package/src/app/robots.tsx +1 -1
- package/src/business/client/BusinessMobileRoutes.tsx +1 -1
- package/src/features/Conversation/ChatList/index.tsx +17 -6
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/index.tsx +8 -4
- package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +15 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tools.tsx +3 -1
- package/src/features/Conversation/Messages/AssistantGroup/components/ContentBlock.tsx +3 -2
- package/src/features/Conversation/Messages/AssistantGroup/components/GroupItem.tsx +2 -2
- package/src/features/Conversation/Messages/Supervisor/components/ContentBlock.tsx +25 -26
- package/src/features/Conversation/Messages/Supervisor/components/Group.tsx +4 -2
- package/src/features/Conversation/Messages/Tool/Tool/index.tsx +16 -12
- package/src/features/Conversation/Messages/Tool/index.tsx +20 -11
- package/src/features/Conversation/Messages/index.tsx +1 -1
- package/src/features/Conversation/store/slices/data/action.test.ts +42 -0
- package/src/features/Conversation/store/slices/data/action.ts +4 -2
- package/src/features/Portal/GroupThread/Header/index.tsx +2 -2
- package/src/features/Portal/MessageDetail/Body/index.tsx +3 -3
- package/src/features/Portal/components/Header.tsx +3 -3
- package/src/features/ProfileEditor/AgentTool.tsx +50 -19
- package/src/features/SharePopover/index.tsx +215 -0
- package/src/features/SharePopover/style.ts +10 -0
- package/src/hooks/useNavigateToAgent.ts +3 -3
- package/src/libs/next/proxy/define-config.ts +4 -1
- package/src/locales/default/chat.ts +26 -0
- package/src/proxy.ts +1 -0
- package/src/server/routers/lambda/__tests__/message.test.ts +152 -0
- package/src/server/routers/lambda/__tests__/share.test.ts +227 -0
- package/src/server/routers/lambda/__tests__/topic.test.ts +174 -0
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/message.ts +37 -4
- package/src/server/routers/lambda/share.ts +55 -0
- package/src/server/routers/lambda/topic.ts +45 -0
- package/src/services/message/index.ts +1 -0
- package/src/services/topic/index.ts +16 -0
- package/src/store/chat/slices/portal/action.test.ts +0 -41
- package/src/store/chat/slices/portal/action.ts +0 -25
- package/src/store/chat/slices/thread/action.test.ts +10 -6
- package/src/store/chat/slices/thread/action.ts +10 -3
- package/src/app/[variants]/(main)/group/features/Portal/features/Portal.tsx +0 -105
- package/src/app/[variants]/(main)/group/features/Portal/features/PortalPanel.tsx +0 -23
|
@@ -11,34 +11,33 @@ import { Tools } from '../../AssistantGroup/Tools';
|
|
|
11
11
|
import Reasoning from '../../components/Reasoning';
|
|
12
12
|
import MessageContent from './MessageContent';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
errorContent && error && (content === LOADING_FLAT || !content) ? errorContent : undefined
|
|
26
|
-
}
|
|
27
|
-
id={id}
|
|
28
|
-
/>
|
|
29
|
-
);
|
|
14
|
+
interface ContentBlockProps extends AssistantContentBlock {
|
|
15
|
+
disableEditing?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ContentBlock = memo<ContentBlockProps>(
|
|
19
|
+
({ id, tools, content, reasoning, error, disableEditing }) => {
|
|
20
|
+
const errorContent = useErrorContent(error);
|
|
21
|
+
const isReasoning = useConversationStore(messageStateSelectors.isMessageInReasoning(id));
|
|
22
|
+
const hasTools = tools && tools.length > 0;
|
|
23
|
+
const showReasoning =
|
|
24
|
+
(!!reasoning && reasoning.content?.trim() !== '') || (!reasoning && isReasoning);
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{showReasoning && <Reasoning {...reasoning} id={id} />}
|
|
26
|
+
if (error && (content === LOADING_FLAT || !content))
|
|
27
|
+
return <ErrorContent error={errorContent} id={id} />;
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
<
|
|
29
|
+
return (
|
|
30
|
+
<Flexbox gap={8} id={id}>
|
|
31
|
+
{showReasoning && <Reasoning {...reasoning} id={id} />}
|
|
32
|
+
|
|
33
|
+
{/* Content - markdown text */}
|
|
34
|
+
<MessageContent content={content} hasTools={hasTools} id={id} />
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
36
|
+
{/* Tools */}
|
|
37
|
+
{hasTools && <Tools disableEditing={disableEditing} messageId={id} tools={tools} />}
|
|
38
|
+
</Flexbox>
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
);
|
|
43
42
|
|
|
44
43
|
export default ContentBlock;
|
|
@@ -29,7 +29,7 @@ interface GroupChildrenProps {
|
|
|
29
29
|
messageIndex: number;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const Group = memo<GroupChildrenProps>(({ blocks, id, content }) => {
|
|
32
|
+
const Group = memo<GroupChildrenProps>(({ blocks, id, content, disableEditing }) => {
|
|
33
33
|
const isCollapsed = useConversationStore(messageStateSelectors.isMessageCollapsed(id));
|
|
34
34
|
const contextValue = useMemo(() => ({ assistantGroupId: id }), [id]);
|
|
35
35
|
|
|
@@ -46,7 +46,9 @@ const Group = memo<GroupChildrenProps>(({ blocks, id, content }) => {
|
|
|
46
46
|
<MessageAggregationContext value={contextValue}>
|
|
47
47
|
<Flexbox className={styles.container} gap={8}>
|
|
48
48
|
{blocks.map((item) => {
|
|
49
|
-
return
|
|
49
|
+
return (
|
|
50
|
+
<ContentBlock {...item} disableEditing={disableEditing} key={id + '.' + item.id} />
|
|
51
|
+
);
|
|
50
52
|
})}
|
|
51
53
|
</Flexbox>
|
|
52
54
|
</MessageAggregationContext>
|
|
@@ -20,6 +20,7 @@ const Render = dynamic(() => import('../../AssistantGroup/Tool/Render'), {
|
|
|
20
20
|
export interface InspectorProps {
|
|
21
21
|
apiName: string;
|
|
22
22
|
arguments?: string;
|
|
23
|
+
disableEditing?: boolean;
|
|
23
24
|
identifier: string;
|
|
24
25
|
index: number;
|
|
25
26
|
messageId: string;
|
|
@@ -32,7 +33,7 @@ export interface InspectorProps {
|
|
|
32
33
|
* Tool message component - adapts Tool message data to use AssistantGroup/Tool components
|
|
33
34
|
*/
|
|
34
35
|
const Tool = memo<InspectorProps>(
|
|
35
|
-
({ arguments: requestArgs, apiName, messageId, toolCallId, index, identifier, type }) => {
|
|
36
|
+
({ arguments: requestArgs, apiName, disableEditing, messageId, toolCallId, index, identifier, type }) => {
|
|
36
37
|
const [showDebug, setShowDebug] = useState(false);
|
|
37
38
|
const [showPluginRender, setShowPluginRender] = useState(false);
|
|
38
39
|
const [expand, setExpand] = useState(true);
|
|
@@ -66,16 +67,18 @@ const Tool = memo<InspectorProps>(
|
|
|
66
67
|
>
|
|
67
68
|
<AccordionItem
|
|
68
69
|
action={
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
!disableEditing && (
|
|
71
|
+
<Actions
|
|
72
|
+
assistantMessageId={messageId}
|
|
73
|
+
handleExpand={(expand) => setExpand(!!expand)}
|
|
74
|
+
identifier={identifier}
|
|
75
|
+
setShowDebug={setShowDebug}
|
|
76
|
+
setShowPluginRender={setShowPluginRender}
|
|
77
|
+
showCustomPluginRender={false}
|
|
78
|
+
showDebug={showDebug}
|
|
79
|
+
showPluginRender={showPluginRender}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
79
82
|
}
|
|
80
83
|
itemKey={'tool'}
|
|
81
84
|
paddingBlock={4}
|
|
@@ -83,7 +86,7 @@ const Tool = memo<InspectorProps>(
|
|
|
83
86
|
title={<Inspectors apiName={apiName} identifier={identifier} result={result} />}
|
|
84
87
|
>
|
|
85
88
|
<Flexbox gap={8} paddingBlock={8}>
|
|
86
|
-
{showDebug && (
|
|
89
|
+
{showDebug && !disableEditing && (
|
|
87
90
|
<Debug
|
|
88
91
|
apiName={apiName}
|
|
89
92
|
identifier={identifier}
|
|
@@ -96,6 +99,7 @@ const Tool = memo<InspectorProps>(
|
|
|
96
99
|
<Render
|
|
97
100
|
apiName={apiName}
|
|
98
101
|
arguments={requestArgs}
|
|
102
|
+
disableEditing={disableEditing}
|
|
99
103
|
identifier={identifier}
|
|
100
104
|
messageId={messageId}
|
|
101
105
|
result={result}
|
|
@@ -8,11 +8,12 @@ import { dataSelectors, useConversationStore } from '../../store';
|
|
|
8
8
|
import Tool from './Tool';
|
|
9
9
|
|
|
10
10
|
interface ToolMessageProps {
|
|
11
|
+
disableEditing?: boolean;
|
|
11
12
|
id: string;
|
|
12
13
|
index: number;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
const ToolMessage = memo<ToolMessageProps>(({ id, index }) => {
|
|
16
|
+
const ToolMessage = memo<ToolMessageProps>(({ disableEditing, id, index }) => {
|
|
16
17
|
const { t } = useTranslation('plugin');
|
|
17
18
|
const item = useConversationStore(dataSelectors.getDbMessageById(id), isEqual) as UIChatMessage;
|
|
18
19
|
const deleteToolMessage = useConversationStore((s) => s.deleteToolMessage);
|
|
@@ -29,17 +30,25 @@ const ToolMessage = memo<ToolMessageProps>(({ id, index }) => {
|
|
|
29
30
|
|
|
30
31
|
return (
|
|
31
32
|
<Flexbox gap={4} paddingBlock={12}>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
{!disableEditing && (
|
|
34
|
+
<Alert
|
|
35
|
+
action={
|
|
36
|
+
<Button loading={loading} onClick={handleDelete} size={'small'} type={'primary'}>
|
|
37
|
+
{t('inspector.delete')}
|
|
38
|
+
</Button>
|
|
39
|
+
}
|
|
40
|
+
title={t('inspector.orphanedToolCall')}
|
|
41
|
+
type={'secondary'}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
41
44
|
{item.plugin && (
|
|
42
|
-
<Tool
|
|
45
|
+
<Tool
|
|
46
|
+
{...item.plugin}
|
|
47
|
+
disableEditing={disableEditing}
|
|
48
|
+
index={index}
|
|
49
|
+
messageId={id}
|
|
50
|
+
toolCallId={item.tool_call_id!}
|
|
51
|
+
/>
|
|
43
52
|
)}
|
|
44
53
|
</Flexbox>
|
|
45
54
|
);
|
|
@@ -505,6 +505,48 @@ describe('DataSlice', () => {
|
|
|
505
505
|
);
|
|
506
506
|
});
|
|
507
507
|
|
|
508
|
+
it('should not fetch when topicId is null (new conversation state)', () => {
|
|
509
|
+
const store = createStore({
|
|
510
|
+
context: { agentId: 'test-session', topicId: null, threadId: null },
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
store.getState().useFetchMessages({
|
|
514
|
+
agentId: 'test-session',
|
|
515
|
+
topicId: null,
|
|
516
|
+
threadId: null,
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// SWR should be called with null key when topicId is null
|
|
520
|
+
// This prevents fetching empty data that would overwrite local optimistic updates
|
|
521
|
+
expect(vi.mocked(useClientDataSWRWithSync)).toHaveBeenCalledWith(
|
|
522
|
+
null,
|
|
523
|
+
expect.any(Function),
|
|
524
|
+
expect.any(Object),
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
// messageService.getMessages should NOT be called
|
|
528
|
+
expect(messageService.getMessages).not.toHaveBeenCalled();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('should not fetch when topicId is undefined (new conversation state)', () => {
|
|
532
|
+
const store = createStore({
|
|
533
|
+
context: { agentId: 'test-session', topicId: null, threadId: null },
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
store.getState().useFetchMessages({
|
|
537
|
+
agentId: 'test-session',
|
|
538
|
+
topicId: undefined as any,
|
|
539
|
+
threadId: null,
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// SWR should be called with null key when topicId is undefined
|
|
543
|
+
expect(vi.mocked(useClientDataSWRWithSync)).toHaveBeenCalledWith(
|
|
544
|
+
null,
|
|
545
|
+
expect.any(Function),
|
|
546
|
+
expect.any(Object),
|
|
547
|
+
);
|
|
548
|
+
});
|
|
549
|
+
|
|
508
550
|
it('should use different SWR keys for different threadIds', () => {
|
|
509
551
|
const store1 = createStore({
|
|
510
552
|
context: { agentId: 'session-1', topicId: 'topic-1', threadId: 'thread-1' },
|
|
@@ -103,13 +103,15 @@ export const dataSlice: StateCreator<
|
|
|
103
103
|
useFetchMessages: (context, skipFetch) => {
|
|
104
104
|
// When skipFetch is true, SWR key is null - no fetch occurs
|
|
105
105
|
// This is used when external messages are provided (e.g., creating new thread)
|
|
106
|
-
|
|
106
|
+
// Also skip fetch when topicId is null (new conversation state) - there's no server data,
|
|
107
|
+
// only local optimistic updates. Fetching would return empty array and overwrite local data.
|
|
108
|
+
const shouldFetch = !skipFetch && !!context.agentId && !!context.topicId;
|
|
107
109
|
|
|
108
110
|
return useClientDataSWRWithSync<UIChatMessage[]>(
|
|
109
111
|
shouldFetch ? ['CONVERSATION_FETCH_MESSAGES', context] : null,
|
|
110
112
|
|
|
111
113
|
async () => {
|
|
112
|
-
return messageService.getMessages(context
|
|
114
|
+
return messageService.getMessages(context);
|
|
113
115
|
},
|
|
114
116
|
{
|
|
115
117
|
onData: (data) => {
|
|
@@ -12,10 +12,10 @@ import { useSessionStore } from '@/store/session';
|
|
|
12
12
|
import { sessionSelectors } from '@/store/session/selectors';
|
|
13
13
|
|
|
14
14
|
const Header = memo(() => {
|
|
15
|
-
const
|
|
15
|
+
const clearPortalStack = useChatStore((s) => s.clearPortalStack);
|
|
16
16
|
const close = () => {
|
|
17
17
|
useAgentGroupStore.setState({ activeThreadAgentId: '' });
|
|
18
|
-
|
|
18
|
+
clearPortalStack();
|
|
19
19
|
};
|
|
20
20
|
const activeThreadAgentId = useAgentGroupStore((s) => s.activeThreadAgentId);
|
|
21
21
|
|
|
@@ -15,9 +15,9 @@ const md = css`
|
|
|
15
15
|
`;
|
|
16
16
|
|
|
17
17
|
const MessageDetailBody = () => {
|
|
18
|
-
const [messageDetailId,
|
|
18
|
+
const [messageDetailId, clearPortalStack] = useChatStore((s) => [
|
|
19
19
|
chatPortalSelectors.messageDetailId(s),
|
|
20
|
-
s.
|
|
20
|
+
s.clearPortalStack,
|
|
21
21
|
]);
|
|
22
22
|
|
|
23
23
|
const message = useChatStore(dbMessageSelectors.getDbMessageById(messageDetailId || ''), isEqual);
|
|
@@ -26,7 +26,7 @@ const MessageDetailBody = () => {
|
|
|
26
26
|
|
|
27
27
|
useEffect(() => {
|
|
28
28
|
if (!message) {
|
|
29
|
-
|
|
29
|
+
clearPortalStack();
|
|
30
30
|
}
|
|
31
31
|
}, [message]);
|
|
32
32
|
|
|
@@ -10,10 +10,10 @@ import { useChatStore } from '@/store/chat';
|
|
|
10
10
|
import { chatPortalSelectors } from '@/store/chat/selectors';
|
|
11
11
|
|
|
12
12
|
const Header = memo<{ title: ReactNode }>(({ title }) => {
|
|
13
|
-
const [canGoBack, goBack,
|
|
13
|
+
const [canGoBack, goBack, clearPortalStack] = useChatStore((s) => [
|
|
14
14
|
chatPortalSelectors.canGoBack(s),
|
|
15
15
|
s.goBack,
|
|
16
|
-
s.
|
|
16
|
+
s.clearPortalStack,
|
|
17
17
|
]);
|
|
18
18
|
|
|
19
19
|
return (
|
|
@@ -30,7 +30,7 @@ const Header = memo<{ title: ReactNode }>(({ title }) => {
|
|
|
30
30
|
<ActionIcon
|
|
31
31
|
icon={PanelRightCloseIcon}
|
|
32
32
|
onClick={() => {
|
|
33
|
-
|
|
33
|
+
clearPortalStack();
|
|
34
34
|
}}
|
|
35
35
|
size={DESKTOP_HEADER_ICON_SIZE}
|
|
36
36
|
/>
|
|
@@ -16,7 +16,7 @@ import PluginStore from '@/features/PluginStore';
|
|
|
16
16
|
import { useCheckPluginsIsInstalled } from '@/hooks/useCheckPluginsIsInstalled';
|
|
17
17
|
import { useFetchInstalledPlugins } from '@/hooks/useFetchInstalledPlugins';
|
|
18
18
|
import { useAgentStore } from '@/store/agent';
|
|
19
|
-
import {
|
|
19
|
+
import { agentSelectors, chatConfigByIdSelectors } from '@/store/agent/selectors';
|
|
20
20
|
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
21
21
|
import { useToolStore } from '@/store/tool';
|
|
22
22
|
import {
|
|
@@ -82,6 +82,11 @@ const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label
|
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
export interface AgentToolProps {
|
|
85
|
+
/**
|
|
86
|
+
* Optional agent ID to use instead of currentAgentConfig
|
|
87
|
+
* Used in group profile to specify which member's plugins to display
|
|
88
|
+
*/
|
|
89
|
+
agentId?: string;
|
|
85
90
|
/**
|
|
86
91
|
* Whether to filter tools by availableInWeb property
|
|
87
92
|
* @default false
|
|
@@ -100,15 +105,17 @@ export interface AgentToolProps {
|
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
const AgentTool = memo<AgentToolProps>(
|
|
103
|
-
({ showWebBrowsing = false, filterAvailableInWeb = false, useAllMetaList = false }) => {
|
|
108
|
+
({ agentId, showWebBrowsing = false, filterAvailableInWeb = false, useAllMetaList = false }) => {
|
|
104
109
|
const { t } = useTranslation('setting');
|
|
105
|
-
const
|
|
110
|
+
const activeAgentId = useAgentStore((s) => s.activeAgentId);
|
|
111
|
+
const effectiveAgentId = agentId || activeAgentId || '';
|
|
112
|
+
const config = useAgentStore(agentSelectors.getAgentConfigById(effectiveAgentId), isEqual);
|
|
106
113
|
|
|
107
114
|
// Plugin state management
|
|
108
115
|
const plugins = config?.plugins || [];
|
|
109
116
|
|
|
110
|
-
const
|
|
111
|
-
const
|
|
117
|
+
const updateAgentConfigById = useAgentStore((s) => s.updateAgentConfigById);
|
|
118
|
+
const updateAgentChatConfigById = useAgentStore((s) => s.updateAgentChatConfigById);
|
|
112
119
|
const installedPluginList = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
|
|
113
120
|
|
|
114
121
|
// Use appropriate builtin list based on prop
|
|
@@ -117,8 +124,8 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
117
124
|
isEqual,
|
|
118
125
|
);
|
|
119
126
|
|
|
120
|
-
// Web browsing uses searchMode instead of plugins array
|
|
121
|
-
const isSearchEnabled = useAgentStore(
|
|
127
|
+
// Web browsing uses searchMode instead of plugins array - use byId selector
|
|
128
|
+
const isSearchEnabled = useAgentStore(chatConfigByIdSelectors.isEnableSearchById(effectiveAgentId));
|
|
122
129
|
|
|
123
130
|
// Klavis 相关状态
|
|
124
131
|
const allKlavisServers = useToolStore(klavisStoreSelectors.getServers, isEqual);
|
|
@@ -144,11 +151,34 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
144
151
|
// 使用 SWR 加载用户的 Klavis 集成(从数据库)
|
|
145
152
|
useFetchUserKlavisServers(isKlavisEnabledInEnv);
|
|
146
153
|
|
|
147
|
-
// Toggle web browsing via searchMode
|
|
154
|
+
// Toggle web browsing via searchMode - use byId action
|
|
148
155
|
const toggleWebBrowsing = useCallback(async () => {
|
|
156
|
+
if (!effectiveAgentId) return;
|
|
149
157
|
const nextMode = isSearchEnabled ? 'off' : 'auto';
|
|
150
|
-
await
|
|
151
|
-
}, [isSearchEnabled,
|
|
158
|
+
await updateAgentChatConfigById(effectiveAgentId, { searchMode: nextMode });
|
|
159
|
+
}, [isSearchEnabled, updateAgentChatConfigById, effectiveAgentId]);
|
|
160
|
+
|
|
161
|
+
// Toggle a plugin - use byId action
|
|
162
|
+
const togglePlugin = useCallback(
|
|
163
|
+
async (pluginId: string, state?: boolean) => {
|
|
164
|
+
if (!effectiveAgentId) return;
|
|
165
|
+
const currentPlugins = plugins;
|
|
166
|
+
const hasPlugin = currentPlugins.includes(pluginId);
|
|
167
|
+
const shouldEnable = state !== undefined ? state : !hasPlugin;
|
|
168
|
+
|
|
169
|
+
let newPlugins: string[];
|
|
170
|
+
if (shouldEnable && !hasPlugin) {
|
|
171
|
+
newPlugins = [...currentPlugins, pluginId];
|
|
172
|
+
} else if (!shouldEnable && hasPlugin) {
|
|
173
|
+
newPlugins = currentPlugins.filter((id) => id !== pluginId);
|
|
174
|
+
} else {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
await updateAgentConfigById(effectiveAgentId, { plugins: newPlugins });
|
|
179
|
+
},
|
|
180
|
+
[effectiveAgentId, plugins, updateAgentConfigById],
|
|
181
|
+
);
|
|
152
182
|
|
|
153
183
|
// Check if a tool is enabled (handles web browsing specially)
|
|
154
184
|
const isToolEnabled = useCallback(
|
|
@@ -167,10 +197,10 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
167
197
|
if (showWebBrowsing && identifier === WEB_BROWSING_IDENTIFIER) {
|
|
168
198
|
await toggleWebBrowsing();
|
|
169
199
|
} else {
|
|
170
|
-
await
|
|
200
|
+
await togglePlugin(identifier);
|
|
171
201
|
}
|
|
172
202
|
},
|
|
173
|
-
[toggleWebBrowsing,
|
|
203
|
+
[toggleWebBrowsing, togglePlugin, showWebBrowsing],
|
|
174
204
|
);
|
|
175
205
|
|
|
176
206
|
// Set default tab based on installed plugins (only on first load)
|
|
@@ -240,7 +270,7 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
240
270
|
[isKlavisEnabledInEnv, allKlavisServers],
|
|
241
271
|
);
|
|
242
272
|
|
|
243
|
-
// Handle plugin remove via Tag close
|
|
273
|
+
// Handle plugin remove via Tag close - use byId actions
|
|
244
274
|
const handleRemovePlugin =
|
|
245
275
|
(
|
|
246
276
|
pluginId: string | { enabled: boolean; identifier: string; settings: Record<string, any> },
|
|
@@ -250,9 +280,10 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
250
280
|
e.stopPropagation();
|
|
251
281
|
const identifier = typeof pluginId === 'string' ? pluginId : pluginId?.identifier;
|
|
252
282
|
if (showWebBrowsing && identifier === WEB_BROWSING_IDENTIFIER) {
|
|
253
|
-
|
|
283
|
+
if (!effectiveAgentId) return;
|
|
284
|
+
await updateAgentChatConfigById(effectiveAgentId, { searchMode: 'off' });
|
|
254
285
|
} else {
|
|
255
|
-
|
|
286
|
+
await togglePlugin(identifier, false);
|
|
256
287
|
}
|
|
257
288
|
};
|
|
258
289
|
|
|
@@ -304,13 +335,13 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
304
335
|
label={item.title}
|
|
305
336
|
onUpdate={async () => {
|
|
306
337
|
setUpdating(true);
|
|
307
|
-
await
|
|
338
|
+
await togglePlugin(item.identifier);
|
|
308
339
|
setUpdating(false);
|
|
309
340
|
}}
|
|
310
341
|
/>
|
|
311
342
|
),
|
|
312
343
|
})),
|
|
313
|
-
[installedPluginList, plugins,
|
|
344
|
+
[installedPluginList, plugins, togglePlugin],
|
|
314
345
|
);
|
|
315
346
|
|
|
316
347
|
// All tab items (市场 tab)
|
|
@@ -411,7 +442,7 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
411
442
|
label={item.title}
|
|
412
443
|
onUpdate={async () => {
|
|
413
444
|
setUpdating(true);
|
|
414
|
-
await
|
|
445
|
+
await togglePlugin(item.identifier);
|
|
415
446
|
setUpdating(false);
|
|
416
447
|
}}
|
|
417
448
|
/>
|
|
@@ -435,7 +466,7 @@ const AgentTool = memo<AgentToolProps>(
|
|
|
435
466
|
plugins,
|
|
436
467
|
isToolEnabled,
|
|
437
468
|
handleToggleTool,
|
|
438
|
-
|
|
469
|
+
togglePlugin,
|
|
439
470
|
t,
|
|
440
471
|
]);
|
|
441
472
|
|