@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
@@ -1,4 +1,48 @@
1
- import { get } from "./serviceHelper";
1
+ import { AiContext } from "../ai/AiTerminal";
2
+
3
+ export const AGENT_BASE_URL = "/alpaca/editor/agent";
4
+
5
+ export interface StartAgentRequest {
6
+ agentId: string;
7
+ messages: any[];
8
+ sessionId: string;
9
+ profileId: string;
10
+ model?: string;
11
+ itemid: string;
12
+ language: string;
13
+ version: number;
14
+ selection?: string[];
15
+ selectedText?: string;
16
+ allowedFunctions?: string[];
17
+ addContextContent?: boolean;
18
+ addAllContent?: boolean;
19
+ addSelectedComponents?: boolean;
20
+ profile?: string;
21
+ }
22
+
23
+ export interface StartAgentResponse {
24
+ agentId: string;
25
+ status: string;
26
+ message: string;
27
+ startedAt: string;
28
+ }
29
+
30
+ export interface AgentStreamMessage {
31
+ type: string;
32
+ data: any;
33
+ timestamp: string;
34
+ error?: string;
35
+ }
36
+
37
+ export enum AgentStreamMessageType {
38
+ StatusUpdate = "StatusUpdate",
39
+ ContentChunk = "ContentChunk",
40
+ ToolCall = "ToolCall",
41
+ ToolResult = "ToolResult",
42
+ EditOperations = "EditOperations",
43
+ Completed = "Completed",
44
+ Error = "Error",
45
+ }
2
46
 
