@lobehub/chat 1.141.8 → 1.141.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/apps/desktop/package.json +1 -0
  3. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +279 -52
  4. package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +392 -0
  5. package/changelog/v1.json +18 -0
  6. package/package.json +1 -1
  7. package/packages/agent-runtime/src/core/InterventionChecker.ts +173 -0
  8. package/packages/agent-runtime/src/core/UsageCounter.ts +248 -0
  9. package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +334 -0
  10. package/packages/agent-runtime/src/core/__tests__/UsageCounter.test.ts +873 -0
  11. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +32 -26
  12. package/packages/agent-runtime/src/core/index.ts +2 -0
  13. package/packages/agent-runtime/src/core/runtime.ts +31 -18
  14. package/packages/agent-runtime/src/types/instruction.ts +1 -1
  15. package/packages/agent-runtime/src/types/state.ts +3 -3
  16. package/packages/agent-runtime/src/types/usage.ts +34 -25
  17. package/packages/context-engine/src/index.ts +1 -0
  18. package/packages/context-engine/src/tools/ToolNameResolver.ts +2 -2
  19. package/packages/context-engine/src/tools/ToolsEngine.ts +37 -8
  20. package/packages/context-engine/src/tools/__tests__/ToolsEngine.test.ts +149 -5
  21. package/packages/context-engine/src/tools/__tests__/utils.test.ts +2 -2
  22. package/packages/context-engine/src/tools/index.ts +1 -0
  23. package/packages/context-engine/src/tools/types.ts +18 -3
  24. package/packages/context-engine/src/tools/utils.ts +4 -4
  25. package/packages/types/src/tool/builtin.ts +54 -1
  26. package/packages/types/src/tool/index.ts +1 -0
  27. package/packages/types/src/tool/intervention.ts +114 -0
  28. package/packages/types/src/user/settings/tool.ts +37 -0
  29. package/src/app/[variants]/(main)/discover/(list)/(home)/Client.tsx +2 -2
  30. package/src/app/[variants]/(main)/discover/(list)/(home)/HomePage.tsx +2 -2
  31. package/src/app/[variants]/(main)/discover/DiscoverRouter.tsx +2 -1
  32. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +4 -2
  33. package/src/store/chat/slices/builtinTool/actions/{dalle.test.ts → __tests__/dalle.test.ts} +2 -5
  34. package/src/store/chat/slices/builtinTool/actions/__tests__/{localFile.test.ts → localSystem.test.ts} +4 -4
  35. package/src/store/chat/slices/builtinTool/actions/index.ts +2 -2
  36. package/src/store/chat/slices/builtinTool/actions/{localFile.ts → localSystem.ts} +183 -69
  37. package/src/store/electron/selectors/__tests__/desktopState.test.ts +3 -3
  38. package/src/store/electron/selectors/desktopState.ts +11 -2
  39. package/src/tools/local-system/Placeholder/ListFiles.tsx +10 -8
  40. package/src/tools/local-system/Placeholder/SearchFiles.tsx +12 -10
  41. package/src/tools/local-system/Placeholder/index.tsx +1 -1
  42. package/src/tools/local-system/Render/ReadLocalFile/ReadFileSkeleton.tsx +8 -18
  43. package/src/tools/local-system/Render/ReadLocalFile/ReadFileView.tsx +21 -6
  44. package/src/tools/local-system/Render/SearchFiles/Result.tsx +5 -4
  45. package/src/tools/local-system/Render/SearchFiles/SearchQuery/SearchView.tsx +4 -15
  46. package/src/tools/local-system/Render/SearchFiles/index.tsx +3 -2
  47. package/src/tools/local-system/type.ts +39 -0
  48. package/src/tools/local-system/Placeholder/ReadLocalFile.tsx +0 -9
  49. package/src/tools/local-system/Render/ReadLocalFile/style.ts +0 -37
  50. /package/src/store/chat/slices/builtinTool/actions/{search.test.ts → __tests__/search.test.ts} +0 -0
