@lobehub/lobehub 2.0.0-next.353 → 2.0.0-next.355
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/.agents/skills/add-provider-doc/SKILL.md +90 -0
- package/.agents/skills/add-setting-env/SKILL.md +106 -0
- package/.agents/skills/debug/SKILL.md +66 -0
- package/.agents/skills/desktop/SKILL.md +78 -0
- package/.agents/skills/desktop/references/feature-implementation.md +99 -0
- package/.agents/skills/desktop/references/local-tools.md +133 -0
- package/.agents/skills/desktop/references/menu-config.md +103 -0
- package/.agents/skills/desktop/references/window-management.md +143 -0
- package/.agents/skills/drizzle/SKILL.md +129 -0
- package/.agents/skills/drizzle/references/db-migrations.md +50 -0
- package/.agents/skills/hotkey/SKILL.md +90 -0
- package/{.cursor/rules/i18n.mdc → .agents/skills/i18n/SKILL.md} +14 -23
- package/.agents/skills/linear/SKILL.md +51 -0
- package/.agents/skills/microcopy/SKILL.md +83 -0
- package/.agents/skills/modal/SKILL.md +102 -0
- package/{.cursor/rules/project-structure.mdc → .agents/skills/project-overview/SKILL.md} +65 -37
- package/.agents/skills/react/SKILL.md +73 -0
- package/.agents/skills/react/references/layout-kit.md +100 -0
- package/.agents/skills/recent-data/SKILL.md +108 -0
- package/.agents/skills/testing/SKILL.md +89 -0
- package/.agents/skills/testing/references/agent-runtime-e2e.md +126 -0
- package/.agents/skills/testing/references/db-model-test.md +124 -0
- package/.agents/skills/testing/references/desktop-controller-test.md +124 -0
- package/.agents/skills/testing/references/electron-ipc-test.md +63 -0
- package/.agents/skills/testing/references/zustand-store-action-test.md +150 -0
- package/.agents/skills/typescript/SKILL.md +52 -0
- package/.agents/skills/zustand/SKILL.md +78 -0
- package/.agents/skills/zustand/references/action-patterns.md +125 -0
- package/.agents/skills/zustand/references/slice-organization.md +125 -0
- package/AGENTS.md +42 -55
- package/CHANGELOG.md +60 -0
- package/CLAUDE.md +57 -46
- package/GEMINI.md +47 -39
- package/changelog/v1.json +18 -0
- package/locales/en-US/plugin.json +3 -0
- package/locales/zh-CN/plugin.json +3 -0
- package/package.json +1 -1
- package/packages/builtin-tool-memory/src/client/Render/SearchUserMemory/index.tsx +3 -11
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +0 -13
- package/packages/context-engine/src/engine/messages/__tests__/MessagesEngine.test.ts +0 -25
- package/packages/database/src/models/__tests__/topics/topic.create.test.ts +3 -3
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
- package/src/app/[variants]/(main)/agent/features/Conversation/ConversationArea.tsx +4 -0
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/InboxItem.tsx +19 -29
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/List.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider.tsx +1 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +2 -3
- package/src/features/ShareModal/SharePdf/PdfPreview.tsx +1 -2
- package/src/libs/pdfjs/index.tsx +25 -0
- package/src/locales/default/plugin.ts +3 -0
- package/src/server/modules/Mecha/ContextEngineering/__tests__/serverMessagesEngine.test.ts +0 -25
- package/src/services/chat/chat.test.ts +19 -19
- package/src/services/chat/index.ts +8 -3
- package/src/services/chat/mecha/agentConfigResolver.test.ts +72 -55
- package/src/services/chat/mecha/agentConfigResolver.ts +28 -4
- package/src/services/chat/mecha/contextEngineering.test.ts +21 -14
- package/src/services/chat/mecha/contextEngineering.ts +12 -0
- package/src/services/chat/types.ts +7 -1
- package/src/store/chat/agents/createAgentExecutors.ts +15 -4
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +1 -0
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
- package/src/store/test-coverage.md +5 -5
- package/.cursor/rules/add-provider-doc.mdc +0 -183
- package/.cursor/rules/add-setting-env.mdc +0 -175
- package/.cursor/rules/cursor-rules.mdc +0 -28
- package/.cursor/rules/db-migrations.mdc +0 -46
- package/.cursor/rules/debug-usage.mdc +0 -86
- package/.cursor/rules/desktop-controller-tests.mdc +0 -189
- package/.cursor/rules/desktop-feature-implementation.mdc +0 -155
- package/.cursor/rules/desktop-local-tools-implement.mdc +0 -81
- package/.cursor/rules/desktop-menu-configuration.mdc +0 -209
- package/.cursor/rules/desktop-window-management.mdc +0 -301
- package/.cursor/rules/drizzle-schema-style-guide.mdc +0 -218
- package/.cursor/rules/hotkey.mdc +0 -162
- package/.cursor/rules/linear.mdc +0 -53
- package/.cursor/rules/microcopy-cn.mdc +0 -158
- package/.cursor/rules/microcopy-en.mdc +0 -148
- package/.cursor/rules/modal-imperative.mdc +0 -162
- package/.cursor/rules/packages/react-layout-kit.mdc +0 -122
- package/.cursor/rules/project-introduce.mdc +0 -36
- package/.cursor/rules/react.mdc +0 -169
- package/.cursor/rules/recent-data-usage.mdc +0 -139
- package/.cursor/rules/rules-index.mdc +0 -44
- package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +0 -285
- package/.cursor/rules/testing-guide/db-model-test.mdc +0 -455
- package/.cursor/rules/testing-guide/electron-ipc-test.mdc +0 -80
- package/.cursor/rules/testing-guide/testing-guide.mdc +0 -534
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +0 -574
- package/.cursor/rules/typescript.mdc +0 -55
- package/.cursor/rules/zustand-action-patterns.mdc +0 -328
- package/.cursor/rules/zustand-slice-organization.mdc +0 -308
- package/src/libs/pdfjs/pdf.worker.ts +0 -1
- package/src/libs/pdfjs/worker.ts +0 -12
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/AGENTS.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/SKILL.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-event-handler-refs.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-use-latest.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-api-routes.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-defer-await.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-dependencies.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-parallel.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-suspense-boundaries.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-barrel-imports.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-conditional.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-defer-third-party.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-dynamic-imports.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-preload.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-event-listeners.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-localstorage-schema.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-passive-event-listeners.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-swr-dedup.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-batch-dom-css.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-function-results.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-property-access.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-storage.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-combine-iterations.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-early-exit.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-hoist-regexp.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-index-maps.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-length-check-first.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-min-max-loop.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-set-map-lookups.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-tosorted-immutable.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-activity.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-conditional-render.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-content-visibility.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hoist-jsx.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-svg-precision.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-defer-reads.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-dependencies.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-derived-state.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-functional-setstate.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-lazy-state-init.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-memo.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-transitions.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-after-nonblocking.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-lru.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-react.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-parallel-fetching.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-serialization.md +0 -0
|
@@ -155,7 +155,7 @@ describe('ChatService', () => {
|
|
|
155
155
|
]),
|
|
156
156
|
messages: expect.anything(),
|
|
157
157
|
}),
|
|
158
|
-
|
|
158
|
+
expect.anything(),
|
|
159
159
|
);
|
|
160
160
|
});
|
|
161
161
|
|
|
@@ -185,7 +185,7 @@ describe('ChatService', () => {
|
|
|
185
185
|
type: 'enabled',
|
|
186
186
|
},
|
|
187
187
|
}),
|
|
188
|
-
|
|
188
|
+
expect.anything(),
|
|
189
189
|
);
|
|
190
190
|
});
|
|
191
191
|
|
|
@@ -214,7 +214,7 @@ describe('ChatService', () => {
|
|
|
214
214
|
type: 'disabled',
|
|
215
215
|
},
|
|
216
216
|
}),
|
|
217
|
-
|
|
217
|
+
expect.anything(),
|
|
218
218
|
);
|
|
219
219
|
});
|
|
220
220
|
|
|
@@ -244,7 +244,7 @@ describe('ChatService', () => {
|
|
|
244
244
|
type: 'enabled',
|
|
245
245
|
},
|
|
246
246
|
}),
|
|
247
|
-
|
|
247
|
+
expect.anything(),
|
|
248
248
|
);
|
|
249
249
|
});
|
|
250
250
|
|
|
@@ -270,7 +270,7 @@ describe('ChatService', () => {
|
|
|
270
270
|
expect.objectContaining({
|
|
271
271
|
reasoning_effort: 'high',
|
|
272
272
|
}),
|
|
273
|
-
|
|
273
|
+
expect.anything(),
|
|
274
274
|
);
|
|
275
275
|
});
|
|
276
276
|
|
|
@@ -296,7 +296,7 @@ describe('ChatService', () => {
|
|
|
296
296
|
expect.objectContaining({
|
|
297
297
|
thinkingBudget: 5000,
|
|
298
298
|
}),
|
|
299
|
-
|
|
299
|
+
expect.anything(),
|
|
300
300
|
);
|
|
301
301
|
});
|
|
302
302
|
});
|
|
@@ -370,7 +370,7 @@ describe('ChatService', () => {
|
|
|
370
370
|
enabledSearch: undefined,
|
|
371
371
|
tools: undefined,
|
|
372
372
|
},
|
|
373
|
-
|
|
373
|
+
expect.anything(),
|
|
374
374
|
);
|
|
375
375
|
});
|
|
376
376
|
|
|
@@ -396,7 +396,7 @@ describe('ChatService', () => {
|
|
|
396
396
|
stream: true,
|
|
397
397
|
tools: undefined,
|
|
398
398
|
},
|
|
399
|
-
|
|
399
|
+
expect.anything(),
|
|
400
400
|
);
|
|
401
401
|
});
|
|
402
402
|
});
|
|
@@ -494,7 +494,7 @@ describe('ChatService', () => {
|
|
|
494
494
|
enabledSearch: undefined,
|
|
495
495
|
tools: undefined,
|
|
496
496
|
},
|
|
497
|
-
|
|
497
|
+
expect.anything(),
|
|
498
498
|
);
|
|
499
499
|
});
|
|
500
500
|
|
|
@@ -583,7 +583,7 @@ describe('ChatService', () => {
|
|
|
583
583
|
enabledSearch: undefined,
|
|
584
584
|
tools: undefined,
|
|
585
585
|
},
|
|
586
|
-
|
|
586
|
+
expect.anything(),
|
|
587
587
|
);
|
|
588
588
|
});
|
|
589
589
|
|
|
@@ -781,7 +781,7 @@ describe('ChatService', () => {
|
|
|
781
781
|
{ content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
|
|
782
782
|
],
|
|
783
783
|
},
|
|
784
|
-
|
|
784
|
+
expect.anything(),
|
|
785
785
|
);
|
|
786
786
|
});
|
|
787
787
|
|
|
@@ -888,7 +888,7 @@ describe('ChatService', () => {
|
|
|
888
888
|
{ content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
|
|
889
889
|
],
|
|
890
890
|
},
|
|
891
|
-
|
|
891
|
+
expect.anything(),
|
|
892
892
|
);
|
|
893
893
|
});
|
|
894
894
|
|
|
@@ -926,7 +926,7 @@ describe('ChatService', () => {
|
|
|
926
926
|
{ content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
|
|
927
927
|
],
|
|
928
928
|
},
|
|
929
|
-
|
|
929
|
+
expect.anything(),
|
|
930
930
|
);
|
|
931
931
|
});
|
|
932
932
|
});
|
|
@@ -985,7 +985,7 @@ describe('ChatService', () => {
|
|
|
985
985
|
}),
|
|
986
986
|
]),
|
|
987
987
|
}),
|
|
988
|
-
|
|
988
|
+
expect.anything(),
|
|
989
989
|
);
|
|
990
990
|
});
|
|
991
991
|
|
|
@@ -1036,7 +1036,7 @@ describe('ChatService', () => {
|
|
|
1036
1036
|
expect.objectContaining({
|
|
1037
1037
|
enabledSearch: true,
|
|
1038
1038
|
}),
|
|
1039
|
-
|
|
1039
|
+
expect.anything(),
|
|
1040
1040
|
);
|
|
1041
1041
|
});
|
|
1042
1042
|
|
|
@@ -1087,7 +1087,7 @@ describe('ChatService', () => {
|
|
|
1087
1087
|
expect.objectContaining({
|
|
1088
1088
|
enabledSearch: undefined,
|
|
1089
1089
|
}),
|
|
1090
|
-
|
|
1090
|
+
expect.anything(),
|
|
1091
1091
|
);
|
|
1092
1092
|
});
|
|
1093
1093
|
});
|
|
@@ -1385,7 +1385,7 @@ describe('ChatService private methods', () => {
|
|
|
1385
1385
|
expect.objectContaining({
|
|
1386
1386
|
enabledContextCaching: false,
|
|
1387
1387
|
}),
|
|
1388
|
-
|
|
1388
|
+
expect.anything(),
|
|
1389
1389
|
);
|
|
1390
1390
|
});
|
|
1391
1391
|
|
|
@@ -1438,7 +1438,7 @@ describe('ChatService private methods', () => {
|
|
|
1438
1438
|
expect.objectContaining({
|
|
1439
1439
|
reasoning_effort: 'high',
|
|
1440
1440
|
}),
|
|
1441
|
-
|
|
1441
|
+
expect.anything(),
|
|
1442
1442
|
);
|
|
1443
1443
|
});
|
|
1444
1444
|
|
|
@@ -1464,7 +1464,7 @@ describe('ChatService private methods', () => {
|
|
|
1464
1464
|
expect.objectContaining({
|
|
1465
1465
|
thinkingBudget: 5000,
|
|
1466
1466
|
}),
|
|
1467
|
-
|
|
1467
|
+
expect.anything(),
|
|
1468
1468
|
);
|
|
1469
1469
|
});
|
|
1470
1470
|
});
|
|
@@ -284,7 +284,7 @@ class ChatService {
|
|
|
284
284
|
stream: chatConfig.enableStreaming !== false,
|
|
285
285
|
tools,
|
|
286
286
|
},
|
|
287
|
-
options,
|
|
287
|
+
{ ...options, agentId: targetAgentId, topicId },
|
|
288
288
|
);
|
|
289
289
|
};
|
|
290
290
|
|
|
@@ -314,7 +314,7 @@ class ChatService {
|
|
|
314
314
|
};
|
|
315
315
|
|
|
316
316
|
getChatCompletion = async (params: Partial<ChatStreamPayload>, options?: FetchOptions) => {
|
|
317
|
-
const { signal, responseAnimation } = options ?? {};
|
|
317
|
+
const { agentId, signal, responseAnimation, topicId } = options ?? {};
|
|
318
318
|
|
|
319
319
|
const { provider = ModelProvider.OpenAI, ...res } = params;
|
|
320
320
|
|
|
@@ -401,7 +401,12 @@ class ChatService {
|
|
|
401
401
|
const traceHeader = createTraceHeader({ ...options?.trace });
|
|
402
402
|
|
|
403
403
|
const headers = await createHeaderWithAuth({
|
|
404
|
-
headers: {
|
|
404
|
+
headers: {
|
|
405
|
+
'Content-Type': 'application/json',
|
|
406
|
+
...traceHeader,
|
|
407
|
+
...(agentId && { 'x-agent-id': agentId }),
|
|
408
|
+
...(topicId && { 'x-topic-id': topicId }),
|
|
409
|
+
},
|
|
405
410
|
provider,
|
|
406
411
|
});
|
|
407
412
|
|
|
@@ -48,6 +48,31 @@ describe('resolveAgentConfig', () => {
|
|
|
48
48
|
expect(result.isBuiltinAgent).toBe(false);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
it('should treat agent with non-builtin slug as regular agent', () => {
|
|
52
|
+
// Agent has a random slug that is NOT a valid builtin agent slug
|
|
53
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
54
|
+
() => 'sister-religious-mostly-effort',
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const result = resolveAgentConfig({ agentId: 'test-agent' });
|
|
58
|
+
|
|
59
|
+
expect(result.isBuiltinAgent).toBe(false);
|
|
60
|
+
expect(result.slug).toBeUndefined();
|
|
61
|
+
expect(result.plugins).toEqual(['plugin-a', 'plugin-b']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should treat agent with random slug as regular agent', () => {
|
|
65
|
+
// Another example of random/custom slug
|
|
66
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
67
|
+
() => 'my-custom-agent-slug',
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const result = resolveAgentConfig({ agentId: 'test-agent' });
|
|
71
|
+
|
|
72
|
+
expect(result.isBuiltinAgent).toBe(false);
|
|
73
|
+
expect(result.slug).toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
51
76
|
it('should return empty array when agent config has no plugins', () => {
|
|
52
77
|
vi.spyOn(agentSelectors.agentSelectors, 'getAgentConfigById').mockReturnValue(
|
|
53
78
|
() =>
|
|
@@ -668,6 +693,29 @@ describe('resolveAgentConfig', () => {
|
|
|
668
693
|
expect(result.agentConfig.systemRole.trim()).toBe('You are a helpful assistant');
|
|
669
694
|
expect(result.chatConfig.enableHistoryCount).toBe(false);
|
|
670
695
|
});
|
|
696
|
+
|
|
697
|
+
it('should not duplicate injection when page-agent itself is used in page scope', () => {
|
|
698
|
+
// page-agent is a builtin agent with slug 'page-agent'
|
|
699
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
700
|
+
() => 'page-agent',
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
704
|
+
plugins: [PageAgentIdentifier],
|
|
705
|
+
systemRole: 'Page agent system prompt',
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
const result = resolveAgentConfig({
|
|
709
|
+
agentId: 'page-agent-id',
|
|
710
|
+
scope: 'page',
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
// page-agent should NOT have its tools/systemRole injected again
|
|
714
|
+
expect(result.plugins.filter((p) => p === PageAgentIdentifier)).toHaveLength(1);
|
|
715
|
+
expect(result.agentConfig.systemRole).toBe('Page agent system prompt');
|
|
716
|
+
expect(result.isBuiltinAgent).toBe(true);
|
|
717
|
+
expect(result.slug).toBe('page-agent');
|
|
718
|
+
});
|
|
671
719
|
});
|
|
672
720
|
|
|
673
721
|
describe('supervisor agent (detected via groupId)', () => {
|
|
@@ -695,13 +743,11 @@ describe('resolveAgentConfig', () => {
|
|
|
695
743
|
});
|
|
696
744
|
|
|
697
745
|
describe('supervisor with own slug (priority check)', () => {
|
|
698
|
-
//
|
|
699
|
-
// it should still use 'group-supervisor' slug when in group scope
|
|
700
|
-
|
|
746
|
+
// When supervisor agent has its own slug, it should still use 'group-supervisor' slug when in group scope
|
|
701
747
|
it('should use group-supervisor slug even when agent has its own slug in group scope', () => {
|
|
702
748
|
// Supervisor agent has its own slug (e.g., from being a builtin agent)
|
|
703
749
|
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
704
|
-
() => '
|
|
750
|
+
() => 'agent-builder',
|
|
705
751
|
);
|
|
706
752
|
|
|
707
753
|
// Mock: groupById returns the group
|
|
@@ -734,54 +780,6 @@ describe('resolveAgentConfig', () => {
|
|
|
734
780
|
expect(result.slug).toBe('group-supervisor');
|
|
735
781
|
expect(result.plugins).toContain(GroupManagementIdentifier);
|
|
736
782
|
});
|
|
737
|
-
|
|
738
|
-
it('should use agent own slug when scope is not group', () => {
|
|
739
|
-
// Supervisor agent has its own slug
|
|
740
|
-
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
741
|
-
() => 'some-agent-slug',
|
|
742
|
-
);
|
|
743
|
-
|
|
744
|
-
// Mock: groupById returns the group
|
|
745
|
-
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
746
|
-
() => mockGroupWithSupervisor as any,
|
|
747
|
-
);
|
|
748
|
-
|
|
749
|
-
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
750
|
-
plugins: ['agent-specific-plugin'],
|
|
751
|
-
systemRole: 'Agent specific system role',
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
const result = resolveAgentConfig({
|
|
755
|
-
agentId: 'supervisor-agent-id',
|
|
756
|
-
groupId: 'group-123',
|
|
757
|
-
scope: 'main', // Not 'group' scope
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
// Should use agent's own slug since scope is not 'group'
|
|
761
|
-
expect(result.isBuiltinAgent).toBe(true);
|
|
762
|
-
expect(result.slug).toBe('some-agent-slug');
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
it('should use agent own slug when groupId is not provided', () => {
|
|
766
|
-
// Supervisor agent has its own slug
|
|
767
|
-
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
768
|
-
() => 'some-agent-slug',
|
|
769
|
-
);
|
|
770
|
-
|
|
771
|
-
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
772
|
-
plugins: ['agent-specific-plugin'],
|
|
773
|
-
systemRole: 'Agent specific system role',
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
const result = resolveAgentConfig({
|
|
777
|
-
agentId: 'supervisor-agent-id',
|
|
778
|
-
scope: 'group', // Even with group scope, no groupId
|
|
779
|
-
});
|
|
780
|
-
|
|
781
|
-
// Should use agent's own slug since no groupId
|
|
782
|
-
expect(result.isBuiltinAgent).toBe(true);
|
|
783
|
-
expect(result.slug).toBe('some-agent-slug');
|
|
784
|
-
});
|
|
785
783
|
});
|
|
786
784
|
|
|
787
785
|
it('should detect supervisor agent using groupId for direct lookup', () => {
|
|
@@ -872,6 +870,25 @@ describe('resolveAgentConfig', () => {
|
|
|
872
870
|
expect(result.plugins).toEqual(['plugin-a', 'plugin-b']); // Falls back to agent config plugins
|
|
873
871
|
});
|
|
874
872
|
|
|
873
|
+
it('should treat as regular agent when scope is not group even with groupId', () => {
|
|
874
|
+
// Mock: groupById returns the group (supervisor agent exists)
|
|
875
|
+
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
876
|
+
() => mockGroupWithSupervisor as any,
|
|
877
|
+
);
|
|
878
|
+
|
|
879
|
+
// groupId is provided but scope is 'main', not 'group'
|
|
880
|
+
const result = resolveAgentConfig({
|
|
881
|
+
agentId: 'supervisor-agent-id',
|
|
882
|
+
groupId: 'group-123',
|
|
883
|
+
scope: 'main', // Not 'group' scope
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// Should NOT be identified as group-supervisor because scope !== 'group'
|
|
887
|
+
expect(result.isBuiltinAgent).toBe(false);
|
|
888
|
+
expect(result.slug).toBeUndefined();
|
|
889
|
+
expect(result.plugins).toEqual(['plugin-a', 'plugin-b']);
|
|
890
|
+
});
|
|
891
|
+
|
|
875
892
|
it('should treat as regular agent when agentId does not match group supervisorAgentId', () => {
|
|
876
893
|
// Mock: groupById returns the group
|
|
877
894
|
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
@@ -1016,7 +1033,7 @@ describe('resolveAgentConfig', () => {
|
|
|
1016
1033
|
|
|
1017
1034
|
it('should filter lobe-gtd for builtin agent when isSubTask is true', () => {
|
|
1018
1035
|
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
1019
|
-
() => '
|
|
1036
|
+
() => 'agent-builder',
|
|
1020
1037
|
);
|
|
1021
1038
|
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
1022
1039
|
plugins: ['lobe-gtd', 'runtime-plugin'],
|
|
@@ -1034,7 +1051,7 @@ describe('resolveAgentConfig', () => {
|
|
|
1034
1051
|
|
|
1035
1052
|
it('should keep lobe-gtd for builtin agent when isSubTask is false', () => {
|
|
1036
1053
|
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
1037
|
-
() => '
|
|
1054
|
+
() => 'agent-builder',
|
|
1038
1055
|
);
|
|
1039
1056
|
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
1040
1057
|
plugins: ['lobe-gtd', 'runtime-plugin'],
|
|
@@ -1089,7 +1106,7 @@ describe('resolveAgentConfig', () => {
|
|
|
1089
1106
|
|
|
1090
1107
|
it('should return empty plugins for builtin agent when disableTools is true', () => {
|
|
1091
1108
|
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
1092
|
-
() => '
|
|
1109
|
+
() => 'agent-builder',
|
|
1093
1110
|
);
|
|
1094
1111
|
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
1095
1112
|
plugins: ['runtime-plugin-1', 'runtime-plugin-2'],
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BUILTIN_AGENT_SLUGS,
|
|
3
|
+
type BuiltinAgentSlug,
|
|
4
|
+
getAgentRuntimeConfig,
|
|
5
|
+
} from '@lobechat/builtin-agents';
|
|
2
6
|
import { PageAgentIdentifier } from '@lobechat/builtin-tool-page-agent';
|
|
3
7
|
import {
|
|
4
8
|
type LobeAgentChatConfig,
|
|
@@ -15,6 +19,18 @@ import { agentGroupByIdSelectors, agentGroupSelectors } from '@/store/agentGroup
|
|
|
15
19
|
|
|
16
20
|
const log = debug('mecha:agentConfigResolver');
|
|
17
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Set of valid builtin agent slugs for O(1) lookup
|
|
24
|
+
*/
|
|
25
|
+
const VALID_BUILTIN_SLUGS = new Set<string>(Object.values(BUILTIN_AGENT_SLUGS));
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a slug is a valid builtin agent slug
|
|
29
|
+
*/
|
|
30
|
+
const isBuiltinAgentSlug = (slug: string): slug is BuiltinAgentSlug => {
|
|
31
|
+
return VALID_BUILTIN_SLUGS.has(slug);
|
|
32
|
+
};
|
|
33
|
+
|
|
18
34
|
/**
|
|
19
35
|
* Applies params adjustments based on chatConfig settings.
|
|
20
36
|
*
|
|
@@ -187,12 +203,20 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
187
203
|
|
|
188
204
|
// If not identified as supervisor, check agent store for slug
|
|
189
205
|
if (!slug) {
|
|
190
|
-
|
|
191
|
-
log('slug from agentStore: %s (agentId: %s)',
|
|
206
|
+
const storeSlug = agentSelectors.getAgentSlugById(agentId)(agentStoreState) ?? undefined;
|
|
207
|
+
log('slug from agentStore: %s (agentId: %s)', storeSlug, agentId);
|
|
208
|
+
|
|
209
|
+
// Only use the slug if it's a valid builtin agent slug
|
|
210
|
+
// Regular agents may have random slugs that should be ignored
|
|
211
|
+
if (storeSlug && isBuiltinAgentSlug(storeSlug)) {
|
|
212
|
+
slug = storeSlug;
|
|
213
|
+
} else if (storeSlug) {
|
|
214
|
+
log('slug %s is not a valid builtin agent slug, treating as regular agent', storeSlug);
|
|
215
|
+
}
|
|
192
216
|
}
|
|
193
217
|
|
|
194
218
|
if (!slug) {
|
|
195
|
-
log('agentId %s is not a builtin agent (no slug found)', agentId);
|
|
219
|
+
log('agentId %s is not a builtin agent (no valid builtin slug found)', agentId);
|
|
196
220
|
// Regular agent - use provided plugins if available, fallback to agent's plugins
|
|
197
221
|
const finalPlugins = plugins && plugins.length > 0 ? plugins : basePlugins;
|
|
198
222
|
|
|
@@ -583,7 +583,7 @@ describe('contextEngineering', () => {
|
|
|
583
583
|
});
|
|
584
584
|
|
|
585
585
|
describe('Message preprocessing processors', () => {
|
|
586
|
-
it('should
|
|
586
|
+
it('should keep all messages (no history truncation)', async () => {
|
|
587
587
|
const messages: UIChatMessage[] = [
|
|
588
588
|
{
|
|
589
589
|
role: 'user',
|
|
@@ -626,13 +626,12 @@ describe('contextEngineering', () => {
|
|
|
626
626
|
messages,
|
|
627
627
|
model: 'gpt-4',
|
|
628
628
|
provider: 'openai',
|
|
629
|
-
enableHistoryCount: true,
|
|
630
|
-
historyCount: 4, // Should keep last 2 messages
|
|
631
629
|
});
|
|
632
630
|
|
|
633
|
-
// Should
|
|
634
|
-
expect(result).toHaveLength(
|
|
631
|
+
// Should keep all messages
|
|
632
|
+
expect(result).toHaveLength(5);
|
|
635
633
|
expect(result).toEqual([
|
|
634
|
+
{ content: 'Message 1', role: 'user' },
|
|
636
635
|
{ content: 'Response 1', role: 'assistant' },
|
|
637
636
|
{ content: 'Message 2', role: 'user' },
|
|
638
637
|
{ content: 'Response 2', role: 'assistant' },
|
|
@@ -704,7 +703,7 @@ describe('contextEngineering', () => {
|
|
|
704
703
|
]);
|
|
705
704
|
});
|
|
706
705
|
|
|
707
|
-
it('should combine
|
|
706
|
+
it('should combine system role and input template correctly', async () => {
|
|
708
707
|
const messages: UIChatMessage[] = [
|
|
709
708
|
{
|
|
710
709
|
role: 'user',
|
|
@@ -735,16 +734,18 @@ describe('contextEngineering', () => {
|
|
|
735
734
|
provider: 'openai',
|
|
736
735
|
systemRole: 'System instructions.',
|
|
737
736
|
inputTemplate: 'Processed: {{text}}',
|
|
738
|
-
enableHistoryCount: true,
|
|
739
|
-
historyCount: 2, // Should keep last 1 message
|
|
740
737
|
});
|
|
741
738
|
|
|
742
|
-
// System role should be first
|
|
739
|
+
// System role should be first, followed by all messages with input template applied to user messages
|
|
743
740
|
expect(result).toEqual([
|
|
744
741
|
{
|
|
745
742
|
content: 'System instructions.',
|
|
746
743
|
role: 'system',
|
|
747
744
|
},
|
|
745
|
+
{
|
|
746
|
+
content: 'Processed: Old message 1',
|
|
747
|
+
role: 'user',
|
|
748
|
+
},
|
|
748
749
|
{
|
|
749
750
|
role: 'assistant',
|
|
750
751
|
content: 'Old response',
|
|
@@ -782,7 +783,7 @@ describe('contextEngineering', () => {
|
|
|
782
783
|
]);
|
|
783
784
|
});
|
|
784
785
|
|
|
785
|
-
it('should handle
|
|
786
|
+
it('should handle system role injection with all messages (no history truncation)', async () => {
|
|
786
787
|
const messages: UIChatMessage[] = [
|
|
787
788
|
{
|
|
788
789
|
role: 'user',
|
|
@@ -812,18 +813,24 @@ describe('contextEngineering', () => {
|
|
|
812
813
|
model: 'gpt-4',
|
|
813
814
|
provider: 'openai',
|
|
814
815
|
systemRole: 'System role here.',
|
|
815
|
-
enableHistoryCount: true,
|
|
816
|
-
historyCount: 1, // Should keep only 1 message
|
|
817
816
|
});
|
|
818
817
|
|
|
819
|
-
// Should have system role +
|
|
818
|
+
// Should have system role + all messages
|
|
820
819
|
expect(result).toEqual([
|
|
821
820
|
{
|
|
822
821
|
content: 'System role here.',
|
|
823
822
|
role: 'system',
|
|
824
823
|
},
|
|
825
824
|
{
|
|
826
|
-
content: 'Message
|
|
825
|
+
content: 'Message 1',
|
|
826
|
+
role: 'user',
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
content: 'Message 2',
|
|
830
|
+
role: 'user',
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
content: 'Message 3',
|
|
827
834
|
role: 'user',
|
|
828
835
|
},
|
|
829
836
|
]);
|
|
@@ -394,6 +394,18 @@ export const contextEngineering = async ({
|
|
|
394
394
|
...(gtdConfig && { gtd: gtdConfig }),
|
|
395
395
|
});
|
|
396
396
|
|
|
397
|
+
log('Input messages count: %d', messages.length);
|
|
398
|
+
|
|
397
399
|
const result = await engine.process();
|
|
400
|
+
|
|
401
|
+
log('Output messages count: %d', result.messages.length);
|
|
402
|
+
|
|
403
|
+
if (messages.length > 0 && result.messages.length === 0) {
|
|
404
|
+
log(
|
|
405
|
+
'WARNING: Messages were reduced to 0! Input messages: %o',
|
|
406
|
+
messages.map((m) => ({ id: m.id, role: m.role })),
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
398
410
|
return result.messages;
|
|
399
411
|
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { type FetchSSEOptions } from '@lobechat/fetch-sse';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type RuntimeInitialContext,
|
|
4
|
+
type RuntimeStepContext,
|
|
5
|
+
type TracePayload,
|
|
6
|
+
} from '@lobechat/types';
|
|
3
7
|
|
|
4
8
|
export interface FetchOptions extends FetchSSEOptions {
|
|
9
|
+
agentId?: string;
|
|
5
10
|
historySummary?: string;
|
|
6
11
|
/** Initial context for page editor (captured at operation start) */
|
|
7
12
|
initialContext?: RuntimeInitialContext;
|
|
8
13
|
signal?: AbortSignal | undefined;
|
|
9
14
|
/** Step context for page editor (updated each step) */
|
|
10
15
|
stepContext?: RuntimeStepContext;
|
|
16
|
+
topicId?: string;
|
|
11
17
|
trace?: TracePayload;
|
|
12
18
|
}
|
|
@@ -22,10 +22,9 @@ import type { ChatToolPayload, ConversationContext, CreateMessageParams } from '
|
|
|
22
22
|
import debug from 'debug';
|
|
23
23
|
import pMap from 'p-map';
|
|
24
24
|
|
|
25
|
-
import type { ResolvedAgentConfig } from '@/services/chat/mecha';
|
|
26
|
-
|
|
27
25
|
import { LOADING_FLAT } from '@/const/message';
|
|
28
26
|
import { aiAgentService } from '@/services/aiAgent';
|
|
27
|
+
import type { ResolvedAgentConfig } from '@/services/chat/mecha';
|
|
29
28
|
import { agentByIdSelectors } from '@/store/agent/selectors';
|
|
30
29
|
import { getAgentStoreState } from '@/store/agent/store';
|
|
31
30
|
import type { ChatStore } from '@/store/chat/store';
|
|
@@ -96,7 +95,12 @@ export const createAgentExecutors = (context: {
|
|
|
96
95
|
const llmPayload = (instruction as AgentInstructionCallLlm)
|
|
97
96
|
.payload as GeneralAgentCallLLMInstructionPayload;
|
|
98
97
|
|
|
99
|
-
log(
|
|
98
|
+
log(
|
|
99
|
+
`${stagePrefix} Starting session. Input: state.messages=%d, llmPayload.messages=%d, messageKey=%s`,
|
|
100
|
+
state.messages.length,
|
|
101
|
+
llmPayload.messages.length,
|
|
102
|
+
context.messageKey,
|
|
103
|
+
);
|
|
100
104
|
|
|
101
105
|
let assistantMessageId: string;
|
|
102
106
|
|
|
@@ -184,6 +188,12 @@ export const createAgentExecutors = (context: {
|
|
|
184
188
|
// Get latest messages from store (already updated by internal_fetchAIChatMessage)
|
|
185
189
|
const latestMessages = context.get().dbMessagesMap[context.messageKey] || [];
|
|
186
190
|
|
|
191
|
+
log(
|
|
192
|
+
`${stagePrefix} After fetch: dbMessagesMap[${context.messageKey}]=%d messages, available keys=%o`,
|
|
193
|
+
latestMessages.length,
|
|
194
|
+
Object.keys(context.get().dbMessagesMap),
|
|
195
|
+
);
|
|
196
|
+
|
|
187
197
|
// Get updated assistant message to extract usage/cost information
|
|
188
198
|
const assistantMessage = latestMessages.find((m) => m.id === assistantMessageId);
|
|
189
199
|
|
|
@@ -206,10 +216,11 @@ export const createAgentExecutors = (context: {
|
|
|
206
216
|
}
|
|
207
217
|
|
|
208
218
|
log(
|
|
209
|
-
'[%s:%d] call_llm completed, finishType: %s',
|
|
219
|
+
'[%s:%d] call_llm completed, finishType: %s, outputMessages: %d',
|
|
210
220
|
state.operationId,
|
|
211
221
|
state.stepCount,
|
|
212
222
|
finishType,
|
|
223
|
+
latestMessages.length,
|
|
213
224
|
);
|
|
214
225
|
|
|
215
226
|
// Accumulate usage and cost to state
|
|
@@ -259,6 +259,7 @@ export const conversationLifecycle: StateCreator<
|
|
|
259
259
|
if (data?.topics) {
|
|
260
260
|
const pageSize = systemStatusSelectors.topicPageSize(useGlobalStore.getState());
|
|
261
261
|
get().internal_updateTopics(operationContext.agentId, {
|
|
262
|
+
groupId: operationContext.groupId,
|
|
262
263
|
items: data.topics.items,
|
|
263
264
|
pageSize,
|
|
264
265
|
total: data.topics.total,
|
|
@@ -757,20 +757,24 @@ export const streamingExecutor: StateCreator<
|
|
|
757
757
|
nextContext = { ...nextContext, stepContext };
|
|
758
758
|
|
|
759
759
|
log(
|
|
760
|
-
'[internal_execAgentRuntime][step-%d]: phase=%s, status=%s, stepContext=%O',
|
|
760
|
+
'[internal_execAgentRuntime][step-%d]: phase=%s, status=%s, state.messages=%d, dbMessagesMap[%s]=%d, stepContext=%O',
|
|
761
761
|
stepCount,
|
|
762
762
|
nextContext.phase,
|
|
763
763
|
state.status,
|
|
764
|
+
state.messages.length,
|
|
765
|
+
messageKey,
|
|
766
|
+
currentDBMessages.length,
|
|
764
767
|
stepContext,
|
|
765
768
|
);
|
|
766
769
|
|
|
767
770
|
const result = await runtime.step(state, nextContext);
|
|
768
771
|
|
|
769
772
|
log(
|
|
770
|
-
'[internal_execAgentRuntime] Step %d completed, events: %d, newStatus=%s',
|
|
773
|
+
'[internal_execAgentRuntime] Step %d completed, events: %d, newStatus=%s, newState.messages=%d',
|
|
771
774
|
stepCount,
|
|
772
775
|
result.events.length,
|
|
773
776
|
result.newState.status,
|
|
777
|
+
result.newState.messages.length,
|
|
774
778
|
);
|
|
775
779
|
|
|
776
780
|
// After parallel tool batch completes, refresh messages to ensure all tool results are synced
|