@lobehub/lobehub 2.0.0-next.48 → 2.0.0-next.49

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 (90) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/changelog/v1.json +12 -0
  5. package/locales/ar/chat.json +1 -0
  6. package/locales/ar/topic.json +1 -0
  7. package/locales/bg-BG/chat.json +1 -0
  8. package/locales/bg-BG/topic.json +1 -0
  9. package/locales/de-DE/chat.json +1 -0
  10. package/locales/de-DE/topic.json +1 -0
  11. package/locales/en-US/chat.json +1 -0
  12. package/locales/en-US/topic.json +1 -0
  13. package/locales/es-ES/chat.json +1 -0
  14. package/locales/es-ES/topic.json +1 -0
  15. package/locales/fa-IR/chat.json +1 -0
  16. package/locales/fa-IR/topic.json +1 -0
  17. package/locales/fr-FR/chat.json +1 -0
  18. package/locales/fr-FR/topic.json +1 -0
  19. package/locales/it-IT/chat.json +1 -0
  20. package/locales/it-IT/topic.json +1 -0
  21. package/locales/ja-JP/chat.json +1 -0
  22. package/locales/ja-JP/topic.json +1 -0
  23. package/locales/ko-KR/chat.json +1 -0
  24. package/locales/ko-KR/topic.json +1 -0
  25. package/locales/nl-NL/chat.json +1 -0
  26. package/locales/nl-NL/topic.json +1 -0
  27. package/locales/pl-PL/chat.json +1 -0
  28. package/locales/pl-PL/topic.json +1 -0
  29. package/locales/pt-BR/chat.json +1 -0
  30. package/locales/pt-BR/topic.json +1 -0
  31. package/locales/ru-RU/chat.json +1 -0
  32. package/locales/ru-RU/topic.json +1 -0
  33. package/locales/tr-TR/chat.json +1 -0
  34. package/locales/tr-TR/topic.json +1 -0
  35. package/locales/vi-VN/chat.json +1 -0
  36. package/locales/vi-VN/topic.json +1 -0
  37. package/locales/zh-CN/chat.json +1 -0
  38. package/locales/zh-CN/discover.json +1 -1
  39. package/locales/zh-CN/topic.json +1 -0
  40. package/locales/zh-TW/chat.json +1 -0
  41. package/locales/zh-TW/topic.json +1 -0
  42. package/package.json +9 -3
  43. package/packages/agent-runtime/src/core/InterventionChecker.ts +5 -16
  44. package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +27 -80
  45. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +32 -13
  46. package/packages/agent-runtime/src/core/runtime.ts +7 -3
  47. package/packages/agent-runtime/src/types/event.ts +2 -1
  48. package/packages/agent-runtime/src/types/generalAgent.ts +1 -0
  49. package/packages/agent-runtime/src/types/instruction.ts +3 -2
  50. package/packages/agent-runtime/src/types/state.ts +3 -1
  51. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +4 -1
  52. package/packages/database/src/models/message.ts +3 -0
  53. package/packages/obervability-otel/src/node.ts +15 -1
  54. package/packages/types/src/message/common/base.ts +2 -2
  55. package/packages/types/src/message/common/tools.ts +16 -10
  56. package/packages/types/src/message/ui/chat.ts +7 -1
  57. package/packages/types/src/tool/intervention.ts +2 -3
  58. package/packages/types/src/user/settings/tool.ts +15 -28
  59. package/renovate.json +28 -11
  60. package/src/app/[variants]/(main)/chat/components/topic/features/Topic/TopicListContent/TopicItem/TopicContent.tsx +1 -1
  61. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/Actions.tsx +1 -1
  62. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +20 -15
  63. package/src/features/Conversation/Messages/Group/GroupContext.tsx +15 -0
  64. package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +2 -4
  65. package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +3 -5
  66. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +19 -7
  67. package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/index.tsx +14 -12
  68. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ApprovalActions.tsx +143 -0
  69. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/KeyValueEditor.tsx +213 -0
  70. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ModeSelector.tsx +134 -0
  71. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/index.tsx +99 -0
  72. package/src/features/Conversation/Messages/Group/Tool/Render/RejectedResponse.tsx +45 -0
  73. package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +23 -1
  74. package/src/features/Conversation/Messages/Group/Tool/index.tsx +42 -18
  75. package/src/features/Conversation/Messages/Group/Tools.tsx +3 -1
  76. package/src/locales/default/chat.ts +22 -0
  77. package/src/locales/default/common.ts +1 -0
  78. package/src/locales/default/topic.ts +1 -0
  79. package/src/server/routers/lambda/message.ts +4 -1
  80. package/src/server/services/message/index.ts +13 -0
  81. package/src/services/message/index.ts +17 -2
  82. package/src/store/chat/agents/GeneralChatAgent.ts +141 -24
  83. package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +605 -0
  84. package/src/store/chat/agents/createAgentExecutors.ts +144 -26
  85. package/src/store/chat/agents/createToolEngine.ts +22 -0
  86. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +106 -0
  87. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +54 -26
  88. package/src/store/chat/slices/message/reducer.ts +2 -1
  89. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +26 -1
  90. package/src/store/user/slices/settings/action.ts +15 -0