3
47
  export interface AgentChat {
4
48
  id: string;
@@ -13,13 +57,19 @@ export interface AgentChat {
13
57
  profileId?: string;
14
58
  profileName: string;
15
59
  model: string;
16
- status: "active" | "completed" | "error" | "abandoned";
60
+ status: string;
17
61
  createdDate: string;
18
62
  updatedDate: string;
19
63
  lastMessageDate?: string;
20
64
  totalTokensUsed: number;
21
65
  totalInputTokens: number;
22
66
  totalOutputTokens: number;
67
+ totalCachedInputTokens: number;
68
+ totalInputTokenCost: number;
69
+ totalOutputTokenCost: number;
70
+ totalCachedInputTokenCost: number;
71
+ totalCost: number;
72
+ currency: string;
23
73
  messageCount: number;
24
74
  metadata?: string;
25
75
  messages?: AgentChatMessage[];
@@ -32,28 +82,34 @@ export interface AgentChatMessage {
32
82
  role: string;
33
83
  content: string;
34
84
  name: string;
35
- toolCallId?: string; // For tool result messages that reference a specific tool call
36
- messageType?: string; // content, tool_result
85
+ toolCallId?: string;
86
+ messageType: string;
37
87
  isCompleted: boolean;
38
- model?: string;
88
+ model: string;
39
89
  tokensUsed: number;
40
90
  inputTokens: number;
41
91
  outputTokens: number;
92
+ cachedInputTokens: number;
93
+ inputTokenCost: number;
94
+ outputTokenCost: number;
95
+ cachedInputTokenCost: number;
96
+ totalCost: number;
97
+ currency: string;
42
98
  responseTimeMs?: number;
43
99
  createdDate: string;
44
- editOperations?: any[];
45
- longRunningTaskIds?: string[];
100
+ editOperations?: string;
101
+ longRunningTaskIds?: string;
46
102
  metadata?: string;
47
- toolCalls?: AgentChatToolCall[]; // Tool calls associated with this message
103
+ toolCalls?: AgentChatToolCall[];
48
104
  }
49
105
 
50
106
  export interface AgentChatToolCall {
51
107
  id: string;
52
108
  messageId: string;
53
- toolCallId: string; // The tool call ID from the AI response
109
+ toolCallId: string;
54
110
  functionName: string;
55
- functionArguments?: string;
56
- functionResult?: any;
111
+ functionArguments: string;
112
+ functionResult?: string;
57
113
  functionError?: string;
58
114
  isCompleted: boolean;
59
115
  responseTimeMs?: number;
@@ -61,34 +117,352 @@ export interface AgentChatToolCall {
61
117
  }
62
118
 
63
119
  /**
64
- * Gets all agents for the current user
120
+ * Starts an agent execution and returns immediately
65
121
  */
66
- export async function getAgents(
67
- status?: "active" | "completed" | "error" | "abandoned",
68
- limit = 100
69
- ) {
70
- const url = `/alpaca/editor/ai/GetAgents${status ? `?status=${status}` : ""}${limit ? `${status ? "&" : "?"}limit=${limit}` : ""}`;
71
- const response = await get<AgentChat[]>(url);
72
- return response;
122
+ export async function startAgent(
123
+ request: StartAgentRequest,
124
+ context: AiContext,
125
+ ): Promise<StartAgentResponse> {
126
+ const response = await fetch(AGENT_BASE_URL + "/start", {
127
+ method: "POST",
128
+ headers: {
129
+ "Content-Type": "application/json",
130
+ },
131
+ body: JSON.stringify(request),
132
+ });
133
+
134
+ if (!response.ok) {
135
+ throw new Error(
136
+ `Failed to start agent: ${response.status} ${response.statusText}`,
137
+ );
138
+ }
139
+
140
+ return await response.json();
73
141
  }
74
142
 
75
143
  /**
76
- * Gets a specific agent with its messages
144
+ * Connects to an agent's real-time stream using Server-Sent Events
77
145
  */
78
- export async function getAgent(agentId: string) {
79
- const url = `/alpaca/editor/ai/GetAgent?agentId=${agentId}`;
80
- const response = await get<AgentChat>(url);
81
- return response;
146
+ export async function connectToAgentStream(
147
+ agentId: string,
148
+ context: AiContext,
149
+ onMessage: (message: AgentStreamMessage) => void,
150
+ signal?: AbortSignal,
151
+ ): Promise<void> {
152
+ // Add cache-busting timestamp to prevent browser connection reuse
153
+ const timestamp = Date.now();
154
+ const streamUrl =
155
+ AGENT_BASE_URL + "/stream?agentId=" + agentId + "&t=" + timestamp;
156
+
157
+ // Retry logic to handle potential race conditions
158
+ let retryCount = 0;
159
+ const maxRetries = 3;
160
+ const retryDelay = 500; // 500ms
161
+
162
+ while (retryCount <= maxRetries) {
163
+ try {
164
+ const response = await fetch(streamUrl, {
165
+ headers: {
166
+ Accept: "text/event-stream",
167
+ "Cache-Control": "no-cache",
168
+ Pragma: "no-cache",
169
+ },
170
+ cache: "no-store",
171
+ signal,
172
+ });
173
+
174
+ if (response.ok) {
175
+ // Success - proceed with stream processing
176
+ await processEventStream(response, onMessage);
177
+ return;
178
+ }
179
+
180
+ // If 404 and we haven't maxed out retries, wait and retry
181
+ if (response.status === 404 && retryCount < maxRetries) {
182
+ retryCount++;
183
+ console.warn(
184
+ `Agent stream not ready (404), retrying in ${retryDelay}ms... (${retryCount}/${maxRetries})`,
185
+ );
186
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
187
+ continue;
188
+ }
189
+
190
+ // For other errors or max retries reached, throw
191
+ throw new Error(
192
+ `Failed to connect to agent stream: ${response.status} ${response.statusText}`,
193
+ );
194
+ } catch (error) {
195
+ if (signal?.aborted) {
196
+ throw error; // Don't retry if cancelled
197
+ }
198
+
199
+ if (retryCount >= maxRetries) {
200
+ throw error;
201
+ }
202
+
203
+ retryCount++;
204
+ console.warn(
205
+ `Error connecting to agent stream, retrying... (${retryCount}/${maxRetries})`,
206
+ error,
207
+ );
208
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
209
+ }
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Processes the event stream response
215
+ */
216
+ async function processEventStream(
217
+ response: Response,
218
+ onMessage: (message: AgentStreamMessage) => void,
219
+ ): Promise<void> {
220
+ const reader = response.body?.getReader();
221
+ if (!reader) {
222
+ throw new Error("Failed to get stream reader");
223
+ }
224
+
225
+ const decoder = new TextDecoder();
226
+ let buffer = "";
227
+
228
+ try {
229
+ while (true) {
230
+ const { done, value } = await reader.read();
231
+
232
+ if (done) {
233
+ break;
234
+ }
235
+
236
+ buffer += decoder.decode(value, { stream: true });
237
+
238
+ // Process complete messages
239
+ const lines = buffer.split("\n");
240
+
241
+ // Only keep the last line if it doesn't end with a newline (incomplete)
242
+ // If the buffer ends with \n, then all lines are complete
243
+ if (buffer.endsWith("\n")) {
244
+ // All lines are complete, process them all
245
+ buffer = "";
246
+ } else {
247
+ // Last line might be incomplete, keep it for next iteration
248
+ buffer = lines.pop() || "";
249
+ }
250
+
251
+ for (const line of lines) {
252
+ if (line.startsWith("data: ")) {
253
+ try {
254
+ const data = line.slice(6); // Remove 'data: ' prefix
255
+ if (data.trim()) {
256
+ const message: AgentStreamMessage = JSON.parse(data);
257
+
258
+ // Normalize message type (handle both numeric and string enum values)
259
+ if (typeof message.type === "number") {
260
+ switch (message.type) {
261
+ case 0:
262
+ message.type = AgentStreamMessageType.StatusUpdate;
263
+ break;
264
+ case 1:
265
+ message.type = AgentStreamMessageType.ContentChunk;
266
+ break;
267
+ case 2:
268
+ message.type = AgentStreamMessageType.ToolCall;
269
+ break;
270
+ case 3:
271
+ message.type = AgentStreamMessageType.ToolResult;
272
+ break;
273
+ case 4:
274
+ message.type = AgentStreamMessageType.EditOperations;
275
+ break;
276
+ case 5:
277
+ message.type = AgentStreamMessageType.Completed;
278
+ break;
279
+ case 6:
280
+ message.type = AgentStreamMessageType.Error;
281
+ break;
282
+ default:
283
+ message.type = `Unknown_${message.type}`;
284
+ }
285
+ }
286
+
287
+ onMessage(message);
288
+
289
+ // Break on completion or error
290
+ if (
291
+ message.type === AgentStreamMessageType.Completed ||
292
+ message.type === AgentStreamMessageType.Error
293
+ ) {
294
+ // console.log("Stream ending due to message type:", message.type);
295
+ return;
296
+ }
297
+ }
298
+ } catch (error) {
299
+ console.error("Failed to parse SSE message:", error, line);
300
+ }
301
+ }
302
+ }
303
+ }
304
+ } finally {
305
+ // console.log("Stream ended, releasing reader lock");
306
+ reader.releaseLock();
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Gets the current status of an agent execution
312
+ */
313
+ export async function getAgentStatus(
314
+ agentId: string,
315
+ context: AiContext,
316
+ ): Promise<any> {
317
+ const response = await fetch(
318
+ AGENT_BASE_URL + "/getAgentStatus?agentId=" + agentId,
319
+ {
320
+ method: "GET",
321
+ headers: {
322
+ "Content-Type": "application/json",
323
+ },
324
+ },
325
+ );
326
+
327
+ if (!response.ok) {
328
+ throw new Error(
329
+ `Failed to get agent status: ${response.status} ${response.statusText}`,
330
+ );
331
+ }
332
+
333
+ return await response.json();
334
+ }
335
+
336
+ /**
337
+ * Gets all currently running agents for monitoring
338
+ */
339
+ export async function getRunningAgents(context: AiContext): Promise<any> {
340
+ const response = await fetch(AGENT_BASE_URL + "/getRunningAgents", {
341
+ method: "GET",
342
+ headers: {
343
+ "Content-Type": "application/json",
344
+ },
345
+ });
346
+
347
+ if (!response.ok) {
348
+ throw new Error(
349
+ `Failed to get running agents: ${response.status} ${response.statusText}`,
350
+ );
351
+ }
352
+
353
+ return await response.json();
354
+ }
355
+
356
+ /**
357
+ * Gets a single agent by ID with complete message history and cost information
358
+ */
359
+ export async function getAgent(
360
+ agentId: string,
361
+ context: AiContext,
362
+ ): Promise<any> {
363
+ const response = await fetch(
364
+ AGENT_BASE_URL + "/getAgent?agentId=" + agentId,
365
+ {
366
+ method: "GET",
367
+ headers: {
368
+ "Content-Type": "application/json",
369
+ },
370
+ },
371
+ );
372
+
373
+ if (!response.ok) {
374
+ throw new Error(
375
+ `Failed to get agent: ${response.status} ${response.statusText}`,
376
+ );
377
+ }
378
+
379
+ return await response.json();
380
+ }
381
+
382
+ /**
383
+ * Gets all agents for the current user
384
+ */
385
+ export async function getAgents(
386
+ context: AiContext,
387
+ status?: string,
388
+ limit?: number,
389
+ ): Promise<any> {
390
+ const params = new URLSearchParams();
391
+ if (status) params.append("status", status);
392
+ if (limit) params.append("limit", limit.toString());
393
+
394
+ const queryString = params.toString();
395
+ const url =
396
+ AGENT_BASE_URL + "/getAgents" + (queryString ? `?${queryString}` : "");
397
+
398
+ const response = await fetch(url, {
399
+ method: "GET",
400
+ headers: {
401
+ "Content-Type": "application/json",
402
+ },
403
+ });
404
+
405
+ if (!response.ok) {
406
+ throw new Error(
407
+ `Failed to get agents: ${response.status} ${response.statusText}`,
408
+ );
409
+ }
410
+
411
+ return await response.json();
82
412
  }
83
413
 
84
414
  /**
85
415
  * Gets chat history for the current user
86
416
  */
87
417
  export async function getChatHistory(
88
- status?: "active" | "completed" | "error" | "abandoned",
89
- limit = 50
90
- ) {
91
- const url = `/alpaca/editor/ai/GetChatHistory${status ? `?status=${status}` : ""}${limit ? `${status ? "&" : "?"}limit=${limit}` : ""}`;
92
- const response = await get<AgentChat[]>(url);
93
- return response;
94
- }
418
+ context: AiContext,
419
+ status?: string,
420
+ limit?: number,
421
+ ): Promise<any> {
422
+ const params = new URLSearchParams();
423
+ if (status) params.append("status", status);
424
+ if (limit) params.append("limit", limit.toString());
425
+
426
+ const queryString = params.toString();
427
+ const url =
428
+ AGENT_BASE_URL + "/chatHistory" + (queryString ? `?${queryString}` : "");
429
+
430
+ const response = await fetch(url, {
431
+ method: "GET",
432
+ headers: {
433
+ "Content-Type": "application/json",
434
+ },
435
+ });
436
+
437
+ if (!response.ok) {
438
+ throw new Error(
439
+ `Failed to get chat history: ${response.status} ${response.statusText}`,
440
+ );
441
+ }
442
+
443
+ return await response.json();
444
+ }
445
+
446
+ /**
447
+ * Cancels a running agent execution
448
+ */
449
+ export async function cancelAgent(
450
+ agentId: string,
451
+ context: AiContext,
452
+ ): Promise<any> {
453
+ const response = await fetch(AGENT_BASE_URL + "/cancelAgent", {
454
+ method: "POST",
455
+ headers: {
456
+ "Content-Type": "application/json",
457
+ },
458
+ body: JSON.stringify({ agentId }),
459
+ });
460
+
461
+ if (!response.ok) {
462
+ throw new Error(
463
+ `Failed to cancel agent: ${response.status} ${response.statusText}`,
464
+ );
465
+ }
466
+
467
+ return await response.json();
468
+ }
@@ -5,12 +5,17 @@ import { JsonCleaner } from "../utils/jsonCleaner";
5
5
 
6
6
  import { ExecutionResult, get, post } from "./serviceHelper";
7
7
 
8
+ export type AiModel = {
9
+ id: string; // Guid as string
10
+ name: string;
11
+ };
12
+
8
13
  export type AiProfile = {
9
14
  id: string;
10
15
  name: string;
11
16
  instructions: string;
12
- defaultModel: string;
13
- models: string[];
17
+ defaultModel: string | null; // Guid as string, nullable
18
+ models: AiModel[]; // Array of model objects with id and name
14
19
  prompts: { prompt: string; title: string }[];
15
20
  errorMessage?: string;
16
21
  };
@@ -50,6 +55,17 @@ export interface ExecutePromptResponse {
50
55
  deltaContent?: string;
51
56
  previousContentLength?: number;
52
57
  totalContentLength?: number;
58
+ // Updated agent name (only sent when the agent name has been updated)
59
+ agentName?: string;
60
+ totalCost?: number;
61
+ totalInputTokenCost?: number;
62
+ totalOutputTokenCost?: number;
63
+ totalCachedTokenCost?: number;
64
+ totalInputTokens?: number;
65
+ totalOutputTokens?: number;
66
+ totalCachedTokens?: number;
67
+ totalTokens?: number;
68
+ totalMessages?: number;
53
69
  }
54
70
 
55
71
  // Unified options interface that supports both calling patterns
@@ -57,6 +73,7 @@ export interface ExecutePromptOptions {
57
73
  // Common options
58
74
  sessionId?: string | null;
59
75
  model?: string | null;
76
+ stream?: boolean;
60
77
 
61
78
  // AiTerminal style options
62
79
  profileId?: string;
@@ -84,6 +101,7 @@ export async function executePrompt(
84
101
  options: ExecutePromptOptions = {},
85
102
  requestOptions?: RequestInit,
86
103
  callback?: (response: any) => void,
104
+ stream?: boolean,
87
105
  ): Promise<ExecutePromptResponse | null> {
88
106
  let endpoint: string;
89
107
  let requestBody: any;
@@ -91,7 +109,7 @@ export async function executePrompt(
91
109
 
92
110
  // Handle context - either direct AiContext or need to create it
93
111
  let aiContext: AiContext;
94
- if ("endpoint" in context) {
112
+ if ("promptData" in context) {
95
113
  // Direct AiContext (AiTerminal style)
96
114
  aiContext = context;
97
115
  } else {
@@ -103,7 +121,8 @@ export async function executePrompt(
103
121
  }
104
122
  }
105
123
 
106
- endpoint = aiContext.endpoint;
124
+ // Use hardcoded endpoint for old prompt system
125
+ endpoint = "/alpaca/editor/ai/Prompt";
107
126
 
108
127
  // Build unified request body
109
128
  requestBody = {
@@ -111,7 +130,7 @@ export async function executePrompt(
111
130
  ...aiContext.promptData,
112
131
  sessionId: options.sessionId,
113
132
  model: options.model,
114
-
133
+ stream: stream,
115
134
  // Include all provided options
116
135
  ...options,
117
136
  };
@@ -58,7 +58,6 @@ export async function generatePageContext(
58
58
  const result = await executePrompt(
59
59
  messages,
60
60
  {
61
- endpoint: "/alpaca/editor/ai/prompt",
62
61
  promptData: {
63
62
  itemid: editContext.contentEditorItem!.id,
64
63
  language: editContext.contentEditorItem!.language,
@@ -1,8 +1,14 @@
1
1
  import { get, post } from "./serviceHelper";
2
- import { SystemStatus, UserPreferences } from "../../types";
2
+ import { SystemStatus, UserPreferences, EditorSettings } from "../../types";
3
+
3
4
  export function getSystemStatus() {
4
5
  return get<SystemStatus>("/alpaca/editor/status");
5
6
  }
7
+
8
+ export function getEditorSettings() {
9
+ return get<EditorSettings>("/alpaca/editor/settings");
10
+ }
11
+
6
12
  export function saveUserPreferences(preferences: UserPreferences) {
7
13
  return post<UserPreferences>("/alpaca/editor/savepreferences", preferences);
8
14
  }
@@ -64,7 +64,10 @@ export function ComponentPalette({}: {}) {
64
64
  );
65
65
 
66
66
  return (
67
- <div className="tour-component-palette relative flex h-full flex-col">
67
+ <div
68
+ className="tour-component-palette relative flex h-full flex-col"
69
+ data-testid="component-palette"
70
+ >
68
71
  <div className="p-4">
69
72
  <FilterInput
70
73
  ref={filterRef}
@@ -46,6 +46,7 @@ export function EditHistory() {
46
46
  }}
47
47
  icon={undoCommand.icon}
48
48
  label={undoCommand.label}
49
+ data-testid="undo-button"
49
50
  disabled={editContext.isCommandDisabled({ command: undoCommand })}
50
51
  />
51
52
  <SimpleIconButton
@@ -54,6 +55,7 @@ export function EditHistory() {
54
55
  }}
55
56
  icon={redoCommand.icon}
56
57
  label={redoCommand.label}
58
+ data-testid="redo-button"
57
59
  disabled={editContext.isCommandDisabled({ command: redoCommand })}
58
60
  />
59
61
  </SimpleToolbar>
@@ -1,5 +1,7 @@
1
+ import "@uiw/react-textarea-code-editor/dist.css";
2
+
1
3
  import { Splitter, SplitterPanel } from "primereact/splitter";
2
- import { InputTextarea } from "primereact/inputtextarea";
4
+ import CodeEditor from "@uiw/react-textarea-code-editor";
3
5
  import { ActionButton } from "../../components/ActionButton";
4
6
  import { useEffect, useState } from "react";
5
7
 
@@ -256,12 +258,22 @@ export function GraphQL() {
256
258
  <Splitter layout="horizontal" style={{ height: "100%" }}>
257
259
  <SplitterPanel size={50} className="relative">
258
260
  <div className="absolute inset-0 flex items-stretch">
259
- <InputTextarea
260
- value={query}
261
- onChange={(e) => setQuery(e.target.value)}
262
- className="min-w-0 flex-1 text-xs"
263
- style={{ height: "100%" }}
264
- />
261
+ <div className="min-w-0 flex-1">
262
+ <CodeEditor
263
+ value={query}
264
+ language="graphql"
265
+ placeholder="Enter your GraphQL query here..."
266
+ onChange={(evn: any) => setQuery(evn.target.value)}
267
+ padding={15}
268
+ style={{
269
+ fontSize: 12,
270
+ backgroundColor: "#f8f9fa",
271
+ fontFamily:
272
+ "ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace",
273
+ height: "100%",
274
+ }}
275
+ />
276
+ </div>
265
277
  <ActionButton
266
278
  onClick={refresh}
267
279
  disabled={showSpinner}
@@ -50,7 +50,7 @@ export function MainContentTree({
50
50
 
51
51
  useEffect(() => {
52
52
  if (selectedItem) setSelectedItemIds([selectedItem.id]);
53
- }, [selectedItem]);
53
+ }, [selectedItem?.id]);
54
54
 
55
55
  useEffect(() => {
56
56
  const loadInsertOptions = async () => {
@@ -67,7 +67,7 @@ export function SEOInfo() {
67
67
 
68
68
  // Try to send AI analysis request
69
69
  const aiServiceUrl =
70
- editContext.configuration.services.aiService.promptUrl;
70
+ editContext.configuration.services.aiService.endpoint;
71
71
  console.log("AI Service URL:", aiServiceUrl);
72
72
 
73
73
  if (!aiServiceUrl) {