@alpaca-editor/core 1.0.4049 → 1.0.4053
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/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/editor/Terminal.js +3 -3
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.js +2 -2
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentHistory.d.ts +4 -4
- package/dist/editor/ai/AgentHistory.js +1 -1
- package/dist/editor/ai/AgentHistory.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +4 -0
- package/dist/editor/ai/AgentTerminal.js +753 -0
- package/dist/editor/ai/AgentTerminal.js.map +1 -0
- package/dist/editor/ai/Agents.d.ts +1 -3
- package/dist/editor/ai/Agents.js +213 -353
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiPromptPopover.js +2 -2
- package/dist/editor/ai/AiPromptPopover.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +0 -1
- package/dist/editor/ai/AiResponseMessage.js +23 -143
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +5 -23
- package/dist/editor/ai/AiTerminal.js +81 -824
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/ai/DancingDots.d.ts +1 -0
- package/dist/editor/ai/DancingDots.js +6 -0
- package/dist/editor/ai/DancingDots.js.map +1 -0
- package/dist/editor/ai/ToolCallDisplay.d.ts +37 -0
- package/dist/editor/ai/ToolCallDisplay.js +154 -0
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -0
- package/dist/editor/client/EditorClient.js +5 -1
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +23 -30
- package/dist/editor/services/agentService.js +62 -124
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.js +1 -0
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.js +8 -6
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/Section.js +4 -3
- package/dist/editor/ui/Section.js.map +1 -1
- package/dist/editor/utils.d.ts +4 -0
- package/dist/editor/utils.js +23 -0
- package/dist/editor/utils.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +18 -33
- package/package.json +1 -1
- package/src/components/ui/textarea.tsx +1 -1
- package/src/editor/Terminal.tsx +4 -4
- package/src/editor/ai/AgentCostDisplay.tsx +7 -11
- package/src/editor/ai/AgentHistory.tsx +7 -9
- package/src/editor/ai/AgentTerminal.tsx +1094 -0
- package/src/editor/ai/Agents.tsx +340 -477
- package/src/editor/ai/AiPromptPopover.tsx +2 -2
- package/src/editor/ai/AiResponseMessage.tsx +85 -366
- package/src/editor/ai/AiTerminal.tsx +142 -1213
- package/src/editor/ai/DancingDots.tsx +14 -0
- package/src/editor/ai/ToolCallDisplay.tsx +363 -0
- package/src/editor/client/EditorClient.tsx +6 -1
- package/src/editor/services/agentService.ts +89 -162
- package/src/editor/sidebar/GraphQL.tsx +1 -0
- package/src/editor/sidebar/ViewSelector.tsx +82 -57
- package/src/editor/ui/Section.tsx +4 -3
- package/src/editor/utils.ts +29 -0
- package/src/revision.ts +2 -2
- package/dist/editor/ai/EditorAiTerminal.d.ts +0 -6
- package/dist/editor/ai/EditorAiTerminal.js +0 -7
- package/dist/editor/ai/EditorAiTerminal.js.map +0 -1
- package/src/editor/ai/EditorAiTerminal.tsx +0 -23
|
@@ -4,27 +4,13 @@ import { useEffect, useRef, useState } from "react";
|
|
|
4
4
|
import { TerminalService } from "primereact/terminalservice";
|
|
5
5
|
import { Terminal } from "../Terminal";
|
|
6
6
|
import { useEditContext } from "../client/editContext";
|
|
7
|
-
import {
|
|
8
|
-
import { ChevronDown } from "lucide-react";
|
|
7
|
+
import { Dropdown } from "primereact/dropdown";
|
|
9
8
|
import { WizardIcon } from "../ui/Icons";
|
|
10
9
|
import { AiResponseMessage } from "./AiResponseMessage";
|
|
11
|
-
import { loadAiProfiles } from "../services/aiService";
|
|
12
|
-
import { startAgent, connectToAgentStream, cancelAgent, checkAgentState, convertAgentMessagesToTerminalFormat, AgentStreamMessageType, } from "../services/agentService";
|
|
10
|
+
import { loadAiProfiles, executePrompt, } from "../services/aiService";
|
|
13
11
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
14
|
-
import { Settings
|
|
15
|
-
|
|
16
|
-
import { Tooltip, TooltipContent, TooltipTrigger, } from "../../components/ui/tooltip";
|
|
17
|
-
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
18
|
-
// Helper function to format message content
|
|
19
|
-
function formatMessageContent(content) {
|
|
20
|
-
return (content
|
|
21
|
-
?.trim()
|
|
22
|
-
?.replaceAll("\n", "<br>")
|
|
23
|
-
?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") // Bold markdown
|
|
24
|
-
?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" class="text-blue-500" rel="noopener noreferrer">$1</a>') || "" // Links
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
export function AiTerminal({ closeButton, createAiContext, defaultProfile, options, onAgentNameUpdate, }) {
|
|
12
|
+
import { Settings } from "lucide-react";
|
|
13
|
+
export function AiTerminal({ closeButton, createAiContext, defaultProfile, options, }) {
|
|
28
14
|
const editContext = useEditContext();
|
|
29
15
|
const [showPredefined, setShowPredefined] = useState(false);
|
|
30
16
|
if (!editContext)
|
|
@@ -36,25 +22,12 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
36
22
|
const [profiles, setProfiles] = useState([]);
|
|
37
23
|
const [activeProfile, setActiveProfile] = useState();
|
|
38
24
|
const [initialPromptExecuted, setInitialPromptExecuted] = useState(false);
|
|
39
|
-
const [agentId
|
|
25
|
+
const [agentId] = useState(() => crypto.randomUUID());
|
|
40
26
|
const selection = editContext.selection;
|
|
41
27
|
const terminalRef = useRef(null);
|
|
42
28
|
const [responseMessages, setResponseMessages] = useState(options?.initialMessages || []);
|
|
43
29
|
const [showSettings, setShowSettings] = useState(false);
|
|
44
30
|
const settingsRef = useRef(null);
|
|
45
|
-
const [isRunning, setIsRunning] = useState(false);
|
|
46
|
-
const abortControllerRef = useRef(null);
|
|
47
|
-
const [terminalError, setTerminalError] = useState(null);
|
|
48
|
-
// Debug function - expose globally for testing
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
window.testAiTerminalError = (errorMsg = "Test error message") => {
|
|
51
|
-
console.log("Manually triggering error:", errorMsg);
|
|
52
|
-
setTerminalError(errorMsg);
|
|
53
|
-
};
|
|
54
|
-
return () => {
|
|
55
|
-
delete window.testAiTerminalError;
|
|
56
|
-
};
|
|
57
|
-
}, []);
|
|
58
31
|
useEffect(() => {
|
|
59
32
|
if (options?.initialPrompt && !initialPromptExecuted && model) {
|
|
60
33
|
// Set the initial prompt text into the terminal's state.
|
|
@@ -78,293 +51,6 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
78
51
|
}
|
|
79
52
|
fetchProfiles();
|
|
80
53
|
}, [editContext?.currentItemDescriptor]);
|
|
81
|
-
// Agent reconnection logic
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
async function checkAndReconnectAgent() {
|
|
84
|
-
if (!editContext || !editContext.sessionId || !agentId) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const context = createAiContext({ editContext });
|
|
88
|
-
try {
|
|
89
|
-
const agentState = await checkAgentState(agentId, context);
|
|
90
|
-
if (agentState.exists && agentState.agent) {
|
|
91
|
-
const agent = agentState.agent;
|
|
92
|
-
// Convert and set persisted messages if not already set via options
|
|
93
|
-
if (agent.messages &&
|
|
94
|
-
(!options?.initialMessages || options.initialMessages.length === 0)) {
|
|
95
|
-
const terminalMessages = convertAgentMessagesToTerminalFormat(agent.messages);
|
|
96
|
-
setMessages(terminalMessages);
|
|
97
|
-
setResponseMessages(terminalMessages);
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
}
|
|
101
|
-
// Set cost information if available
|
|
102
|
-
if (agent.totalCost !== undefined) {
|
|
103
|
-
setResponse((prev) => ({
|
|
104
|
-
...prev,
|
|
105
|
-
totalCost: agent.totalCost,
|
|
106
|
-
totalInputTokenCost: agent.totalInputTokenCost,
|
|
107
|
-
totalOutputTokenCost: agent.totalOutputTokenCost,
|
|
108
|
-
totalCachedTokenCost: agent.totalCachedInputTokenCost,
|
|
109
|
-
totalInputTokens: agent.totalInputTokens,
|
|
110
|
-
totalOutputTokens: agent.totalOutputTokens,
|
|
111
|
-
totalCachedTokens: agent.totalCachedInputTokens,
|
|
112
|
-
}));
|
|
113
|
-
}
|
|
114
|
-
// If agent is still running, reconnect to the stream
|
|
115
|
-
if (agentState.isRunning) {
|
|
116
|
-
setIsRunning(true);
|
|
117
|
-
await reconnectToRunningAgent(agentId, context);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
setTerminalError(`Reconnection failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
// Only try to reconnect if we have an agentId and edit context
|
|
130
|
-
if (agentId && editContext && editContext.sessionId) {
|
|
131
|
-
checkAndReconnectAgent();
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
}
|
|
135
|
-
}, [agentId, editContext?.sessionId]); // Run when agentId or session becomes available
|
|
136
|
-
// Function to reconnect to a running agent stream
|
|
137
|
-
async function reconnectToRunningAgent(agentId, context) {
|
|
138
|
-
try {
|
|
139
|
-
const abortController = new AbortController();
|
|
140
|
-
abortControllerRef.current = abortController;
|
|
141
|
-
setIsRunning(true);
|
|
142
|
-
let accumulatedResponse = {
|
|
143
|
-
messages: [...messages],
|
|
144
|
-
editOperations: [],
|
|
145
|
-
numInputTokens: 0,
|
|
146
|
-
numOutputTokens: 0,
|
|
147
|
-
numCachedTokens: 0,
|
|
148
|
-
state: "running",
|
|
149
|
-
};
|
|
150
|
-
let completionProcessed = false;
|
|
151
|
-
await connectToAgentStream(agentId, context, (streamMessage) => {
|
|
152
|
-
if (!streamMessage || !streamMessage.type) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
// Reuse stream message handling, but pipe updates into Terminal via TerminalService
|
|
156
|
-
try {
|
|
157
|
-
const terminalCallback = (text, finished) => {
|
|
158
|
-
TerminalService.emit("response", { text, finished });
|
|
159
|
-
};
|
|
160
|
-
const result = handleStreamMessage(streamMessage, accumulatedResponse, completionProcessed, context, terminalCallback);
|
|
161
|
-
if (result) {
|
|
162
|
-
accumulatedResponse = result.accumulatedResponse;
|
|
163
|
-
completionProcessed = result.completionProcessed;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
catch (error) { }
|
|
167
|
-
}, abortController.signal);
|
|
168
|
-
setIsRunning(false);
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
setIsRunning(false);
|
|
172
|
-
// Check if it's an abort error (user cancelled)
|
|
173
|
-
const isAbortError = error instanceof Error &&
|
|
174
|
-
(error.name === "AbortError" ||
|
|
175
|
-
error.message.toLowerCase().includes("abort"));
|
|
176
|
-
if (!isAbortError) {
|
|
177
|
-
const errorMessage = `Failed to reconnect to agent: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
178
|
-
setTerminalError(errorMessage);
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Extract stream message handling logic into a separate function for reuse
|
|
185
|
-
function handleStreamMessage(streamMessage, accumulatedResponse, completionProcessed, context, terminalCallback) {
|
|
186
|
-
// Safety check for valid stream message
|
|
187
|
-
if (!streamMessage || !streamMessage.type) {
|
|
188
|
-
console.error("Invalid stream message received:", streamMessage);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
console.log("Processing stream message:", streamMessage.type, streamMessage);
|
|
192
|
-
// Handle different message types (this logic was already in the component)
|
|
193
|
-
switch (streamMessage.type) {
|
|
194
|
-
case AgentStreamMessageType.StatusUpdate:
|
|
195
|
-
break;
|
|
196
|
-
case AgentStreamMessageType.ContentChunk:
|
|
197
|
-
if (streamMessage.data && typeof streamMessage.data === "object") {
|
|
198
|
-
if (streamMessage.data.isIncremental &&
|
|
199
|
-
streamMessage.data.deltaContent) {
|
|
200
|
-
// Handle incremental content updates
|
|
201
|
-
const updatedMessages = [...(accumulatedResponse.messages || [])];
|
|
202
|
-
let lastAssistantMessage = null;
|
|
203
|
-
let lastAssistantIndex = -1;
|
|
204
|
-
// Find the last assistant message
|
|
205
|
-
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
206
|
-
if (updatedMessages[i].role === "assistant") {
|
|
207
|
-
lastAssistantMessage = updatedMessages[i];
|
|
208
|
-
lastAssistantIndex = i;
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
// If no assistant message exists, or the last one has tool calls, create a new one
|
|
213
|
-
if (!lastAssistantMessage ||
|
|
214
|
-
(lastAssistantMessage.tool_calls &&
|
|
215
|
-
lastAssistantMessage.tool_calls.length > 0)) {
|
|
216
|
-
lastAssistantMessage = {
|
|
217
|
-
id: crypto.randomUUID(),
|
|
218
|
-
role: "assistant",
|
|
219
|
-
name: "assistant",
|
|
220
|
-
content: "",
|
|
221
|
-
tool_calls: [],
|
|
222
|
-
};
|
|
223
|
-
updatedMessages.push(lastAssistantMessage);
|
|
224
|
-
lastAssistantIndex = updatedMessages.length - 1;
|
|
225
|
-
}
|
|
226
|
-
// Append the delta content to build up the full message
|
|
227
|
-
const currentContent = lastAssistantMessage.content || "";
|
|
228
|
-
const newContent = currentContent + streamMessage.data.deltaContent;
|
|
229
|
-
// Update the message with accumulated content
|
|
230
|
-
updatedMessages[lastAssistantIndex] = {
|
|
231
|
-
...lastAssistantMessage,
|
|
232
|
-
content: newContent,
|
|
233
|
-
};
|
|
234
|
-
// Update accumulated response with incremental content
|
|
235
|
-
const newAccumulated = {
|
|
236
|
-
...accumulatedResponse,
|
|
237
|
-
...streamMessage.data,
|
|
238
|
-
messages: updatedMessages,
|
|
239
|
-
editOperations: streamMessage.data.editOperations ||
|
|
240
|
-
accumulatedResponse.editOperations,
|
|
241
|
-
};
|
|
242
|
-
accumulatedResponse = newAccumulated;
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
// Non-incremental update - use as provided
|
|
246
|
-
const newAccumulated = {
|
|
247
|
-
...accumulatedResponse,
|
|
248
|
-
...streamMessage.data,
|
|
249
|
-
editOperations: streamMessage.data.editOperations ||
|
|
250
|
-
accumulatedResponse.editOperations,
|
|
251
|
-
};
|
|
252
|
-
accumulatedResponse = newAccumulated;
|
|
253
|
-
}
|
|
254
|
-
// Always trigger UI update for content chunks
|
|
255
|
-
handleResponse(accumulatedResponse, terminalCallback, false);
|
|
256
|
-
}
|
|
257
|
-
return { accumulatedResponse, completionProcessed };
|
|
258
|
-
case AgentStreamMessageType.ToolCall:
|
|
259
|
-
if (streamMessage.data && typeof streamMessage.data === "object") {
|
|
260
|
-
// Find or create the assistant message that contains this tool call
|
|
261
|
-
const updatedMessages = [...(accumulatedResponse.messages || [])];
|
|
262
|
-
let lastAssistantMessage = null;
|
|
263
|
-
let lastAssistantIndex = -1;
|
|
264
|
-
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
265
|
-
if (updatedMessages[i].role === "assistant") {
|
|
266
|
-
lastAssistantMessage = updatedMessages[i];
|
|
267
|
-
lastAssistantIndex = i;
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// If no assistant message exists, create one
|
|
272
|
-
if (!lastAssistantMessage) {
|
|
273
|
-
lastAssistantMessage = {
|
|
274
|
-
id: crypto.randomUUID(),
|
|
275
|
-
role: "assistant",
|
|
276
|
-
name: "assistant",
|
|
277
|
-
content: "",
|
|
278
|
-
tool_calls: [],
|
|
279
|
-
};
|
|
280
|
-
updatedMessages.push(lastAssistantMessage);
|
|
281
|
-
lastAssistantIndex = updatedMessages.length - 1;
|
|
282
|
-
}
|
|
283
|
-
const toolCall = {
|
|
284
|
-
id: streamMessage.data.id,
|
|
285
|
-
displayName: streamMessage.data.displayName,
|
|
286
|
-
function: {
|
|
287
|
-
name: streamMessage.data.function?.name || streamMessage.data.name,
|
|
288
|
-
arguments: streamMessage.data.function?.arguments ||
|
|
289
|
-
streamMessage.data.arguments,
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
const existingToolCalls = lastAssistantMessage.tool_calls || [];
|
|
293
|
-
const existingIndex = existingToolCalls.findIndex((tc) => tc.id === toolCall.id);
|
|
294
|
-
let updatedToolCalls;
|
|
295
|
-
if (existingIndex >= 0) {
|
|
296
|
-
updatedToolCalls = [...existingToolCalls];
|
|
297
|
-
updatedToolCalls[existingIndex] = toolCall;
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
updatedToolCalls = [...existingToolCalls, toolCall];
|
|
301
|
-
}
|
|
302
|
-
updatedMessages[lastAssistantIndex] = {
|
|
303
|
-
...lastAssistantMessage,
|
|
304
|
-
tool_calls: updatedToolCalls,
|
|
305
|
-
};
|
|
306
|
-
const newAccumulated = {
|
|
307
|
-
...accumulatedResponse,
|
|
308
|
-
messages: updatedMessages,
|
|
309
|
-
};
|
|
310
|
-
accumulatedResponse = newAccumulated;
|
|
311
|
-
handleResponse(accumulatedResponse, terminalCallback, false);
|
|
312
|
-
}
|
|
313
|
-
return { accumulatedResponse, completionProcessed };
|
|
314
|
-
case AgentStreamMessageType.ToolResult:
|
|
315
|
-
if (streamMessage.data && typeof streamMessage.data === "object") {
|
|
316
|
-
const updatedMessages = [...(accumulatedResponse.messages || [])];
|
|
317
|
-
// Find the assistant message with the matching tool call ID
|
|
318
|
-
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
319
|
-
if (updatedMessages[i].role === "assistant" &&
|
|
320
|
-
updatedMessages[i].tool_calls) {
|
|
321
|
-
const toolCalls = updatedMessages[i].tool_calls || [];
|
|
322
|
-
const toolCallIndex = toolCalls.findIndex((tc) => tc.id === streamMessage.data.toolCallId);
|
|
323
|
-
if (toolCallIndex >= 0) {
|
|
324
|
-
const updatedToolCalls = [...toolCalls];
|
|
325
|
-
updatedToolCalls[toolCallIndex] = {
|
|
326
|
-
...updatedToolCalls[toolCallIndex],
|
|
327
|
-
function: {
|
|
328
|
-
...updatedToolCalls[toolCallIndex].function,
|
|
329
|
-
result: streamMessage.data.result,
|
|
330
|
-
error: streamMessage.data.error,
|
|
331
|
-
},
|
|
332
|
-
};
|
|
333
|
-
updatedMessages[i] = {
|
|
334
|
-
...updatedMessages[i],
|
|
335
|
-
tool_calls: updatedToolCalls,
|
|
336
|
-
};
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
const newAccumulated = {
|
|
342
|
-
...accumulatedResponse,
|
|
343
|
-
messages: updatedMessages,
|
|
344
|
-
};
|
|
345
|
-
accumulatedResponse = newAccumulated;
|
|
346
|
-
handleResponse(accumulatedResponse, terminalCallback, false);
|
|
347
|
-
}
|
|
348
|
-
return { accumulatedResponse, completionProcessed };
|
|
349
|
-
case AgentStreamMessageType.Completed:
|
|
350
|
-
if (!completionProcessed) {
|
|
351
|
-
completionProcessed = true;
|
|
352
|
-
console.log("Agent execution completed");
|
|
353
|
-
setIsRunning(false);
|
|
354
|
-
if (streamMessage.data) {
|
|
355
|
-
handleResponse(streamMessage.data, terminalCallback, true);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return { accumulatedResponse, completionProcessed };
|
|
359
|
-
case AgentStreamMessageType.Error:
|
|
360
|
-
console.error("Agent execution error:", streamMessage);
|
|
361
|
-
setIsRunning(false);
|
|
362
|
-
setTerminalError(streamMessage.error || "Agent execution failed");
|
|
363
|
-
return { accumulatedResponse, completionProcessed };
|
|
364
|
-
default:
|
|
365
|
-
console.warn("Unknown stream message type:", streamMessage.type);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
54
|
useEffect(() => {
|
|
369
55
|
if (activeProfile?.defaultModelId)
|
|
370
56
|
setModel(activeProfile.defaultModelId);
|
|
@@ -380,7 +66,10 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
380
66
|
console.log("AiTerminal: Loading initial messages", options.initialMessages);
|
|
381
67
|
// Format the initial messages for display
|
|
382
68
|
const formattedMessages = options.initialMessages.map((message) => {
|
|
383
|
-
const formattedContent =
|
|
69
|
+
const formattedContent = message.content
|
|
70
|
+
?.trim()
|
|
71
|
+
?.replaceAll("\n", "<br>")
|
|
72
|
+
?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
|
|
384
73
|
return {
|
|
385
74
|
...message,
|
|
386
75
|
content: message.content || "",
|
|
@@ -388,6 +77,7 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
388
77
|
tool_calls: message.tool_calls || [],
|
|
389
78
|
};
|
|
390
79
|
});
|
|
80
|
+
console.log("AiTerminal: Formatted messages", formattedMessages);
|
|
391
81
|
// Update the internal message states
|
|
392
82
|
setMessages(formattedMessages);
|
|
393
83
|
setResponseMessages(formattedMessages);
|
|
@@ -395,101 +85,57 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
395
85
|
const initialResponse = {
|
|
396
86
|
messages: formattedMessages,
|
|
397
87
|
editOperations: [],
|
|
398
|
-
numInputTokens:
|
|
399
|
-
numOutputTokens:
|
|
400
|
-
numCachedTokens:
|
|
88
|
+
numInputTokens: 0,
|
|
89
|
+
numOutputTokens: 0,
|
|
90
|
+
numCachedTokens: 0,
|
|
401
91
|
state: "loaded",
|
|
402
|
-
// Include cost information from loaded agent session
|
|
403
|
-
totalCost: options?.totalCost,
|
|
404
|
-
totalInputTokenCost: options?.totalInputTokenCost,
|
|
405
|
-
totalOutputTokenCost: options?.totalOutputTokenCost,
|
|
406
|
-
totalCachedTokenCost: options?.totalCachedTokenCost,
|
|
407
|
-
totalInputTokens: options?.totalInputTokens,
|
|
408
|
-
totalOutputTokens: options?.totalOutputTokens,
|
|
409
|
-
totalCachedTokens: options?.totalCachedTokens,
|
|
410
92
|
};
|
|
93
|
+
console.log("AiTerminal: Setting response", initialResponse);
|
|
411
94
|
setResponse(initialResponse);
|
|
412
95
|
// Create individual Terminal messages for each conversation message
|
|
413
96
|
const terminalMessages = [];
|
|
414
|
-
|
|
415
|
-
let i = 0;
|
|
416
|
-
while (i < formattedMessages.length) {
|
|
417
|
-
const message = formattedMessages[i];
|
|
418
|
-
if (!message) {
|
|
419
|
-
i++;
|
|
420
|
-
continue;
|
|
421
|
-
}
|
|
97
|
+
formattedMessages.forEach((message) => {
|
|
422
98
|
if (message.role === "user") {
|
|
423
99
|
// User messages appear as commands
|
|
424
100
|
terminalMessages.push({
|
|
425
101
|
type: "command",
|
|
426
102
|
text: message.content,
|
|
427
103
|
});
|
|
428
|
-
i++;
|
|
429
|
-
// Look for following assistant messages to group together
|
|
430
|
-
const assistantMessages = [];
|
|
431
|
-
while (i < formattedMessages.length) {
|
|
432
|
-
const assistantMessage = formattedMessages[i];
|
|
433
|
-
if (assistantMessage && assistantMessage.role === "assistant") {
|
|
434
|
-
assistantMessages.push(assistantMessage);
|
|
435
|
-
i++;
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
break;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
// If we have assistant messages, render them with AiResponseMessage
|
|
442
|
-
if (assistantMessages.length > 0) {
|
|
443
|
-
terminalMessages.push({
|
|
444
|
-
type: "response",
|
|
445
|
-
text: (_jsx(AiResponseMessage, { messages: assistantMessages, editOperations: [], finished: true })),
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
104
|
}
|
|
449
105
|
else if (message.role === "assistant") {
|
|
450
|
-
//
|
|
106
|
+
// Assistant messages appear as responses
|
|
451
107
|
terminalMessages.push({
|
|
452
108
|
type: "response",
|
|
453
|
-
text: (_jsx(
|
|
109
|
+
text: (_jsx("div", { dangerouslySetInnerHTML: {
|
|
110
|
+
__html: message.formattedContent || message.content || "",
|
|
111
|
+
} })),
|
|
454
112
|
});
|
|
455
|
-
i++;
|
|
456
113
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
114
|
+
// Add tool calls if present
|
|
115
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
116
|
+
message.tool_calls.forEach((toolCall) => {
|
|
117
|
+
terminalMessages.push({
|
|
118
|
+
type: "response",
|
|
119
|
+
text: (_jsxs("div", { className: "text-gray-1 text-xs", children: ["\uD83D\uDD27 ", toolCall.displayName] })),
|
|
120
|
+
});
|
|
121
|
+
});
|
|
460
122
|
}
|
|
461
|
-
}
|
|
123
|
+
});
|
|
462
124
|
setInitialTerminalMessages(terminalMessages);
|
|
463
125
|
}
|
|
464
126
|
}, [options?.initialMessages]);
|
|
465
127
|
// Handle click outside for settings popover
|
|
466
128
|
useEffect(() => {
|
|
467
129
|
function handleClickOutside(event) {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
// Check if the click is inside any dropdown menu content
|
|
474
|
-
// Radix UI dropdown menus can have various selectors
|
|
475
|
-
const isInDropdown = target.closest([
|
|
476
|
-
"[data-radix-popper-content-wrapper]",
|
|
477
|
-
'[data-slot="dropdown-menu-content"]',
|
|
478
|
-
"[data-radix-dropdown-menu-content]",
|
|
479
|
-
'[role="menu"]',
|
|
480
|
-
".dropdown-menu", // fallback for any custom dropdown classes
|
|
481
|
-
].join(", "));
|
|
482
|
-
if (isInDropdown) {
|
|
483
|
-
return;
|
|
130
|
+
if (settingsRef.current &&
|
|
131
|
+
!settingsRef.current.contains(event.target)) {
|
|
132
|
+
setShowSettings(false);
|
|
484
133
|
}
|
|
485
|
-
// If we get here, it's a click outside both the settings and any dropdown
|
|
486
|
-
setShowSettings(false);
|
|
487
134
|
}
|
|
488
135
|
if (showSettings) {
|
|
489
|
-
|
|
490
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
136
|
+
document.addEventListener("click", handleClickOutside);
|
|
491
137
|
return () => {
|
|
492
|
-
document.removeEventListener("
|
|
138
|
+
document.removeEventListener("click", handleClickOutside);
|
|
493
139
|
};
|
|
494
140
|
}
|
|
495
141
|
}, [showSettings]);
|
|
@@ -498,7 +144,10 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
498
144
|
// Replace the conversation history with the authoritative response from AI
|
|
499
145
|
if (updatedMessages && Array.isArray(updatedMessages)) {
|
|
500
146
|
const formattedMessages = updatedMessages.map((message) => {
|
|
501
|
-
const formattedContent =
|
|
147
|
+
const formattedContent = message.content
|
|
148
|
+
?.trim()
|
|
149
|
+
?.replaceAll("\n", "<br>")
|
|
150
|
+
?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
|
|
502
151
|
return {
|
|
503
152
|
...message,
|
|
504
153
|
content: message.content || "",
|
|
@@ -509,11 +158,11 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
509
158
|
// Update the messages state with the complete conversation from AI
|
|
510
159
|
setMessages([...formattedMessages]);
|
|
511
160
|
setResponseMessages([...formattedMessages]);
|
|
512
|
-
terminalCallback(_jsx(AiResponseMessage, { messages: formattedMessages, editOperations: response.editOperations, finished: isFinished
|
|
161
|
+
terminalCallback(_jsx(AiResponseMessage, { messages: formattedMessages, editOperations: response.editOperations, finished: isFinished }), isFinished);
|
|
513
162
|
}
|
|
514
163
|
else {
|
|
515
164
|
// Fallback: if no messages in response, keep current state
|
|
516
|
-
terminalCallback(_jsx(AiResponseMessage, { messages: messagesRef.current, editOperations: response.editOperations, finished: isFinished
|
|
165
|
+
terminalCallback(_jsx(AiResponseMessage, { messages: messagesRef.current, editOperations: response.editOperations, finished: isFinished }), isFinished);
|
|
517
166
|
}
|
|
518
167
|
}
|
|
519
168
|
async function commandHandler(text, callback) {
|
|
@@ -524,11 +173,13 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
524
173
|
name: "user",
|
|
525
174
|
tool_calls: [], // Add empty toolCalls array
|
|
526
175
|
};
|
|
527
|
-
const context = createAiContext({ editContext });
|
|
176
|
+
const context = createAiContext({ editContext: editContext });
|
|
177
|
+
let lastOpIndex = 0;
|
|
528
178
|
const selectedText = editContext?.selectedRange?.text || null;
|
|
529
179
|
if (!activeProfile || !model)
|
|
530
180
|
return;
|
|
531
|
-
//
|
|
181
|
+
// Build complete message history for API call
|
|
182
|
+
const conversationHistory = [...messagesRef.current, userMessage];
|
|
532
183
|
const messages = [
|
|
533
184
|
...(options?.hiddenSystemPrompt
|
|
534
185
|
? [
|
|
@@ -541,408 +192,35 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
541
192
|
},
|
|
542
193
|
]
|
|
543
194
|
: []),
|
|
544
|
-
|
|
195
|
+
...conversationHistory,
|
|
545
196
|
];
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
setIsRunning(true);
|
|
573
|
-
let accumulatedResponse = {
|
|
574
|
-
messages: [...messages],
|
|
575
|
-
editOperations: [],
|
|
576
|
-
numInputTokens: 0,
|
|
577
|
-
numOutputTokens: 0,
|
|
578
|
-
numCachedTokens: 0,
|
|
579
|
-
state: "running",
|
|
580
|
-
};
|
|
581
|
-
let completionProcessed = false;
|
|
582
|
-
try {
|
|
583
|
-
console.log("Connecting to agent stream");
|
|
584
|
-
await connectToAgentStream(agentId, context, (streamMessage) => {
|
|
585
|
-
// console.log(
|
|
586
|
-
// "Stream message:",
|
|
587
|
-
// streamMessage,
|
|
588
|
-
// new Date().toISOString(),
|
|
589
|
-
// );
|
|
590
|
-
// Safety check for valid stream message
|
|
591
|
-
if (!streamMessage || !streamMessage.type) {
|
|
592
|
-
console.error("Invalid stream message received:", streamMessage);
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
console.log("Processing stream message:", streamMessage.type, streamMessage);
|
|
596
|
-
// Message types are now normalized in agentService.ts
|
|
597
|
-
switch (streamMessage.type) {
|
|
598
|
-
case AgentStreamMessageType.StatusUpdate:
|
|
599
|
-
//console.log("Status update:", streamMessage.data);
|
|
600
|
-
break;
|
|
601
|
-
case AgentStreamMessageType.ContentChunk:
|
|
602
|
-
if (streamMessage.data &&
|
|
603
|
-
typeof streamMessage.data === "object") {
|
|
604
|
-
// Handle incremental content updates
|
|
605
|
-
if (streamMessage.data.isIncremental &&
|
|
606
|
-
streamMessage.data.deltaContent) {
|
|
607
|
-
// Find the last assistant message to append delta content to
|
|
608
|
-
const updatedMessages = [
|
|
609
|
-
...(accumulatedResponse.messages || []),
|
|
610
|
-
];
|
|
611
|
-
let lastAssistantMessage = null;
|
|
612
|
-
let lastAssistantIndex = -1;
|
|
613
|
-
// Find the last assistant message
|
|
614
|
-
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
615
|
-
if (updatedMessages[i].role === "assistant") {
|
|
616
|
-
lastAssistantMessage = updatedMessages[i];
|
|
617
|
-
lastAssistantIndex = i;
|
|
618
|
-
break;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
// If no assistant message exists, or the last one has tool calls, create a new one
|
|
622
|
-
if (!lastAssistantMessage ||
|
|
623
|
-
(lastAssistantMessage.tool_calls &&
|
|
624
|
-
lastAssistantMessage.tool_calls.length > 0)) {
|
|
625
|
-
lastAssistantMessage = {
|
|
626
|
-
id: crypto.randomUUID(),
|
|
627
|
-
role: "assistant",
|
|
628
|
-
name: "assistant",
|
|
629
|
-
content: "",
|
|
630
|
-
tool_calls: [],
|
|
631
|
-
};
|
|
632
|
-
updatedMessages.push(lastAssistantMessage);
|
|
633
|
-
lastAssistantIndex = updatedMessages.length - 1;
|
|
634
|
-
}
|
|
635
|
-
// Append the delta content to build up the full message
|
|
636
|
-
const currentContent = lastAssistantMessage.content || "";
|
|
637
|
-
const newContent = currentContent + streamMessage.data.deltaContent;
|
|
638
|
-
// Update the message with accumulated content
|
|
639
|
-
updatedMessages[lastAssistantIndex] = {
|
|
640
|
-
...lastAssistantMessage,
|
|
641
|
-
content: newContent,
|
|
642
|
-
};
|
|
643
|
-
// Update accumulated response with incremental content
|
|
644
|
-
accumulatedResponse = {
|
|
645
|
-
...accumulatedResponse,
|
|
646
|
-
...streamMessage.data,
|
|
647
|
-
messages: updatedMessages,
|
|
648
|
-
editOperations: streamMessage.data.editOperations ||
|
|
649
|
-
accumulatedResponse.editOperations,
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
else {
|
|
653
|
-
// Non-incremental update - use as provided
|
|
654
|
-
accumulatedResponse = {
|
|
655
|
-
...accumulatedResponse,
|
|
656
|
-
...streamMessage.data,
|
|
657
|
-
editOperations: streamMessage.data.editOperations ||
|
|
658
|
-
accumulatedResponse.editOperations,
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
// Always trigger UI update for content chunks
|
|
662
|
-
handleResponse(accumulatedResponse, callback, false);
|
|
663
|
-
}
|
|
664
|
-
break;
|
|
665
|
-
case AgentStreamMessageType.ToolCall:
|
|
666
|
-
// console.log("Tool call received:", streamMessage.data);
|
|
667
|
-
if (streamMessage.data &&
|
|
668
|
-
typeof streamMessage.data === "object") {
|
|
669
|
-
// Find or create the assistant message that contains this tool call
|
|
670
|
-
const updatedMessages = [
|
|
671
|
-
...(accumulatedResponse.messages || []),
|
|
672
|
-
];
|
|
673
|
-
let lastAssistantMessage = null;
|
|
674
|
-
let lastAssistantIndex = -1;
|
|
675
|
-
// Find the last assistant message
|
|
676
|
-
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
677
|
-
if (updatedMessages[i].role === "assistant") {
|
|
678
|
-
lastAssistantMessage = updatedMessages[i];
|
|
679
|
-
lastAssistantIndex = i;
|
|
680
|
-
break;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
// If no assistant message exists, create one
|
|
684
|
-
if (!lastAssistantMessage) {
|
|
685
|
-
lastAssistantMessage = {
|
|
686
|
-
id: crypto.randomUUID(),
|
|
687
|
-
role: "assistant",
|
|
688
|
-
name: "assistant",
|
|
689
|
-
content: "",
|
|
690
|
-
tool_calls: [],
|
|
691
|
-
};
|
|
692
|
-
updatedMessages.push(lastAssistantMessage);
|
|
693
|
-
lastAssistantIndex = updatedMessages.length - 1;
|
|
694
|
-
}
|
|
695
|
-
// Add or update the tool call
|
|
696
|
-
const toolCall = {
|
|
697
|
-
id: streamMessage.data.id,
|
|
698
|
-
displayName: streamMessage.data.displayName,
|
|
699
|
-
function: {
|
|
700
|
-
name: streamMessage.data.function?.name ||
|
|
701
|
-
streamMessage.data.name,
|
|
702
|
-
arguments: streamMessage.data.function?.arguments ||
|
|
703
|
-
streamMessage.data.arguments,
|
|
704
|
-
},
|
|
705
|
-
};
|
|
706
|
-
// Update or add the tool call to the assistant message
|
|
707
|
-
const existingToolCalls = lastAssistantMessage.tool_calls || [];
|
|
708
|
-
const existingIndex = existingToolCalls.findIndex((tc) => tc.id === toolCall.id);
|
|
709
|
-
let updatedToolCalls;
|
|
710
|
-
if (existingIndex >= 0) {
|
|
711
|
-
// Update existing tool call
|
|
712
|
-
updatedToolCalls = [...existingToolCalls];
|
|
713
|
-
updatedToolCalls[existingIndex] = toolCall;
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
// Add new tool call
|
|
717
|
-
updatedToolCalls = [...existingToolCalls, toolCall];
|
|
718
|
-
}
|
|
719
|
-
// Update the assistant message with the tool call
|
|
720
|
-
updatedMessages[lastAssistantIndex] = {
|
|
721
|
-
...lastAssistantMessage,
|
|
722
|
-
tool_calls: updatedToolCalls,
|
|
723
|
-
};
|
|
724
|
-
// Update accumulated response
|
|
725
|
-
accumulatedResponse = {
|
|
726
|
-
...accumulatedResponse,
|
|
727
|
-
messages: updatedMessages,
|
|
728
|
-
};
|
|
729
|
-
// Trigger UI update to show the tool call
|
|
730
|
-
handleResponse(accumulatedResponse, callback, false);
|
|
731
|
-
}
|
|
732
|
-
break;
|
|
733
|
-
case AgentStreamMessageType.ToolResult:
|
|
734
|
-
console.log("Tool result received:", streamMessage.data);
|
|
735
|
-
if (streamMessage.data &&
|
|
736
|
-
typeof streamMessage.data === "object") {
|
|
737
|
-
// Find the assistant message and tool call to update with the result
|
|
738
|
-
const updatedMessages = [
|
|
739
|
-
...(accumulatedResponse.messages || []),
|
|
740
|
-
];
|
|
741
|
-
let messageUpdated = false;
|
|
742
|
-
// Find the assistant message with the matching tool call ID
|
|
743
|
-
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
744
|
-
if (updatedMessages[i].role === "assistant" &&
|
|
745
|
-
updatedMessages[i].tool_calls) {
|
|
746
|
-
const toolCalls = updatedMessages[i].tool_calls || [];
|
|
747
|
-
const toolCallIndex = toolCalls.findIndex((tc) => tc.id === streamMessage.data.toolCallId);
|
|
748
|
-
if (toolCallIndex >= 0) {
|
|
749
|
-
// Update the tool call with the result or error
|
|
750
|
-
const updatedToolCalls = [...toolCalls];
|
|
751
|
-
updatedToolCalls[toolCallIndex] = {
|
|
752
|
-
...updatedToolCalls[toolCallIndex],
|
|
753
|
-
function: {
|
|
754
|
-
...updatedToolCalls[toolCallIndex].function,
|
|
755
|
-
result: streamMessage.data.result,
|
|
756
|
-
error: streamMessage.data.error,
|
|
757
|
-
},
|
|
758
|
-
};
|
|
759
|
-
// Update the message
|
|
760
|
-
updatedMessages[i] = {
|
|
761
|
-
...updatedMessages[i],
|
|
762
|
-
tool_calls: updatedToolCalls,
|
|
763
|
-
};
|
|
764
|
-
messageUpdated = true;
|
|
765
|
-
break;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
// Update accumulated response
|
|
770
|
-
accumulatedResponse = {
|
|
771
|
-
...accumulatedResponse,
|
|
772
|
-
messages: updatedMessages,
|
|
773
|
-
};
|
|
774
|
-
// Trigger UI update to show the tool result
|
|
775
|
-
handleResponse(accumulatedResponse, callback, false);
|
|
776
|
-
}
|
|
777
|
-
break;
|
|
778
|
-
case AgentStreamMessageType.Completed:
|
|
779
|
-
if (streamMessage.data &&
|
|
780
|
-
typeof streamMessage.data === "object") {
|
|
781
|
-
// Final completion data
|
|
782
|
-
const finalResponse = {
|
|
783
|
-
messages: accumulatedResponse.messages ||
|
|
784
|
-
streamMessage.data.messages,
|
|
785
|
-
editOperations: streamMessage.data.editOperations ||
|
|
786
|
-
accumulatedResponse.editOperations,
|
|
787
|
-
numInputTokens: streamMessage.data.tokenUsage?.inputTokens ||
|
|
788
|
-
accumulatedResponse.numInputTokens,
|
|
789
|
-
numOutputTokens: streamMessage.data.tokenUsage?.outputTokens ||
|
|
790
|
-
accumulatedResponse.numOutputTokens,
|
|
791
|
-
numCachedTokens: streamMessage.data.tokenUsage?.cachedTokens ||
|
|
792
|
-
accumulatedResponse.numCachedTokens,
|
|
793
|
-
state: "completed",
|
|
794
|
-
agentName: streamMessage.data.agentName,
|
|
795
|
-
error: streamMessage.data.error || accumulatedResponse.error,
|
|
796
|
-
// Add cost fields from backend (try both tokenUsage object and direct properties)
|
|
797
|
-
totalCost: streamMessage.data.totalCost ||
|
|
798
|
-
streamMessage.data.tokenUsage?.totalCost,
|
|
799
|
-
totalInputTokenCost: streamMessage.data.totalInputTokenCost ||
|
|
800
|
-
streamMessage.data.tokenUsage?.inputTokenCost,
|
|
801
|
-
totalOutputTokenCost: streamMessage.data.totalOutputTokenCost ||
|
|
802
|
-
streamMessage.data.tokenUsage?.outputTokenCost,
|
|
803
|
-
totalCachedTokenCost: streamMessage.data.totalCachedTokenCost ||
|
|
804
|
-
streamMessage.data.tokenUsage?.cachedTokenCost,
|
|
805
|
-
totalInputTokens: streamMessage.data.totalInputTokens ||
|
|
806
|
-
streamMessage.data.tokenUsage?.inputTokens,
|
|
807
|
-
totalOutputTokens: streamMessage.data.totalOutputTokens ||
|
|
808
|
-
streamMessage.data.tokenUsage?.outputTokens,
|
|
809
|
-
totalCachedTokens: streamMessage.data.totalCachedTokens ||
|
|
810
|
-
streamMessage.data.tokenUsage?.cachedTokens,
|
|
811
|
-
};
|
|
812
|
-
completionProcessed = true;
|
|
813
|
-
setResponse(finalResponse);
|
|
814
|
-
// On completion, just update the internal state and call the final callback
|
|
815
|
-
// Don't call handleResponse again as it would duplicate the message
|
|
816
|
-
// The Terminal component will clear the streaming response and add it to the message history
|
|
817
|
-
const formattedMessages = (finalResponse.messages || []).map((message) => {
|
|
818
|
-
const formattedContent = formatMessageContent(message.content || "");
|
|
819
|
-
return {
|
|
820
|
-
...message,
|
|
821
|
-
content: message.content || "",
|
|
822
|
-
formattedContent: formattedContent,
|
|
823
|
-
tool_calls: message.tool_calls || [],
|
|
824
|
-
};
|
|
825
|
-
});
|
|
826
|
-
// Update internal state without triggering a new render via handleResponse
|
|
827
|
-
setMessages([...formattedMessages]);
|
|
828
|
-
setResponseMessages([...formattedMessages]);
|
|
829
|
-
// Only call the terminal callback to mark completion (this will add to history)
|
|
830
|
-
callback(_jsx(AiResponseMessage, { messages: formattedMessages, editOperations: finalResponse.editOperations, finished: true, error: finalResponse.error }), true);
|
|
831
|
-
// Handle agent name update
|
|
832
|
-
if (finalResponse.agentName && onAgentNameUpdate) {
|
|
833
|
-
onAgentNameUpdate(finalResponse.agentName);
|
|
834
|
-
}
|
|
835
|
-
if (context.callback)
|
|
836
|
-
context.callback(finalResponse);
|
|
837
|
-
//editContext?.requestRefresh("immediate");
|
|
838
|
-
}
|
|
839
|
-
break;
|
|
840
|
-
case AgentStreamMessageType.Error:
|
|
841
|
-
const errorMessage = streamMessage.error || "Unknown agent error";
|
|
842
|
-
console.error("Agent execution error:", errorMessage);
|
|
843
|
-
console.log("Displaying error in terminal:", errorMessage);
|
|
844
|
-
setTerminalError(errorMessage);
|
|
845
|
-
const errorResponse = {
|
|
846
|
-
...accumulatedResponse,
|
|
847
|
-
state: "error",
|
|
848
|
-
error: errorMessage,
|
|
849
|
-
};
|
|
850
|
-
setResponse(errorResponse);
|
|
851
|
-
// Display the error in the terminal
|
|
852
|
-
console.log("Calling handleResponse with error");
|
|
853
|
-
handleResponse(errorResponse, callback, true);
|
|
854
|
-
// Also try using TerminalService directly as fallback
|
|
855
|
-
console.log("Also trying TerminalService.emit");
|
|
856
|
-
TerminalService.emit("response", {
|
|
857
|
-
text: (_jsx(AiResponseMessage, { messages: [], editOperations: [], finished: true, error: errorMessage })),
|
|
858
|
-
type: "response",
|
|
859
|
-
});
|
|
860
|
-
console.log("Error callback completed");
|
|
861
|
-
setIsRunning(false);
|
|
862
|
-
abortControllerRef.current = null;
|
|
863
|
-
return; // Exit the stream processing
|
|
864
|
-
default:
|
|
865
|
-
console.log("Unhandled stream message type:", streamMessage.type);
|
|
866
|
-
break;
|
|
867
|
-
}
|
|
868
|
-
}, abortController.signal);
|
|
869
|
-
// If we reach here, the stream ended without a Completed message
|
|
870
|
-
if (!completionProcessed) {
|
|
871
|
-
// console.log("Stream ended without Completed message, cleaning up");
|
|
872
|
-
const finalResponse = {
|
|
873
|
-
...accumulatedResponse,
|
|
874
|
-
state: "completed",
|
|
875
|
-
};
|
|
876
|
-
setResponse(finalResponse);
|
|
877
|
-
handleResponse(finalResponse, callback, true);
|
|
878
|
-
}
|
|
879
|
-
setIsRunning(false);
|
|
880
|
-
abortControllerRef.current = null;
|
|
881
|
-
}
|
|
882
|
-
catch (streamError) {
|
|
883
|
-
console.error("Stream connection error:", streamError);
|
|
884
|
-
const isAbort = streamError?.name === "AbortError" ||
|
|
885
|
-
streamError?.message?.toLowerCase?.().includes("abort");
|
|
886
|
-
if (isAbort) {
|
|
887
|
-
const cancelledResponse = {
|
|
888
|
-
...accumulatedResponse,
|
|
889
|
-
state: "cancelled",
|
|
890
|
-
};
|
|
891
|
-
setResponse(cancelledResponse);
|
|
892
|
-
handleResponse(cancelledResponse, callback, true);
|
|
893
|
-
}
|
|
894
|
-
else {
|
|
895
|
-
const errorMessage = streamError instanceof Error
|
|
896
|
-
? streamError.message
|
|
897
|
-
: "Unknown stream error";
|
|
898
|
-
const errorResponse = {
|
|
899
|
-
...accumulatedResponse,
|
|
900
|
-
state: "error",
|
|
901
|
-
error: errorMessage,
|
|
902
|
-
};
|
|
903
|
-
setResponse(errorResponse);
|
|
904
|
-
// Display the error in the terminal
|
|
905
|
-
callback(_jsx(AiResponseMessage, { messages: accumulatedResponse.messages || [], editOperations: accumulatedResponse.editOperations || [], finished: true, error: errorMessage }), true);
|
|
906
|
-
}
|
|
907
|
-
setIsRunning(false);
|
|
908
|
-
abortControllerRef.current = null;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
catch (error) {
|
|
912
|
-
console.error("Error starting agent:", error);
|
|
913
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
914
|
-
// Display error in the terminal
|
|
915
|
-
const errorResponse = {
|
|
916
|
-
messages: [...messages],
|
|
917
|
-
editOperations: [],
|
|
918
|
-
numInputTokens: 0,
|
|
919
|
-
numOutputTokens: 0,
|
|
920
|
-
numCachedTokens: 0,
|
|
921
|
-
state: "error",
|
|
922
|
-
error: errorMessage,
|
|
923
|
-
};
|
|
924
|
-
setResponse(errorResponse);
|
|
925
|
-
// Show the error in the terminal
|
|
926
|
-
callback(_jsx(AiResponseMessage, { messages: [], editOperations: [], finished: true, error: errorMessage }), true);
|
|
927
|
-
setIsRunning(false);
|
|
928
|
-
abortControllerRef.current = null;
|
|
929
|
-
}
|
|
197
|
+
const response = await executePrompt(messages, context, {
|
|
198
|
+
profileId: activeProfile.id,
|
|
199
|
+
selection,
|
|
200
|
+
selectedText,
|
|
201
|
+
model,
|
|
202
|
+
sessionId: editContext.sessionId,
|
|
203
|
+
agentId,
|
|
204
|
+
addSelectedComponents: true,
|
|
205
|
+
}, undefined, (response) => {
|
|
206
|
+
setResponse(response);
|
|
207
|
+
handleResponse(response, callback, false);
|
|
208
|
+
});
|
|
209
|
+
if (response) {
|
|
210
|
+
handleResponse(response, callback, true);
|
|
211
|
+
if (context.callback)
|
|
212
|
+
context.callback(response);
|
|
213
|
+
editContext?.requestRefresh("immediate");
|
|
214
|
+
}
|
|
215
|
+
// if (response?.responseText)
|
|
216
|
+
// newMessages.push({
|
|
217
|
+
// content: response.responseText,
|
|
218
|
+
// role: "assistant",
|
|
219
|
+
// name: "assistant",
|
|
220
|
+
// });
|
|
221
|
+
setResponse(response ? response : undefined);
|
|
222
|
+
// setMessages(newMessages);
|
|
930
223
|
}
|
|
931
|
-
// Stop button handler to cancel running agent and abort stream
|
|
932
|
-
const handleStop = async () => {
|
|
933
|
-
try {
|
|
934
|
-
if (!isRunning)
|
|
935
|
-
return;
|
|
936
|
-
const context = createAiContext({ editContext });
|
|
937
|
-
await cancelAgent(agentId, context);
|
|
938
|
-
}
|
|
939
|
-
catch (e) {
|
|
940
|
-
// Ignore cancel errors; still abort client-side stream
|
|
941
|
-
}
|
|
942
|
-
finally {
|
|
943
|
-
abortControllerRef.current?.abort();
|
|
944
|
-
}
|
|
945
|
-
};
|
|
946
224
|
useEffect(() => {
|
|
947
225
|
TerminalService.on("command", commandHandler);
|
|
948
226
|
return () => {
|
|
@@ -954,39 +232,18 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
|
|
|
954
232
|
setResponseMessages([]);
|
|
955
233
|
setResponse(undefined);
|
|
956
234
|
setInitialTerminalMessages(undefined);
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
numInputTokens: response.numInputTokens,
|
|
963
|
-
numOutputTokens: response.numOutputTokens,
|
|
964
|
-
numCachedTokens: response.numCachedTokens,
|
|
965
|
-
totalCost: response.totalCost,
|
|
966
|
-
inputCost: response.totalInputTokenCost,
|
|
967
|
-
outputCost: response.totalOutputTokenCost,
|
|
968
|
-
cachedCost: response.totalCachedTokenCost,
|
|
969
|
-
}
|
|
970
|
-
: null, totalTokens: response
|
|
971
|
-
? {
|
|
972
|
-
input: response.totalInputTokens || response.numInputTokens,
|
|
973
|
-
output: response.totalOutputTokens ||
|
|
974
|
-
response.numOutputTokens,
|
|
975
|
-
cached: response.totalCachedTokens ||
|
|
976
|
-
response.numCachedTokens,
|
|
977
|
-
totalCost: response.totalCost || 0,
|
|
978
|
-
inputCost: response.totalInputTokenCost || 0,
|
|
979
|
-
outputCost: response.totalOutputTokenCost || 0,
|
|
980
|
-
cachedCost: response.totalCachedTokenCost || 0,
|
|
981
|
-
}
|
|
982
|
-
: undefined }), prompt: prompt, setPrompt: setPrompt, submitOverride: isRunning ? (_jsx(SimpleIconButton, { icon: _jsx(Square, { size: 16, strokeWidth: 1 }), label: "Stop", onClick: handleStop, disabled: !isRunning })) : undefined, statusbar: _jsxs("div", { className: "flex flex-1 items-center justify-between gap-1", children: [_jsxs(Popover, { open: showPredefined, onOpenChange: setShowPredefined, children: [_jsxs(Tooltip, { delayDuration: 500, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx("a", { className: "ml-1 flex cursor-pointer items-center gap-1 text-xs", children: _jsx(WizardIcon, { className: "text-theme-secondary h-5 w-5" }) }) }) }), _jsx(TooltipContent, { children: "Predefined prompts" })] }), _jsx(PopoverContent, { className: "w-96 p-2", align: "start", side: "top", children: activeProfile?.prompts?.length ? (_jsx("div", { className: "flex max-h-64 flex-col gap-1 overflow-y-auto", children: activeProfile.prompts.map((p, index) => (_jsx("div", { className: "mb-1 cursor-pointer rounded-lg border border-gray-200 p-1.5 text-xs text-gray-700 hover:bg-gray-50", onClick: () => {
|
|
983
|
-
setPrompt(p.prompt);
|
|
984
|
-
setShowPredefined(false);
|
|
985
|
-
}, children: p.title }, index))) })) : (_jsx("div", { className: "text-xs text-gray-500", children: "No predefined prompts available" })) })] }), editContext.selection?.length > 0 && (_jsxs("div", { className: "mr-2 flex items-center text-xs text-red-400", children: [editContext.selection.length, " items selected", _jsx(SimpleIconButton, { icon: "pi pi-times", label: "Clear selection", onClick: () => {
|
|
235
|
+
}, initialMessages: initialTerminalMessages, infobar: response?.numInputTokens && response?.numInputTokens > 0 ? (_jsxs("div", { className: "text-right text-gray-400", style: { fontSize: "10px" }, children: ["Tokens in: ", response?.numInputTokens?.toLocaleString(), " out:", " ", response?.numOutputTokens?.toLocaleString(), " ", response?.numCachedTokens
|
|
236
|
+
? `cached: ${response?.numCachedTokens?.toLocaleString()} `
|
|
237
|
+
: "", response?.state] })) : null, prompt: prompt, setPrompt: setPrompt, statusbar: _jsxs("div", { className: "flex flex-1 items-center justify-between gap-1", children: [_jsxs("a", { className: "ml-1 flex cursor-pointer items-center gap-1 text-xs", onClick: () => {
|
|
238
|
+
setShowPredefined(!showPredefined);
|
|
239
|
+
}, children: [_jsx(WizardIcon, { className: "h-5 w-5" }), "Predefined prompts"] }), editContext.selection?.length > 0 && (_jsxs("div", { className: "mr-2 flex items-center text-xs text-red-400", children: [editContext.selection.length, " items selected", _jsx(SimpleIconButton, { icon: "pi pi-times", label: "Clear selection", onClick: () => {
|
|
986
240
|
editContext.select([]);
|
|
987
|
-
} })] }))
|
|
988
|
-
|
|
241
|
+
} })] })), showPredefined && (_jsx("div", { className: "absolute right-0 bottom-8 left-0 flex flex-col gap-1 overflow-y-auto bg-white p-3 pb-1 text-sm", children: activeProfile &&
|
|
242
|
+
activeProfile.prompts.map((p, index) => (_jsx("div", { className: "mb-1 cursor-pointer rounded-lg border border-gray-200 p-1.5 text-xs text-gray-700", onClick: () => {
|
|
243
|
+
setPrompt(p.prompt);
|
|
244
|
+
setShowPredefined(false);
|
|
245
|
+
}, children: p.title }, index))) }))] }), toolbar: _jsxs("div", { className: "flex items-stretch gap-1", children: [_jsxs("div", { className: "relative", ref: settingsRef, children: [_jsx(SimpleIconButton, { icon: _jsx(Settings, { size: 16, strokeWidth: 1 }), label: "Settings", onClick: () => setShowSettings(!showSettings) }), showSettings && (_jsxs("div", { className: "absolute top-8 right-0 z-50 flex flex-col gap-2 rounded-lg border border-gray-200 bg-white p-3 shadow-lg", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-gray-700", children: "Profile" }), _jsx(Dropdown, { className: "text-sm", value: activeProfile, onChange: (e) => setActiveProfile(e.value), optionLabel: "name", options: profiles })] }), activeProfile && (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-gray-700", children: "Model" }), _jsx(Dropdown, { className: "text-sm", value: model, onChange: (e) => setModel(e.value), options: activeProfile.models })] }))] }))] }), closeButton] }), commandHandler: (v, callback) => {
|
|
989
246
|
commandHandler(v, callback);
|
|
990
|
-
} }) }),
|
|
247
|
+
} }) }), activeProfile?.errorMessage && (_jsx("div", { className: "absolute inset-0 grid items-center justify-center p-2 text-sm text-red-500", children: activeProfile?.errorMessage })), !model && (_jsx("div", { className: "absolute inset-0 grid items-center justify-center text-sm text-gray-400", children: !activeProfile ? "No profile selected" : "No model selected" }))] }) }));
|
|
991
248
|
}
|
|
992
249
|
//# sourceMappingURL=AiTerminal.js.map
|