@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.
- package/CHANGELOG.md +25 -0
- package/apps/desktop/src/main/modules/networkProxy/dispatcher.ts +16 -16
- package/apps/desktop/src/main/modules/networkProxy/tester.ts +11 -11
- package/apps/desktop/src/main/modules/networkProxy/urlBuilder.ts +3 -3
- package/apps/desktop/src/main/modules/networkProxy/validator.ts +10 -10
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/agent-runtime/src/core/runtime.ts +36 -1
- package/packages/agent-runtime/src/types/event.ts +1 -0
- package/packages/agent-runtime/src/types/generalAgent.ts +16 -0
- package/packages/agent-runtime/src/types/instruction.ts +30 -0
- package/packages/agent-runtime/src/types/runtime.ts +7 -0
- package/packages/types/src/message/common/metadata.ts +3 -0
- package/packages/types/src/message/common/tools.ts +2 -2
- package/packages/types/src/tool/search/index.ts +8 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +7 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +15 -14
- package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/index.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -1
- package/src/features/Conversation/Messages/User/index.tsx +3 -3
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +2 -2
- package/src/services/search.ts +2 -2
- package/src/store/chat/agents/GeneralChatAgent.ts +98 -0
- package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +366 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/call-llm.test.ts +1217 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/call-tool.test.ts +1976 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/finish.test.ts +453 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/index.ts +4 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockInstructions.ts +126 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockMessages.ts +94 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockOperations.ts +96 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockStore.ts +138 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/assertions.ts +185 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/index.ts +3 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/operationTestUtils.ts +94 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +139 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/request-human-approve.test.ts +545 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/resolve-aborted-tools.test.ts +686 -0
- package/src/store/chat/agents/createAgentExecutors.ts +313 -80
- package/src/store/chat/selectors.ts +1 -0
- package/src/store/chat/slices/aiChat/__tests__/ai-chat.integration.test.ts +667 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/cancel-functionality.test.ts +137 -27
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +163 -125
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +12 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +286 -19
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +0 -112
- package/src/store/chat/slices/aiChat/actions/conversationControl.ts +42 -99
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +90 -57
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +5 -25
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +220 -98
- package/src/store/chat/slices/aiChat/actions/streamingStates.ts +0 -34
- package/src/store/chat/slices/aiChat/initialState.ts +0 -28
- package/src/store/chat/slices/aiChat/selectors.test.ts +280 -0
- package/src/store/chat/slices/aiChat/selectors.ts +31 -7
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +21 -30
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +29 -49
- package/src/store/chat/slices/builtinTool/actions/interpreter.ts +83 -48
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +78 -28
- package/src/store/chat/slices/builtinTool/actions/search.ts +146 -59
- package/src/store/chat/slices/builtinTool/selectors.test.ts +258 -0
- package/src/store/chat/slices/builtinTool/selectors.ts +25 -4
- package/src/store/chat/slices/message/action.test.ts +134 -16
- package/src/store/chat/slices/message/actions/internals.ts +33 -7
- package/src/store/chat/slices/message/actions/optimisticUpdate.ts +85 -52
- package/src/store/chat/slices/message/initialState.ts +0 -10
- package/src/store/chat/slices/message/selectors/messageState.ts +34 -12
- package/src/store/chat/slices/operation/__tests__/actions.test.ts +712 -16
- package/src/store/chat/slices/operation/__tests__/integration.test.ts +342 -0
- package/src/store/chat/slices/operation/__tests__/selectors.test.ts +257 -17
- package/src/store/chat/slices/operation/actions.ts +218 -11
- package/src/store/chat/slices/operation/selectors.ts +135 -6
- package/src/store/chat/slices/operation/types.ts +29 -3
- package/src/store/chat/slices/plugin/action.test.ts +30 -322
- package/src/store/chat/slices/plugin/actions/internals.ts +0 -14
- package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +21 -19
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +45 -27
- package/src/store/chat/slices/plugin/actions/publicApi.ts +3 -4
- package/src/store/chat/slices/plugin/actions/workflow.ts +0 -55
- package/src/store/chat/slices/thread/selectors/index.ts +4 -2
- package/src/store/chat/slices/translate/action.ts +54 -41
- package/src/tools/web-browsing/ExecutionRuntime/index.ts +5 -2
- package/src/tools/web-browsing/Portal/Search/Footer.tsx +11 -9
package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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:
|
|
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
|
|
136
|
+
const isRunning = operationSelectors.isMainWindowAgentRuntimeRunning(store);
|
|
136
137
|
|
|
137
|
-
if (
|
|
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:
|
|
153
|
+
generating: isAgentRuntimeRunning || isSendingMessage,
|
|
153
154
|
send: handleSend,
|
|
154
155
|
stop,
|
|
155
156
|
}),
|
|
156
|
-
[canNotSend,
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
296
|
+
const isAgentRunning = operationSelectors.isMainWindowAgentRuntimeRunning(store);
|
|
296
297
|
const isCreating = messageStateSelectors.isCreatingMessage(store);
|
|
297
298
|
|
|
298
|
-
if (
|
|
299
|
+
if (isAgentRunning) {
|
|
299
300
|
stopGenerateMessage();
|
|
300
301
|
return;
|
|
301
302
|
}
|
package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/index.tsx
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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,
|
|
63
|
+
const [editing, creating, isInRAGFlow] = useChatStore((s) => [
|
|
64
64
|
messageStateSelectors.isMessageEditing(id)(s),
|
|
65
|
-
messageStateSelectors.
|
|
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 ||
|
|
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 [
|
|
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,
|
|
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,
|
|
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(
|
|
14
|
+
const trackVisibility = useChatStore(operationSelectors.isAgentRuntimeRunning);
|
|
15
15
|
const str = useChatStore(displayMessageSelectors.mainAIChatsMessageString);
|
|
16
16
|
const reasoningStr = useChatStore(displayMessageSelectors.mainAILatestMessageReasoningContent);
|
|
17
17
|
|
package/src/services/search.ts
CHANGED
|
@@ -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 };
|