@alpaca-editor/core 1.0.4027 → 1.0.4031

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/dist/components/ActionButton.js +2 -2
  2. package/dist/components/ActionButton.js.map +1 -1
  3. package/dist/components/SimpleLanguageSelector.js +3 -1
  4. package/dist/components/SimpleLanguageSelector.js.map +1 -1
  5. package/dist/components/ui/button.d.ts +1 -1
  6. package/dist/config/config.js +1 -1
  7. package/dist/config/config.js.map +1 -1
  8. package/dist/config/types.d.ts +1 -1
  9. package/dist/editor/ContextMenu.js +0 -1
  10. package/dist/editor/ContextMenu.js.map +1 -1
  11. package/dist/editor/Editor.js +9 -3
  12. package/dist/editor/Editor.js.map +1 -1
  13. package/dist/editor/FieldListField.js +16 -24
  14. package/dist/editor/FieldListField.js.map +1 -1
  15. package/dist/editor/ImageEditButton.js +1 -1
  16. package/dist/editor/ImageEditButton.js.map +1 -1
  17. package/dist/editor/ImageEditor.js +1 -1
  18. package/dist/editor/ImageEditor.js.map +1 -1
  19. package/dist/editor/MainLayout.js +2 -2
  20. package/dist/editor/MainLayout.js.map +1 -1
  21. package/dist/editor/Terminal.js +1 -1
  22. package/dist/editor/Terminal.js.map +1 -1
  23. package/dist/editor/Titlebar.js +0 -1
  24. package/dist/editor/Titlebar.js.map +1 -1
  25. package/dist/editor/ai/AgentCostDisplay.d.ts +26 -0
  26. package/dist/editor/ai/AgentCostDisplay.js +65 -0
  27. package/dist/editor/ai/AgentCostDisplay.js.map +1 -0
  28. package/dist/editor/ai/Agents.js +59 -15
  29. package/dist/editor/ai/Agents.js.map +1 -1
  30. package/dist/editor/ai/AiPromptPopover.d.ts +7 -0
  31. package/dist/editor/ai/AiPromptPopover.js +111 -0
  32. package/dist/editor/ai/AiPromptPopover.js.map +1 -0
  33. package/dist/editor/ai/AiResponseMessage.d.ts +1 -0
  34. package/dist/editor/ai/AiResponseMessage.js +101 -23
  35. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  36. package/dist/editor/ai/AiTerminal.d.ts +15 -1
  37. package/dist/editor/ai/AiTerminal.js +379 -48
  38. package/dist/editor/ai/AiTerminal.js.map +1 -1
  39. package/dist/editor/ai/editorAiContext.d.ts +0 -1
  40. package/dist/editor/ai/editorAiContext.js +0 -2
  41. package/dist/editor/ai/editorAiContext.js.map +1 -1
  42. package/dist/editor/client/EditorClient.d.ts +3 -2
  43. package/dist/editor/client/EditorClient.js +326 -68
  44. package/dist/editor/client/EditorClient.js.map +1 -1
  45. package/dist/editor/client/editContext.d.ts +6 -4
  46. package/dist/editor/client/editContext.js.map +1 -1
  47. package/dist/editor/client/fieldModificationStore.d.ts +19 -0
  48. package/dist/editor/client/fieldModificationStore.js +125 -0
  49. package/dist/editor/client/fieldModificationStore.js.map +1 -0
  50. package/dist/editor/client/itemsRepository.d.ts +1 -1
  51. package/dist/editor/client/itemsRepository.js +38 -28
  52. package/dist/editor/client/itemsRepository.js.map +1 -1
  53. package/dist/editor/client/operations.d.ts +1 -0
  54. package/dist/editor/client/operations.js +39 -31
  55. package/dist/editor/client/operations.js.map +1 -1
  56. package/dist/editor/commands/componentCommands.js +5 -3
  57. package/dist/editor/commands/componentCommands.js.map +1 -1
  58. package/dist/editor/commands/itemCommands.js.map +1 -1
  59. package/dist/editor/component-designer/aiContext.js +0 -2
  60. package/dist/editor/component-designer/aiContext.js.map +1 -1
  61. package/dist/editor/field-types/DropLinkEditor.js +1 -1
  62. package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
  63. package/dist/editor/field-types/MultiLineText.js +5 -7
  64. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  65. package/dist/editor/field-types/RichTextEditorComponent.js +5 -7
  66. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  67. package/dist/editor/field-types/SingleLineText.js +5 -7
  68. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  69. package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
  70. package/dist/editor/hooks/useEditorSettings.js +61 -0
  71. package/dist/editor/hooks/useEditorSettings.js.map +1 -0
  72. package/dist/editor/menubar/ItemActionsMenu.js +2 -2
  73. package/dist/editor/menubar/ItemActionsMenu.js.map +1 -1
  74. package/dist/editor/menubar/PageSelector.js +1 -1
  75. package/dist/editor/menubar/PageSelector.js.map +1 -1
  76. package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
  77. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  78. package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
  79. package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
  80. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
  81. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  82. package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
  83. package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
  84. package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +4 -3
  85. package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
  86. package/dist/editor/page-editor-chrome/FrameMenu.js +9 -1
  87. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  88. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -1
  89. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
  90. package/dist/editor/page-viewer/EditorForm.js +1 -1
  91. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  92. package/dist/editor/page-viewer/PageViewer.js +9 -8
  93. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  94. package/dist/editor/page-viewer/PageViewerFrame.js +7 -1
  95. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  96. package/dist/editor/page-viewer/pageViewContext.js +40 -6
  97. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  98. package/dist/editor/reviews/Comment.js +7 -6
  99. package/dist/editor/reviews/Comment.js.map +1 -1
  100. package/dist/editor/services/agentService.d.ts +84 -12
  101. package/dist/editor/services/agentService.js +256 -15
  102. package/dist/editor/services/agentService.js.map +1 -1
  103. package/dist/editor/services/aiService.d.ts +17 -3
  104. package/dist/editor/services/aiService.js +5 -3
  105. package/dist/editor/services/aiService.js.map +1 -1
  106. package/dist/editor/services/contextService.js +0 -1
  107. package/dist/editor/services/contextService.js.map +1 -1
  108. package/dist/editor/services/systemService.d.ts +2 -1
  109. package/dist/editor/services/systemService.js +3 -0
  110. package/dist/editor/services/systemService.js.map +1 -1
  111. package/dist/editor/sidebar/ComponentPalette.js +1 -1
  112. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  113. package/dist/editor/sidebar/EditHistory.js +2 -2
  114. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  115. package/dist/editor/sidebar/GraphQL.d.ts +1 -0
  116. package/dist/editor/sidebar/GraphQL.js +8 -2
  117. package/dist/editor/sidebar/GraphQL.js.map +1 -1
  118. package/dist/editor/sidebar/MainContentTree.js +1 -1
  119. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  120. package/dist/editor/sidebar/SEOInfo.js +1 -1
  121. package/dist/editor/sidebar/SEOInfo.js.map +1 -1
  122. package/dist/editor/sidebar/ViewSelector.d.ts +4 -1
  123. package/dist/editor/sidebar/ViewSelector.js +64 -48
  124. package/dist/editor/sidebar/ViewSelector.js.map +1 -1
  125. package/dist/editor/ui/PerfectTree.js +2 -11
  126. package/dist/editor/ui/PerfectTree.js.map +1 -1
  127. package/dist/editor/ui/SimpleIconButton.d.ts +2 -0
  128. package/dist/editor/ui/SimpleIconButton.js +8 -4
  129. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  130. package/dist/index.d.ts +4 -2
  131. package/dist/index.js +3 -1
  132. package/dist/index.js.map +1 -1
  133. package/dist/page-wizard/steps/CollectStep.js +1 -1
  134. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  135. package/dist/page-wizard/steps/StructureStep.js +1 -1
  136. package/dist/page-wizard/steps/StructureStep.js.map +1 -1
  137. package/dist/page-wizard/steps/TranslateStep.js +233 -18
  138. package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
  139. package/dist/revision.d.ts +2 -2
  140. package/dist/revision.js +2 -2
  141. package/dist/splash-screen/RecentPages.js +1 -13
  142. package/dist/splash-screen/RecentPages.js.map +1 -1
  143. package/dist/splash-screen/SplashScreen.js +1 -1
  144. package/dist/splash-screen/SplashScreen.js.map +1 -1
  145. package/dist/styles.css +88 -3
  146. package/dist/types.d.ts +6 -0
  147. package/package.json +2 -2
  148. package/src/components/ActionButton.tsx +3 -2
  149. package/src/components/SimpleLanguageSelector.tsx +6 -1
  150. package/src/config/config.tsx +1 -1
  151. package/src/config/types.ts +1 -1
  152. package/src/editor/ContextMenu.tsx +0 -3
  153. package/src/editor/Editor.tsx +11 -3
  154. package/src/editor/FieldListField.tsx +22 -31
  155. package/src/editor/ImageEditButton.tsx +1 -0
  156. package/src/editor/ImageEditor.tsx +1 -0
  157. package/src/editor/MainLayout.tsx +2 -2
  158. package/src/editor/Terminal.tsx +1 -1
  159. package/src/editor/Titlebar.tsx +0 -2
  160. package/src/editor/ai/AgentCostDisplay.tsx +237 -0
  161. package/src/editor/ai/Agents.tsx +69 -20
  162. package/src/editor/ai/AiPromptPopover.tsx +209 -0
  163. package/src/editor/ai/AiResponseMessage.tsx +201 -60
  164. package/src/editor/ai/AiTerminal.tsx +502 -71
  165. package/src/editor/ai/editorAiContext.ts +0 -3
  166. package/src/editor/client/EditorClient.tsx +409 -117
  167. package/src/editor/client/editContext.ts +7 -5
  168. package/src/editor/client/fieldModificationStore.ts +196 -0
  169. package/src/editor/client/itemsRepository.ts +41 -31
  170. package/src/editor/client/operations.ts +95 -76
  171. package/src/editor/commands/componentCommands.tsx +9 -3
  172. package/src/editor/commands/itemCommands.tsx +0 -1
  173. package/src/editor/component-designer/aiContext.ts +0 -2
  174. package/src/editor/field-types/DropLinkEditor.tsx +1 -1
  175. package/src/editor/field-types/MultiLineText.tsx +9 -9
  176. package/src/editor/field-types/RichTextEditorComponent.tsx +8 -8
  177. package/src/editor/field-types/SingleLineText.tsx +9 -9
  178. package/src/editor/hooks/useEditorSettings.ts +68 -0
  179. package/src/editor/menubar/ItemActionsMenu.tsx +3 -2
  180. package/src/editor/menubar/PageSelector.tsx +1 -1
  181. package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
  182. package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -0
  183. package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +2 -0
  184. package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +2 -0
  185. package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +4 -3
  186. package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
  187. package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -1
  188. package/src/editor/page-viewer/EditorForm.tsx +1 -0
  189. package/src/editor/page-viewer/PageViewer.tsx +9 -8
  190. package/src/editor/page-viewer/PageViewerFrame.tsx +7 -1
  191. package/src/editor/page-viewer/pageViewContext.ts +40 -5
  192. package/src/editor/reviews/Comment.tsx +7 -7
  193. package/src/editor/services/agentService.ts +405 -31
  194. package/src/editor/services/aiService.ts +22 -5
  195. package/src/editor/services/contextService.ts +0 -1
  196. package/src/editor/services/systemService.ts +7 -1
  197. package/src/editor/sidebar/ComponentPalette.tsx +4 -1
  198. package/src/editor/sidebar/EditHistory.tsx +2 -0
  199. package/src/editor/sidebar/GraphQL.tsx +19 -7
  200. package/src/editor/sidebar/MainContentTree.tsx +1 -1
  201. package/src/editor/sidebar/SEOInfo.tsx +1 -1
  202. package/src/editor/sidebar/ViewSelector.tsx +80 -64
  203. package/src/editor/ui/PerfectTree.tsx +2 -18
  204. package/src/editor/ui/SimpleIconButton.tsx +56 -38
  205. package/src/index.ts +4 -2
  206. package/src/page-wizard/steps/CollectStep.tsx +0 -2
  207. package/src/page-wizard/steps/StructureStep.tsx +3 -0
  208. package/src/page-wizard/steps/TranslateStep.tsx +473 -62
  209. package/src/revision.ts +2 -2
  210. package/src/splash-screen/RecentPages.tsx +0 -14
  211. package/src/splash-screen/SplashScreen.tsx +3 -2
  212. package/src/types.ts +7 -0
  213. package/dist/editor/ai/AiPopup.d.ts +0 -10
  214. package/dist/editor/ai/AiPopup.js +0 -23
  215. package/dist/editor/ai/AiPopup.js.map +0 -1
  216. package/dist/editor/ai/AiToolCall.d.ts +0 -5
  217. package/dist/editor/ai/AiToolCall.js +0 -28
  218. package/dist/editor/ai/AiToolCall.js.map +0 -1
  219. package/src/editor/ai/AiPopup.tsx +0 -59
  220. package/src/editor/ai/AiToolCall.tsx +0 -71
