@blackbox_ai/blackbox-cli 0.8.6 → 1.0.2
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/README.md +20 -0
- package/dist/assets/notification.mp3 +0 -0
- package/dist/package.json +2 -2
- package/dist/src/commands/configure/ConfigureUI.d.ts +1 -1
- package/dist/src/commands/configure/ConfigureUI.js +91 -22
- package/dist/src/commands/configure/ConfigureUI.js.map +1 -1
- package/dist/src/commands/configure.js +57 -16
- package/dist/src/commands/configure.js.map +1 -1
- package/dist/src/commands/shortcut.d.ts +10 -0
- package/dist/src/commands/shortcut.js +72 -0
- package/dist/src/commands/shortcut.js.map +1 -0
- package/dist/src/commands/voice.js +39 -34
- package/dist/src/commands/voice.js.map +1 -1
- package/dist/src/config/config.js +8 -5
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/modelFetcher.d.ts +4 -0
- package/dist/src/config/modelFetcher.js +68 -22
- package/dist/src/config/modelFetcher.js.map +1 -1
- package/dist/src/config/settings.d.ts +2 -0
- package/dist/src/config/settings.js +48 -3
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +64 -6
- package/dist/src/config/settingsSchema.js +62 -6
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/gemini.js +31 -20
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/services/BuiltinCommandLoader.js +4 -2
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/agentExecutor.d.ts +57 -0
- package/dist/src/services/agentExecutor.js +149 -0
- package/dist/src/services/agentExecutor.js.map +1 -0
- package/dist/src/services/voiceRecordingService.d.ts +13 -1
- package/dist/src/services/voiceRecordingService.js +38 -5
- package/dist/src/services/voiceRecordingService.js.map +1 -1
- package/dist/src/ui/App.d.ts +1 -1
- package/dist/src/ui/App.js +292 -26
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/colors.js +3 -0
- package/dist/src/ui/colors.js.map +1 -1
- package/dist/src/ui/commands/agentCommand.d.ts +7 -0
- package/dist/src/ui/commands/agentCommand.js +181 -0
- package/dist/src/ui/commands/agentCommand.js.map +1 -0
- package/dist/src/ui/commands/modelCommand.js +15 -2
- package/dist/src/ui/commands/modelCommand.js.map +1 -1
- package/dist/src/ui/commands/quitCommand.js +5 -0
- package/dist/src/ui/commands/quitCommand.js.map +1 -1
- package/dist/src/ui/commands/subAgentsCommand.d.ts +7 -0
- package/dist/src/ui/commands/subAgentsCommand.js +32 -0
- package/dist/src/ui/commands/subAgentsCommand.js.map +1 -0
- package/dist/src/ui/commands/types.d.ts +24 -3
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/commands/voiceCommand.js +70 -34
- package/dist/src/ui/commands/voiceCommand.js.map +1 -1
- package/dist/src/ui/commands/voiceCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/voiceCommand.test.js +131 -0
- package/dist/src/ui/commands/voiceCommand.test.js.map +1 -0
- package/dist/src/ui/components/AgentModelSelector.d.ts +17 -0
- package/dist/src/ui/components/AgentModelSelector.js +41 -0
- package/dist/src/ui/components/AgentModelSelector.js.map +1 -0
- package/dist/src/ui/components/AgentSetDialog.d.ts +15 -0
- package/dist/src/ui/components/AgentSetDialog.js +52 -0
- package/dist/src/ui/components/AgentSetDialog.js.map +1 -0
- package/dist/src/ui/components/ApiKeyInputDialog.d.ts +17 -0
- package/dist/src/ui/components/ApiKeyInputDialog.js +44 -0
- package/dist/src/ui/components/ApiKeyInputDialog.js.map +1 -0
- package/dist/src/ui/components/AsciiArt.d.ts +3 -3
- package/dist/src/ui/components/AsciiArt.js +18 -18
- package/dist/src/ui/components/AsciiArt.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.js +50 -7
- package/dist/src/ui/components/AuthDialog.js.map +1 -1
- package/dist/src/ui/components/Footer.d.ts +8 -0
- package/dist/src/ui/components/Footer.js +17 -4
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/GenericProviderKeyPrompt.js +1 -1
- package/dist/src/ui/components/GenericProviderKeyPrompt.js.map +1 -1
- package/dist/src/ui/components/Header.js +6 -10
- package/dist/src/ui/components/Header.js.map +1 -1
- package/dist/src/ui/components/HistoryBrowserDialog.js +2 -1
- package/dist/src/ui/components/HistoryBrowserDialog.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +3 -1
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/MemoryUsageDisplay.js +41 -8
- package/dist/src/ui/components/MemoryUsageDisplay.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.js +4 -3
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/StatsDisplay.js +1 -1
- package/dist/src/ui/components/StatsDisplay.js.map +1 -1
- package/dist/src/ui/components/SuggestionsDisplay.js +3 -2
- package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
- package/dist/src/ui/components/Tips.js +1 -1
- package/dist/src/ui/components/Tips.js.map +1 -1
- package/dist/src/ui/components/agents/SingleAgentDialog.d.ts +23 -0
- package/dist/src/ui/components/agents/SingleAgentDialog.js +222 -0
- package/dist/src/ui/components/agents/SingleAgentDialog.js.map +1 -0
- package/dist/src/ui/components/messages/DiffRenderer.js +1 -9
- package/dist/src/ui/components/messages/DiffRenderer.js.map +1 -1
- package/dist/src/ui/components/messages/ErrorMessage.js +2 -1
- package/dist/src/ui/components/messages/ErrorMessage.js.map +1 -1
- package/dist/src/ui/components/messages/InfoMessage.js +2 -1
- package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js +4 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.js +4 -4
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/UserMessage.js +1 -2
- package/dist/src/ui/components/messages/UserMessage.js.map +1 -1
- package/dist/src/ui/components/multiagent/MultiAgentConfigDialog.js +1 -19
- package/dist/src/ui/components/multiagent/MultiAgentConfigDialog.js.map +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.js +14 -4
- package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
- package/dist/src/ui/contexts/SessionContext.d.ts +18 -0
- package/dist/src/ui/contexts/SessionContext.js +25 -1
- package/dist/src/ui/contexts/SessionContext.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.d.ts +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.js +13 -2
- package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +2 -2
- package/dist/src/ui/hooks/slashCommandProcessor.js +65 -29
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAdaptiveStream.d.ts +2 -1
- package/dist/src/ui/hooks/useAdaptiveStream.js +2 -2
- package/dist/src/ui/hooks/useAdaptiveStream.js.map +1 -1
- package/dist/src/ui/hooks/useAgentCommandProcessor.d.ts +26 -0
- package/dist/src/ui/hooks/useAgentCommandProcessor.js +967 -0
- package/dist/src/ui/hooks/useAgentCommandProcessor.js.map +1 -0
- package/dist/src/ui/hooks/useAuthCommand.d.ts +2 -1
- package/dist/src/ui/hooks/useAuthCommand.js +22 -2
- package/dist/src/ui/hooks/useAuthCommand.js.map +1 -1
- package/dist/src/ui/hooks/useDialogClose.d.ts +2 -0
- package/dist/src/ui/hooks/useDialogClose.js +5 -0
- package/dist/src/ui/hooks/useDialogClose.js.map +1 -1
- package/dist/src/ui/hooks/useEncryptedStream.d.ts +2 -1
- package/dist/src/ui/hooks/useEncryptedStream.js +31 -3
- package/dist/src/ui/hooks/useEncryptedStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.d.ts +2 -1
- package/dist/src/ui/hooks/useGeminiStream.js +292 -22
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useInputBoxColor.d.ts +13 -0
- package/dist/src/ui/hooks/useInputBoxColor.js +62 -0
- package/dist/src/ui/hooks/useInputBoxColor.js.map +1 -0
- package/dist/src/ui/hooks/useMessageQueue.d.ts +7 -1
- package/dist/src/ui/hooks/useMessageQueue.js +9 -3
- package/dist/src/ui/hooks/useMessageQueue.js.map +1 -1
- package/dist/src/ui/hooks/useSingleAgentCommand.d.ts +10 -0
- package/dist/src/ui/hooks/useSingleAgentCommand.js +21 -0
- package/dist/src/ui/hooks/useSingleAgentCommand.js.map +1 -0
- package/dist/src/ui/themes/ansi-light.js +1 -0
- package/dist/src/ui/themes/ansi-light.js.map +1 -1
- package/dist/src/ui/themes/ansi.js +1 -0
- package/dist/src/ui/themes/ansi.js.map +1 -1
- package/dist/src/ui/themes/atom-one-dark.js +14 -13
- package/dist/src/ui/themes/atom-one-dark.js.map +1 -1
- package/dist/src/ui/themes/ayu-light.js +14 -13
- package/dist/src/ui/themes/ayu-light.js.map +1 -1
- package/dist/src/ui/themes/ayu.js +14 -13
- package/dist/src/ui/themes/ayu.js.map +1 -1
- package/dist/src/ui/themes/blackbox-dark.js +36 -31
- package/dist/src/ui/themes/blackbox-dark.js.map +1 -1
- package/dist/src/ui/themes/blackbox-light.js +41 -39
- package/dist/src/ui/themes/blackbox-light.js.map +1 -1
- package/dist/src/ui/themes/default-light.js +20 -20
- package/dist/src/ui/themes/default-light.js.map +1 -1
- package/dist/src/ui/themes/default.js +27 -27
- package/dist/src/ui/themes/default.js.map +1 -1
- package/dist/src/ui/themes/dracula.js +14 -13
- package/dist/src/ui/themes/dracula.js.map +1 -1
- package/dist/src/ui/themes/github-dark.js +14 -13
- package/dist/src/ui/themes/github-dark.js.map +1 -1
- package/dist/src/ui/themes/github-light.js +14 -13
- package/dist/src/ui/themes/github-light.js.map +1 -1
- package/dist/src/ui/themes/googlecode.js +1 -0
- package/dist/src/ui/themes/googlecode.js.map +1 -1
- package/dist/src/ui/themes/no-color.js +2 -0
- package/dist/src/ui/themes/no-color.js.map +1 -1
- package/dist/src/ui/themes/semantic-tokens.d.ts +1 -0
- package/dist/src/ui/themes/semantic-tokens.js +3 -0
- package/dist/src/ui/themes/semantic-tokens.js.map +1 -1
- package/dist/src/ui/themes/shades-of-purple.js +1 -0
- package/dist/src/ui/themes/shades-of-purple.js.map +1 -1
- package/dist/src/ui/themes/theme.d.ts +2 -0
- package/dist/src/ui/themes/theme.js +35 -28
- package/dist/src/ui/themes/theme.js.map +1 -1
- package/dist/src/ui/themes/xcode.js +1 -0
- package/dist/src/ui/themes/xcode.js.map +1 -1
- package/dist/src/ui/types.d.ts +5 -0
- package/dist/src/ui/utils/agentConversationUtils.d.ts +28 -0
- package/dist/src/ui/utils/agentConversationUtils.js +71 -0
- package/dist/src/ui/utils/agentConversationUtils.js.map +1 -0
- package/dist/src/ui/utils/colorBlend.d.ts +33 -0
- package/dist/src/ui/utils/colorBlend.js +61 -0
- package/dist/src/ui/utils/colorBlend.js.map +1 -0
- package/dist/src/ui/utils/commandUtils.d.ts +2 -1
- package/dist/src/ui/utils/commandUtils.js +14 -1
- package/dist/src/ui/utils/commandUtils.js.map +1 -1
- package/dist/src/ui/utils/commandUtils.test.js +25 -0
- package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
- package/dist/src/ui/utils/terminalBackgroundDetector.d.ts +35 -0
- package/dist/src/ui/utils/terminalBackgroundDetector.js +200 -0
- package/dist/src/ui/utils/terminalBackgroundDetector.js.map +1 -0
- package/dist/src/utils/agentCommandBuilder.d.ts +48 -0
- package/dist/src/utils/agentCommandBuilder.js +328 -0
- package/dist/src/utils/agentCommandBuilder.js.map +1 -0
- package/dist/src/utils/apiKeyUtils.d.ts +47 -0
- package/dist/src/utils/apiKeyUtils.js +177 -0
- package/dist/src/utils/apiKeyUtils.js.map +1 -0
- package/dist/src/utils/backgroundUpdateCheck.js +104 -7
- package/dist/src/utils/backgroundUpdateCheck.js.map +1 -1
- package/dist/src/utils/memoryManager.d.ts +85 -0
- package/dist/src/utils/memoryManager.js +258 -0
- package/dist/src/utils/memoryManager.js.map +1 -0
- package/dist/src/utils/memoryMonitor.d.ts +89 -0
- package/dist/src/utils/memoryMonitor.js +238 -0
- package/dist/src/utils/memoryMonitor.js.map +1 -0
- package/dist/src/utils/memoryWrapper.d.ts +18 -0
- package/dist/src/utils/memoryWrapper.js +62 -0
- package/dist/src/utils/memoryWrapper.js.map +1 -0
- package/dist/src/utils/preLaunchUpdateCheck.js +17 -7
- package/dist/src/utils/preLaunchUpdateCheck.js.map +1 -1
- package/dist/src/utils/shellShortcut.d.ts +45 -0
- package/dist/src/utils/shellShortcut.js +221 -0
- package/dist/src/utils/shellShortcut.js.map +1 -0
- package/dist/src/utils/soundNotification.d.ts +15 -0
- package/dist/src/utils/soundNotification.js +138 -0
- package/dist/src/utils/soundNotification.js.map +1 -0
- package/dist/src/utils/userStartupWarnings.js +0 -20
- package/dist/src/utils/userStartupWarnings.js.map +1 -1
- package/dist/src/utils/versionStorage.d.ts +20 -0
- package/dist/src/utils/versionStorage.js +62 -0
- package/dist/src/utils/versionStorage.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Blackbox
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useRef } from 'react';
|
|
7
|
+
import { ToolCallStatus } from '../types.js';
|
|
8
|
+
import { AgentExecutor } from '../../services/agentExecutor.js';
|
|
9
|
+
/**
|
|
10
|
+
* Enhanced parser to extract tool information from agent output
|
|
11
|
+
*/
|
|
12
|
+
function extractToolCallFromAgentOutput(toolContent) {
|
|
13
|
+
const lines = toolContent.split('\n');
|
|
14
|
+
let toolName = '';
|
|
15
|
+
let args = {};
|
|
16
|
+
let inPayload = false;
|
|
17
|
+
let payloadContent = '';
|
|
18
|
+
for (const line of lines) {
|
|
19
|
+
const cleanLine = line.replace(/[│\s]/g, '');
|
|
20
|
+
// Extract tool name
|
|
21
|
+
if (cleanLine.startsWith('Tool')) {
|
|
22
|
+
toolName = cleanLine.substring(4).trim();
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// Start of invocation payload
|
|
26
|
+
if (cleanLine.includes('Invocationpayload:') || cleanLine.includes('payload:')) {
|
|
27
|
+
inPayload = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
// End of payload section
|
|
31
|
+
if (cleanLine.includes('Result:') || cleanLine.includes('Output:')) {
|
|
32
|
+
inPayload = false;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
// Collect payload content
|
|
36
|
+
if (inPayload) {
|
|
37
|
+
payloadContent += line + '\n';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Parse JSON payload if present
|
|
41
|
+
try {
|
|
42
|
+
if (payloadContent.trim()) {
|
|
43
|
+
// Clean up the payload content and try to parse as JSON
|
|
44
|
+
const cleanPayload = payloadContent.trim().replace(/^[{]/, '{').replace(/[}]$/, '}');
|
|
45
|
+
args = JSON.parse(cleanPayload);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// If JSON parsing fails, return null - this isn't a valid tool call
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (!toolName || Object.keys(args).length === 0) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
callId: `agent-tool-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
57
|
+
name: toolName,
|
|
58
|
+
args,
|
|
59
|
+
isClientInitiated: false, // These come from the agent, not user
|
|
60
|
+
prompt_id: `agent-${Date.now()}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse Gemini agent streaming JSON output to extract text content and tool information
|
|
65
|
+
*/
|
|
66
|
+
function parseGeminiStreamingOutput(jsonLine) {
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(jsonLine);
|
|
69
|
+
// Handle streaming text deltas
|
|
70
|
+
if (parsed.type === 'message' && parsed.role === 'assistant' && parsed.content && parsed.delta === true) {
|
|
71
|
+
return { type: 'delta', text: parsed.content };
|
|
72
|
+
}
|
|
73
|
+
// Handle complete assistant messages
|
|
74
|
+
if (parsed.type === 'message' && parsed.role === 'assistant' && parsed.content && !parsed.delta) {
|
|
75
|
+
return { type: 'final', text: parsed.content };
|
|
76
|
+
}
|
|
77
|
+
// Handle tool use events
|
|
78
|
+
if (parsed.type === 'tool_use') {
|
|
79
|
+
const toolCall = {
|
|
80
|
+
callId: parsed.tool_use_id || `gemini-tool-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
81
|
+
name: parsed.tool_name,
|
|
82
|
+
args: parsed.parameters || {},
|
|
83
|
+
isClientInitiated: false, // These come from Gemini, not user
|
|
84
|
+
prompt_id: `gemini-${Date.now()}`,
|
|
85
|
+
};
|
|
86
|
+
return { type: 'tool_use', toolCall };
|
|
87
|
+
}
|
|
88
|
+
// Handle tool results
|
|
89
|
+
if (parsed.type === 'tool_result') {
|
|
90
|
+
return {
|
|
91
|
+
type: 'tool_result',
|
|
92
|
+
toolResult: {
|
|
93
|
+
toolId: parsed.tool_use_id,
|
|
94
|
+
result: parsed.output || parsed.result || ''
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Handle final result
|
|
99
|
+
if (parsed.type === 'result' && parsed.status === 'success') {
|
|
100
|
+
return { type: 'final', text: parsed.result || '' };
|
|
101
|
+
}
|
|
102
|
+
// Skip init, system messages and other metadata
|
|
103
|
+
if (parsed.type === 'init' || parsed.type === 'system') {
|
|
104
|
+
return { type: 'skip' };
|
|
105
|
+
}
|
|
106
|
+
// Skip other metadata but log for debugging
|
|
107
|
+
return { type: 'skip' };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.log('[Gemini Parser] JSON parse error:', error);
|
|
111
|
+
return { type: 'skip' };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Parse Codex agent streaming JSON output to extract text content and tool information
|
|
116
|
+
*/
|
|
117
|
+
function parseCodexStreamingOutput(jsonLine) {
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(jsonLine);
|
|
120
|
+
// Handle agent messages (text content)
|
|
121
|
+
if (parsed.type === 'item.completed' &&
|
|
122
|
+
parsed.item?.type === 'agent_message' &&
|
|
123
|
+
parsed.item?.text) {
|
|
124
|
+
return { type: 'delta', text: parsed.item.text + '\n\n' };
|
|
125
|
+
}
|
|
126
|
+
// Handle command execution start (tool use)
|
|
127
|
+
if (parsed.type === 'item.started' &&
|
|
128
|
+
parsed.item?.type === 'command_execution' &&
|
|
129
|
+
parsed.item?.command) {
|
|
130
|
+
// Create a tool call for command execution
|
|
131
|
+
const toolCall = {
|
|
132
|
+
callId: parsed.item.id || `codex-cmd-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
133
|
+
name: 'shell',
|
|
134
|
+
args: { command: parsed.item.command },
|
|
135
|
+
isClientInitiated: false,
|
|
136
|
+
prompt_id: `codex-${Date.now()}`,
|
|
137
|
+
};
|
|
138
|
+
return { type: 'tool_use', toolCall };
|
|
139
|
+
}
|
|
140
|
+
// Handle command execution completion (tool result)
|
|
141
|
+
if (parsed.type === 'item.completed' &&
|
|
142
|
+
parsed.item?.type === 'command_execution') {
|
|
143
|
+
const exitCode = parsed.item.exit_code;
|
|
144
|
+
return {
|
|
145
|
+
type: 'tool_result',
|
|
146
|
+
toolResult: {
|
|
147
|
+
toolId: parsed.item.id,
|
|
148
|
+
result: exitCode === 0 ? 'Command completed successfully' : `Command failed with exit code: ${exitCode}`
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Handle thread start
|
|
153
|
+
if (parsed.type === 'thread.started') {
|
|
154
|
+
return { type: 'skip' }; // Skip thread metadata
|
|
155
|
+
}
|
|
156
|
+
// Skip other metadata
|
|
157
|
+
return { type: 'skip' };
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.log('[Codex Parser] JSON parse error:', error);
|
|
161
|
+
return { type: 'skip' };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Parse Claude Code agent streaming JSON output to extract text content and tool information
|
|
166
|
+
*/
|
|
167
|
+
function parseClaudeStreamingOutput(jsonLine) {
|
|
168
|
+
try {
|
|
169
|
+
const parsed = JSON.parse(jsonLine);
|
|
170
|
+
// Handle streaming text deltas
|
|
171
|
+
if (parsed.type === 'stream_event' &&
|
|
172
|
+
parsed.event?.type === 'content_block_delta' &&
|
|
173
|
+
parsed.event?.delta?.type === 'text_delta') {
|
|
174
|
+
return { type: 'delta', text: parsed.event.delta.text };
|
|
175
|
+
}
|
|
176
|
+
// Handle tool use in assistant messages
|
|
177
|
+
if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
178
|
+
const content = parsed.message.content;
|
|
179
|
+
if (Array.isArray(content)) {
|
|
180
|
+
// Check for tool use
|
|
181
|
+
const toolUse = content.find(item => item.type === 'tool_use');
|
|
182
|
+
if (toolUse) {
|
|
183
|
+
// Create a proper ToolCallRequestInfo for the scheduler
|
|
184
|
+
const toolCall = {
|
|
185
|
+
callId: toolUse.id || `claude-tool-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
186
|
+
name: toolUse.name,
|
|
187
|
+
args: toolUse.input || {},
|
|
188
|
+
isClientInitiated: false, // These come from Claude, not user
|
|
189
|
+
prompt_id: `claude-${Date.now()}`,
|
|
190
|
+
};
|
|
191
|
+
return { type: 'tool_use', toolCall };
|
|
192
|
+
}
|
|
193
|
+
// Extract text content
|
|
194
|
+
const text = content
|
|
195
|
+
.filter(item => item.type === 'text')
|
|
196
|
+
.map(item => item.text)
|
|
197
|
+
.join('\n');
|
|
198
|
+
if (text) {
|
|
199
|
+
return { type: 'final', text };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { type: 'final', text: content };
|
|
203
|
+
}
|
|
204
|
+
// Handle tool results in user messages
|
|
205
|
+
if (parsed.type === 'user' && parsed.message?.content) {
|
|
206
|
+
const content = parsed.message.content;
|
|
207
|
+
if (Array.isArray(content)) {
|
|
208
|
+
const toolResult = content.find(item => item.type === 'tool_result');
|
|
209
|
+
if (toolResult) {
|
|
210
|
+
// Return tool result information for the scheduler to handle
|
|
211
|
+
return {
|
|
212
|
+
type: 'tool_result',
|
|
213
|
+
toolResult: {
|
|
214
|
+
toolId: toolResult.tool_use_id,
|
|
215
|
+
result: typeof toolResult.content === 'string' ? toolResult.content : JSON.stringify(toolResult.content)
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Handle final result (this is the cleanest output)
|
|
222
|
+
if (parsed.type === 'result' && parsed.result) {
|
|
223
|
+
return { type: 'final', text: parsed.result };
|
|
224
|
+
}
|
|
225
|
+
// Handle streaming result chunks - when Claude sends partial result data
|
|
226
|
+
if (parsed.type === 'result' && parsed.partial_result) {
|
|
227
|
+
return { type: 'delta', text: parsed.partial_result };
|
|
228
|
+
}
|
|
229
|
+
// Skip system messages and other metadata
|
|
230
|
+
return { type: 'skip' };
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.log('[Claude Parser] JSON parse error:', error);
|
|
234
|
+
return { type: 'skip' };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Parse agent output to extract tool calls and clean message content
|
|
239
|
+
*/
|
|
240
|
+
function parseAgentOutputForToolCalls(output, lastProcessedLength, scheduleToolCalls, abortSignal, agentType) {
|
|
241
|
+
// Only process new content since last parse
|
|
242
|
+
const newContent = output.substring(lastProcessedLength);
|
|
243
|
+
if (!newContent) {
|
|
244
|
+
return { newProcessedLength: lastProcessedLength, messageContent: output };
|
|
245
|
+
}
|
|
246
|
+
let messageContent = '';
|
|
247
|
+
let currentToolContent = '';
|
|
248
|
+
let inToolBox = false;
|
|
249
|
+
const toolCallsToSchedule = [];
|
|
250
|
+
const lines = output.split('\n');
|
|
251
|
+
// Handle Claude Code agent's JSON streaming output format
|
|
252
|
+
if (agentType === 'claude') {
|
|
253
|
+
// For the fallback parsing in this context, we just use the raw output
|
|
254
|
+
// The actual streaming parsing happens in the main execution flow
|
|
255
|
+
messageContent = output;
|
|
256
|
+
// Fallback: if no content extracted, try the old method
|
|
257
|
+
if (!messageContent) {
|
|
258
|
+
const cleanMessages = [];
|
|
259
|
+
for (const line of lines) {
|
|
260
|
+
const trimmedLine = line.trim();
|
|
261
|
+
// Skip tool call indicators, empty lines, and JSON lines
|
|
262
|
+
if (trimmedLine.startsWith('✦ ') || !trimmedLine ||
|
|
263
|
+
(trimmedLine.startsWith('{') && trimmedLine.endsWith('}'))) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
// Add regular text content
|
|
267
|
+
cleanMessages.push(trimmedLine);
|
|
268
|
+
}
|
|
269
|
+
messageContent = cleanMessages.join('\n\n').trim();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// Handle other agents with tool box format
|
|
274
|
+
for (let i = 0; i < lines.length; i++) {
|
|
275
|
+
const line = lines[i];
|
|
276
|
+
// Detect tool box start
|
|
277
|
+
if (line.includes('┌─')) {
|
|
278
|
+
inToolBox = true;
|
|
279
|
+
currentToolContent = '';
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
// Detect tool box end
|
|
283
|
+
if (line.includes('└─') && inToolBox) {
|
|
284
|
+
inToolBox = false;
|
|
285
|
+
// Try to extract a tool call from the content
|
|
286
|
+
const toolCall = extractToolCallFromAgentOutput(currentToolContent);
|
|
287
|
+
if (toolCall) {
|
|
288
|
+
toolCallsToSchedule.push(toolCall);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
// If it's not a valid tool call, add it to message content
|
|
292
|
+
messageContent += `\n┌─\n${currentToolContent}\n└─\n`;
|
|
293
|
+
}
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
// Collect tool box content
|
|
297
|
+
if (inToolBox) {
|
|
298
|
+
currentToolContent += line + '\n';
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
// Regular message content (skip tool call indicators)
|
|
302
|
+
if (!line.startsWith('✦ ')) {
|
|
303
|
+
messageContent += line + '\n';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
messageContent = messageContent.trim();
|
|
307
|
+
}
|
|
308
|
+
// Schedule any tool calls we found
|
|
309
|
+
if (toolCallsToSchedule.length > 0) {
|
|
310
|
+
scheduleToolCalls(toolCallsToSchedule, abortSignal);
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
newProcessedLength: output.length,
|
|
314
|
+
messageContent
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Format a tool call for inclusion in conversation history.
|
|
319
|
+
* This ensures the agent has context about what tools were used.
|
|
320
|
+
*/
|
|
321
|
+
function formatToolCallForHistory(toolCall) {
|
|
322
|
+
const toolName = toolCall.name;
|
|
323
|
+
const args = toolCall.args || {};
|
|
324
|
+
// Extract the most relevant argument for the summary
|
|
325
|
+
let summary = '';
|
|
326
|
+
const toolNameLower = toolName.toLowerCase();
|
|
327
|
+
if (toolNameLower.includes('write') || toolNameLower.includes('edit')) {
|
|
328
|
+
const filePath = (args['file_path'] || args['path'] || args['target_file']);
|
|
329
|
+
summary = filePath ? `file: ${filePath}` : 'file operation';
|
|
330
|
+
}
|
|
331
|
+
else if (toolNameLower.includes('read')) {
|
|
332
|
+
const filePath = (args['file_path'] || args['path'] || args['target_file']);
|
|
333
|
+
summary = filePath ? `file: ${filePath}` : 'file read';
|
|
334
|
+
}
|
|
335
|
+
else if (toolNameLower.includes('shell') || toolNameLower.includes('bash') || toolNameLower.includes('execute')) {
|
|
336
|
+
const command = (args['command'] || args['cmd']);
|
|
337
|
+
summary = command ? `command: ${command.substring(0, 100)}${command.length > 100 ? '...' : ''}` : 'command execution';
|
|
338
|
+
}
|
|
339
|
+
else if (toolNameLower.includes('search') || toolNameLower.includes('grep')) {
|
|
340
|
+
const query = (args['query'] || args['pattern'] || args['search_query']);
|
|
341
|
+
summary = query ? `query: ${query}` : 'search';
|
|
342
|
+
}
|
|
343
|
+
else if (toolNameLower.includes('list') || toolNameLower.includes('directory')) {
|
|
344
|
+
const path = (args['path'] || args['directory']);
|
|
345
|
+
summary = path ? `path: ${path}` : 'directory listing';
|
|
346
|
+
}
|
|
347
|
+
else if (toolNameLower.includes('todo')) {
|
|
348
|
+
const todos = args['todos'];
|
|
349
|
+
summary = todos ? `${todos.length} todo(s)` : 'todo list';
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
// Generic fallback - show first argument
|
|
353
|
+
const firstKey = Object.keys(args)[0];
|
|
354
|
+
if (firstKey) {
|
|
355
|
+
const firstValue = args[firstKey];
|
|
356
|
+
if (typeof firstValue === 'string') {
|
|
357
|
+
summary = `${firstKey}: ${firstValue.substring(0, 50)}${firstValue.length > 50 ? '...' : ''}`;
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
summary = `${firstKey}: ${JSON.stringify(firstValue).substring(0, 50)}`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return `[Tool Used: ${toolName}${summary ? ` - ${summary}` : ''}]`;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Format a tool result for inclusion in conversation history.
|
|
368
|
+
* Extracts and truncates the actual content from tool results.
|
|
369
|
+
*/
|
|
370
|
+
function formatToolResultForHistory(toolId, result) {
|
|
371
|
+
try {
|
|
372
|
+
// Try to parse result as JSON to extract meaningful content
|
|
373
|
+
const parsed = JSON.parse(result);
|
|
374
|
+
// Handle different tool result formats
|
|
375
|
+
if (parsed.content !== undefined) {
|
|
376
|
+
// ReadFile, WriteFile, Edit tools typically have 'content' field
|
|
377
|
+
const content = typeof parsed.content === 'string' ? parsed.content : JSON.stringify(parsed.content);
|
|
378
|
+
const truncated = content.length > 300 ? content.substring(0, 300) + '...(truncated)' : content;
|
|
379
|
+
return `[Tool Result: ${truncated}]`;
|
|
380
|
+
}
|
|
381
|
+
else if (parsed.data !== undefined) {
|
|
382
|
+
// ReadDataFile and similar tools have 'data' field
|
|
383
|
+
const data = typeof parsed.data === 'string' ? parsed.data : JSON.stringify(parsed.data);
|
|
384
|
+
const truncated = data.length > 300 ? data.substring(0, 300) + '...(truncated)' : data;
|
|
385
|
+
return `[Tool Result: ${truncated}]`;
|
|
386
|
+
}
|
|
387
|
+
else if (parsed.results !== undefined) {
|
|
388
|
+
// WebSearch typically has 'results' array
|
|
389
|
+
const results = Array.isArray(parsed.results) ? parsed.results : [parsed.results];
|
|
390
|
+
const summary = results.slice(0, 3).map((r) => {
|
|
391
|
+
if (typeof r === 'string')
|
|
392
|
+
return r;
|
|
393
|
+
if (r && typeof r === 'object') {
|
|
394
|
+
const obj = r;
|
|
395
|
+
if (obj['title'] && obj['snippet'])
|
|
396
|
+
return `${obj['title']}: ${obj['snippet']}`;
|
|
397
|
+
if (obj['title'])
|
|
398
|
+
return String(obj['title']);
|
|
399
|
+
}
|
|
400
|
+
return JSON.stringify(r);
|
|
401
|
+
}).join('; ');
|
|
402
|
+
const truncated = summary.length > 300 ? summary.substring(0, 300) + '...(truncated)' : summary;
|
|
403
|
+
return `[Tool Result: ${truncated}]`;
|
|
404
|
+
}
|
|
405
|
+
else if (parsed.html !== undefined || parsed.text !== undefined) {
|
|
406
|
+
// WebFetch typically has 'html' or 'text' field
|
|
407
|
+
const content = parsed.text || parsed.html || '';
|
|
408
|
+
const truncated = content.length > 300 ? content.substring(0, 300) + '...(truncated)' : content;
|
|
409
|
+
return `[Tool Result: ${truncated}]`;
|
|
410
|
+
}
|
|
411
|
+
else if (parsed.output !== undefined) {
|
|
412
|
+
// Shell command output
|
|
413
|
+
const output = typeof parsed.output === 'string' ? parsed.output : JSON.stringify(parsed.output);
|
|
414
|
+
const truncated = output.length > 300 ? output.substring(0, 300) + '...(truncated)' : output;
|
|
415
|
+
return `[Tool Result: ${truncated}]`;
|
|
416
|
+
}
|
|
417
|
+
else if (parsed.success !== undefined) {
|
|
418
|
+
// Success/failure result
|
|
419
|
+
const message = parsed.message || parsed.error || (parsed.success ? 'Success' : 'Failed');
|
|
420
|
+
return `[Tool Result: ${message}]`;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
// Generic JSON result
|
|
424
|
+
const jsonStr = JSON.stringify(parsed);
|
|
425
|
+
const truncated = jsonStr.length > 300 ? jsonStr.substring(0, 300) + '...(truncated)' : jsonStr;
|
|
426
|
+
return `[Tool Result: ${truncated}]`;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
// If not JSON, treat as plain text
|
|
431
|
+
const truncated = result.length > 300 ? result.substring(0, 300) + '...(truncated)' : result;
|
|
432
|
+
return `[Tool Result: ${truncated}]`;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
export const useAgentCommandProcessor = (addItemToHistory, setPendingHistoryItem, onExec, onDebugMessage, config, scheduleToolCalls, clearActiveAgentSession, settings) => {
|
|
436
|
+
// Track the current pending item state with a ref for reading
|
|
437
|
+
const currentPendingItemRef = useRef(null);
|
|
438
|
+
// Wrapper to sync the ref when setPendingHistoryItem is called
|
|
439
|
+
const syncedSetPendingHistoryItem = useCallback((newItem) => {
|
|
440
|
+
currentPendingItemRef.current = newItem;
|
|
441
|
+
setPendingHistoryItem(newItem);
|
|
442
|
+
}, [setPendingHistoryItem]);
|
|
443
|
+
// Claude/Gemini streaming handler - exactly like Gemini's handleContentEvent
|
|
444
|
+
const handleStreamingContentEvent = useCallback((textDelta, currentMessageBuffer, userMessageTimestamp) => {
|
|
445
|
+
let newMessageBuffer = currentMessageBuffer + textDelta;
|
|
446
|
+
if (currentPendingItemRef.current?.type !== 'gemini' &&
|
|
447
|
+
currentPendingItemRef.current?.type !== 'gemini_content') {
|
|
448
|
+
if (currentPendingItemRef.current) {
|
|
449
|
+
addItemToHistory(currentPendingItemRef.current, userMessageTimestamp);
|
|
450
|
+
}
|
|
451
|
+
const newItem = { type: 'gemini', text: '' };
|
|
452
|
+
syncedSetPendingHistoryItem(newItem);
|
|
453
|
+
newMessageBuffer = textDelta;
|
|
454
|
+
}
|
|
455
|
+
// Update the existing message with accumulated content (same as Gemini)
|
|
456
|
+
const updatedItem = {
|
|
457
|
+
type: (currentPendingItemRef.current?.type || 'gemini'),
|
|
458
|
+
text: newMessageBuffer,
|
|
459
|
+
};
|
|
460
|
+
syncedSetPendingHistoryItem(updatedItem);
|
|
461
|
+
return newMessageBuffer;
|
|
462
|
+
}, [addItemToHistory, syncedSetPendingHistoryItem]);
|
|
463
|
+
const executeAgent = useCallback((agentType, model, task, abortSignal, options) => {
|
|
464
|
+
// Early return for blackbox agent - it should be handled directly by useGeminiStream
|
|
465
|
+
// to reuse all streaming, tools, and UI components
|
|
466
|
+
if (agentType === 'blackbox') {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const userMessageTimestamp = Date.now();
|
|
470
|
+
// Note: User message is already added to history by the auto-routing logic in useGeminiStream
|
|
471
|
+
// We don't need to add it again here to avoid showing "/agent <task>"
|
|
472
|
+
const executeCommand = async (resolve) => {
|
|
473
|
+
let cumulativeOutput = '';
|
|
474
|
+
let lastProcessedLength = 0;
|
|
475
|
+
// Start with clean slate for real-time streaming (like Gemini does)
|
|
476
|
+
setPendingHistoryItem(null);
|
|
477
|
+
const executor = new AgentExecutor();
|
|
478
|
+
// Check if CLI is installed (since blackbox agent already returned early,
|
|
479
|
+
// we only get here for claude, codex, and gemini agents)
|
|
480
|
+
const isInstalled = await executor.checkAgentCLI(agentType);
|
|
481
|
+
if (!isInstalled) {
|
|
482
|
+
onDebugMessage(`${agentType} CLI not found. Installing...`);
|
|
483
|
+
// Show installation message
|
|
484
|
+
setPendingHistoryItem({
|
|
485
|
+
type: 'gemini_content',
|
|
486
|
+
text: `Installing ${agentType} CLI...\n\nThis may take a few moments...`,
|
|
487
|
+
});
|
|
488
|
+
const installSuccess = await executor.installAgentCLI(agentType, (chunk) => {
|
|
489
|
+
onDebugMessage(`Install output: ${chunk}`);
|
|
490
|
+
}, abortSignal);
|
|
491
|
+
if (!installSuccess) {
|
|
492
|
+
setPendingHistoryItem(null);
|
|
493
|
+
addItemToHistory({
|
|
494
|
+
type: 'error',
|
|
495
|
+
text: `Failed to install ${agentType} CLI. Please install it manually.`,
|
|
496
|
+
}, userMessageTimestamp);
|
|
497
|
+
resolve();
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
// Clear installation message and start agent execution
|
|
501
|
+
setPendingHistoryItem({
|
|
502
|
+
type: 'gemini_content',
|
|
503
|
+
text: '',
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
try {
|
|
507
|
+
// Create message buffer for streaming (like Gemini does)
|
|
508
|
+
let streamingMessageBuffer = '';
|
|
509
|
+
// Track tool calls for conversation history (multi-turn context)
|
|
510
|
+
const toolCallsHistory = [];
|
|
511
|
+
// Execute the agent with optimized streaming output
|
|
512
|
+
const executionPromise = executor.executeAgent({
|
|
513
|
+
agentType,
|
|
514
|
+
model,
|
|
515
|
+
task,
|
|
516
|
+
cwd: config.getTargetDir(),
|
|
517
|
+
pastMessages: options?.pastMessages,
|
|
518
|
+
settings,
|
|
519
|
+
onOutput: (_, chunk) => {
|
|
520
|
+
// Handle Claude, Codex, and Gemini streaming differently - use direct event processing like Gemini
|
|
521
|
+
if (agentType === 'claude' || agentType === 'codex' || agentType === 'gemini') {
|
|
522
|
+
// Add chunk to cumulative output for JSON buffer management
|
|
523
|
+
cumulativeOutput += chunk;
|
|
524
|
+
// Try to parse complete JSON objects from the buffer
|
|
525
|
+
const jsonLines = cumulativeOutput.split('\n');
|
|
526
|
+
let processedUpTo = 0;
|
|
527
|
+
for (let i = 0; i < jsonLines.length - 1; i++) { // Skip last line as it might be incomplete
|
|
528
|
+
const line = jsonLines[i].trim();
|
|
529
|
+
if (line) {
|
|
530
|
+
const parsed = agentType === 'claude'
|
|
531
|
+
? parseClaudeStreamingOutput(line)
|
|
532
|
+
: agentType === 'codex'
|
|
533
|
+
? parseCodexStreamingOutput(line)
|
|
534
|
+
: parseGeminiStreamingOutput(line);
|
|
535
|
+
if (parsed.type === 'delta' && parsed.text) {
|
|
536
|
+
streamingMessageBuffer = handleStreamingContentEvent(parsed.text, streamingMessageBuffer, userMessageTimestamp);
|
|
537
|
+
}
|
|
538
|
+
else if (parsed.type === 'tool_use' && parsed.toolCall) {
|
|
539
|
+
// Track tool call for conversation history (multi-turn context)
|
|
540
|
+
toolCallsHistory.push(formatToolCallForHistory(parsed.toolCall));
|
|
541
|
+
// Create a proper tool call display object for the UI
|
|
542
|
+
// Format it like Gemini tools: name is the tool, description is the main argument
|
|
543
|
+
let toolDescription = '';
|
|
544
|
+
if (parsed.toolCall.args) {
|
|
545
|
+
// Extract the most relevant argument for the description based on tool type
|
|
546
|
+
const args = parsed.toolCall.args;
|
|
547
|
+
const toolName = parsed.toolCall.name.toLowerCase();
|
|
548
|
+
if (toolName.includes('websearch') || toolName.includes('web_search')) {
|
|
549
|
+
toolDescription = (args['query'] || args['search_query']) || 'web search';
|
|
550
|
+
}
|
|
551
|
+
else if (toolName.includes('todowrite') || toolName.includes('todo_write')) {
|
|
552
|
+
const todos = args['todos'];
|
|
553
|
+
if (todos && todos.length > 0) {
|
|
554
|
+
toolDescription = `${todos.length} todo${todos.length > 1 ? 's' : ''}: ${todos[0].content}${todos.length > 1 ? '...' : ''}`;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
toolDescription = 'todo list';
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
else if (toolName.includes('edit') || toolName.includes('write')) {
|
|
561
|
+
toolDescription = (args['file_path'] || args['path']) || 'file operation';
|
|
562
|
+
}
|
|
563
|
+
else if (toolName.includes('read') || toolName.includes('list')) {
|
|
564
|
+
toolDescription = (args['file_path'] || args['path'] || args['directory']) || 'file access';
|
|
565
|
+
}
|
|
566
|
+
else if (toolName.includes('shell') || toolName.includes('execute')) {
|
|
567
|
+
toolDescription = (args['command'] || args['cmd']) || 'command execution';
|
|
568
|
+
}
|
|
569
|
+
else if (args['file_path'] || args['path']) {
|
|
570
|
+
toolDescription = (args['file_path'] || args['path']);
|
|
571
|
+
}
|
|
572
|
+
else if (args['command']) {
|
|
573
|
+
toolDescription = args['command'];
|
|
574
|
+
}
|
|
575
|
+
else if (args['query']) {
|
|
576
|
+
toolDescription = args['query'];
|
|
577
|
+
}
|
|
578
|
+
else if (args['content']) {
|
|
579
|
+
toolDescription = 'content provided';
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
// Fallback to first argument value
|
|
583
|
+
const firstValue = Object.values(args)[0];
|
|
584
|
+
toolDescription = typeof firstValue === 'string' ? firstValue : 'execution';
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
// Create appropriate result display based on tool type to match ToolMessage expectations
|
|
588
|
+
let resultDisplay = undefined;
|
|
589
|
+
const command = parsed.toolCall.args?.['command'] || '';
|
|
590
|
+
const toolName = parsed.toolCall.name.toLowerCase();
|
|
591
|
+
// Handle special Codex commands with enhanced formatting
|
|
592
|
+
if (command.startsWith('update_plan')) {
|
|
593
|
+
// Extract and format plan data
|
|
594
|
+
const planMatch = command.match(/update_plan\s+'(.+)'/);
|
|
595
|
+
if (planMatch) {
|
|
596
|
+
try {
|
|
597
|
+
const planData = JSON.parse(planMatch[1]);
|
|
598
|
+
if (Array.isArray(planData)) {
|
|
599
|
+
const todoDisplay = {
|
|
600
|
+
type: 'todo_list',
|
|
601
|
+
todos: planData.map((item, index) => ({
|
|
602
|
+
id: `plan-${index}`,
|
|
603
|
+
content: item.description || 'Unknown task',
|
|
604
|
+
status: item.status === 'completed' ? 'completed' :
|
|
605
|
+
item.status === 'in_progress' ? 'in_progress' :
|
|
606
|
+
'pending'
|
|
607
|
+
})),
|
|
608
|
+
};
|
|
609
|
+
resultDisplay = todoDisplay;
|
|
610
|
+
toolDescription = `Update project plan (${planData.length} tasks)`;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
catch (_e) {
|
|
614
|
+
// Fallback to showing the command
|
|
615
|
+
toolDescription = 'Update project plan';
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
else if (command.includes('apply_patch')) {
|
|
620
|
+
// Extract patch content and format as proper diff like Edit tool
|
|
621
|
+
const patchMatch = command.match(/apply_patch\s+<<'PATCH'\n([\s\S]*?)\nPATCH/);
|
|
622
|
+
if (patchMatch) {
|
|
623
|
+
const patchContent = patchMatch[1];
|
|
624
|
+
// Extract file information from patch
|
|
625
|
+
const addFileMatch = patchContent.match(/\*\*\* Add File: (.+)/);
|
|
626
|
+
const updateFileMatch = patchContent.match(/\*\*\* Update File: (.+)/);
|
|
627
|
+
const modifyFileMatch = patchContent.match(/\*\*\* Modify File: (.+)/);
|
|
628
|
+
if (addFileMatch) {
|
|
629
|
+
const fileName = addFileMatch[1];
|
|
630
|
+
// Extract new file content from patch (lines starting with +)
|
|
631
|
+
const contentLines = patchContent.split('\n').filter(line => line.startsWith('+') && !line.startsWith('+++'));
|
|
632
|
+
const newContent = contentLines.map(line => line.substring(1)).join('\n');
|
|
633
|
+
// Create proper unified diff format like Edit tool for new files
|
|
634
|
+
const baseName = fileName.split('/').pop() || fileName;
|
|
635
|
+
const newContentLines = newContent.split('\n');
|
|
636
|
+
const unifiedDiff = `--- /dev/null
|
|
637
|
+
+++ ${baseName}
|
|
638
|
+
@@ -0,0 +1,${newContentLines.length} @@
|
|
639
|
+
${newContentLines.map(line => `+${line}`).join('\n')}`;
|
|
640
|
+
const fileDiff = {
|
|
641
|
+
fileDiff: unifiedDiff,
|
|
642
|
+
fileName: baseName,
|
|
643
|
+
originalContent: '',
|
|
644
|
+
newContent,
|
|
645
|
+
};
|
|
646
|
+
resultDisplay = fileDiff;
|
|
647
|
+
toolDescription = `Add file: ${fileName}`;
|
|
648
|
+
}
|
|
649
|
+
else if (updateFileMatch || modifyFileMatch) {
|
|
650
|
+
const fileName = (updateFileMatch || modifyFileMatch)[1];
|
|
651
|
+
// Parse the patch to extract old and new content
|
|
652
|
+
const patchLines = patchContent.split('\n');
|
|
653
|
+
let oldContent = '';
|
|
654
|
+
let newContent = '';
|
|
655
|
+
let inOldSection = false;
|
|
656
|
+
let inNewSection = false;
|
|
657
|
+
for (const line of patchLines) {
|
|
658
|
+
if (line.startsWith('@@')) {
|
|
659
|
+
// Start of a diff section
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
else if (line.startsWith('-') && !line.startsWith('---')) {
|
|
663
|
+
// Old content (removed lines)
|
|
664
|
+
oldContent += line.substring(1) + '\n';
|
|
665
|
+
inOldSection = true;
|
|
666
|
+
}
|
|
667
|
+
else if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
668
|
+
// New content (added lines)
|
|
669
|
+
newContent += line.substring(1) + '\n';
|
|
670
|
+
inNewSection = true;
|
|
671
|
+
}
|
|
672
|
+
else if (!line.startsWith('***') && !line.startsWith('---') && !line.startsWith('+++')) {
|
|
673
|
+
// Context lines (unchanged)
|
|
674
|
+
if (inOldSection)
|
|
675
|
+
oldContent += line + '\n';
|
|
676
|
+
if (inNewSection)
|
|
677
|
+
newContent += line + '\n';
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// If we couldn't parse properly, fall back to showing the raw patch
|
|
681
|
+
if (!oldContent && !newContent) {
|
|
682
|
+
// Try to reconstruct from the patch format
|
|
683
|
+
const beforeMatch = patchContent.match(/(?:^|\n)([^@\-+*].*)(?=\n[-+@])/gm);
|
|
684
|
+
const afterMatch = patchContent.match(/(?:^|\n)\+(.*)$/gm);
|
|
685
|
+
if (beforeMatch && afterMatch) {
|
|
686
|
+
oldContent = beforeMatch.join('\n');
|
|
687
|
+
newContent = afterMatch.map(line => line.substring(1)).join('\n');
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
// Create a proper unified diff format like Edit tool uses
|
|
691
|
+
const baseName = fileName.split('/').pop() || fileName;
|
|
692
|
+
const unifiedDiff = `--- ${baseName}
|
|
693
|
+
+++ ${baseName}
|
|
694
|
+
@@ -1,${oldContent.split('\n').length} +1,${newContent.split('\n').length} @@
|
|
695
|
+
${oldContent.split('\n').map(line => `-${line}`).join('\n')}
|
|
696
|
+
${newContent.split('\n').map(line => `+${line}`).join('\n')}`;
|
|
697
|
+
const fileDiff = {
|
|
698
|
+
fileDiff: unifiedDiff,
|
|
699
|
+
fileName: baseName,
|
|
700
|
+
originalContent: oldContent.trim(),
|
|
701
|
+
newContent: newContent.trim(),
|
|
702
|
+
};
|
|
703
|
+
resultDisplay = fileDiff;
|
|
704
|
+
toolDescription = `Update file: ${fileName}`;
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
toolDescription = 'Apply patch';
|
|
708
|
+
// Fallback for unrecognized patch format
|
|
709
|
+
resultDisplay = `🔧 Applying patch
|
|
710
|
+
|
|
711
|
+
${patchContent.substring(0, 500)}${patchContent.length > 500 ? '...' : ''}`;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
else if (toolName.includes('edit') || toolName.includes('replace') || parsed.toolCall.name === 'Edit' || parsed.toolCall.name === 'replace') {
|
|
716
|
+
// For edit/replace tools, create a FileDiff object that ToolMessage expects
|
|
717
|
+
const args = parsed.toolCall.args;
|
|
718
|
+
if (args['old_string'] && args['new_string']) {
|
|
719
|
+
const filePath = (args['file_path'] || 'file');
|
|
720
|
+
const oldString = args['old_string'];
|
|
721
|
+
const newString = args['new_string'];
|
|
722
|
+
const fileName = filePath.split('/').pop() || filePath;
|
|
723
|
+
// Create a proper unified diff format that DiffRenderer expects (same as Edit tool)
|
|
724
|
+
// Use the same logic as the Edit tool in packages/core/src/tools/edit.ts
|
|
725
|
+
const oldLines = oldString.split('\n');
|
|
726
|
+
const newLines = newString.split('\n');
|
|
727
|
+
// Create unified diff format similar to what Edit tool generates
|
|
728
|
+
const diffContent = `--- ${fileName}
|
|
729
|
+
+++ ${fileName}
|
|
730
|
+
@@ -1,${oldLines.length} +1,${newLines.length} @@
|
|
731
|
+
${oldLines.map(line => `-${line}`).join('\n')}
|
|
732
|
+
${newLines.map(line => `+${line}`).join('\n')}`;
|
|
733
|
+
const fileDiff = {
|
|
734
|
+
fileDiff: diffContent,
|
|
735
|
+
fileName,
|
|
736
|
+
originalContent: oldString,
|
|
737
|
+
newContent: newString,
|
|
738
|
+
};
|
|
739
|
+
resultDisplay = fileDiff;
|
|
740
|
+
// Update tool description to be more specific
|
|
741
|
+
const instruction = args['instruction'];
|
|
742
|
+
toolDescription = instruction ? `${fileName}: ${instruction}` : `Replace in ${fileName}`;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
else if (toolName.includes('write') || parsed.toolCall.name === 'write_file') {
|
|
746
|
+
// For write_file tools, create a FileDiff object that ToolMessage expects
|
|
747
|
+
const args = parsed.toolCall.args;
|
|
748
|
+
if (args['content'] && args['file_path']) {
|
|
749
|
+
const filePath = args['file_path'];
|
|
750
|
+
const newContent = args['content'];
|
|
751
|
+
const fileName = filePath.split('/').pop() || filePath;
|
|
752
|
+
// Create a unified diff format for new file (like Edit tool does for new files)
|
|
753
|
+
const newContentLines = newContent.split('\n');
|
|
754
|
+
const diffContent = `--- /dev/null
|
|
755
|
+
+++ ${fileName}
|
|
756
|
+
@@ -0,0 +1,${newContentLines.length} @@
|
|
757
|
+
${newContentLines.map(line => `+${line}`).join('\n')}`;
|
|
758
|
+
const fileDiff = {
|
|
759
|
+
fileDiff: diffContent,
|
|
760
|
+
fileName,
|
|
761
|
+
originalContent: '', // Assume new file for now
|
|
762
|
+
newContent,
|
|
763
|
+
};
|
|
764
|
+
resultDisplay = fileDiff;
|
|
765
|
+
toolDescription = `Write file: ${fileName}`;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
else if (toolName.includes('todowrite') || toolName.includes('todo_write')) {
|
|
769
|
+
// For todo tools, create a TodoResultDisplay object that ToolMessage expects
|
|
770
|
+
const args = parsed.toolCall.args;
|
|
771
|
+
if (args['todos'] && Array.isArray(args['todos'])) {
|
|
772
|
+
const todoDisplay = {
|
|
773
|
+
type: 'todo_list',
|
|
774
|
+
todos: args['todos'],
|
|
775
|
+
};
|
|
776
|
+
resultDisplay = todoDisplay;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
else if (toolName.includes('websearch') || toolName.includes('web_search')) {
|
|
780
|
+
// For web search tools, create a formatted string display
|
|
781
|
+
const args = parsed.toolCall.args;
|
|
782
|
+
const query = (args['query'] || args['search_query']);
|
|
783
|
+
if (query) {
|
|
784
|
+
resultDisplay = `🔍 Searching for: "${query}"
|
|
785
|
+
|
|
786
|
+
This tool will search the web and return relevant results.`;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const toolCallDisplay = {
|
|
790
|
+
callId: parsed.toolCall.callId,
|
|
791
|
+
name: parsed.toolCall.name,
|
|
792
|
+
description: toolDescription,
|
|
793
|
+
status: ToolCallStatus.Executing, // Show as executing initially
|
|
794
|
+
resultDisplay,
|
|
795
|
+
confirmationDetails: undefined,
|
|
796
|
+
};
|
|
797
|
+
// Add the current text content to history first
|
|
798
|
+
if (streamingMessageBuffer.trim()) {
|
|
799
|
+
addItemToHistory({ type: 'gemini', text: streamingMessageBuffer }, userMessageTimestamp);
|
|
800
|
+
streamingMessageBuffer = '';
|
|
801
|
+
}
|
|
802
|
+
// Add the tool call as a tool group to history
|
|
803
|
+
const toolGroup = {
|
|
804
|
+
type: 'tool_group',
|
|
805
|
+
tools: [toolCallDisplay],
|
|
806
|
+
};
|
|
807
|
+
addItemToHistory(toolGroup, userMessageTimestamp);
|
|
808
|
+
// Reset the pending item for continued streaming
|
|
809
|
+
syncedSetPendingHistoryItem({ type: 'gemini', text: '' });
|
|
810
|
+
}
|
|
811
|
+
else if (parsed.type === 'tool_result' && parsed.toolResult) {
|
|
812
|
+
// Track tool result for conversation history (multi-turn context)
|
|
813
|
+
toolCallsHistory.push(formatToolResultForHistory(parsed.toolResult.toolId, parsed.toolResult.result));
|
|
814
|
+
// Update the last tool call with the result
|
|
815
|
+
// For now, we'll just continue with text streaming since updating
|
|
816
|
+
// the tool result in history is complex. The tool result will be
|
|
817
|
+
// visible in the agent's final output.
|
|
818
|
+
}
|
|
819
|
+
processedUpTo = cumulativeOutput.indexOf(line) + line.length + 1;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
// Keep only unprocessed part of the buffer
|
|
823
|
+
if (processedUpTo > 0) {
|
|
824
|
+
cumulativeOutput = cumulativeOutput.substring(processedUpTo);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
// Handle other agents with cumulative/incremental detection
|
|
829
|
+
if (cumulativeOutput && chunk.startsWith(cumulativeOutput)) {
|
|
830
|
+
// This is a cumulative chunk - use it directly instead of appending
|
|
831
|
+
cumulativeOutput = chunk;
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
// This is an incremental chunk - append it
|
|
835
|
+
cumulativeOutput += chunk;
|
|
836
|
+
}
|
|
837
|
+
// Parse agent output for tool calls and schedule them properly
|
|
838
|
+
if (scheduleToolCalls) {
|
|
839
|
+
const { newProcessedLength, messageContent } = parseAgentOutputForToolCalls(cumulativeOutput, lastProcessedLength, scheduleToolCalls, abortSignal, agentType);
|
|
840
|
+
lastProcessedLength = newProcessedLength;
|
|
841
|
+
// Update UI with the message content (excluding tool calls)
|
|
842
|
+
setPendingHistoryItem((item) => ({
|
|
843
|
+
type: item?.type,
|
|
844
|
+
text: messageContent || cumulativeOutput,
|
|
845
|
+
}));
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
// Fallback: just show the raw output if no tool scheduler available
|
|
849
|
+
setPendingHistoryItem((item) => ({
|
|
850
|
+
type: item?.type,
|
|
851
|
+
text: cumulativeOutput,
|
|
852
|
+
}));
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
onComplete: (_, exitCode) => {
|
|
857
|
+
onDebugMessage(`${agentType} agent completed with exit code: ${exitCode}`);
|
|
858
|
+
},
|
|
859
|
+
onError: (_, error) => {
|
|
860
|
+
onDebugMessage(`${agentType} agent error: ${error.message}`);
|
|
861
|
+
},
|
|
862
|
+
abortSignal,
|
|
863
|
+
});
|
|
864
|
+
// Wait for execution to complete
|
|
865
|
+
const result = await executionPromise;
|
|
866
|
+
setPendingHistoryItem(null);
|
|
867
|
+
// Get final output
|
|
868
|
+
let finalOutput;
|
|
869
|
+
if (agentType === 'claude' || agentType === 'codex' || agentType === 'gemini') {
|
|
870
|
+
finalOutput = streamingMessageBuffer || '(Agent produced no output)';
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
finalOutput = cumulativeOutput || '(Agent produced no output)';
|
|
874
|
+
}
|
|
875
|
+
if (result.error) {
|
|
876
|
+
// Even on error, save the partial response to conversation history
|
|
877
|
+
// This ensures multi-turn works even if the request failed mid-stream
|
|
878
|
+
if (options?.onResponseComplete && (streamingMessageBuffer.trim() || toolCallsHistory.length > 0)) {
|
|
879
|
+
// Build response with tool call history for multi-turn context
|
|
880
|
+
const toolCallsSummary = toolCallsHistory.length > 0
|
|
881
|
+
? '\n\n--- Tool Activity ---\n' + toolCallsHistory.join('\n') + '\n--- End Tool Activity ---\n\n'
|
|
882
|
+
: '';
|
|
883
|
+
options.onResponseComplete(toolCallsSummary + streamingMessageBuffer.trim() + '\n\n[Response ended with error]');
|
|
884
|
+
}
|
|
885
|
+
// Clear active agent session when agent exits with error
|
|
886
|
+
if (clearActiveAgentSession) {
|
|
887
|
+
clearActiveAgentSession();
|
|
888
|
+
}
|
|
889
|
+
finalOutput = `Error: ${result.error.message}\n\n${finalOutput}`;
|
|
890
|
+
addItemToHistory({
|
|
891
|
+
type: 'error',
|
|
892
|
+
text: finalOutput,
|
|
893
|
+
}, userMessageTimestamp);
|
|
894
|
+
}
|
|
895
|
+
else if (abortSignal.aborted) {
|
|
896
|
+
// Even when cancelled, save the partial response to conversation history
|
|
897
|
+
// This ensures multi-turn works even if the request was cancelled mid-stream
|
|
898
|
+
if (options?.onResponseComplete && (streamingMessageBuffer.trim() || toolCallsHistory.length > 0)) {
|
|
899
|
+
// Build response with tool call history for multi-turn context
|
|
900
|
+
const toolCallsSummary = toolCallsHistory.length > 0
|
|
901
|
+
? '\n\n--- Tool Activity ---\n' + toolCallsHistory.join('\n') + '\n--- End Tool Activity ---\n\n'
|
|
902
|
+
: '';
|
|
903
|
+
options.onResponseComplete(toolCallsSummary + streamingMessageBuffer.trim() + '\n\n[Response was cancelled]');
|
|
904
|
+
}
|
|
905
|
+
finalOutput = `Agent execution was cancelled.\n\n${finalOutput}`;
|
|
906
|
+
addItemToHistory({
|
|
907
|
+
type: 'error',
|
|
908
|
+
text: finalOutput,
|
|
909
|
+
}, userMessageTimestamp);
|
|
910
|
+
}
|
|
911
|
+
else if (result.exitCode !== 0 && result.exitCode !== null) {
|
|
912
|
+
// Even on non-zero exit, save the partial response to conversation history
|
|
913
|
+
// This ensures multi-turn works even if the agent exited with an error
|
|
914
|
+
if (options?.onResponseComplete && (streamingMessageBuffer.trim() || toolCallsHistory.length > 0)) {
|
|
915
|
+
// Build response with tool call history for multi-turn context
|
|
916
|
+
const toolCallsSummary = toolCallsHistory.length > 0
|
|
917
|
+
? '\n\n--- Tool Activity ---\n' + toolCallsHistory.join('\n') + '\n--- End Tool Activity ---\n\n'
|
|
918
|
+
: '';
|
|
919
|
+
options.onResponseComplete(toolCallsSummary + streamingMessageBuffer.trim() + `\n\n[Agent exited with code ${result.exitCode}]`);
|
|
920
|
+
}
|
|
921
|
+
// Clear active agent session when agent exits with non-zero code
|
|
922
|
+
if (clearActiveAgentSession) {
|
|
923
|
+
clearActiveAgentSession();
|
|
924
|
+
}
|
|
925
|
+
finalOutput = `Agent exited with code ${result.exitCode}.\n\n${finalOutput}`;
|
|
926
|
+
addItemToHistory({
|
|
927
|
+
type: 'error',
|
|
928
|
+
text: finalOutput,
|
|
929
|
+
}, userMessageTimestamp);
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
// Success - add as gemini response (only if there's actual text content)
|
|
933
|
+
if (finalOutput && finalOutput !== '(Agent produced no output)') {
|
|
934
|
+
addItemToHistory({
|
|
935
|
+
type: 'gemini',
|
|
936
|
+
text: finalOutput,
|
|
937
|
+
}, userMessageTimestamp);
|
|
938
|
+
// Call the onResponseComplete callback to update conversation history
|
|
939
|
+
// Include tool call history for multi-turn context
|
|
940
|
+
if (options?.onResponseComplete) {
|
|
941
|
+
const toolCallsSummary = toolCallsHistory.length > 0
|
|
942
|
+
? '\n\n--- Tool Activity ---\n' + toolCallsHistory.join('\n') + '\n--- End Tool Activity ---\n\n'
|
|
943
|
+
: '';
|
|
944
|
+
options.onResponseComplete(toolCallsSummary + finalOutput);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
resolve();
|
|
949
|
+
}
|
|
950
|
+
catch (err) {
|
|
951
|
+
setPendingHistoryItem(null);
|
|
952
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
953
|
+
addItemToHistory({
|
|
954
|
+
type: 'error',
|
|
955
|
+
text: `An unexpected error occurred during ${agentType} agent execution: ${errorMessage}`,
|
|
956
|
+
}, userMessageTimestamp);
|
|
957
|
+
resolve();
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
const execPromise = new Promise((resolve) => {
|
|
961
|
+
executeCommand(resolve);
|
|
962
|
+
});
|
|
963
|
+
onExec(execPromise);
|
|
964
|
+
}, [addItemToHistory, onExec, setPendingHistoryItem, onDebugMessage, config, handleStreamingContentEvent, syncedSetPendingHistoryItem, scheduleToolCalls]);
|
|
965
|
+
return { executeAgent };
|
|
966
|
+
};
|
|
967
|
+
//# sourceMappingURL=useAgentCommandProcessor.js.map
|