@lobehub/lobehub 2.0.0-next.254 → 2.0.0-next.256
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/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/en-US/plugin.json +1 -0
- package/locales/zh-CN/plugin.json +1 -0
- package/package.json +1 -1
- package/packages/builtin-tool-notebook/src/client/Placeholder/CreateDocument.tsx +6 -6
- package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +23 -10
- package/packages/builtin-tool-notebook/src/client/Streaming/CreateDocument/index.tsx +1 -1
- package/packages/database/src/models/__tests__/agent.test.ts +91 -4
- package/packages/database/src/models/agent.ts +15 -7
- package/packages/editor-runtime/src/EditorRuntime.ts +1 -1
- package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +65 -4
- package/packages/editor-runtime/src/__tests__/__snapshots__/EditorRuntime.real.test.ts.snap +108 -17
- package/packages/editor-runtime/src/__tests__/fixtures/remove-then-add.json +636 -0
- package/packages/editor-runtime/src/__tests__/fixtures/remove.json +1 -0
- package/packages/types/src/agent/agentConfig.ts +8 -8
- package/src/app/[variants]/(main)/chat/features/Portal/_layout/Mobile.tsx +2 -1
- package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +2 -1
- package/src/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems.tsx +2 -2
- package/src/app/[variants]/(main)/page/{features/PageTitle → PageTitle}/index.tsx +0 -1
- package/src/app/[variants]/(main)/page/[id]/index.tsx +43 -1
- package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/Content.tsx +15 -15
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/Editing.tsx +3 -3
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/index.tsx +7 -12
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/useDropdownMenu.tsx +5 -5
- package/src/app/[variants]/(main)/page/_layout/Body/List/index.tsx +7 -7
- package/src/app/[variants]/(main)/page/_layout/Body/index.tsx +15 -9
- package/src/app/[variants]/(main)/page/_layout/Body/useDropdownMenu.tsx +3 -3
- package/src/app/[variants]/(main)/page/_layout/DataSync.tsx +15 -0
- package/src/app/[variants]/(main)/page/_layout/Header/AddButton.tsx +2 -2
- package/src/app/[variants]/(main)/page/_layout/index.tsx +2 -0
- package/src/app/[variants]/(main)/page/index.tsx +3 -7
- package/src/components/Editor/AutoSaveHint.tsx +1 -1
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/Intervention/ApprovalActions.tsx +2 -2
- package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +18 -15
- package/src/features/Conversation/Messages/AssistantGroup/components/Group.tsx +3 -3
- package/src/features/Conversation/Messages/Contexts/MessageAggregationContext.ts +15 -0
- package/src/features/Conversation/Messages/Supervisor/components/Group.tsx +3 -3
- package/src/features/Conversation/Messages/User/Actions/index.tsx +5 -1
- package/src/features/EditorCanvas/AutoSaveHint.tsx +37 -0
- package/src/features/{PageEditor → EditorCanvas}/DiffAllToolbar.tsx +57 -16
- package/src/features/EditorCanvas/DocumentIdMode.tsx +111 -0
- package/src/features/EditorCanvas/EditorCanvas.tsx +148 -0
- package/src/features/EditorCanvas/EditorDataMode.tsx +64 -0
- package/src/features/EditorCanvas/ErrorBoundary.tsx +66 -0
- package/src/features/EditorCanvas/InlineToolbar.tsx +245 -0
- package/src/features/EditorCanvas/InternalEditor.tsx +134 -0
- package/src/features/{PageEditor/EditorCanvas → EditorCanvas}/actions.ts +10 -8
- package/src/features/EditorCanvas/index.ts +9 -0
- package/src/features/PageEditor/EditorCanvas/index.tsx +14 -111
- package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +95 -0
- package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +1 -1
- package/src/features/PageEditor/Header/Breadcrumb.tsx +2 -2
- package/src/features/PageEditor/Header/index.tsx +15 -18
- package/src/features/PageEditor/Header/useMenu.tsx +12 -9
- package/src/features/PageEditor/PageEditor.tsx +45 -21
- package/src/features/PageEditor/PageEditorProvider.tsx +13 -1
- package/src/features/PageEditor/PageTitle/index.tsx +2 -2
- package/src/features/PageEditor/StoreUpdater.tsx +35 -308
- package/src/features/PageEditor/{Body/Title.tsx → TitleSection.tsx} +16 -16
- package/src/features/PageEditor/store/action.ts +96 -188
- package/src/features/PageEditor/store/initialState.ts +16 -21
- package/src/features/PageEditor/store/selectors.ts +3 -4
- package/src/features/PageExplorer/PageExplorerPlaceholder.tsx +22 -14
- package/src/features/PageExplorer/index.tsx +34 -67
- package/src/features/Portal/Artifacts/index.ts +0 -2
- package/src/features/Portal/Document/AutoSaveHint.tsx +7 -6
- package/src/features/Portal/Document/Body.tsx +1 -3
- package/src/features/Portal/Document/EditorCanvas.tsx +7 -50
- package/src/features/Portal/Document/Header.tsx +13 -10
- package/src/features/Portal/Document/TodoList.tsx +6 -4
- package/src/features/Portal/Document/Wrapper.tsx +3 -11
- package/src/features/Portal/Document/index.ts +0 -2
- package/src/features/Portal/FilePreview/index.ts +0 -2
- package/src/features/Portal/GroupThread/index.ts +0 -3
- package/src/features/Portal/MessageDetail/index.ts +0 -2
- package/src/features/Portal/Notebook/index.ts +0 -2
- package/src/features/Portal/Plugins/index.ts +0 -2
- package/src/features/Portal/Thread/index.ts +0 -3
- package/src/features/Portal/components/Header.tsx +18 -6
- package/src/features/Portal/router.tsx +34 -97
- package/src/features/Portal/type.ts +0 -2
- package/src/libs/next/config/define-config.ts +4 -1
- package/src/locales/default/plugin.ts +1 -0
- package/src/store/chat/slices/portal/action.test.ts +218 -15
- package/src/store/chat/slices/portal/action.ts +194 -41
- package/src/store/chat/slices/portal/initialState.ts +40 -1
- package/src/store/chat/slices/portal/selectors/thread.ts +44 -3
- package/src/store/chat/slices/portal/selectors.test.ts +119 -17
- package/src/store/chat/slices/portal/selectors.ts +117 -36
- package/src/store/document/index.ts +17 -5
- package/src/store/document/slices/document/action.ts +209 -0
- package/src/store/document/slices/document/index.ts +6 -0
- package/src/store/document/slices/editor/action.test.ts +340 -0
- package/src/store/document/slices/editor/action.ts +133 -149
- package/src/store/document/slices/editor/index.ts +9 -2
- package/src/store/document/slices/editor/initialState.ts +66 -29
- package/src/store/document/slices/editor/reducer.test.ts +217 -0
- package/src/store/document/slices/editor/reducer.ts +67 -0
- package/src/store/document/slices/editor/selectors.test.ts +395 -0
- package/src/store/document/slices/editor/selectors.ts +107 -5
- package/src/store/document/store.ts +12 -13
- package/src/store/file/slices/document/action.ts +19 -188
- package/src/store/file/slices/document/initialState.ts +0 -30
- package/src/store/file/slices/document/selectors.ts +25 -59
- package/src/store/notebook/index.ts +5 -4
- package/src/store/page/index.ts +2 -0
- package/src/store/page/initialState.ts +92 -0
- package/src/store/page/selectors.ts +5 -0
- package/src/store/page/slices/crud/action.ts +477 -0
- package/src/store/page/slices/crud/index.ts +2 -0
- package/src/store/page/slices/crud/initialState.ts +7 -0
- package/src/store/page/slices/internal/action.ts +32 -0
- package/src/store/page/slices/internal/index.ts +2 -0
- package/src/store/page/slices/internal/reducer.ts +105 -0
- package/src/store/page/slices/list/action.ts +206 -0
- package/src/store/page/slices/list/index.ts +3 -0
- package/src/store/page/slices/list/initialState.ts +29 -0
- package/src/store/page/slices/list/selectors.ts +90 -0
- package/src/store/page/slices/selection/action.ts +67 -0
- package/src/store/page/slices/selection/index.ts +2 -0
- package/src/store/page/slices/selection/initialState.ts +11 -0
- package/src/store/page/store.ts +29 -0
- package/src/store/tool/slices/lobehubSkillStore/selectors.ts +10 -20
- package/src/utils/identifier.ts +8 -2
- package/src/features/Conversation/Messages/AssistantGroup/components/GroupContext.ts +0 -15
- package/src/features/Conversation/Messages/Supervisor/components/GroupContext.ts +0 -15
- package/src/features/PageEditor/Body/index.tsx +0 -68
- package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +0 -316
- package/src/features/PageEditor/Header/AutoSaveHint.tsx +0 -27
- package/src/features/Portal/Artifacts/useEnable.ts +0 -4
- package/src/features/Portal/Document/DocumentEditorProvider.tsx +0 -34
- package/src/features/Portal/Document/StoreUpdater.tsx +0 -80
- package/src/features/Portal/Document/Title.tsx +0 -54
- package/src/features/Portal/Document/store/action.ts +0 -114
- package/src/features/Portal/Document/store/index.ts +0 -21
- package/src/features/Portal/Document/store/initialState.ts +0 -24
- package/src/features/Portal/Document/useEnable.ts +0 -8
- package/src/features/Portal/FilePreview/useEnable.ts +0 -6
- package/src/features/Portal/GroupThread/hook.ts +0 -9
- package/src/features/Portal/MessageDetail/useEnable.ts +0 -4
- package/src/features/Portal/Notebook/useEnable.ts +0 -6
- package/src/features/Portal/Plugins/useEnable.ts +0 -6
- package/src/features/Portal/Thread/hook.ts +0 -8
- package/src/store/document/slices/notebook/action.ts +0 -119
- package/src/store/document/slices/notebook/index.ts +0 -3
- package/src/store/document/slices/notebook/initialState.ts +0 -12
- package/src/store/document/slices/notebook/selectors.ts +0 -26
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.256](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.255...v2.0.0-next.256)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-10**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor page and notebook document usage.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor page and notebook document usage, closes [#11345](https://github.com/lobehub/lobe-chat/issues/11345) ([88721eb](https://github.com/lobehub/lobe-chat/commit/88721eb))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.255](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.254...v2.0.0-next.255)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2026-01-10**</sup>
|
|
33
|
+
|
|
34
|
+
#### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **misc**: Fix auto add group member crash.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's fixed
|
|
44
|
+
|
|
45
|
+
- **misc**: Fix auto add group member crash, closes [#11387](https://github.com/lobehub/lobe-chat/issues/11387) ([fe4ff91](https://github.com/lobehub/lobe-chat/commit/fe4ff91))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 2.0.0-next.254](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.253...v2.0.0-next.254)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2026-01-10**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"improvements": [
|
|
5
|
+
"Refactor page and notebook document usage."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2026-01-10",
|
|
9
|
+
"version": "2.0.0-next.256"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"fixes": [
|
|
14
|
+
"Fix auto add group member crash."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2026-01-10",
|
|
18
|
+
"version": "2.0.0-next.255"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"improvements": [
|
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
"builtins.lobe-local-system.inspector.noResults": "No results",
|
|
93
93
|
"builtins.lobe-local-system.inspector.rename.result": "<old>{{oldName}}</old> → <new>{{newName}}</new>",
|
|
94
94
|
"builtins.lobe-local-system.title": "Local System",
|
|
95
|
+
"builtins.lobe-notebook.actions.collapse": "Collapse",
|
|
95
96
|
"builtins.lobe-notebook.actions.copy": "Copy",
|
|
96
97
|
"builtins.lobe-notebook.actions.creating": "Creating document...",
|
|
97
98
|
"builtins.lobe-notebook.actions.edit": "Edit",
|
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
"builtins.lobe-local-system.inspector.noResults": "无结果",
|
|
93
93
|
"builtins.lobe-local-system.inspector.rename.result": "<old>{{oldName}}</old> → <new>{{newName}}</new>",
|
|
94
94
|
"builtins.lobe-local-system.title": "本地系统",
|
|
95
|
+
"builtins.lobe-notebook.actions.collapse": "收起",
|
|
95
96
|
"builtins.lobe-notebook.actions.copy": "复制",
|
|
96
97
|
"builtins.lobe-notebook.actions.creating": "文档创建中...",
|
|
97
98
|
"builtins.lobe-notebook.actions.edit": "编辑",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.256",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -21,7 +21,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
21
21
|
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
22
22
|
border-radius: 16px;
|
|
23
23
|
|
|
24
|
-
background: ${cssVar.
|
|
24
|
+
background: ${cssVar.colorBgContainer};
|
|
25
25
|
`,
|
|
26
26
|
content: css`
|
|
27
27
|
padding-block: 16px;
|
|
@@ -42,11 +42,11 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
42
42
|
transform: translateX(-50%);
|
|
43
43
|
|
|
44
44
|
display: inline-flex;
|
|
45
|
-
gap:
|
|
45
|
+
gap: 8px;
|
|
46
46
|
align-items: center;
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
padding-inline:
|
|
48
|
+
height: 32px;
|
|
49
|
+
padding-inline: 16px;
|
|
50
50
|
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
51
51
|
border-radius: 16px;
|
|
52
52
|
|
|
@@ -90,8 +90,8 @@ export const CreateDocumentPlaceholder = memo<BuiltinPlaceholderProps<CreateDocu
|
|
|
90
90
|
)}
|
|
91
91
|
</ScrollShadow>
|
|
92
92
|
<div className={styles.statusTag}>
|
|
93
|
-
<NeuralNetworkLoading size={
|
|
94
|
-
<span
|
|
93
|
+
<NeuralNetworkLoading size={16} />
|
|
94
|
+
<span>{t('builtins.lobe-notebook.actions.creating')}</span>
|
|
95
95
|
</div>
|
|
96
96
|
</Flexbox>
|
|
97
97
|
);
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
import { ActionIcon, CopyButton, Flexbox, Markdown, ScrollShadow } from '@lobehub/ui';
|
|
4
4
|
import { Button } from 'antd';
|
|
5
5
|
import { createStaticStyles } from 'antd-style';
|
|
6
|
-
import { Maximize2, NotebookText, PencilLine } from 'lucide-react';
|
|
6
|
+
import { Maximize2, Minimize2, NotebookText, PencilLine } from 'lucide-react';
|
|
7
7
|
import { memo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
|
|
10
10
|
import { useChatStore } from '@/store/chat';
|
|
11
|
+
import { chatPortalSelectors } from '@/store/chat/slices/portal/selectors';
|
|
11
12
|
|
|
12
13
|
import { NotebookDocument } from '../../../types';
|
|
13
14
|
|
|
@@ -21,7 +22,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
21
22
|
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
22
23
|
border-radius: 16px;
|
|
23
24
|
|
|
24
|
-
background: ${cssVar.
|
|
25
|
+
background: ${cssVar.colorBgContainer};
|
|
25
26
|
`,
|
|
26
27
|
content: css`
|
|
27
28
|
padding-inline: 16px;
|
|
@@ -60,10 +61,20 @@ interface DocumentCardProps {
|
|
|
60
61
|
|
|
61
62
|
const DocumentCard = memo<DocumentCardProps>(({ document }) => {
|
|
62
63
|
const { t } = useTranslation('plugin');
|
|
63
|
-
const openDocument = useChatStore((s) =>
|
|
64
|
+
const [portalDocumentId, openDocument, closeDocument] = useChatStore((s) => [
|
|
65
|
+
chatPortalSelectors.portalDocumentId(s),
|
|
66
|
+
s.openDocument,
|
|
67
|
+
s.closeDocument,
|
|
68
|
+
]);
|
|
64
69
|
|
|
65
|
-
const
|
|
66
|
-
|
|
70
|
+
const isExpanded = portalDocumentId === document.id;
|
|
71
|
+
|
|
72
|
+
const handleToggle = () => {
|
|
73
|
+
if (isExpanded) {
|
|
74
|
+
closeDocument();
|
|
75
|
+
} else {
|
|
76
|
+
openDocument(document.id);
|
|
77
|
+
}
|
|
67
78
|
};
|
|
68
79
|
|
|
69
80
|
return (
|
|
@@ -82,7 +93,7 @@ const DocumentCard = memo<DocumentCardProps>(({ document }) => {
|
|
|
82
93
|
/>
|
|
83
94
|
<ActionIcon
|
|
84
95
|
icon={PencilLine}
|
|
85
|
-
onClick={
|
|
96
|
+
onClick={handleToggle}
|
|
86
97
|
size={'small'}
|
|
87
98
|
title={t('builtins.lobe-notebook.actions.edit')}
|
|
88
99
|
/>
|
|
@@ -95,16 +106,18 @@ const DocumentCard = memo<DocumentCardProps>(({ document }) => {
|
|
|
95
106
|
</Markdown>
|
|
96
107
|
</ScrollShadow>
|
|
97
108
|
|
|
98
|
-
{/* Floating expand button */}
|
|
109
|
+
{/* Floating expand/collapse button */}
|
|
99
110
|
<Button
|
|
100
111
|
className={styles.expandButton}
|
|
101
112
|
color={'default'}
|
|
102
|
-
icon={<Maximize2 size={14} />}
|
|
103
|
-
onClick={
|
|
113
|
+
icon={isExpanded ? <Minimize2 size={14} /> : <Maximize2 size={14} />}
|
|
114
|
+
onClick={handleToggle}
|
|
104
115
|
shape={'round'}
|
|
105
116
|
variant={'outlined'}
|
|
106
117
|
>
|
|
107
|
-
{
|
|
118
|
+
{isExpanded
|
|
119
|
+
? t('builtins.lobe-notebook.actions.collapse')
|
|
120
|
+
: t('builtins.lobe-notebook.actions.expand')}
|
|
108
121
|
</Button>
|
|
109
122
|
</Flexbox>
|
|
110
123
|
);
|
|
@@ -20,7 +20,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
20
20
|
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
21
21
|
border-radius: 16px;
|
|
22
22
|
|
|
23
|
-
background: ${cssVar.
|
|
23
|
+
background: ${cssVar.colorBgContainer};
|
|
24
24
|
`,
|
|
25
25
|
header: css`
|
|
26
26
|
padding-block: 10px;
|
|
@@ -155,6 +155,33 @@ describe('AgentModel', () => {
|
|
|
155
155
|
expect(result).not.toBeNull();
|
|
156
156
|
expect(result!.files).toHaveLength(0);
|
|
157
157
|
});
|
|
158
|
+
|
|
159
|
+
it('should not return agent belonging to another user', async () => {
|
|
160
|
+
const agentId = 'test-agent-other-user';
|
|
161
|
+
// Create agent for user2
|
|
162
|
+
await serverDB.insert(agents).values({ id: agentId, userId: userId2 });
|
|
163
|
+
|
|
164
|
+
// Try to access with user1's model
|
|
165
|
+
const result = await agentModel.getAgentConfigById(agentId);
|
|
166
|
+
|
|
167
|
+
expect(result).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should not return knowledge from another user agent', async () => {
|
|
171
|
+
const agentId = 'test-agent-cross-user-knowledge';
|
|
172
|
+
// Create agent for user2 with knowledge
|
|
173
|
+
await serverDB.insert(agents).values({ id: agentId, userId: userId2 });
|
|
174
|
+
await serverDB
|
|
175
|
+
.insert(agentsKnowledgeBases)
|
|
176
|
+
.values({ agentId, knowledgeBaseId: 'kb2', userId: userId2 });
|
|
177
|
+
await serverDB.insert(agentsFiles).values({ agentId, fileId: '3', userId: userId2 });
|
|
178
|
+
|
|
179
|
+
// Try to access with user1's model
|
|
180
|
+
const result = await agentModel.getAgentConfigById(agentId);
|
|
181
|
+
|
|
182
|
+
// Should return null since user1 cannot access user2's agent
|
|
183
|
+
expect(result).toBeNull();
|
|
184
|
+
});
|
|
158
185
|
});
|
|
159
186
|
|
|
160
187
|
describe('getAgentConfig', () => {
|
|
@@ -197,15 +224,14 @@ describe('AgentModel', () => {
|
|
|
197
224
|
expect(result).toBeNull();
|
|
198
225
|
});
|
|
199
226
|
|
|
200
|
-
it('should find agent by ID
|
|
227
|
+
it('should not find agent by ID if it belongs to another user', async () => {
|
|
201
228
|
const agentId = 'test-agent-cross-user';
|
|
202
229
|
await serverDB.insert(agents).values({ id: agentId, userId: userId2 });
|
|
203
230
|
|
|
204
|
-
// ID lookup should work across users
|
|
231
|
+
// ID lookup should not work across users for security
|
|
205
232
|
const result = await agentModel.getAgentConfig(agentId);
|
|
206
233
|
|
|
207
|
-
expect(result).
|
|
208
|
-
expect(result?.id).toBe(agentId);
|
|
234
|
+
expect(result).toBeNull();
|
|
209
235
|
});
|
|
210
236
|
|
|
211
237
|
it('should prefer ID match over slug match', async () => {
|
|
@@ -257,6 +283,67 @@ describe('AgentModel', () => {
|
|
|
257
283
|
|
|
258
284
|
expect(result).toBeUndefined();
|
|
259
285
|
});
|
|
286
|
+
|
|
287
|
+
it('should not return agent from another user session', async () => {
|
|
288
|
+
const agentId = 'test-agent-other-user-session';
|
|
289
|
+
const sessionId = 'test-session-other-user';
|
|
290
|
+
// Create agent and session for user2
|
|
291
|
+
await serverDB.insert(agents).values({ id: agentId, userId: userId2 });
|
|
292
|
+
await serverDB.insert(sessions).values({ id: sessionId, userId: userId2 });
|
|
293
|
+
await serverDB
|
|
294
|
+
.insert(agentsToSessions)
|
|
295
|
+
.values({ agentId, sessionId, userId: userId2 });
|
|
296
|
+
|
|
297
|
+
// Try to access with user1's model
|
|
298
|
+
const result = await agentModel.findBySessionId(sessionId);
|
|
299
|
+
|
|
300
|
+
expect(result).toBeUndefined();
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('getAgentAssignedKnowledge', () => {
|
|
305
|
+
it('should return knowledge bases and files for the agent', async () => {
|
|
306
|
+
const agentId = 'test-agent-knowledge';
|
|
307
|
+
await serverDB.insert(agents).values({ id: agentId, userId });
|
|
308
|
+
await serverDB
|
|
309
|
+
.insert(agentsKnowledgeBases)
|
|
310
|
+
.values({ agentId, knowledgeBaseId: 'kb1', userId, enabled: true });
|
|
311
|
+
await serverDB.insert(agentsFiles).values({ agentId, fileId: '1', userId, enabled: true });
|
|
312
|
+
|
|
313
|
+
const result = await agentModel.getAgentAssignedKnowledge(agentId);
|
|
314
|
+
|
|
315
|
+
expect(result.knowledgeBases).toHaveLength(1);
|
|
316
|
+
expect(result.files).toHaveLength(1);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should not return knowledge from another user', async () => {
|
|
320
|
+
const agentId = 'test-agent-knowledge-other-user';
|
|
321
|
+
// Create agent with knowledge for user2
|
|
322
|
+
await serverDB.insert(agents).values({ id: agentId, userId: userId2 });
|
|
323
|
+
await serverDB
|
|
324
|
+
.insert(agentsKnowledgeBases)
|
|
325
|
+
.values({ agentId, knowledgeBaseId: 'kb2', userId: userId2, enabled: true });
|
|
326
|
+
await serverDB
|
|
327
|
+
.insert(agentsFiles)
|
|
328
|
+
.values({ agentId, fileId: '3', userId: userId2, enabled: true });
|
|
329
|
+
|
|
330
|
+
// Try to access with user1's model
|
|
331
|
+
const result = await agentModel.getAgentAssignedKnowledge(agentId);
|
|
332
|
+
|
|
333
|
+
// Should return empty arrays since user1 cannot access user2's knowledge
|
|
334
|
+
expect(result.knowledgeBases).toHaveLength(0);
|
|
335
|
+
expect(result.files).toHaveLength(0);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should handle empty knowledge bases and files', async () => {
|
|
339
|
+
const agentId = 'test-agent-no-knowledge';
|
|
340
|
+
await serverDB.insert(agents).values({ id: agentId, userId });
|
|
341
|
+
|
|
342
|
+
const result = await agentModel.getAgentAssignedKnowledge(agentId);
|
|
343
|
+
|
|
344
|
+
expect(result.knowledgeBases).toHaveLength(0);
|
|
345
|
+
expect(result.files).toHaveLength(0);
|
|
346
|
+
});
|
|
260
347
|
});
|
|
261
348
|
|
|
262
349
|
describe('createAgentKnowledgeBase', () => {
|
|
@@ -28,7 +28,9 @@ export class AgentModel {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
getAgentConfigById = async (id: string) => {
|
|
31
|
-
const agent = await this.db.query.agents.findFirst({
|
|
31
|
+
const agent = await this.db.query.agents.findFirst({
|
|
32
|
+
where: and(eq(agents.id, id), eq(agents.userId, this.userId)),
|
|
33
|
+
});
|
|
32
34
|
|
|
33
35
|
if (!agent) return null;
|
|
34
36
|
|
|
@@ -76,9 +78,9 @@ export class AgentModel {
|
|
|
76
78
|
*/
|
|
77
79
|
getAgentConfig = async (idOrSlug: string) => {
|
|
78
80
|
const agent = await this.db.query.agents.findFirst({
|
|
79
|
-
where:
|
|
80
|
-
eq(agents.
|
|
81
|
-
|
|
81
|
+
where: and(
|
|
82
|
+
eq(agents.userId, this.userId),
|
|
83
|
+
or(eq(agents.id, idOrSlug), eq(agents.slug, idOrSlug)),
|
|
82
84
|
),
|
|
83
85
|
});
|
|
84
86
|
|
|
@@ -118,17 +120,20 @@ export class AgentModel {
|
|
|
118
120
|
|
|
119
121
|
getAgentAssignedKnowledge = async (id: string) => {
|
|
120
122
|
// Run both queries in parallel for better performance
|
|
123
|
+
// Include userId check to ensure user can only access their own agent's knowledge
|
|
121
124
|
const [knowledgeBaseResult, fileResult] = await Promise.all([
|
|
122
125
|
this.db
|
|
123
126
|
.select({ enabled: agentsKnowledgeBases.enabled, knowledgeBases })
|
|
124
127
|
.from(agentsKnowledgeBases)
|
|
125
|
-
.where(
|
|
128
|
+
.where(
|
|
129
|
+
and(eq(agentsKnowledgeBases.agentId, id), eq(agentsKnowledgeBases.userId, this.userId)),
|
|
130
|
+
)
|
|
126
131
|
.orderBy(desc(agentsKnowledgeBases.createdAt))
|
|
127
132
|
.leftJoin(knowledgeBases, eq(knowledgeBases.id, agentsKnowledgeBases.knowledgeBaseId)),
|
|
128
133
|
this.db
|
|
129
134
|
.select({ enabled: agentsFiles.enabled, files })
|
|
130
135
|
.from(agentsFiles)
|
|
131
|
-
.where(eq(agentsFiles.agentId, id))
|
|
136
|
+
.where(and(eq(agentsFiles.agentId, id), eq(agentsFiles.userId, this.userId)))
|
|
132
137
|
.orderBy(desc(agentsFiles.createdAt))
|
|
133
138
|
.leftJoin(files, eq(files.id, agentsFiles.fileId)),
|
|
134
139
|
]);
|
|
@@ -150,7 +155,10 @@ export class AgentModel {
|
|
|
150
155
|
*/
|
|
151
156
|
findBySessionId = async (sessionId: string) => {
|
|
152
157
|
const item = await this.db.query.agentsToSessions.findFirst({
|
|
153
|
-
where:
|
|
158
|
+
where: and(
|
|
159
|
+
eq(agentsToSessions.sessionId, sessionId),
|
|
160
|
+
eq(agentsToSessions.userId, this.userId),
|
|
161
|
+
),
|
|
154
162
|
});
|
|
155
163
|
|
|
156
164
|
if (!item) return;
|
|
@@ -109,7 +109,7 @@ export class EditorRuntime {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// Set markdown content directly - the editor will convert it internally
|
|
112
|
-
editor.setDocument('markdown', markdown);
|
|
112
|
+
editor.setDocument('markdown', markdown, { keepId: true });
|
|
113
113
|
|
|
114
114
|
// Get the resulting document to count nodes
|
|
115
115
|
const jsonState = editor.getDocument('json') as any;
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
CommonPlugin,
|
|
3
3
|
type IEditor,
|
|
4
4
|
Kernel,
|
|
5
|
+
ListPlugin,
|
|
5
6
|
LitexmlPlugin,
|
|
6
7
|
MarkdownPlugin,
|
|
7
8
|
moment,
|
|
@@ -10,6 +11,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
10
11
|
|
|
11
12
|
import { EditorRuntime } from '../EditorRuntime';
|
|
12
13
|
import editAllFixture from './fixtures/edit-all.json';
|
|
14
|
+
import removeThenAddFixture from './fixtures/remove-then-add.json';
|
|
13
15
|
import removeFixture from './fixtures/remove.json';
|
|
14
16
|
|
|
15
17
|
describe('EditorRuntime - Real Cases', () => {
|
|
@@ -20,7 +22,7 @@ describe('EditorRuntime - Real Cases', () => {
|
|
|
20
22
|
|
|
21
23
|
beforeEach(() => {
|
|
22
24
|
editor = new Kernel();
|
|
23
|
-
editor.registerPlugins([CommonPlugin, MarkdownPlugin, LitexmlPlugin]);
|
|
25
|
+
editor.registerPlugins([CommonPlugin, MarkdownPlugin, ListPlugin, LitexmlPlugin]);
|
|
24
26
|
editor.initNodeEditor();
|
|
25
27
|
|
|
26
28
|
runtime = new EditorRuntime();
|
|
@@ -163,9 +165,6 @@ describe('EditorRuntime - Real Cases', () => {
|
|
|
163
165
|
|
|
164
166
|
// Verify paragraphs were removed
|
|
165
167
|
const xmlAfter = editor.getDocument('litexml') as unknown as string;
|
|
166
|
-
const paragraphsAfter = [...xmlAfter.matchAll(/<p id="([^"]+)"/g)];
|
|
167
|
-
|
|
168
|
-
expect(paragraphsAfter.length).toBe(initialCount - 7);
|
|
169
168
|
|
|
170
169
|
// Verify the removed IDs are no longer present
|
|
171
170
|
expect(xmlAfter).not.toContain('id="wps3"');
|
|
@@ -179,4 +178,66 @@ describe('EditorRuntime - Real Cases', () => {
|
|
|
179
178
|
expect(xmlAfter).toMatchSnapshot();
|
|
180
179
|
});
|
|
181
180
|
});
|
|
181
|
+
|
|
182
|
+
describe('modifyNodes - remove then add', () => {
|
|
183
|
+
it('should remove 13 paragraphs then insert a list', async () => {
|
|
184
|
+
// Initialize editor with the JSON fixture
|
|
185
|
+
editor.setDocument('json', removeThenAddFixture);
|
|
186
|
+
await moment();
|
|
187
|
+
|
|
188
|
+
// First operation: remove 13 paragraphs
|
|
189
|
+
const removeResult = await runtime.modifyNodes({
|
|
190
|
+
operations: [
|
|
191
|
+
{ action: 'remove', id: 'x4qr' },
|
|
192
|
+
{ action: 'remove', id: 'xfvd' },
|
|
193
|
+
{ action: 'remove', id: 'xqzz' },
|
|
194
|
+
{ action: 'remove', id: 'zrby' },
|
|
195
|
+
{ action: 'remove', id: '02gk' },
|
|
196
|
+
{ action: 'remove', id: '0dl6' },
|
|
197
|
+
{ action: 'remove', id: '0ops' },
|
|
198
|
+
{ action: 'remove', id: '1rnx' },
|
|
199
|
+
{ action: 'remove', id: '22sj' },
|
|
200
|
+
{ action: 'remove', id: '2dx5' },
|
|
201
|
+
{ action: 'remove', id: '3gva' },
|
|
202
|
+
{ action: 'remove', id: '3rzw' },
|
|
203
|
+
{ action: 'remove', id: '434i' },
|
|
204
|
+
],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await moment();
|
|
208
|
+
|
|
209
|
+
// Verify all remove operations succeeded
|
|
210
|
+
expect(removeResult.successCount).toBe(13);
|
|
211
|
+
expect(removeResult.totalCount).toBe(13);
|
|
212
|
+
expect(removeResult.results.every((r) => r.success)).toBe(true);
|
|
213
|
+
expect(removeResult.results.every((r) => r.action === 'remove')).toBe(true);
|
|
214
|
+
|
|
215
|
+
// Verify the content was removed
|
|
216
|
+
const removed = editor.getDocument('litexml') as unknown as string;
|
|
217
|
+
expect(removed).toMatchSnapshot('remove 13 paragraphs');
|
|
218
|
+
|
|
219
|
+
// Second operation: insert a list after wtm5
|
|
220
|
+
const insertResult = await runtime.modifyNodes({
|
|
221
|
+
operations: [
|
|
222
|
+
{
|
|
223
|
+
action: 'insert',
|
|
224
|
+
afterId: 'wtm5',
|
|
225
|
+
litexml:
|
|
226
|
+
'<ul><li>西湖风景区:杭州的灵魂,世界文化遗产</li><li>灵隐寺:杭州最著名的佛教寺庙</li><li>西溪国家湿地公园:中国第一个国家湿地公园</li><li>宋城:以宋代文化为主题的大型主题公园</li></ul>',
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
});
|
|
230
|
+
await moment();
|
|
231
|
+
|
|
232
|
+
// Verify insert operation succeeded
|
|
233
|
+
expect(insertResult.successCount).toBe(1);
|
|
234
|
+
expect(insertResult.totalCount).toBe(1);
|
|
235
|
+
expect(insertResult.results.every((r) => r.success)).toBe(true);
|
|
236
|
+
expect(insertResult.results.every((r) => r.action === 'insert')).toBe(true);
|
|
237
|
+
|
|
238
|
+
// Verify full output
|
|
239
|
+
const xmlAfter = editor.getDocument('litexml') as unknown as string;
|
|
240
|
+
expect(xmlAfter).toMatchSnapshot('insert new');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
182
243
|
});
|