@@ -9,20 +9,22 @@ import {
9
9
  GeneralAgentCallToolsBatchInstructionPayload,
10
10
  GeneralAgentCallingToolInstructionPayload,
11
11
  GeneralAgentConfig,
12
+ InterventionChecker,
12
13
  } from '@lobechat/agent-runtime';
14
+ import type { ChatToolPayload, HumanInterventionConfig } from '@lobechat/types';
13
15
 
14
16
  /**
15
17
  * ChatAgent - The "Brain" of the chat agent
16
18
  *
17
19
  * This agent implements a simple but powerful decision loop:
18
20
  * 1. user_input → call_llm (with optional RAG/Search preprocessing)
19
- * 2. llm_result → check for tool_calls
20
- * - If has tool_calls → call_tools_batch (parallel execution)
21
- * - If no tool_callsfinish
21
+ * 2. llm_result → check for tool_calls and intervention requirements
22
+ * - Tools not requiring intervention → call_tools_batch (execute immediately)
23
+ * - Tools requiring interventionrequest_human_approve (wait for approval)
24
+ * - Mixed (both types) → [call_tools_batch, request_human_approve] (execute safe ones first, then request approval)
25
+ * - No tool_calls → finish
22
26
  * 3. tools_batch_result → call_llm (process tool results)
23
27
  *
24
- * Note: RAG and Search workflow preprocessing are handled externally
25
- * before creating the agent runtime, keeping the agent logic simple.
26
28
  */