@@ -10,14 +10,24 @@ import { Dropdown } from "primereact/dropdown";
10
10
 
11
11
  import { WizardIcon } from "../ui/Icons";
12
12
  import { AiResponseMessage } from "./AiResponseMessage";
13
+ import { AiProfile, loadAiProfiles } from "../services/aiService";
13
14
  import {
14
- AiProfile,
15
- loadAiProfiles,
16
- executePrompt,
17
- } from "../services/aiService";
15
+ startAgent,
16
+ connectToAgentStream,
17
+ getAgentStatus,
18
+ getRunningAgents,
19
+ getAgent,
20
+ getAgents,
21
+ getChatHistory,
22
+ cancelAgent,
23
+ AgentStreamMessageType,
24
+ StartAgentRequest,
25
+ AgentStreamMessage,
26
+ } from "../services/agentService";
18
27
  import { EditOperation } from "../../types";
19
28
  import { SimpleIconButton } from "../ui/SimpleIconButton";
20
29
  import { Settings } from "lucide-react";
30
+ import { AgentCostDisplay } from "./AgentCostDisplay";
21
31
 
22
32
  type Response = {
23
33
  messages: Message[];
@@ -27,6 +37,14 @@ type Response = {
27
37
  numCachedTokens: number;
28
38
  state: string;
29
39
  agentName?: string; // Updated agent name (only sent when the agent name has been updated)
40
+ // Cost information from backend
41
+ totalCost?: number;
42
+ totalInputTokenCost?: number;
43
+ totalOutputTokenCost?: number;
44
+ totalCachedTokenCost?: number;
45
+ totalInputTokens?: number;
46
+ totalOutputTokens?: number;
47
+ totalCachedTokens?: number;
30
48
  };
31
49
 
32
50
  export type ToolCall = {
@@ -48,9 +66,22 @@ export type Message = {
48
66
  tool_call_id?: string; // For tool response messages
49
67
  };
50
68
 
69
+ // Helper function to format message content
70
+ function formatMessageContent(content: string): string {
71
+ return (
72
+ content
73
+ ?.trim()
74
+ ?.replaceAll("\n", "<br>")
75
+ ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") // Bold markdown
76
+ ?.replace(
77
+ /\[([^\]]+)\]\(([^)]+)\)/g,
78
+ '<a href="$2" target="_blank" class="text-blue-500" rel="noopener noreferrer">$1</a>',
79
+ ) || "" // Links
80
+ );
81
+ }
82
+
51
83
  export type AiContext = {
52
84
  promptData: any;
53
- endpoint: string;
54
85
  callback?: (response: Response) => void;
55
86
  };
