@alpaca-editor/core 1.0.4026 → 1.0.4030

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 (224) 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 +83 -29
  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 +104 -18
  35. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  36. package/dist/editor/ai/AiTerminal.d.ts +18 -2
  37. package/dist/editor/ai/AiTerminal.js +423 -63
  38. package/dist/editor/ai/AiTerminal.js.map +1 -1
  39. package/dist/editor/ai/EditorAiTerminal.d.ts +2 -1
  40. package/dist/editor/ai/EditorAiTerminal.js +2 -2
  41. package/dist/editor/ai/EditorAiTerminal.js.map +1 -1
  42. package/dist/editor/ai/editorAiContext.d.ts +0 -1
  43. package/dist/editor/ai/editorAiContext.js +0 -2
  44. package/dist/editor/ai/editorAiContext.js.map +1 -1
  45. package/dist/editor/client/EditorClient.d.ts +3 -2
  46. package/dist/editor/client/EditorClient.js +326 -68
  47. package/dist/editor/client/EditorClient.js.map +1 -1
  48. package/dist/editor/client/editContext.d.ts +6 -4
  49. package/dist/editor/client/editContext.js.map +1 -1
  50. package/dist/editor/client/fieldModificationStore.d.ts +19 -0
  51. package/dist/editor/client/fieldModificationStore.js +125 -0
  52. package/dist/editor/client/fieldModificationStore.js.map +1 -0
  53. package/dist/editor/client/itemsRepository.d.ts +1 -1
  54. package/dist/editor/client/itemsRepository.js +38 -28
  55. package/dist/editor/client/itemsRepository.js.map +1 -1
  56. package/dist/editor/client/operations.d.ts +1 -0
  57. package/dist/editor/client/operations.js +39 -31
  58. package/dist/editor/client/operations.js.map +1 -1
  59. package/dist/editor/commands/componentCommands.js +5 -3
  60. package/dist/editor/commands/componentCommands.js.map +1 -1
  61. package/dist/editor/commands/itemCommands.js.map +1 -1
  62. package/dist/editor/component-designer/aiContext.js +0 -2
  63. package/dist/editor/component-designer/aiContext.js.map +1 -1
  64. package/dist/editor/field-types/DropLinkEditor.js +1 -1
  65. package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
  66. package/dist/editor/field-types/MultiLineText.js +5 -7
  67. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  68. package/dist/editor/field-types/RichTextEditorComponent.js +5 -7
  69. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  70. package/dist/editor/field-types/SingleLineText.js +5 -7
  71. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  72. package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
  73. package/dist/editor/hooks/useEditorSettings.js +61 -0
  74. package/dist/editor/hooks/useEditorSettings.js.map +1 -0
  75. package/dist/editor/menubar/ItemActionsMenu.js +2 -2
  76. package/dist/editor/menubar/ItemActionsMenu.js.map +1 -1
  77. package/dist/editor/menubar/PageSelector.js +1 -1
  78. package/dist/editor/menubar/PageSelector.js.map +1 -1
  79. package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
  80. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  81. package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
  82. package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
  83. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
  84. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  85. package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
  86. package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
  87. package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +4 -3
  88. package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
  89. package/dist/editor/page-editor-chrome/FrameMenu.js +9 -1
  90. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  91. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -1
  92. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
  93. package/dist/editor/page-viewer/EditorForm.js +1 -1
  94. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  95. package/dist/editor/page-viewer/PageViewer.js +9 -8
  96. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  97. package/dist/editor/page-viewer/PageViewerFrame.js +7 -1
  98. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  99. package/dist/editor/page-viewer/pageViewContext.js +40 -6
  100. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  101. package/dist/editor/reviews/Comment.js +7 -6
  102. package/dist/editor/reviews/Comment.js.map +1 -1
  103. package/dist/editor/services/agentService.d.ts +84 -12
  104. package/dist/editor/services/agentService.js +256 -15
  105. package/dist/editor/services/agentService.js.map +1 -1
  106. package/dist/editor/services/aiService.d.ts +18 -3
  107. package/dist/editor/services/aiService.js +5 -3
  108. package/dist/editor/services/aiService.js.map +1 -1
  109. package/dist/editor/services/contextService.js +0 -1
  110. package/dist/editor/services/contextService.js.map +1 -1
  111. package/dist/editor/services/systemService.d.ts +2 -1
  112. package/dist/editor/services/systemService.js +3 -0
  113. package/dist/editor/services/systemService.js.map +1 -1
  114. package/dist/editor/sidebar/ComponentPalette.js +1 -1
  115. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  116. package/dist/editor/sidebar/EditHistory.js +2 -2
  117. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  118. package/dist/editor/sidebar/GraphQL.d.ts +1 -0
  119. package/dist/editor/sidebar/GraphQL.js +8 -2
  120. package/dist/editor/sidebar/GraphQL.js.map +1 -1
  121. package/dist/editor/sidebar/MainContentTree.js +1 -1
  122. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  123. package/dist/editor/sidebar/SEOInfo.js +1 -1
  124. package/dist/editor/sidebar/SEOInfo.js.map +1 -1
  125. package/dist/editor/sidebar/ViewSelector.d.ts +4 -1
  126. package/dist/editor/sidebar/ViewSelector.js +64 -48
  127. package/dist/editor/sidebar/ViewSelector.js.map +1 -1
  128. package/dist/editor/ui/PerfectTree.js +2 -11
  129. package/dist/editor/ui/PerfectTree.js.map +1 -1
  130. package/dist/editor/ui/SimpleIconButton.d.ts +2 -0
  131. package/dist/editor/ui/SimpleIconButton.js +8 -4
  132. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  133. package/dist/index.d.ts +2 -0
  134. package/dist/index.js +2 -0
  135. package/dist/index.js.map +1 -1
  136. package/dist/page-wizard/steps/CollectStep.js +1 -1
  137. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  138. package/dist/page-wizard/steps/StructureStep.js +1 -1
  139. package/dist/page-wizard/steps/StructureStep.js.map +1 -1
  140. package/dist/page-wizard/steps/TranslateStep.js +233 -18
  141. package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
  142. package/dist/revision.d.ts +2 -2
  143. package/dist/revision.js +2 -2
  144. package/dist/splash-screen/RecentPages.js +1 -13
  145. package/dist/splash-screen/RecentPages.js.map +1 -1
  146. package/dist/splash-screen/SplashScreen.js +1 -1
  147. package/dist/splash-screen/SplashScreen.js.map +1 -1
  148. package/dist/styles.css +88 -3
  149. package/dist/types.d.ts +6 -0
  150. package/package.json +2 -2
  151. package/src/components/ActionButton.tsx +3 -2
  152. package/src/components/SimpleLanguageSelector.tsx +6 -1
  153. package/src/config/config.tsx +1 -1
  154. package/src/config/types.ts +1 -1
  155. package/src/editor/ContextMenu.tsx +0 -3
  156. package/src/editor/Editor.tsx +11 -3
  157. package/src/editor/FieldListField.tsx +22 -31
  158. package/src/editor/ImageEditButton.tsx +1 -0
  159. package/src/editor/ImageEditor.tsx +1 -0
  160. package/src/editor/MainLayout.tsx +2 -2
  161. package/src/editor/Terminal.tsx +1 -1
  162. package/src/editor/Titlebar.tsx +0 -2
  163. package/src/editor/ai/AgentCostDisplay.tsx +237 -0
  164. package/src/editor/ai/Agents.tsx +147 -65
  165. package/src/editor/ai/AiPromptPopover.tsx +209 -0
  166. package/src/editor/ai/AiResponseMessage.tsx +232 -45
  167. package/src/editor/ai/AiTerminal.tsx +559 -88
  168. package/src/editor/ai/EditorAiTerminal.tsx +3 -0
  169. package/src/editor/ai/editorAiContext.ts +0 -3
  170. package/src/editor/client/EditorClient.tsx +409 -117
  171. package/src/editor/client/editContext.ts +7 -5
  172. package/src/editor/client/fieldModificationStore.ts +196 -0
  173. package/src/editor/client/itemsRepository.ts +41 -31
  174. package/src/editor/client/operations.ts +95 -76
  175. package/src/editor/commands/componentCommands.tsx +9 -3
  176. package/src/editor/commands/itemCommands.tsx +0 -1
  177. package/src/editor/component-designer/aiContext.ts +0 -2
  178. package/src/editor/field-types/DropLinkEditor.tsx +1 -1
  179. package/src/editor/field-types/MultiLineText.tsx +9 -9
  180. package/src/editor/field-types/RichTextEditorComponent.tsx +8 -8
  181. package/src/editor/field-types/SingleLineText.tsx +9 -9
  182. package/src/editor/hooks/useEditorSettings.ts +68 -0
  183. package/src/editor/menubar/ItemActionsMenu.tsx +3 -2
  184. package/src/editor/menubar/PageSelector.tsx +1 -1
  185. package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
  186. package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -0
  187. package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +2 -0
  188. package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +2 -0
  189. package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +4 -3
  190. package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
  191. package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -1
  192. package/src/editor/page-viewer/EditorForm.tsx +1 -0
  193. package/src/editor/page-viewer/PageViewer.tsx +9 -8
  194. package/src/editor/page-viewer/PageViewerFrame.tsx +7 -1
  195. package/src/editor/page-viewer/pageViewContext.ts +40 -5
  196. package/src/editor/reviews/Comment.tsx +7 -7
  197. package/src/editor/services/agentService.ts +405 -31
  198. package/src/editor/services/aiService.ts +24 -5
  199. package/src/editor/services/contextService.ts +0 -1
  200. package/src/editor/services/systemService.ts +7 -1
  201. package/src/editor/sidebar/ComponentPalette.tsx +4 -1
  202. package/src/editor/sidebar/EditHistory.tsx +2 -0
  203. package/src/editor/sidebar/GraphQL.tsx +19 -7
  204. package/src/editor/sidebar/MainContentTree.tsx +1 -1
  205. package/src/editor/sidebar/SEOInfo.tsx +1 -1
  206. package/src/editor/sidebar/ViewSelector.tsx +80 -64
  207. package/src/editor/ui/PerfectTree.tsx +2 -18
  208. package/src/editor/ui/SimpleIconButton.tsx +56 -38
  209. package/src/index.ts +2 -0
  210. package/src/page-wizard/steps/CollectStep.tsx +0 -2
  211. package/src/page-wizard/steps/StructureStep.tsx +3 -0
  212. package/src/page-wizard/steps/TranslateStep.tsx +473 -62
  213. package/src/revision.ts +2 -2
  214. package/src/splash-screen/RecentPages.tsx +0 -14
  215. package/src/splash-screen/SplashScreen.tsx +3 -2
  216. package/src/types.ts +7 -0
  217. package/dist/editor/ai/AiPopup.d.ts +0 -10
  218. package/dist/editor/ai/AiPopup.js +0 -23
  219. package/dist/editor/ai/AiPopup.js.map +0 -1
  220. package/dist/editor/ai/AiToolCall.d.ts +0 -5
  221. package/dist/editor/ai/AiToolCall.js +0 -28
  222. package/dist/editor/ai/AiToolCall.js.map +0 -1
  223. package/src/editor/ai/AiPopup.tsx +0 -59
  224. 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[];