27
29
  export class GeneralChatAgent implements Agent {
28
30
  private config: GeneralAgentConfig;
@@ -31,6 +33,63 @@ export class GeneralChatAgent implements Agent {
31
33
  this.config = config;
32
34
  }
33
35
 
36
+ /**
37
+ * Get intervention configuration for a specific tool call
38
+ */
39
+ private getToolInterventionConfig(
40
+ toolCalling: ChatToolPayload,
41
+ state: AgentState,
42
+ ): HumanInterventionConfig | undefined {
43
+ const { identifier, apiName } = toolCalling;
44
+ const manifest = state.toolManifestMap[identifier];
45
+
46
+ if (!manifest) return undefined;
47
+
48
+ // Find the specific API in the manifest
49
+ const api = manifest.api?.find((a: any) => a.name === apiName);
50
+
51
+ // API-level config takes precedence over tool-level config
52
+ return api?.humanIntervention ?? manifest.humanIntervention;
53
+ }
54
+
55
+ /**
56
+ * Check if tool calls need human intervention
57
+ * Returns [toolsNeedingIntervention, toolsToExecute]
58
+ */
59
+ private checkInterventionNeeded(
60
+ toolsCalling: ChatToolPayload[],
61
+ state: AgentState,
62
+ ): [ChatToolPayload[], ChatToolPayload[]] {
63
+ const toolsNeedingIntervention: ChatToolPayload[] = [];
64
+ const toolsToExecute: ChatToolPayload[] = [];
65
+
66
+ for (const toolCalling of toolsCalling) {
67
+ const config = this.getToolInterventionConfig(toolCalling, state);
68
+
69
+ // Parse arguments for intervention checking
70
+ let toolArgs: Record<string, any> = {};
71
+ try {
72
+ toolArgs = JSON.parse(toolCalling.arguments || '{}');
73
+ } catch {
74
+ // Invalid JSON, treat as empty args
75
+ }
76
+
77
+ const policy = InterventionChecker.shouldIntervene({
78
+ config,
79
+ toolArgs,
80
+ });
81
+
82
+ if (policy === 'never') {
83
+ toolsToExecute.push(toolCalling);
84
+ } else {
85
+ // 'require' or 'first' (when not confirmed) requires intervention
86
+ toolsNeedingIntervention.push(toolCalling);
87
+ }
88
+ }
89
+
90
+ return [toolsNeedingIntervention, toolsToExecute];
91
+ }
92
+
34
93
  async runner(
35
94
  context: AgentRuntimeContext,
36
95
  state: AgentState,
@@ -55,26 +114,47 @@ export class GeneralChatAgent implements Agent {
55
114
  context.payload as GeneralAgentCallLLMResultPayload;
56
115
 
57
116
  if (hasToolsCalling && toolsCalling && toolsCalling.length > 0) {
58
- // No intervention needed, proceed with tool execution
59
- // Use batch execution for multiple tool calls to improve performance
60
- if (toolsCalling.length > 1) {
61
- return {
62
- payload: {
63
- parentMessageId,
64
- toolsCalling,
65
- } as GeneralAgentCallToolsBatchInstructionPayload,
66
- type: 'call_tools_batch',
67
- };
68
- } else if (toolsCalling.length === 1) {
69
- // Single tool executes directly
70
- return {
71
- payload: {
72
- parentMessageId,
73
- toolCalling: toolsCalling[0],
74
- } as GeneralAgentCallingToolInstructionPayload,
75
- type: 'call_tool',
76
- };
117
+ // Check which tools need human intervention
118
+ const [toolsNeedingIntervention, toolsToExecute] = this.checkInterventionNeeded(
119
+ toolsCalling,
120
+ state,
121
+ );
122
+
123
+ const instructions: AgentInstruction[] = [];
124
+
125
+ // Execute tools that don't need intervention first
126
+ // These will run immediately before any approval requests
127
+ if (toolsToExecute.length > 0) {
128
+ if (toolsToExecute.length > 1) {
129
+ instructions.push({
130
+ payload: {
131
+ parentMessageId,
132
+ toolsCalling: toolsToExecute,
133
+ } as GeneralAgentCallToolsBatchInstructionPayload,
134
+ type: 'call_tools_batch',
135
+ });
136
+ } else {
137
+ instructions.push({
138
+ payload: {
139
+ parentMessageId,
140
+ toolCalling: toolsToExecute[0],
141
+ } as GeneralAgentCallingToolInstructionPayload,
142
+ type: 'call_tool',
143
+ });
144
+ }
145
+ }
146
+
147
+ // Request approval for tools that need intervention
148
+ // Runtime will execute this after safe tools and pause with status='waiting_for_human'
149
+ if (toolsNeedingIntervention.length > 0) {
150
+ instructions.push({
151
+ pendingToolsCalling: toolsNeedingIntervention,
152
+ reason: 'human_intervention_required',
153
+ type: 'request_human_approve',
154
+ });
77
155
  }
156
+
157
+ return instructions;
78
158
  }
79
159
 
80
160
  // No tool calls, conversation is complete
@@ -88,6 +168,24 @@ export class GeneralChatAgent implements Agent {
88
168
  case 'tool_result': {
89
169
  const { parentMessageId } = context.payload as GeneralAgentCallToolResultPayload;
90
170
 
171
+ // Check if there are still pending tool messages waiting for approval
172
+ const pendingToolMessages = state.messages.filter(
173
+ (m: any) => m.role === 'tool' && m.pluginIntervention?.status === 'pending',
174
+ );
175
+
176
+ // If there are pending tools, wait for human approval
177
+ if (pendingToolMessages.length > 0) {
178
+ const pendingTools = pendingToolMessages.map((m: any) => m.plugin).filter(Boolean);
179
+
180
+ return {
181
+ pendingToolsCalling: pendingTools,
182
+ reason: 'Some tools still pending approval',
183
+ skipCreateToolMessage: true,
184
+ type: 'request_human_approve',
185
+ };
186
+ }
187
+
188
+ // No pending tools, continue to call LLM with tool results
91
189
  return {
92
190
  payload: {
93
191
  messages: state.messages,
@@ -102,6 +200,25 @@ export class GeneralChatAgent implements Agent {
102
200
 
103
201
  case 'tools_batch_result': {
104
202
  const { parentMessageId } = context.payload as GeneralAgentCallToolResultPayload;
203
+
204
+ // Check if there are still pending tool messages waiting for approval
205
+ const pendingToolMessages = state.messages.filter(
206
+ (m: any) => m.role === 'tool' && m.pluginIntervention?.status === 'pending',
207
+ );
208
+
209
+ // If there are pending tools, wait for human approval
210
+ if (pendingToolMessages.length > 0) {
211
+ const pendingTools = pendingToolMessages.map((m: any) => m.plugin).filter(Boolean);
212
+
213
+ return {
214
+ pendingToolsCalling: pendingTools,
215
+ reason: 'Some tools still pending approval',
216
+ skipCreateToolMessage: true,
217
+ type: 'request_human_approve',
218
+ };
219
+ }
220
+
221
+ // No pending tools, continue to call LLM with tool results
105
222
  return {
106
223
  payload: {
107
224
  messages: state.messages,