56
87
 
@@ -58,6 +89,15 @@ export type AiTerminalOptions = {
58
89
  initialPrompt?: string;
59
90
  hiddenSystemPrompt?: string;
60
91
  initialMessages?: Message[];
92
+ agentId?: string;
93
+ // Cost information for loaded agent sessions
94
+ totalCost?: number;
95
+ totalInputTokenCost?: number;
96
+ totalOutputTokenCost?: number;
97
+ totalCachedTokenCost?: number;
98
+ totalInputTokens?: number;
99
+ totalOutputTokens?: number;
100
+ totalCachedTokens?: number;
61
101
  };
62
102
 
63
103
  export function AiTerminal({
@@ -87,7 +127,9 @@ export function AiTerminal({
87
127
  const [profiles, setProfiles] = useState<AiProfile[]>([]);
88
128
  const [activeProfile, setActiveProfile] = useState<AiProfile>();
89
129
  const [initialPromptExecuted, setInitialPromptExecuted] = useState(false);
90
- const [agentId, setAgentId] = useState<string>(() => crypto.randomUUID());
130
+ const [agentId, setAgentId] = useState<string>(
131
+ () => options?.agentId || crypto.randomUUID(),
132
+ );
91
133
  const selection = editContext.selection;
92
134
  const terminalRef = useRef<{ submit: () => void }>(null);
93
135
  const [responseMessages, setResponseMessages] = useState<Message[]>(
@@ -150,11 +192,7 @@ export function AiTerminal({
150
192
 
151
193
  // Format the initial messages for display
152
194
  const formattedMessages = options.initialMessages.map((message) => {
153
- const formattedContent =
154
- message.content
155
- ?.trim()
156
- ?.replaceAll("\n", "<br>")
157
- ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
195
+ const formattedContent = formatMessageContent(message.content || "");
158
196
 
159
197
  return {
160
198
  ...message,
@@ -164,8 +202,6 @@ export function AiTerminal({
164
202
  };
165
203
  });
166
204
 
167
- console.log("AiTerminal: Formatted messages", formattedMessages);
168
-
169
205
  // Update the internal message states
170
206
  setMessages(formattedMessages);
171
207
  setResponseMessages(formattedMessages);
@@ -174,13 +210,20 @@ export function AiTerminal({
174
210
  const initialResponse: Response = {
175
211
  messages: formattedMessages,
176
212
  editOperations: [],
177
- numInputTokens: 0,
178
- numOutputTokens: 0,
179
- numCachedTokens: 0,
213
+ numInputTokens: options?.totalInputTokens || 0,
214
+ numOutputTokens: options?.totalOutputTokens || 0,
215
+ numCachedTokens: options?.totalCachedTokens || 0,
180
216
  state: "loaded",
217
+ // Include cost information from loaded agent session
218
+ totalCost: options?.totalCost,
219
+ totalInputTokenCost: options?.totalInputTokenCost,
220
+ totalOutputTokenCost: options?.totalOutputTokenCost,
221
+ totalCachedTokenCost: options?.totalCachedTokenCost,
222
+ totalInputTokens: options?.totalInputTokens,
223
+ totalOutputTokens: options?.totalOutputTokens,
224
+ totalCachedTokens: options?.totalCachedTokens,
181
225
  };
182
226
 
183
- console.log("AiTerminal: Setting response", initialResponse);
184
227
  setResponse(initialResponse);
185
228
 
186
229
  // Create individual Terminal messages for each conversation message
@@ -280,11 +323,7 @@ export function AiTerminal({
280
323
  // Replace the conversation history with the authoritative response from AI
281
324
  if (updatedMessages && Array.isArray(updatedMessages)) {
282
325
  const formattedMessages = updatedMessages.map((message) => {
283
- const formattedContent =
284
- message.content
285
- ?.trim()
286
- ?.replaceAll("\n", "<br>")
287
- ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
326
+ const formattedContent = formatMessageContent(message.content || "");
288
327
 
289
328
  return {
290
329
  ...message,
@@ -333,7 +372,6 @@ export function AiTerminal({
333
372
 
334
373
  const context = createAiContext({ editContext });
335
374
 
336
- let lastOpIndex = 0;
337
375
  const selectedText = editContext?.selectedRange?.text || null;
338
376
  if (!activeProfile || !model) return;
339
377
 
@@ -357,47 +395,418 @@ export function AiTerminal({
357
395
  setMessages((prevMessages) => [...prevMessages, userMessage]);
358
396
  setResponseMessages((prevMessages) => [...prevMessages, userMessage]);
359
397
 
360
- const response = await executePrompt(
361
- messages,
362
- context,
363
- {
398
+ try {
399
+ // Step 1: Start the agent execution
400
+ const startRequest: StartAgentRequest = {
401
+ agentId,
402
+ messages,
403
+ sessionId: editContext!.sessionId,
364
404
  profileId: activeProfile.id,
365
- selection,
366
- selectedText,
367
405
  model,
368
- sessionId: editContext!.sessionId,
369
- agentId,
406
+ itemid: context.promptData.itemid,
407
+ language: context.promptData.language,
408
+ version: context.promptData.version,
409
+ selection,
410
+ selectedText: selectedText || undefined,
411
+ allowedFunctions: context.promptData.allowedFunctions,
412
+ addContextContent: context.promptData.addContextContent,
413
+ addAllContent: context.promptData.addAllContent,
370
414
  addSelectedComponents: true,
371
- },
372
- undefined,
373
- (response: any) => {
374
- setResponse(response);
375
- handleResponse(response, callback, false);
376
- },
377
- );
378
-
379
- if (response) {
380
- handleResponse(response, callback, true);
381
-
382
- // Handle agent name update
383
- if ((response as any).agentName && onAgentNameUpdate) {
384
- onAgentNameUpdate((response as any).agentName);
385
- }
386
-
387
- if (context.callback) context.callback(response);
388
- editContext?.requestRefresh("immediate");
389
- }
415
+ profile: activeProfile.name,
416
+ };
390
417
 
391
- // if (response?.responseText)
392
- // newMessages.push({
393
- // content: response.responseText,
394
- // role: "assistant",
395
- // name: "assistant",
396
- // });
418
+ const startResponse = await startAgent(startRequest, context);
419
+ console.log("Agent started:", startResponse);
397
420
 
398
- setResponse(response ? response : undefined);
421
+ // Step 2: Connect to the agent stream for real-time updates
422
+ const abortController = new AbortController();
423
+ let accumulatedResponse: any = {
424
+ messages: [...messages],
425
+ editOperations: [],
426
+ numInputTokens: 0,
427
+ numOutputTokens: 0,
428
+ numCachedTokens: 0,
429
+ state: "running",
430
+ };
431
+ let completionProcessed = false;
432
+
433
+ try {
434
+ console.log("Connecting to agent stream");
435
+ await connectToAgentStream(
436
+ agentId,
437
+ context,
438
+ (streamMessage) => {
439
+ // console.log(
440
+ // "Stream message:",
441
+ // streamMessage,
442
+ // new Date().toISOString(),
443
+ // );
444
+
445
+ // Message types are now normalized in agentService.ts
446
+ switch (streamMessage.type) {
447
+ case AgentStreamMessageType.StatusUpdate:
448
+ //console.log("Status update:", streamMessage.data);
449
+ break;
450
+
451
+ case AgentStreamMessageType.ContentChunk:
452
+ if (streamMessage.data) {
453
+ // Handle incremental content updates
454
+ if (
455
+ streamMessage.data.isIncremental &&
456
+ streamMessage.data.deltaContent
457
+ ) {
458
+ // Find the last assistant message to append delta content to
459
+ const updatedMessages = [
460
+ ...(accumulatedResponse.messages || []),
461
+ ];
462
+ let lastAssistantMessage = null;
463
+ let lastAssistantIndex = -1;
464
+
465
+ // Find the last assistant message
466
+ for (let i = updatedMessages.length - 1; i >= 0; i--) {
467
+ if (updatedMessages[i].role === "assistant") {
468
+ lastAssistantMessage = updatedMessages[i];
469
+ lastAssistantIndex = i;
470
+ break;
471
+ }
472
+ }
473
+
474
+ // If no assistant message exists, or the last one has tool calls, create a new one
475
+ if (
476
+ !lastAssistantMessage ||
477
+ (lastAssistantMessage.tool_calls &&
478
+ lastAssistantMessage.tool_calls.length > 0)
479
+ ) {
480
+ lastAssistantMessage = {
481
+ id: crypto.randomUUID(),
482
+ role: "assistant",
483
+ name: "assistant",
484
+ content: "",
485
+ tool_calls: [],
486
+ };
487
+ updatedMessages.push(lastAssistantMessage);
488
+ lastAssistantIndex = updatedMessages.length - 1;
489
+ }
490
+
491
+ // Append the delta content to build up the full message
492
+ const currentContent = lastAssistantMessage.content || "";
493
+ const newContent =
494
+ currentContent + streamMessage.data.deltaContent;
495
+
496
+ // Update the message with accumulated content
497
+ updatedMessages[lastAssistantIndex] = {
498
+ ...lastAssistantMessage,
499
+ content: newContent,
500
+ };
501
+
502
+ // Update accumulated response with incremental content
503
+ accumulatedResponse = {
504
+ ...accumulatedResponse,
505
+ ...streamMessage.data,
506
+ messages: updatedMessages,
507
+ editOperations:
508
+ streamMessage.data.editOperations ||
509
+ accumulatedResponse.editOperations,
510
+ };
511
+ } else {
512
+ // Non-incremental update - use as provided
513
+ accumulatedResponse = {
514
+ ...accumulatedResponse,
515
+ ...streamMessage.data,
516
+ editOperations:
517
+ streamMessage.data.editOperations ||
518
+ accumulatedResponse.editOperations,
519
+ };
520
+ }
521
+
522
+ // Always trigger UI update for content chunks
523
+ handleResponse(accumulatedResponse, callback, false);
524
+ }
525
+ break;
526
+
527
+ case AgentStreamMessageType.ToolCall:
528
+ // console.log("Tool call received:", streamMessage.data);
529
+ if (streamMessage.data) {
530
+ // Find or create the assistant message that contains this tool call
531
+ const updatedMessages = [
532
+ ...(accumulatedResponse.messages || []),
533
+ ];
534
+ let lastAssistantMessage = null;
535
+ let lastAssistantIndex = -1;
536
+
537
+ // Find the last assistant message
538
+ for (let i = updatedMessages.length - 1; i >= 0; i--) {
539
+ if (updatedMessages[i].role === "assistant") {
540
+ lastAssistantMessage = updatedMessages[i];
541
+ lastAssistantIndex = i;
542
+ break;
543
+ }
544
+ }
545
+
546
+ // If no assistant message exists, create one
547
+ if (!lastAssistantMessage) {
548
+ lastAssistantMessage = {
549
+ id: crypto.randomUUID(),
550
+ role: "assistant",
551
+ name: "assistant",
552
+ content: "",
553
+ tool_calls: [],
554
+ };
555
+ updatedMessages.push(lastAssistantMessage);
556
+ lastAssistantIndex = updatedMessages.length - 1;
557
+ }
558
+
559
+ // Add or update the tool call
560
+ const toolCall = {
561
+ id: streamMessage.data.id,
562
+ displayName: streamMessage.data.displayName,
563
+ function: {
564
+ name:
565
+ streamMessage.data.function?.name ||
566
+ streamMessage.data.name,
567
+ arguments:
568
+ streamMessage.data.function?.arguments ||
569
+ streamMessage.data.arguments,
570
+ },
571
+ };
572
+
573
+ // Update or add the tool call to the assistant message
574
+ const existingToolCalls =
575
+ lastAssistantMessage.tool_calls || [];
576
+ const existingIndex = existingToolCalls.findIndex(
577
+ (tc: any) => tc.id === toolCall.id,
578
+ );
579
+
580
+ let updatedToolCalls;
581
+ if (existingIndex >= 0) {
582
+ // Update existing tool call
583
+ updatedToolCalls = [...existingToolCalls];
584
+ updatedToolCalls[existingIndex] = toolCall;
585
+ } else {
586
+ // Add new tool call
587
+ updatedToolCalls = [...existingToolCalls, toolCall];
588
+ }
589
+
590
+ // Update the assistant message with the tool call
591
+ updatedMessages[lastAssistantIndex] = {
592
+ ...lastAssistantMessage,
593
+ tool_calls: updatedToolCalls,
594
+ };
595
+
596
+ // Update accumulated response
597
+ accumulatedResponse = {
598
+ ...accumulatedResponse,
599
+ messages: updatedMessages,
600
+ };
601
+
602
+ // Trigger UI update to show the tool call
603
+ handleResponse(accumulatedResponse, callback, false);
604
+ }
605
+ break;
606
+
607
+ case AgentStreamMessageType.ToolResult:
608
+ console.log("Tool result received:", streamMessage.data);
609
+ if (streamMessage.data) {
610
+ // Find the assistant message and tool call to update with the result
611
+ const updatedMessages = [
612
+ ...(accumulatedResponse.messages || []),
613
+ ];
614
+
615
+ // Find the assistant message with the matching tool call ID
616
+ for (let i = updatedMessages.length - 1; i >= 0; i--) {
617
+ if (
618
+ updatedMessages[i].role === "assistant" &&
619
+ updatedMessages[i].tool_calls
620
+ ) {
621
+ const toolCalls = updatedMessages[i].tool_calls || [];
622
+ const toolCallIndex = toolCalls.findIndex(
623
+ (tc: any) => tc.id === streamMessage.data.toolCallId,
624
+ );
625
+
626
+ if (toolCallIndex >= 0) {
627
+ // Update the tool call with the result
628
+ const updatedToolCalls = [...toolCalls];
629
+ updatedToolCalls[toolCallIndex] = {
630
+ ...updatedToolCalls[toolCallIndex],
631
+ function: {
632
+ ...updatedToolCalls[toolCallIndex].function,
633
+ result: streamMessage.data.result,
634
+ },
635
+ };
636
+
637
+ // Update the message
638
+ updatedMessages[i] = {
639
+ ...updatedMessages[i],
640
+ tool_calls: updatedToolCalls,
641
+ };
642
+ break;
643
+ }
644
+ }
645
+ }
646
+
647
+ // Update accumulated response
648
+ accumulatedResponse = {
649
+ ...accumulatedResponse,
650
+ messages: updatedMessages,
651
+ };
652
+
653
+ // Trigger UI update to show the tool result
654
+ handleResponse(accumulatedResponse, callback, false);
655
+ }
656
+ break;
657
+
658
+ case AgentStreamMessageType.Completed:
659
+ if (streamMessage.data) {
660
+ // Final completion data
661
+ const finalResponse = {
662
+ messages:
663
+ streamMessage.data.messages ||
664
+ accumulatedResponse.messages,
665
+ editOperations:
666
+ streamMessage.data.editOperations ||
667
+ accumulatedResponse.editOperations,
668
+ numInputTokens:
669
+ streamMessage.data.tokenUsage?.inputTokens ||
670
+ accumulatedResponse.numInputTokens,
671
+ numOutputTokens:
672
+ streamMessage.data.tokenUsage?.outputTokens ||
673
+ accumulatedResponse.numOutputTokens,
674
+ numCachedTokens:
675
+ streamMessage.data.tokenUsage?.cachedTokens ||
676
+ accumulatedResponse.numCachedTokens,
677
+ state: "completed",
678
+ agentName: streamMessage.data.agentName,
679
+ // Add cost fields from backend (try both tokenUsage object and direct properties)
680
+ totalCost:
681
+ streamMessage.data.totalCost ||
682
+ streamMessage.data.tokenUsage?.totalCost,
683
+ totalInputTokenCost:
684
+ streamMessage.data.totalInputTokenCost ||
685
+ streamMessage.data.tokenUsage?.inputTokenCost,
686
+ totalOutputTokenCost:
687
+ streamMessage.data.totalOutputTokenCost ||
688
+ streamMessage.data.tokenUsage?.outputTokenCost,
689
+ totalCachedTokenCost:
690
+ streamMessage.data.totalCachedTokenCost ||
691
+ streamMessage.data.tokenUsage?.cachedTokenCost,
692
+ totalInputTokens:
693
+ streamMessage.data.totalInputTokens ||
694
+ streamMessage.data.tokenUsage?.inputTokens,
695
+ totalOutputTokens:
696
+ streamMessage.data.totalOutputTokens ||
697
+ streamMessage.data.tokenUsage?.outputTokens,
698
+ totalCachedTokens:
699
+ streamMessage.data.totalCachedTokens ||
700
+ streamMessage.data.tokenUsage?.cachedTokens,
701
+ };
702
+
703
+ completionProcessed = true;
704
+ setResponse(finalResponse);
705
+
706
+ // On completion, just update the internal state and call the final callback
707
+ // Don't call handleResponse again as it would duplicate the message
708
+ // The Terminal component will clear the streaming response and add it to the message history
709
+ const formattedMessages = (finalResponse.messages || []).map(
710
+ (message: any) => {
711
+ const formattedContent = formatMessageContent(
712
+ message.content || "",
713
+ );
714
+
715
+ return {
716
+ ...message,
717
+ content: message.content || "",
718
+ formattedContent: formattedContent,
719
+ tool_calls: message.tool_calls || [],
720
+ };
721
+ },
722
+ );
723
+
724
+ // Update internal state without triggering a new render via handleResponse
725
+ setMessages([...formattedMessages]);
726
+ setResponseMessages([...formattedMessages]);
727
+
728
+ // Only call the terminal callback to mark completion (this will add to history)
729
+ callback(
730
+ <AiResponseMessage
731
+ messages={formattedMessages}
732
+ editOperations={finalResponse.editOperations}
733
+ finished={true}
734
+ />,
735
+ true,
736
+ );
737
+
738
+ // Handle agent name update
739
+ if (finalResponse.agentName && onAgentNameUpdate) {
740
+ onAgentNameUpdate(finalResponse.agentName);
741
+ }
742
+
743
+ if (context.callback) context.callback(finalResponse);
744
+ //editContext?.requestRefresh("immediate");
745
+ }
746
+ break;
747
+
748
+ case AgentStreamMessageType.Error:
749
+ console.error("Agent execution error:", streamMessage.error);
750
+ const errorResponse = {
751
+ ...accumulatedResponse,
752
+ state: "error",
753
+ error: streamMessage.error,
754
+ };
755
+ setResponse(errorResponse);
756
+ handleResponse(errorResponse, callback, true);
757
+ break;
758
+
759
+ default:
760
+ console.log(
761
+ "Unhandled stream message type:",
762
+ streamMessage.type,
763
+ );
764
+ break;
765
+ }
766
+ },
767
+ abortController.signal,
768
+ );
399
769
 
400
- // setMessages(newMessages);
770
+ // If we reach here, the stream ended without a Completed message
771
+ if (!completionProcessed) {
772
+ // console.log("Stream ended without Completed message, cleaning up");
773
+ const finalResponse = {
774
+ ...accumulatedResponse,
775
+ state: "completed",
776
+ };
777
+ setResponse(finalResponse);
778
+ handleResponse(finalResponse, callback, true);
779
+ }
780
+ } catch (streamError) {
781
+ console.error("Stream connection error:", streamError);
782
+ const errorMessage =
783
+ streamError instanceof Error
784
+ ? streamError.message
785
+ : "Unknown stream error";
786
+ const errorResponse = {
787
+ ...accumulatedResponse,
788
+ state: "error",
789
+ error: errorMessage,
790
+ };
791
+ setResponse(errorResponse);
792
+ handleResponse(errorResponse, callback, true);
793
+ }
794
+ } catch (error) {
795
+ console.error("Error starting agent:", error);
796
+ const errorMessage =
797
+ error instanceof Error ? error.message : "Unknown error";
798
+ const errorResponse = {
799
+ messages: [...messages],
800
+ editOperations: [],
801
+ numInputTokens: 0,
802
+ numOutputTokens: 0,
803
+ numCachedTokens: 0,
804
+ state: "error",
805
+ error: errorMessage,
806
+ };
807
+ setResponse(errorResponse);
808
+ handleResponse(errorResponse, callback, true);
809
+ }
401
810
  }
402
811
 
403
812
  useEffect(() => {
@@ -428,19 +837,39 @@ export function AiTerminal({
428
837
  }}
429
838
  initialMessages={initialTerminalMessages}
430
839
  infobar={
431
- response?.numInputTokens && response?.numInputTokens > 0 ? (
432
- <div
433
- className="text-right text-gray-400"
434
- style={{ fontSize: "10px" }}
435
- >
436
- Tokens in: {response?.numInputTokens?.toLocaleString()} out:{" "}
437
- {response?.numOutputTokens?.toLocaleString()}{" "}
438
- {response?.numCachedTokens
439
- ? `cached: ${response?.numCachedTokens?.toLocaleString()} `
440
- : ""}
441
- {response?.state}
442
- </div>
443
- ) : null
840
+ <AgentCostDisplay
841
+ response={
842
+ response
843
+ ? {
844
+ numInputTokens: response.numInputTokens,
845
+ numOutputTokens: response.numOutputTokens,
846
+ numCachedTokens: response.numCachedTokens,
847
+ totalCost: response.totalCost,
848
+ inputCost: response.totalInputTokenCost,
849
+ outputCost: response.totalOutputTokenCost,
850
+ cachedCost: response.totalCachedTokenCost,
851
+ }
852
+ : null
853
+ }
854
+ totalTokens={
855
+ response
856
+ ? {
857
+ input:
858
+ response.totalInputTokens || response.numInputTokens,
859
+ output:
860
+ response.totalOutputTokens ||
861
+ response.numOutputTokens,
862
+ cached:
863
+ response.totalCachedTokens ||
864
+ response.numCachedTokens,
865
+ totalCost: response.totalCost || 0,
866
+ inputCost: response.totalInputTokenCost || 0,
867
+ outputCost: response.totalOutputTokenCost || 0,
868
+ cachedCost: response.totalCachedTokenCost || 0,
869
+ }
870
+ : undefined
871
+ }
872
+ />
444
873
  }
445
874
  prompt={prompt}
446
875
  setPrompt={setPrompt}
@@ -515,6 +944,8 @@ export function AiTerminal({
515
944
  value={model}
516
945
  onChange={(e) => setModel(e.value)}
517
946
  options={activeProfile.models}
947
+ optionLabel="name"
948
+ optionValue="id"
518
949
  />
519
950
  </div>
520
951
  )}
@@ -5,10 +5,7 @@ export function createEditorAiContext({
5
5
  }: {
6
6
  editContext: EditContextType;
7
7
  }) {
8
- const aiPromptUrl = editContext.configuration.services.aiService.promptUrl;
9
-
10
8
  return {
11
- endpoint: aiPromptUrl,
12
9
  promptData: {
13
10
  itemid: editContext.currentItemDescriptor?.id,
14
11
  language: editContext.currentItemDescriptor?.language,