@lobehub/lobehub 2.0.0-next.85 → 2.0.0-next.86

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 (88) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/src/main/modules/networkProxy/dispatcher.ts +16 -16
  3. package/apps/desktop/src/main/modules/networkProxy/tester.ts +11 -11
  4. package/apps/desktop/src/main/modules/networkProxy/urlBuilder.ts +3 -3
  5. package/apps/desktop/src/main/modules/networkProxy/validator.ts +10 -10
  6. package/changelog/v1.json +9 -0
  7. package/package.json +1 -1
  8. package/packages/agent-runtime/src/core/runtime.ts +36 -1
  9. package/packages/agent-runtime/src/types/event.ts +1 -0
  10. package/packages/agent-runtime/src/types/generalAgent.ts +16 -0
  11. package/packages/agent-runtime/src/types/instruction.ts +30 -0
  12. package/packages/agent-runtime/src/types/runtime.ts +7 -0
  13. package/packages/types/src/message/common/metadata.ts +3 -0
  14. package/packages/types/src/message/common/tools.ts +2 -2
  15. package/packages/types/src/tool/search/index.ts +8 -2
  16. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
  17. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +7 -2
  18. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +15 -14
  19. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/index.tsx +2 -2
  20. package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
  21. package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
  22. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -1
  23. package/src/features/Conversation/Messages/User/index.tsx +3 -3
  24. package/src/features/Conversation/Messages/index.tsx +3 -3
  25. package/src/features/Conversation/components/AutoScroll.tsx +2 -2
  26. package/src/services/search.ts +2 -2
  27. package/src/store/chat/agents/GeneralChatAgent.ts +98 -0
  28. package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +366 -0
  29. package/src/store/chat/agents/__tests__/createAgentExecutors/call-llm.test.ts +1217 -0
  30. package/src/store/chat/agents/__tests__/createAgentExecutors/call-tool.test.ts +1976 -0
  31. package/src/store/chat/agents/__tests__/createAgentExecutors/finish.test.ts +453 -0
  32. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/index.ts +4 -0
  33. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockInstructions.ts +126 -0
  34. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockMessages.ts +94 -0
  35. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockOperations.ts +96 -0
  36. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockStore.ts +138 -0
  37. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/assertions.ts +185 -0
  38. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/index.ts +3 -0
  39. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/operationTestUtils.ts +94 -0
  40. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +139 -0
  41. package/src/store/chat/agents/__tests__/createAgentExecutors/request-human-approve.test.ts +545 -0
  42. package/src/store/chat/agents/__tests__/createAgentExecutors/resolve-aborted-tools.test.ts +686 -0
  43. package/src/store/chat/agents/createAgentExecutors.ts +313 -80
  44. package/src/store/chat/selectors.ts +1 -0
  45. package/src/store/chat/slices/aiChat/__tests__/ai-chat.integration.test.ts +667 -0
  46. package/src/store/chat/slices/aiChat/actions/__tests__/cancel-functionality.test.ts +137 -27
  47. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +163 -125
  48. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +12 -2
  49. package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +0 -2
  50. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +0 -2
  51. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +286 -19
  52. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +0 -112
  53. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +42 -99
  54. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +90 -57
  55. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +5 -25
  56. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +220 -98
  57. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +0 -34
  58. package/src/store/chat/slices/aiChat/initialState.ts +0 -28
  59. package/src/store/chat/slices/aiChat/selectors.test.ts +280 -0
  60. package/src/store/chat/slices/aiChat/selectors.ts +31 -7
  61. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +21 -30
  62. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +29 -49
  63. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +83 -48
  64. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +78 -28
  65. package/src/store/chat/slices/builtinTool/actions/search.ts +146 -59
  66. package/src/store/chat/slices/builtinTool/selectors.test.ts +258 -0
  67. package/src/store/chat/slices/builtinTool/selectors.ts +25 -4
  68. package/src/store/chat/slices/message/action.test.ts +134 -16
  69. package/src/store/chat/slices/message/actions/internals.ts +33 -7
  70. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +85 -52
  71. package/src/store/chat/slices/message/initialState.ts +0 -10
  72. package/src/store/chat/slices/message/selectors/messageState.ts +34 -12
  73. package/src/store/chat/slices/operation/__tests__/actions.test.ts +712 -16
  74. package/src/store/chat/slices/operation/__tests__/integration.test.ts +342 -0
  75. package/src/store/chat/slices/operation/__tests__/selectors.test.ts +257 -17
  76. package/src/store/chat/slices/operation/actions.ts +218 -11
  77. package/src/store/chat/slices/operation/selectors.ts +135 -6
  78. package/src/store/chat/slices/operation/types.ts +29 -3
  79. package/src/store/chat/slices/plugin/action.test.ts +30 -322
  80. package/src/store/chat/slices/plugin/actions/internals.ts +0 -14
  81. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +21 -19
  82. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +45 -27
  83. package/src/store/chat/slices/plugin/actions/publicApi.ts +3 -4
  84. package/src/store/chat/slices/plugin/actions/workflow.ts +0 -55
  85. package/src/store/chat/slices/thread/selectors/index.ts +4 -2
  86. package/src/store/chat/slices/translate/action.ts +54 -41
  87. package/src/tools/web-browsing/ExecutionRuntime/index.ts +5 -2
  88. package/src/tools/web-browsing/Portal/Search/Footer.tsx +11 -9