@@ -26,6 +36,15 @@ type Response = {
26
36
  numOutputTokens: number;
27
37
  numCachedTokens: number;
28
38
  state: string;
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;
29
48
  };
30
49
 
31
50
  export type ToolCall = {
@@ -47,9 +66,22 @@ export type Message = {
47
66
  tool_call_id?: string; // For tool response messages
48
67
  };
49
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
+
50
83
  export type AiContext = {
51
84
  promptData: any;
52
- endpoint: string;
53
85
  callback?: (response: Response) => void;
54
86
  };
55
87
 
@@ -57,6 +89,15 @@ export type AiTerminalOptions = {
57
89
  initialPrompt?: string;
58
90
  hiddenSystemPrompt?: string;
59
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;
60
101
  };
61
102
 
62
103
  export function AiTerminal({
@@ -64,11 +105,13 @@ export function AiTerminal({
64
105
  createAiContext,
65
106
  defaultProfile,
66
107
  options,
108
+ onAgentNameUpdate,
67
109
  }: {
68
110
  closeButton?: React.ReactNode;
69
111
  createAiContext: ({ editContext }: { editContext: any }) => AiContext;
70
112
  defaultProfile?: string;
71
113
  options?: AiTerminalOptions;
114
+ onAgentNameUpdate?: (name: string) => void;
72
115
  }) {
73
116
  const editContext = useEditContext();
74
117
  const [showPredefined, setShowPredefined] = useState(false);
@@ -84,7 +127,9 @@ export function AiTerminal({
84
127
  const [profiles, setProfiles] = useState<AiProfile[]>([]);
85
128
  const [activeProfile, setActiveProfile] = useState<AiProfile>();
86
129
  const [initialPromptExecuted, setInitialPromptExecuted] = useState(false);
87
- const [agentId] = useState<string>(() => crypto.randomUUID());
130
+ const [agentId, setAgentId] = useState<string>(
131
+ () => options?.agentId || crypto.randomUUID(),
132
+ );
88
133
  const selection = editContext.selection;
89
134
  const terminalRef = useRef<{ submit: () => void }>(null);
90
135
  const [responseMessages, setResponseMessages] = useState<Message[]>(
@@ -147,11 +192,7 @@ export function AiTerminal({
147
192
 
148
193
  // Format the initial messages for display
149
194
  const formattedMessages = options.initialMessages.map((message) => {
150
- const formattedContent =
151
- message.content
152
- ?.trim()
153
- ?.replaceAll("\n", "<br>")
154
- ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
195
+ const formattedContent = formatMessageContent(message.content || "");
155
196
 
156
197
  return {
157
198
  ...message,
@@ -161,8 +202,6 @@ export function AiTerminal({
161
202
  };
162
203
  });
163
204
 
164
- console.log("AiTerminal: Formatted messages", formattedMessages);
165
-
166
205
  // Update the internal message states
167
206
  setMessages(formattedMessages);
168
207
  setResponseMessages(formattedMessages);
@@ -171,52 +210,85 @@ export function AiTerminal({
171
210
  const initialResponse: Response = {
172
211
  messages: formattedMessages,
173
212
  editOperations: [],
174
- numInputTokens: 0,
175
- numOutputTokens: 0,
176
- numCachedTokens: 0,
213
+ numInputTokens: options?.totalInputTokens || 0,
214
+ numOutputTokens: options?.totalOutputTokens || 0,
215
+ numCachedTokens: options?.totalCachedTokens || 0,
177
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,
178
225
  };
179
226
 
180
- console.log("AiTerminal: Setting response", initialResponse);
181
227
  setResponse(initialResponse);
182
228
 
183
229
  // Create individual Terminal messages for each conversation message
184
230
  const terminalMessages: TerminalMessage[] = [];
185
231
 
186
- formattedMessages.forEach((message) => {
232
+ // Group messages by conversation turns (user + assistant pairs)
233
+ let i = 0;
234
+ while (i < formattedMessages.length) {
235
+ const message = formattedMessages[i];
236
+ if (!message) {
237
+ i++;
238
+ continue;
239
+ }
240
+
187
241
  if (message.role === "user") {
188
242
  // User messages appear as commands
189
243
  terminalMessages.push({
190
244
  type: "command",
191
245
  text: message.content,
192
246
  });
247
+ i++;
248
+
249
+ // Look for following assistant messages to group together
250
+ const assistantMessages: Message[] = [];
251
+ while (i < formattedMessages.length) {
252
+ const assistantMessage = formattedMessages[i];
253
+ if (assistantMessage && assistantMessage.role === "assistant") {
254
+ assistantMessages.push(assistantMessage);
255
+ i++;
256
+ } else {
257
+ break;
258
+ }
259
+ }
260
+
261
+ // If we have assistant messages, render them with AiResponseMessage
262
+ if (assistantMessages.length > 0) {
263
+ terminalMessages.push({
264
+ type: "response",
265
+ text: (
266
+ <AiResponseMessage
267
+ messages={assistantMessages}
268
+ editOperations={[]} // No edit operations for historical messages
269
+ finished={true}
270
+ />
271
+ ),
272
+ });
273
+ }
193
274
  } else if (message.role === "assistant") {
194
- // Assistant messages appear as responses
275
+ // Handle standalone assistant messages
195
276
  terminalMessages.push({
196
277
  type: "response",
197
278
  text: (
198
- <div
199
- dangerouslySetInnerHTML={{
200
- __html: message.formattedContent || message.content || "",
201
- }}
279
+ <AiResponseMessage
280
+ messages={[message]}
281
+ editOperations={[]} // No edit operations for historical messages
282
+ finished={true}
202
283
  />
203
284
  ),
204
285
  });
286
+ i++;
287
+ } else {
288
+ // Skip other message types (like tool messages)
289
+ i++;
205
290
  }
206
- // Add tool calls if present
207
- if (message.tool_calls && message.tool_calls.length > 0) {
208
- message.tool_calls.forEach((toolCall) => {
209
- terminalMessages.push({
210
- type: "response",
211
- text: (
212
- <div className="text-xs text-gray-1">
213
- 🔧 {toolCall.displayName}
214
- </div>
215
- ),
216
- })
217
- });
218
- }
219
- });
291
+ }
220
292
 
221
293
  setInitialTerminalMessages(terminalMessages);
222
294
  }
@@ -251,11 +323,7 @@ export function AiTerminal({
251
323
  // Replace the conversation history with the authoritative response from AI
252
324
  if (updatedMessages && Array.isArray(updatedMessages)) {
253
325
  const formattedMessages = updatedMessages.map((message) => {
254
- const formattedContent =
255
- message.content
256
- ?.trim()
257
- ?.replaceAll("\n", "<br>")
258
- ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
326
+ const formattedContent = formatMessageContent(message.content || "");
259
327
 
260
328
  return {
261
329
  ...message,
@@ -304,12 +372,10 @@ export function AiTerminal({
304
372
 
305
373
  const context = createAiContext({ editContext });
306
374
 
307
- let lastOpIndex = 0;
308
375
  const selectedText = editContext?.selectedRange?.text || null;
309
376
  if (!activeProfile || !model) return;
310
377
 
311
- // Build complete message history for API call
312
- const conversationHistory = [...messagesRef.current, userMessage];
378
+ // Only send the new user message - message history will be loaded from database
313
379
  const messages = [
314
380
  ...(options?.hiddenSystemPrompt
315
381
  ? [
@@ -322,44 +388,425 @@ export function AiTerminal({
322
388
  },
323
389
  ]
324
390
  : []),
325
- ...conversationHistory,
391
+ userMessage, // Send only the new user message
326
392
  ];
327
393
 
328
- const response = await executePrompt(
329
- messages,
330
- context,
331
- {
394
+ // Update local state to include the new user message for UI display
395
+ setMessages((prevMessages) => [...prevMessages, userMessage]);
396
+ setResponseMessages((prevMessages) => [...prevMessages, userMessage]);
397
+
398
+ try {
399
+ // Step 1: Start the agent execution
400
+ const startRequest: StartAgentRequest = {
401
+ agentId,
402
+ messages,
403
+ sessionId: editContext!.sessionId,
332
404
  profileId: activeProfile.id,
333
- selection,
334
- selectedText,
335
405
  model,
336
- sessionId: editContext!.sessionId,
337
- 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,
338
414
  addSelectedComponents: true,
339
- },
340
- undefined,
341
- (response: any) => {
342
- setResponse(response);
343
- handleResponse(response, callback, false);
344
- },
345
- );
346
-
347
- if (response) {
348
- handleResponse(response, callback, true);
349
- if (context.callback) context.callback(response);
350
- editContext?.requestRefresh("immediate");
351
- }
415
+ profile: activeProfile.name,
416
+ };
352
417
 
353
- // if (response?.responseText)
354
- // newMessages.push({
355
- // content: response.responseText,
356
- // role: "assistant",
357
- // name: "assistant",
358
- // });
418
+ const startResponse = await startAgent(startRequest, context);
419
+ console.log("Agent started:", startResponse);
359
420
 
360
- 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
+ );
361
769
 
362
- // 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
+ }
363
810
  }
364
811
 
365
812
  useEffect(() => {
@@ -385,22 +832,44 @@ export function AiTerminal({
385
832
  setResponseMessages([]);
386
833
  setResponse(undefined);
387
834
  setInitialTerminalMessages(undefined);
835
+ // Generate a new agentId when clearing the terminal to start fresh
836
+ setAgentId(crypto.randomUUID());
388
837
  }}
389
838
  initialMessages={initialTerminalMessages}
390
839
  infobar={
391
- response?.numInputTokens && response?.numInputTokens > 0 ? (
392
- <div
393
- className="text-right text-gray-400"
394
- style={{ fontSize: "10px" }}
395
- >
396
- Tokens in: {response?.numInputTokens?.toLocaleString()} out:{" "}
397
- {response?.numOutputTokens?.toLocaleString()}{" "}
398
- {response?.numCachedTokens
399
- ? `cached: ${response?.numCachedTokens?.toLocaleString()} `
400
- : ""}
401
- {response?.state}
402
- </div>
403
- ) : 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
+ />
404
873
  }
405
874
  prompt={prompt}
406
875
  setPrompt={setPrompt}
@@ -475,6 +944,8 @@ export function AiTerminal({
475
944
  value={model}
476
945
  onChange={(e) => setModel(e.value)}
477
946
  options={activeProfile.models}
947
+ optionLabel="name"
948
+ optionValue="id"
478
949
  />
479
950
  </div>
480
951
  )}