@@ -369,7 +369,7 @@ describe('AgentRuntime', () => {
369
369
  type: 'tool_pending',
370
370
  });
371
371
 
372
- expect(result.newState.status).toBe('waiting_for_human_input');
372
+ expect(result.newState.status).toBe('waiting_for_human');
373
373
  expect(result.newState.pendingToolsCalling).toBeDefined();
374
374
  });
375
375
 
@@ -396,7 +396,7 @@ describe('AgentRuntime', () => {
396
396
  sessionId: 'test-session',
397
397
  });
398
398
 
399
- expect(result.newState.status).toBe('waiting_for_human_input');
399
+ expect(result.newState.status).toBe('waiting_for_human');
400
400
  expect(result.newState.pendingHumanPrompt).toEqual({
401
401
  prompt: 'Please provide input',
402
402
  metadata: { key: 'value' },
@@ -434,7 +434,7 @@ describe('AgentRuntime', () => {
434
434
  sessionId: 'test-session',
435
435
  });
436
436
 
437
- expect(result.newState.status).toBe('waiting_for_human_input');
437
+ expect(result.newState.status).toBe('waiting_for_human');
438
438
  });
439
439
  });
440
440
 
@@ -733,7 +733,7 @@ describe('AgentRuntime', () => {
733
733
  },
734
734
  tools: {
735
735
  totalCalls: 0,
736
- byTool: {},
736
+ byTool: [],
737
737
  totalTimeMs: 0,
738
738
  },
739
739
  humanInteraction: {
@@ -746,12 +746,12 @@ describe('AgentRuntime', () => {
746
746
 
747
747
  expect(state.cost).toMatchObject({
748
748
  llm: {
749
- byModel: {},
749
+ byModel: [],
750
750
  total: 0,
751
751
  currency: 'USD',
752
752
  },
753
753
  tools: {
754
- byTool: {},
754
+ byTool: [],
755
755
  total: 0,
756
756
  currency: 'USD',
757
757
  },
@@ -890,8 +890,8 @@ describe('AgentRuntime', () => {
890
890
 
891
891
  calculateCost(context: CostCalculationContext): Cost {
892
892
  return {
893
- llm: { byModel: {}, total: 15.0, currency: 'USD' },
894
- tools: { byTool: {}, total: 0, currency: 'USD' },
893
+ llm: { byModel: [], total: 15.0, currency: 'USD' },
894
+ tools: { byTool: [], total: 0, currency: 'USD' },
895
895
  total: 15.0,
896
896
  currency: 'USD',
897
897
  calculatedAt: new Date().toISOString(),
@@ -1018,7 +1018,7 @@ describe('AgentRuntime', () => {
1018
1018
  result = await runtime.step(result.newState, result.nextContext);
1019
1019
 
1020
1020
  // Now should request human approval
1021
- expect(result.newState.status).toBe('waiting_for_human_input');
1021
+ expect(result.newState.status).toBe('waiting_for_human');
1022
1022
  expect(result.newState.pendingToolsCalling).toHaveLength(1);
1023
1023
 
1024
1024
  // Step 2: Approve and execute tool call
@@ -1210,7 +1210,7 @@ describe('AgentRuntime', () => {
1210
1210
  expect(agent.tools.safe_tool).toHaveBeenCalled();
1211
1211
 
1212
1212
  // Should be in waiting state (blocked by approval request)
1213
- expect(result.newState.status).toBe('waiting_for_human_input');
1213
+ expect(result.newState.status).toBe('waiting_for_human');
1214
1214
 
1215
1215
  // Should have pending tool calls
1216
1216
  expect(result.newState.pendingToolsCalling).toHaveLength(1);
@@ -1333,8 +1333,8 @@ describe('AgentRuntime', () => {
1333
1333
  return {
1334
1334
  calculatedAt: new Date().toISOString(),
1335
1335
  currency: 'USD',
1336
- llm: { byModel: {}, currency: 'USD', total: 15.0 },
1337
- tools: { byTool: {}, currency: 'USD', total: 0 },
1336
+ llm: { byModel: [], currency: 'USD', total: 15.0 },
1337
+ tools: { byTool: [], currency: 'USD', total: 0 },
1338
1338
  total: 15.0,
1339
1339
  };
1340
1340
  }
@@ -1396,8 +1396,8 @@ describe('AgentRuntime', () => {
1396
1396
  return {
1397
1397
  calculatedAt: new Date().toISOString(),
1398
1398
  currency: 'USD',
1399
- llm: { byModel: {}, currency: 'USD', total: 0 },
1400
- tools: { byTool: {}, currency: 'USD', total: 20.0 },
1399
+ llm: { byModel: [], currency: 'USD', total: 0 },
1400
+ tools: { byTool: [], currency: 'USD', total: 20.0 },
1401
1401
  total: 20.0,
1402
1402
  };
1403
1403
  }
@@ -1438,8 +1438,8 @@ describe('AgentRuntime', () => {
1438
1438
  const baseCost = context.previousCost || {
1439
1439
  calculatedAt: new Date().toISOString(),
1440
1440
  currency: 'USD',
1441
- llm: { byModel: {}, currency: 'USD', total: 0 },
1442
- tools: { byTool: {}, currency: 'USD', total: 0 },
1441
+ llm: { byModel: [], currency: 'USD', total: 0 },
1442
+ tools: { byTool: [], currency: 'USD', total: 0 },
1443
1443
  total: 0,
1444
1444
  };
1445
1445
 
@@ -1447,7 +1447,7 @@ describe('AgentRuntime', () => {
1447
1447
  ...baseCost,
1448
1448
  calculatedAt: new Date().toISOString(),
1449
1449
  tools: {
1450
- byTool: {},
1450
+ byTool: [],
1451
1451
  currency: 'USD',
1452
1452
  total: baseCost.tools.total + 5.0,
1453
1453
  },
@@ -1514,15 +1514,17 @@ describe('AgentRuntime', () => {
1514
1514
  newUsage.tools.totalCalls += 1;
1515
1515
  newUsage.tools.totalTimeMs += 100;
1516
1516
 
1517
- if (newUsage.tools.byTool[toolName]) {
1518
- newUsage.tools.byTool[toolName].calls += 1;
1519
- newUsage.tools.byTool[toolName].totalTimeMs += 100;
1517
+ const existingTool = newUsage.tools.byTool.find((t) => t.name === toolName);
1518
+ if (existingTool) {
1519
+ existingTool.calls += 1;
1520
+ existingTool.totalTimeMs += 100;
1520
1521
  } else {
1521
- newUsage.tools.byTool[toolName] = {
1522
+ newUsage.tools.byTool.push({
1522
1523
  calls: 1,
1523
1524
  errors: 0,
1525
+ name: toolName,
1524
1526
  totalTimeMs: 100,
1525
- };
1527
+ });
1526
1528
  }
1527
1529
 
1528
1530
  return newUsage;
@@ -1567,10 +1569,14 @@ describe('AgentRuntime', () => {
1567
1569
 
1568
1570
  // Should have per-tool statistics
1569
1571
  expect(result.newState.usage.tools.totalCalls).toBe(2);
1570
- expect(result.newState.usage.tools.byTool.analytics_tool).toBeDefined();
1571
- expect(result.newState.usage.tools.byTool.analytics_tool.calls).toBe(1);
1572
- expect(result.newState.usage.tools.byTool.logging_tool).toBeDefined();
1573
- expect(result.newState.usage.tools.byTool.logging_tool.calls).toBe(1);
1572
+ const analyticsTool = result.newState.usage.tools.byTool.find(
1573
+ (t) => t.name === 'analytics_tool',
1574
+ );
1575
+ const loggingTool = result.newState.usage.tools.byTool.find((t) => t.name === 'logging_tool');
1576
+ expect(analyticsTool).toBeDefined();
1577
+ expect(analyticsTool!.calls).toBe(1);
1578
+ expect(loggingTool).toBeDefined();
1579
+ expect(loggingTool!.calls).toBe(1);
1574
1580
  });
1575
1581
  });
1576
1582
  });
@@ -1 +1,3 @@
1
+ export * from './InterventionChecker';
1
2
  export * from './runtime';
3
+ export * from './UsageCounter';
@@ -120,10 +120,7 @@ export class AgentRuntime {
120
120
  }
121
121
 
122
122
  // Stop execution if blocked
123
- if (
124
- currentState.status === 'waiting_for_human_input' ||
125
- currentState.status === 'interrupted'
126
- ) {
123
+ if (currentState.status === 'waiting_for_human' || currentState.status === 'interrupted') {
127
124
  break;
128
125
  }
129
126
  }
@@ -273,7 +270,7 @@ export class AgentRuntime {
273
270
  tokens: { input: 0, output: 0, total: 0 },
274
271
  },
275
272
  tools: {
276
- byTool: {},
273
+ byTool: [],
277
274
  totalCalls: 0,
278
275
  totalTimeMs: 0,
279
276
  },
@@ -290,12 +287,12 @@ export class AgentRuntime {
290
287
  calculatedAt: now,
291
288
  currency: 'USD',
292
289
  llm: {
293
- byModel: {},
290
+ byModel: [],
294
291
  currency: 'USD',
295
292
  total: 0,
296
293
  },
297
294
  tools: {
298
- byTool: {},
295
+ byTool: [],
299
296
  currency: 'USD',
300
297
  total: 0,
301
298
  },
@@ -308,7 +305,9 @@ export class AgentRuntime {
308
305
  * @param partialState - Partial state to override defaults
309
306
  * @returns Complete AgentState with defaults filled in
310
307
  */
311
- static createInitialState(partialState: Partial<AgentState> & { sessionId: string }): AgentState {
308
+ static createInitialState(
309
+ partialState?: Partial<AgentState> & { sessionId: string },
310
+ ): AgentState {
312
311
  const now = new Date().toISOString();
313
312
 
314
313
  return {
@@ -319,9 +318,10 @@ export class AgentRuntime {
319
318
  messages: [],
320
319
  status: 'idle',
321
320
  stepCount: 0,
321
+ toolManifestMap: {},
322
322
  usage: AgentRuntime.createDefaultUsage(),
323
323
  // User provided values override defaults
324
- ...partialState,
324
+ ...(partialState || { sessionId: '' }),
325
325
  };
326
326
  }
327
327
 
@@ -489,7 +489,7 @@ export class AgentRuntime {
489
489
  const newState = structuredClone(state);
490
490
 
491
491
  newState.lastModified = new Date().toISOString();
492
- newState.status = 'waiting_for_human_input';
492
+ newState.status = 'waiting_for_human';
493
493
  newState.pendingToolsCalling = pendingToolsCalling;
494
494
 
495
495
  const events: AgentEvent[] = [
@@ -515,7 +515,7 @@ export class AgentRuntime {
515
515
  const newState = structuredClone(state);
516
516
 
517
517
  newState.lastModified = new Date().toISOString();
518
- newState.status = 'waiting_for_human_input';
518
+ newState.status = 'waiting_for_human';
519
519
  newState.pendingHumanPrompt = { metadata, prompt };
520
520
 
521
521
  const events: AgentEvent[] = [
@@ -541,7 +541,7 @@ export class AgentRuntime {
541
541
  const newState = structuredClone(state);
542
542
 
543
543
  newState.lastModified = new Date().toISOString();
544
- newState.status = 'waiting_for_human_input';
544
+ newState.status = 'waiting_for_human';
545
545
  newState.pendingHumanSelect = { metadata, multi, options, prompt };
546
546
 
547
547
  const events: AgentEvent[] = [
@@ -641,13 +641,15 @@ export class AgentRuntime {
641
641
  newState.usage.tools.totalCalls += result.newState.usage.tools.totalCalls;
642
642
  newState.usage.tools.totalTimeMs += result.newState.usage.tools.totalTimeMs;
643
643
 
644
- // Merge per-tool statistics
645
- Object.entries(result.newState.usage.tools.byTool).forEach(([tool, stats]) => {
646
- if (newState.usage.tools.byTool[tool]) {
647
- newState.usage.tools.byTool[tool].calls += stats.calls;
648
- newState.usage.tools.byTool[tool].totalTimeMs += stats.totalTimeMs;
644
+ // Merge per-tool statistics (now using array)
645
+ result.newState.usage.tools.byTool.forEach((toolStats) => {
646
+ const existingTool = newState.usage.tools.byTool.find((t) => t.name === toolStats.name);
647
+ if (existingTool) {
648
+ existingTool.calls += toolStats.calls;
649
+ existingTool.totalTimeMs += toolStats.totalTimeMs;
650
+ existingTool.errors += toolStats.errors || 0;
649
651
  } else {
650
- newState.usage.tools.byTool[tool] = { ...stats };
652
+ newState.usage.tools.byTool.push({ ...toolStats });
651
653
  }
652
654
  });
653
655
  }
@@ -656,6 +658,17 @@ export class AgentRuntime {
656
658
  if (result.newState.cost && newState.cost) {
657
659
  newState.cost.tools.total += result.newState.cost.tools.total;
658
660
  newState.cost.total += result.newState.cost.tools.total;
661
+
662
+ // Merge per-tool cost statistics (now using array)
663
+ result.newState.cost.tools.byTool.forEach((toolCost) => {
664
+ const existingToolCost = newState.cost.tools.byTool.find((t) => t.name === toolCost.name);
665
+ if (existingToolCost) {
666
+ existingToolCost.calls += toolCost.calls;
667
+ existingToolCost.totalCost += toolCost.totalCost;
668
+ } else {
669
+ newState.cost.tools.byTool.push({ ...toolCost });
670
+ }
671
+ });
659
672
  }
660
673
  }
661
674
 
@@ -29,7 +29,7 @@ export interface AgentRuntimeContext {
29
29
  stepCount: number;
30
30
  };
31
31
  /** Usage statistics from the current step (if applicable) */
32
- stepUsage?: ModelUsage;
32
+ stepUsage?: ModelUsage | unknown;
33
33
  }
34
34
 
35
35
  /**
@@ -8,13 +8,13 @@ import type { Cost, CostLimit, Usage } from './usage';
8
8
  export interface AgentState {
9
9
  sessionId: string;
10
10
  // --- State Machine ---
11
- status: 'idle' | 'running' | 'waiting_for_human_input' | 'done' | 'error' | 'interrupted';
11
+ status: 'idle' | 'running' | 'waiting_for_human' | 'done' | 'error' | 'interrupted';
12
12
 
13
13
  // --- Core Context ---
14
14
  messages: any[];
15
15
  tools?: any[];
16
16
  systemRole?: string;
17
-
17
+ toolManifestMap: Record<string, any>;
18
18
  // --- Execution Tracking ---
19
19
  /**
20
20
  * Number of execution steps in this session.
@@ -46,7 +46,7 @@ export interface AgentState {
46
46
 
47
47
  // --- HIL ---
48
48
  /**
49
- * When status is 'waiting_for_human_input', this stores pending requests
49
+ * When status is 'waiting_for_human', this stores pending requests
50
50
  * for human-in-the-loop operations.
51
51
  */
52
52
  pendingToolsCalling?: ToolsCalling[];
@@ -1,3 +1,5 @@
1
+ import { ModelUsage } from '@lobechat/types';
2
+
1
3
  /**
2
4
  * Token usage tracking for different types of operations
3
5
  */
@@ -39,14 +41,16 @@ export interface Usage {
39
41
  /** Tool usage statistics */
40
42
  tools: {
41
43
  /** Usage breakdown by tool name */
42
- byTool: Record<
43
- string,
44
- {
45
- calls: number;
46
- errors: number;
47
- totalTimeMs: number;
48
- }
49
- >;
44
+ byTool: Array<{
45
+ /** Number of calls */
46
+ calls: number;
47
+ /** Number of errors */
48
+ errors: number;
49
+ /** Tool name/identifier */
50
+ name: string;
51
+ /** Total execution time in milliseconds */
52
+ totalTimeMs: number;
53
+ }>;
50
54
  /** Number of tool calls executed */
51
55
  totalCalls: number;
52
56
  /** Total tool execution time */
@@ -66,15 +70,18 @@ export interface Cost {
66
70
  /** LLM API costs */
67
71
  llm: {
68
72
  /** Cost per model used */
69
- byModel: Record<
70
- string,
71
- {
72
- currency: string;
73
- inputTokens: number;
74
- outputTokens: number;
75
- totalCost: number;
76
- }
77
- >;
73
+ byModel: Array<{
74
+ /** Model identifier in format "provider/model" */
75
+ id: string;
76
+ /** Model name */
77
+ model: string;
78
+ /** Provider name */
79
+ provider: string;
80
+ /** Total cost for this model */
81
+ totalCost: number;
82
+ /** Detailed usage breakdown */
83
+ usage: ModelUsage;
84
+ }>;
78
85
  currency: string;
79
86
  /** Total LLM cost */
80
87
  total: number;
@@ -82,14 +89,16 @@ export interface Cost {
82
89
  /** Tool execution costs */
83
90
  tools: {
84
91
  /** Cost per tool (if tool has associated costs) */
85
- byTool: Record<
86
- string,
87
- {
88
- calls: number;
89
- currency: string;
90
- totalCost: number;
91
- }
92
- >;
92
+ byTool: Array<{
93
+ /** Number of calls */
94
+ calls: number;
95
+ /** Currency */
96
+ currency: string;
97
+ /** Tool name/identifier */
98
+ name: string;
99
+ /** Total cost for this tool */
100
+ totalCost: number;
101
+ }>;
93
102
  currency: string;
94
103
  /** Total tool cost */
95
104
  total: number;
@@ -32,6 +32,7 @@ export {
32
32
  export type {
33
33
  FunctionCallChecker,
34
34
  GenerateToolsParams,
35
+ LobeToolManifest,
35
36
  PluginEnableChecker,
36
37
  ToolNameGenerator,
37
38
  ToolsEngineOptions,
@@ -1,7 +1,7 @@
1
1
  import { ChatToolPayload, MessageToolCall } from '@lobechat/types';
2
2
  import { Md5 } from 'ts-md5';
3
3
 
4
- import { LobeChatPluginApi, LobeChatPluginManifest } from './types';
4
+ import { LobeChatPluginApi, LobeToolManifest } from './types';
5
5
 
6
6
  // Tool naming constants
7
7
  const PLUGIN_SCHEMA_SEPARATOR = '____';
@@ -57,7 +57,7 @@ export class ToolNameResolver {
57
57
  */
58
58
  resolve(
59
59
  toolCalls: MessageToolCall[],
60
- manifests: Record<string, LobeChatPluginManifest>,
60
+ manifests: Record<string, LobeToolManifest>,
61
61
  ): ChatToolPayload[] {
62
62
  return toolCalls
63
63
  .map((toolCall): ChatToolPayload | null => {
@@ -3,7 +3,7 @@ import debug from 'debug';
3
3
  import {
4
4
  FunctionCallChecker,
5
5
  GenerateToolsParams,
6
- LobeChatPluginManifest,
6
+ LobeToolManifest,
7
7
  PluginEnableChecker,
8
8
  ToolsEngineOptions,
9
9
  ToolsGenerationContext,
@@ -18,7 +18,7 @@ const log = debug('context-engine:tools-engine');
18
18
  * Tools Engine - Unified processing of tools array construction and transformation
19
19
  */
20
20
  export class ToolsEngine {
21
- private manifestSchemas: Map<string, LobeChatPluginManifest>;
21
+ private manifestSchemas: Map<string, LobeToolManifest>;
22
22
  private enableChecker?: PluginEnableChecker;
23
23
  private functionCallChecker?: FunctionCallChecker;
24
24
  private defaultToolIds: string[];
@@ -162,13 +162,13 @@ export class ToolsEngine {
162
162
  context?: ToolsGenerationContext,
163
163
  supportsFunctionCall?: boolean,
164
164
  ): {
165
- enabledManifests: LobeChatPluginManifest[];
165
+ enabledManifests: LobeToolManifest[];
166
166
  filteredPlugins: Array<{
167
167
  id: string;
168
168
  reason: 'not_found' | 'disabled' | 'incompatible';
169
169
  }>;
170
170
  } {
171
- const enabledManifests: LobeChatPluginManifest[] = [];
171
+ const enabledManifests: LobeToolManifest[] = [];
172
172
  const filteredPlugins: Array<{
173
173
  id: string;
174
174
  reason: 'not_found' | 'disabled' | 'incompatible';
@@ -240,7 +240,7 @@ export class ToolsEngine {
240
240
  /**
241
241
  * Convert manifests to UniformTool array
242
242
  */
243
- private convertManifestsToTools(manifests: LobeChatPluginManifest[]): UniformTool[] {
243
+ private convertManifestsToTools(manifests: LobeToolManifest[]): UniformTool[] {
244
244
  log('Converting %d manifests to tools', manifests.length);
245
245
 
246
246
  // Use simplified conversion logic to avoid external package dependencies
@@ -290,14 +290,14 @@ export class ToolsEngine {
290
290
  /**
291
291
  * 获取插件的 manifest
292
292
  */
293
- getPluginManifest(pluginId: string): LobeChatPluginManifest | undefined {
293
+ getPluginManifest(pluginId: string): LobeToolManifest | undefined {
294
294
  return this.manifestSchemas.get(pluginId);
295
295
  }
296
296
 
297
297
  /**
298
298
  * 更新插件 manifest schemas(用于动态添加插件)
299
299
  */
300
- updateManifestSchemas(manifestSchemas: LobeChatPluginManifest[]): void {
300
+ updateManifestSchemas(manifestSchemas: LobeToolManifest[]): void {
301
301
  this.manifestSchemas.clear();
302
302
  for (const schema of manifestSchemas) {
303
303
  this.manifestSchemas.set(schema.identifier, schema);
@@ -307,7 +307,7 @@ export class ToolsEngine {
307
307
  /**
308
308
  * 添加单个插件 manifest
309
309
  */
310
- addPluginManifest(manifest: LobeChatPluginManifest): void {
310
+ addPluginManifest(manifest: LobeToolManifest): void {
311
311
  this.manifestSchemas.set(manifest.identifier, manifest);
312
312
  }
313
313
 
@@ -317,4 +317,33 @@ export class ToolsEngine {
317
317
  removePluginManifest(pluginId: string): boolean {
318
318
  return this.manifestSchemas.delete(pluginId);
319
319
  }
320
+
321
+ /**
322
+ * 获取所有 enabled plugin 的 Manifest Map
323
+ */
324
+ getEnabledPluginManifests(toolIds: string[] = []): Map<string, LobeToolManifest> {
325
+ // Merge user-provided tool IDs with default tool IDs
326
+ const allToolIds = [...toolIds, ...this.defaultToolIds];
327
+
328
+ log('Getting enabled plugin manifests for pluginIds=%o', allToolIds);
329
+
330
+ const manifestMap = new Map<string, LobeToolManifest>();
331
+
332
+ for (const pluginId of allToolIds) {
333
+ const manifest = this.manifestSchemas.get(pluginId);
334
+ if (manifest) {
335
+ manifestMap.set(pluginId, manifest);
336
+ }
337
+ }
338
+
339
+ log('Returning %d enabled plugin manifests', manifestMap.size);
340
+ return manifestMap;
341
+ }
342
+
343
+ /**
344
+ * 获取所有插件的 Manifest Map
345
+ */
346
+ getAllPluginManifests(): Map<string, LobeToolManifest> {
347
+ return new Map(this.manifestSchemas);
348
+ }
320
349
  }