@@ -7,8 +7,9 @@ import { agentSelectors } from '@/store/agent/selectors';
7
7
  import { getChatStoreState, useChatStore } from '@/store/chat';
8
8
  import {
9
9
  aiChatSelectors,
10
- chatSelectors,
10
+ displayMessageSelectors,
11
11
  messageStateSelectors,
12
+ operationSelectors,
12
13
  topicSelectors,
13
14
  } from '@/store/chat/selectors';
14
15
  import { fileChatSelectors, useFileStore } from '@/store/file';
@@ -34,7 +35,7 @@ export const useSend = () => {
34
35
  addAIMessage,
35
36
  stopGenerateMessage,
36
37
  cancelSendMessageInServer,
37
- generating,
38
+ isAgentRuntimeRunning,
38
39
  isSendButtonDisabledByMessage,
39
40
  isSendingMessage,
40
41
  ] = useChatStore((s) => [
@@ -43,7 +44,7 @@ export const useSend = () => {
43
44
  s.addAIMessage,
44
45
  s.stopGenerateMessage,
45
46
  s.cancelSendMessageInServer,
46
- messageStateSelectors.isAIGenerating(s),
47
+ operationSelectors.isMainWindowAgentRuntimeRunning(s),
47
48
  messageStateSelectors.isSendButtonDisabledByMessage(s),
48
49
  aiChatSelectors.isCurrentSendMessageLoading(s),
49
50
  ]);
@@ -73,7 +74,7 @@ export const useSend = () => {
73
74
  return;
74
75
  }
75
76
 
76
- if (messageStateSelectors.isAIGenerating(store)) return;
77
+ if (operationSelectors.isMainWindowAgentRuntimeRunning(store)) return;
77
78
 
78
79
  const inputMessage = store.inputMessage;
79
80
  // 发送时再取一次最新的文件列表,防止闭包拿到旧值
@@ -119,7 +120,7 @@ export const useSend = () => {
119
120
  chat_id: store.activeId || 'unknown',
120
121
  current_topic: topicSelectors.currentActiveTopic(store)?.title || null,
121
122
  has_attachments: fileList.length > 0,
122
- history_message_count: chatSelectors.activeBaseChats(store).length,
123
+ history_message_count: displayMessageSelectors.activeDisplayMessages(store).length,
123
124
  message: inputMessage,
124
125
  message_length: inputMessage.length,
125
126
  message_type: messageType,
@@ -132,9 +133,9 @@ export const useSend = () => {
132
133
 
133
134
  const stop = () => {
134
135
  const store = getChatStoreState();
135
- const generating = messageStateSelectors.isAIGenerating(store);
136
+ const isRunning = operationSelectors.isMainWindowAgentRuntimeRunning(store);
136
137
 
137
- if (generating) {
138
+ if (isRunning) {
138
139
  stopGenerateMessage();
139
140
  return;
140
141
  }
@@ -149,11 +150,11 @@ export const useSend = () => {
149
150
  return useMemo(
150
151
  () => ({
151
152
  disabled: canNotSend,
152
- generating: generating || isSendingMessage,
153
+ generating: isAgentRuntimeRunning || isSendingMessage,
153
154
  send: handleSend,
154
155
  stop,
155
156
  }),
156
- [canNotSend, generating, isSendingMessage, stop, handleSend],
157
+ [canNotSend, isAgentRuntimeRunning, isSendingMessage, stop, handleSend],
157
158
  );
158
159
  };
159
160
 
@@ -175,7 +176,7 @@ export const useSendGroupMessage = () => {
175
176
  ]);
176
177
 
177
178
  const isSupervisorThinking = useChatStore((s) =>
178
- chatSelectors.isSupervisorLoading(s.activeId)(s),
179
+ displayMessageSelectors.isSupervisorLoading(s.activeId)(s),
179
180
  );
180
181
  const { analytics } = useAnalytics();
181
182
  const checkGeminiChineseWarning = useGeminiChineseWarning();
@@ -209,7 +210,7 @@ export const useSendGroupMessage = () => {
209
210
  }
210
211
 
211
212
  if (
212
- chatSelectors.isSupervisorLoading(store.activeId)(store) ||
213
+ displayMessageSelectors.isSupervisorLoading(store.activeId)(store) ||
213
214
  messageStateSelectors.isCreatingMessage(store)
214
215
  )
215
216
  return;
@@ -270,7 +271,7 @@ export const useSendGroupMessage = () => {
270
271
  chat_id: store.activeId || 'unknown',
271
272
  current_topic: topicSelectors.currentActiveTopic(store)?.title || null,
272
273
  has_attachments: fileList.length > 0,
273
- history_message_count: chatSelectors.activeBaseChats(store).length,
274
+ history_message_count: displayMessageSelectors.activeDisplayMessages(store).length,
274
275
  message: inputMessage,
275
276
  message_length: inputMessage.length,
276
277
  message_type: messageType,
@@ -292,10 +293,10 @@ export const useSendGroupMessage = () => {
292
293
 
293
294
  const stop = useCallback(() => {
294
295
  const store = getChatStoreState();
295
- const isAgentGenerating = messageStateSelectors.isAIGenerating(store);
296
+ const isAgentRunning = operationSelectors.isMainWindowAgentRuntimeRunning(store);
296
297
  const isCreating = messageStateSelectors.isCreatingMessage(store);
297
298
 
298
- if (isAgentGenerating) {
299
+ if (isAgentRunning) {
299
300
  stopGenerateMessage();
300
301
  return;
301
302
  }
@@ -7,7 +7,7 @@ import { DEFAULT_AVATAR } from '@/const/meta';
7
7
  import { INBOX_SESSION_ID } from '@/const/session';
8
8
  import { isDesktop } from '@/const/version';
9
9
  import { useChatStore } from '@/store/chat';
10
- import { messageStateSelectors } from '@/store/chat/selectors';
10
+ import { operationSelectors } from '@/store/chat/selectors';
11
11
  import { useGlobalStore } from '@/store/global';
12
12
  import { useSessionStore } from '@/store/session';
13
13
  import { sessionHelpers } from '@/store/session/helpers';
@@ -32,7 +32,7 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
32
32
 
33
33
  const [active] = useSessionStore((s) => [s.activeId === id]);
34
34
  const [loading] = useChatStore((s) => [
35
- messageStateSelectors.isAIGenerating(s) && id === s.activeId,
35
+ operationSelectors.isAgentRuntimeRunning(s) && id === s.activeId,
36
36
  ]);
37
37
 
38
38
  const [pin, title, avatar, avatarBackground, updateAt, members, model, group, sessionType] =
@@ -9,7 +9,7 @@ import { SWRConfiguration } from 'swr';
9
9
  import { useAgentStore } from '@/store/agent';
10
10
  import { agentSelectors } from '@/store/agent/slices/chat';
11
11
  import { useChatStore } from '@/store/chat';
12
- import { messageStateSelectors } from '@/store/chat/selectors';
12
+ import { operationSelectors } from '@/store/chat/selectors';
13
13
  import { useGlobalStore } from '@/store/global';
14
14
  import { globalGeneralSelectors } from '@/store/global/selectors';
15
15
  import { useUserStore } from '@/store/user';
@@ -43,7 +43,7 @@ const BrowserSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
43
43
  const { t } = useTranslation('chat');
44
44
 
45
45
  const [loading, updateMessageInput] = useChatStore((s) => [
46
- messageStateSelectors.isAIGenerating(s),
46
+ operationSelectors.isAgentRuntimeRunning(s),
47
47
  s.updateMessageInput,
48
48
  ]);
49
49
 
@@ -12,7 +12,7 @@ import { API_ENDPOINTS } from '@/services/_url';
12
12
  import { useAgentStore } from '@/store/agent';
13
13
  import { agentSelectors } from '@/store/agent/selectors';
14
14
  import { useChatStore } from '@/store/chat';
15
- import { messageStateSelectors } from '@/store/chat/slices/message/selectors';
15
+ import { operationSelectors } from '@/store/chat/selectors';
16
16
  import { useGlobalStore } from '@/store/global';
17
17
  import { globalGeneralSelectors } from '@/store/global/selectors';
18
18
  import { useUserStore } from '@/store/user';
@@ -54,7 +54,7 @@ const OpenaiSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
54
54
  const { t } = useTranslation('chat');
55
55
 
56
56
  const [loading, updateMessageInput] = useChatStore((s) => [
57
- messageStateSelectors.isAIGenerating(s),
57
+ operationSelectors.isAgentRuntimeRunning(s),
58
58
  s.updateMessageInput,
59
59
  ]);
60
60
 
@@ -119,7 +119,7 @@ const Inspectors = memo<InspectorProps>(
119
119
  const hasResult = hasSuccessResult || hasError;
120
120
 
121
121
  const isPending = intervention?.status === 'pending';
122
- const isReject = intervention?.status === 'rejected';
122
+ const isReject = intervention?.status === 'rejected' || intervention?.status === 'aborted';
123
123
  const isTitleLoading = !hasResult && !isPending;
124
124
 
125
125
  // Compute actual render state based on pinned or hovered
@@ -60,13 +60,13 @@ const UserMessage = memo<UserMessageProps>(({ id, disableEditing, index }) => {
60
60
 
61
61
  const displayMode = useAgentStore(agentChatConfigSelectors.displayMode);
62
62
 
63
- const [editing, generating, isInRAGFlow] = useChatStore((s) => [
63
+ const [editing, creating, isInRAGFlow] = useChatStore((s) => [
64
64
  messageStateSelectors.isMessageEditing(id)(s),
65
- messageStateSelectors.isMessageGenerating(id)(s),
65
+ messageStateSelectors.isMessageCreating(id)(s), // User message only cares about creation (sendMessage)
66
66
  messageStateSelectors.isMessageInRAGFlow(id)(s),
67
67
  ]);
68
68
 
69
- const loading = isInRAGFlow || generating;
69
+ const loading = isInRAGFlow || creating;
70
70
 
71
71
  // Get target name for DM indicator
72
72
  const userName = useUserStore(userProfileSelectors.nickName) || 'User';
@@ -59,9 +59,9 @@ const Item = memo<ChatListItemProps>(
59
59
  const { styles, cx } = useStyles();
60
60
  const containerRef = useRef<HTMLDivElement | null>(null);
61
61
 
62
- const [isMessageLoading, role] = useChatStore((s) => [
63
- messageStateSelectors.isMessageLoading(id)(s),
62
+ const [role, isMessageCreating] = useChatStore((s) => [
64
63
  displayMessageSelectors.getDisplayMessageById(id)(s)?.role,
64
+ messageStateSelectors.isMessageCreating(id)(s),
65
65
  ]);
66
66
 
67
67
  // ======================= Performance Optimization ======================= //
@@ -168,7 +168,7 @@ const Item = memo<ChatListItemProps>(
168
168
  <InPortalThreadContext.Provider value={inPortalThread}>
169
169
  {enableHistoryDivider && <History />}
170
170
  <Flexbox
171
- className={cx(styles.message, className, isMessageLoading && styles.loading)}
171
+ className={cx(styles.message, className, isMessageCreating && styles.loading)}
172
172
  data-index={index}
173
173
  onContextMenu={onContextMenu}
174
174
  ref={containerRef}
@@ -1,7 +1,7 @@
1
1
  import { memo, useEffect } from 'react';
2
2
 
3
3
  import { useChatStore } from '@/store/chat';
4
- import { displayMessageSelectors, messageStateSelectors } from '@/store/chat/selectors';
4
+ import { displayMessageSelectors, operationSelectors } from '@/store/chat/selectors';
5
5
 
6
6
  import BackBottom from './BackBottom';
7
7
 
@@ -11,7 +11,7 @@ interface AutoScrollProps {
11
11
  onScrollToBottom: (type: 'auto' | 'click') => void;
12
12
  }
13
13
  const AutoScroll = memo<AutoScrollProps>(({ atBottom, isScrolling, onScrollToBottom }) => {
14
- const trackVisibility = useChatStore(messageStateSelectors.isAIGenerating);
14
+ const trackVisibility = useChatStore(operationSelectors.isAgentRuntimeRunning);
15
15
  const str = useChatStore(displayMessageSelectors.mainAIChatsMessageString);
16
16
  const reasoningStr = useChatStore(displayMessageSelectors.mainAILatestMessageReasoningContent);
17
17
 
@@ -15,8 +15,8 @@ class SearchService {
15
15
  return toolsClient.search.crawlPages.mutate(params);
16
16
  }
17
17
 
18
- async webSearch(params: SearchQuery) {
19
- return toolsClient.search.webSearch.query(params);
18
+ async webSearch(params: SearchQuery, options?: { signal?: AbortSignal }) {
19
+ return toolsClient.search.webSearch.query(params, { signal: options?.signal });
20
20
  }
21
21
  }
22
22
 
@@ -9,6 +9,7 @@ import {
9
9
  GeneralAgentCallToolsBatchInstructionPayload,
10
10
  GeneralAgentCallingToolInstructionPayload,
11
11
  GeneralAgentConfig,
12
+ HumanAbortPayload,
12
13
  InterventionChecker,
13
14
  } from '@lobechat/agent-runtime';
14
15
  import type { ChatToolPayload, HumanInterventionConfig } from '@lobechat/types';
@@ -115,10 +116,90 @@ export class GeneralChatAgent implements Agent {
115
116
  return [toolsNeedingIntervention, toolsToExecute];
116
117
  }
117
118
 
119
+ /**
120
+ * Extract abort information from current context and state
121
+ * Returns the necessary data to handle abort scenario
122
+ */
123
+ private extractAbortInfo(context: AgentRuntimeContext, state: AgentState) {
124
+ let hasToolsCalling = false;
125
+ let toolsCalling: ChatToolPayload[] = [];
126
+ let parentMessageId = '';
127
+
128
+ // Extract abort info based on current phase
129
+ switch (context.phase) {
130
+ case 'llm_result': {
131
+ const payload = context.payload as GeneralAgentCallLLMResultPayload;
132
+ hasToolsCalling = payload.hasToolsCalling || false;
133
+ toolsCalling = payload.toolsCalling || [];
134
+ parentMessageId = payload.parentMessageId;
135
+ break;
136
+ }
137
+ case 'human_abort': {
138
+ // When user cancels during LLM streaming, we enter human_abort phase
139
+ // The payload contains tool calls info if LLM had started returning them
140
+ const payload = context.payload as any;
141
+ hasToolsCalling = payload.hasToolsCalling || false;
142
+ toolsCalling = payload.toolsCalling || [];
143
+ parentMessageId = payload.parentMessageId;
144
+ break;
145
+ }
146
+ case 'tool_result':
147
+ case 'tools_batch_result': {
148
+ const payload = context.payload as GeneralAgentCallToolResultPayload;
149
+ parentMessageId = payload.parentMessageId;
150
+ // Check if there are pending tool messages
151
+ const pendingToolMessages = state.messages.filter(
152
+ (m: any) => m.role === 'tool' && m.pluginIntervention?.status === 'pending',
153
+ );
154
+ if (pendingToolMessages.length > 0) {
155
+ hasToolsCalling = true;
156
+ toolsCalling = pendingToolMessages.map((m: any) => m.plugin).filter(Boolean);
157
+ }
158
+ break;
159
+ }
160
+ }
161
+
162
+ return { hasToolsCalling, parentMessageId, toolsCalling };
163
+ }
164
+
165
+ /**
166
+ * Handle abort scenario - unified abort handling logic
167
+ */
168
+ private handleAbort(
169
+ context: AgentRuntimeContext,
170
+ state: AgentState,
171
+ ): AgentInstruction | AgentInstruction[] {
172
+ const { hasToolsCalling, parentMessageId, toolsCalling } = this.extractAbortInfo(
173
+ context,
174
+ state,
175
+ );
176
+
177
+ // If there are pending tool calls, resolve them
178
+ if (hasToolsCalling && toolsCalling.length > 0) {
179
+ return {
180
+ payload: { parentMessageId, toolsCalling },
181
+ type: 'resolve_aborted_tools',
182
+ };
183
+ }
184
+
185
+ // No tools to resolve, directly finish
186
+ return {
187
+ reason: 'user_requested',
188
+ reasonDetail: 'Operation cancelled by user',
189
+ type: 'finish',
190
+ };
191
+ }
192
+
118
193
  async runner(
119
194
  context: AgentRuntimeContext,
120
195
  state: AgentState,
121
196
  ): Promise<AgentInstruction | AgentInstruction[]> {
197
+ // Unified abort check: if operation is interrupted, handle abort scenario
198
+ // This check is placed before phase handling to ensure consistent abort behavior
199
+ if (state.status === 'interrupted') {
200
+ return this.handleAbort(context, state);
201
+ }
202
+
122
203
  switch (context.phase) {
123
204
  case 'init':
124
205
  case 'user_input': {
@@ -256,6 +337,23 @@ export class GeneralChatAgent implements Agent {
256
337
  };
257
338
  }
258
339
 
340
+ case 'human_abort': {
341
+ // User aborted the operation
342
+ const { hasToolsCalling, parentMessageId, toolsCalling, reason } =
343
+ context.payload as HumanAbortPayload;
344
+
345
+ // If there are pending tool calls, resolve them
346
+ if (hasToolsCalling && toolsCalling && toolsCalling.length > 0) {
347
+ return {
348
+ payload: { parentMessageId, toolsCalling },
349
+ type: 'resolve_aborted_tools',
350
+ };
351
+ }
352
+
353
+ // No tools to resolve, directly finish
354
+ return { reason: 'user_requested', reasonDetail: reason, type: 'finish' };
355
+ }
356
+
259
357
  case 'error': {
260
358
  // Error occurred, finish execution
261
359
  const { error } = context.payload as { error: any };