@alpaca-editor/core 1.0.4027 → 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 (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 +2 -0
  131. package/dist/index.js +2 -0
  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 +2 -0
  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
@@ -0,0 +1,237 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+ import {
5
+ Popover,
6
+ PopoverContent,
7
+ PopoverTrigger,
8
+ } from "../../components/ui/popover";
9
+ import { useEditorSettings } from "../hooks/useEditorSettings";
10
+
11
+ import { ExecutePromptResponse } from "../services/aiService";
12
+
13
+ interface AgentCostDisplayProps {
14
+ /** Optional className for styling */
15
+ className?: string;
16
+ /** Current response data with token usage */
17
+ response?: {
18
+ numInputTokens: number;
19
+ numOutputTokens: number;
20
+ numCachedTokens: number;
21
+ totalCost?: number;
22
+ inputCost?: number;
23
+ outputCost?: number;
24
+ cachedCost?: number;
25
+ } | null;
26
+ /** Cumulative token usage across all messages in the session */
27
+ totalTokens?: {
28
+ input: number;
29
+ output: number;
30
+ cached: number;
31
+ totalCost: number;
32
+ inputCost: number;
33
+ outputCost: number;
34
+ cachedCost: number;
35
+ };
36
+ }
37
+
38
+ export function AgentCostDisplay({
39
+ className = "",
40
+ response,
41
+ totalTokens,
42
+ }: AgentCostDisplayProps) {
43
+ const [showCostPopover, setShowCostPopover] = useState(false);
44
+ const costPopoverRef = useRef<HTMLDivElement>(null);
45
+ const editorSettings = useEditorSettings();
46
+ const currency = editorSettings?.currency || "USD";
47
+
48
+ // Handle click outside for cost popover
49
+ useEffect(() => {
50
+ function handleClickOutside(event: MouseEvent) {
51
+ if (
52
+ costPopoverRef.current &&
53
+ !costPopoverRef.current.contains(event.target as Node)
54
+ ) {
55
+ setShowCostPopover(false);
56
+ }
57
+ }
58
+
59
+ if (showCostPopover) {
60
+ document.addEventListener("click", handleClickOutside);
61
+ return () => {
62
+ document.removeEventListener("click", handleClickOutside);
63
+ };
64
+ }
65
+ }, [showCostPopover]);
66
+
67
+ // Calculate estimated cost based on typical pricing (rough estimates)
68
+ // Claude 3.5 Sonnet pricing: ~$3/MTok input, ~$15/MTok output
69
+ const calculateEstimatedCost = (
70
+ inputTokens: number,
71
+ outputTokens: number,
72
+ cachedTokens: number,
73
+ ) => {
74
+ const inputCost = (inputTokens / 1000000) * 3; // $3 per million tokens
75
+ const outputCost = (outputTokens / 1000000) * 15; // $15 per million tokens
76
+ const cachedCost = (cachedTokens / 1000000) * 0.3; // Cached tokens are typically much cheaper
77
+ return {
78
+ inputCost,
79
+ outputCost,
80
+ cachedCost,
81
+ totalCost: inputCost + outputCost + cachedCost,
82
+ };
83
+ };
84
+
85
+ // Use actual cost data if available, otherwise calculate estimates
86
+ const currentCost = response
87
+ ? response.totalCost !== undefined
88
+ ? {
89
+ inputCost: response.inputCost ?? 0,
90
+ outputCost: response.outputCost ?? 0,
91
+ cachedCost: response.cachedCost ?? 0,
92
+ totalCost: response.totalCost,
93
+ }
94
+ : calculateEstimatedCost(
95
+ response.numInputTokens,
96
+ response.numOutputTokens,
97
+ response.numCachedTokens,
98
+ )
99
+ : null;
100
+
101
+ const displayTotalCost =
102
+ totalTokens?.totalCost ?? currentCost?.totalCost ?? 0;
103
+ const hasTokenData =
104
+ response &&
105
+ (response.numInputTokens > 0 ||
106
+ response.numOutputTokens > 0 ||
107
+ response.numCachedTokens > 0);
108
+ const hasTotalData =
109
+ totalTokens &&
110
+ (totalTokens.input > 0 || totalTokens.output > 0 || totalTokens.cached > 0);
111
+ const hasCostData =
112
+ (response && response.totalCost && response.totalCost > 0) ||
113
+ (totalTokens && totalTokens.totalCost && totalTokens.totalCost > 0);
114
+
115
+ if (!hasTokenData && !hasTotalData && !hasCostData) {
116
+ return null;
117
+ }
118
+
119
+ return (
120
+ <div className={`relative ${className}`} ref={costPopoverRef}>
121
+ <Popover open={showCostPopover} onOpenChange={setShowCostPopover}>
122
+ <PopoverTrigger asChild>
123
+ <div
124
+ className="cursor-pointer text-right text-gray-400 hover:text-gray-600"
125
+ style={{ fontSize: "10px" }}
126
+ onClick={() => setShowCostPopover(!showCostPopover)}
127
+ >
128
+ Total Cost: {displayTotalCost.toFixed(2)} {currency}
129
+ </div>
130
+ </PopoverTrigger>
131
+ <PopoverContent className="w-80 p-4" align="end" side="bottom">
132
+ <div className="space-y-4">
133
+ <div className="border-b pb-2">
134
+ <h4 className="text-sm font-semibold">Cost Breakdown</h4>
135
+ </div>
136
+
137
+ <div className="space-y-3">
138
+ {/* Session Cost Breakdown */}
139
+ {(hasTotalData || hasTokenData) && (
140
+ <>
141
+ <div className="flex items-center justify-between">
142
+ <span className="text-sm font-semibold text-gray-600">
143
+ Session Cost:
144
+ </span>
145
+ <span className="text-sm font-semibold">
146
+ {displayTotalCost.toFixed(2)} {currency}
147
+ </span>
148
+ </div>
149
+
150
+ <div className="space-y-2 border-t pt-2">
151
+ {hasTotalData ? (
152
+ <>
153
+ <div className="flex items-center justify-between">
154
+ <span className="text-xs text-gray-500">
155
+ Input tokens:
156
+ </span>
157
+ <span className="text-xs">
158
+ {totalTokens.input.toLocaleString()} (
159
+ {totalTokens.inputCost.toFixed(2)} {currency})
160
+ </span>
161
+ </div>
162
+
163
+ <div className="flex items-center justify-between">
164
+ <span className="text-xs text-gray-500">
165
+ Output tokens:
166
+ </span>
167
+ <span className="text-xs">
168
+ {totalTokens.output.toLocaleString()} (
169
+ {totalTokens.outputCost.toFixed(2)} {currency})
170
+ </span>
171
+ </div>
172
+
173
+ {totalTokens.cached > 0 && (
174
+ <div className="flex items-center justify-between">
175
+ <span className="text-xs text-gray-500">
176
+ Cached tokens:
177
+ </span>
178
+ <span className="text-xs">
179
+ {totalTokens.cached.toLocaleString()} (
180
+ {totalTokens.cachedCost.toFixed(2)} {currency})
181
+ </span>
182
+ </div>
183
+ )}
184
+ </>
185
+ ) : (
186
+ response &&
187
+ currentCost && (
188
+ <>
189
+ <div className="flex items-center justify-between">
190
+ <span className="text-xs text-gray-500">
191
+ Input tokens:
192
+ </span>
193
+ <span className="text-xs">
194
+ {response.numInputTokens.toLocaleString()}(
195
+ {currentCost.inputCost.toFixed(2)} {currency})
196
+ </span>
197
+ </div>
198
+
199
+ <div className="flex items-center justify-between">
200
+ <span className="text-xs text-gray-500">
201
+ Output tokens:
202
+ </span>
203
+ <span className="text-xs">
204
+ {response.numOutputTokens.toLocaleString()}(
205
+ {currentCost.outputCost.toFixed(2)} {currency})
206
+ </span>
207
+ </div>
208
+
209
+ {response.numCachedTokens > 0 && (
210
+ <div className="flex items-center justify-between">
211
+ <span className="text-xs text-gray-500">
212
+ Cached tokens:
213
+ </span>
214
+ <span className="text-xs">
215
+ {response.numCachedTokens.toLocaleString()}(
216
+ {currentCost.cachedCost.toFixed(2)} {currency})
217
+ </span>
218
+ </div>
219
+ )}
220
+
221
+ <div className="mt-2 text-xs text-gray-400">
222
+ * Estimated costs based on typical Claude 3.5
223
+ pricing
224
+ </div>
225
+ </>
226
+ )
227
+ )}
228
+ </div>
229
+ </>
230
+ )}
231
+ </div>
232
+ </div>
233
+ </PopoverContent>
234
+ </Popover>
235
+ </div>
236
+ );
237
+ }
@@ -18,6 +18,8 @@ import {
18
18
  PopoverTrigger,
19
19
  } from "../../components/ui/popover";
20
20
  import { Button } from "../../components/ui/button";
21
+ import { useEditContext } from "../client/editContext";
22
+ import { AiContext } from "./AiTerminal";
21
23
 
22
24
  interface TerminalInstance {
23
25
  id: string;
@@ -39,12 +41,13 @@ function convertAgentMessagesToTerminalMessages(
39
41
  name: msg.name,
40
42
  role: msg.role,
41
43
  tool_calls:
42
- msg.toolCalls?.map((toolCall) => ({
44
+ msg.toolCalls?.map((toolCall: any) => ({
43
45
  id: toolCall.toolCallId,
44
46
  displayName: toolCall.functionName,
45
47
  function: {
46
48
  name: toolCall.functionName,
47
49
  arguments: toolCall.functionArguments || "",
50
+ result: toolCall.functionResult,
48
51
  },
49
52
  })) || [],
50
53
  tool_call_id: msg.toolCallId,
@@ -77,6 +80,16 @@ export function Agents({
77
80
  const [historyPopoverOpen, setHistoryPopoverOpen] = useState(false);
78
81
  const [loadingAgents, setLoadingAgents] = useState(true);
79
82
  const nextTerminalNumber = useRef(1);
83
+ const editContext = useEditContext();
84
+
85
+ // Create AI context for service calls
86
+ const createAgentContext = (): AiContext => ({
87
+ promptData: {
88
+ itemid: editContext?.currentItemDescriptor?.id,
89
+ language: editContext?.currentItemDescriptor?.language || "en",
90
+ version: editContext?.currentItemDescriptor?.version || 1,
91
+ },
92
+ });
80
93
 
81
94
  // Load agents from backend on mount
82
95
  useEffect(() => {
@@ -86,27 +99,43 @@ export function Agents({
86
99
  const loadAgentsFromBackend = async () => {
87
100
  try {
88
101
  setLoadingAgents(true);
102
+ const context = createAgentContext();
89
103
 
90
104
  // Load active agents
91
- const activeAgentsResult = await getAgents("active");
105
+ const activeAgentsResult = await getAgents(context, "active");
92
106
 
93
107
  // Load inactive agents for history
94
- const inactiveAgentsResult = await getChatHistory("completed", 20);
108
+ const inactiveAgentsResult = await getChatHistory(
109
+ context,
110
+ "completed",
111
+ 20,
112
+ );
95
113
 
96
- if (activeAgentsResult.type === "success" && activeAgentsResult.data) {
97
- const activeAgents = activeAgentsResult.data;
114
+ if (activeAgentsResult && Array.isArray(activeAgentsResult)) {
115
+ const activeAgents = activeAgentsResult;
98
116
 
99
117
  // Create terminals for active agents
100
118
  const activeTerminals: TerminalInstance[] = await Promise.all(
101
- activeAgents.map(async (agent, index) => {
119
+ activeAgents.map(async (agent: any, index: number) => {
102
120
  // Load messages for each active agent
103
121
  const agentWithMessages = await loadAgentMessages(agent.id);
104
122
 
105
123
  return {
106
124
  id: `agent-${agent.id}`,
107
- title: agent.name || `Agent ${index + 1}`,
125
+ title: agent.name || "New Agent",
108
126
  agentId: agent.id,
109
- options: initialOptions,
127
+ options: {
128
+ ...initialOptions,
129
+ // Pass cost information from loaded agent
130
+ totalCost: agentWithMessages?.totalCost,
131
+ totalInputTokenCost: agentWithMessages?.totalInputTokenCost,
132
+ totalOutputTokenCost: agentWithMessages?.totalOutputTokenCost,
133
+ totalCachedTokenCost:
134
+ agentWithMessages?.totalCachedInputTokenCost,
135
+ totalInputTokens: agentWithMessages?.totalInputTokens,
136
+ totalOutputTokens: agentWithMessages?.totalOutputTokens,
137
+ totalCachedTokens: agentWithMessages?.totalCachedInputTokens,
138
+ },
110
139
  messages: agentWithMessages?.messages
111
140
  ? convertAgentMessagesToTerminalMessages(
112
141
  agentWithMessages.messages,
@@ -116,16 +145,15 @@ export function Agents({
116
145
  }),
117
146
  );
118
147
 
119
- setTerminals(activeTerminals);
120
-
121
148
  // Set the first active terminal as active, or create a new one if none exist
122
149
  if (activeTerminals.length > 0) {
150
+ setTerminals(activeTerminals);
123
151
  setActiveTerminalId(activeTerminals[0]!.id);
124
152
  } else {
125
153
  // Create a default terminal if no active agents
126
154
  const defaultTerminal: TerminalInstance = {
127
155
  id: `terminal-${nextTerminalNumber.current}`,
128
- title: `Agent ${nextTerminalNumber.current}`,
156
+ title: "New Agent",
129
157
  options: initialOptions,
130
158
  };
131
159
  setTerminals([defaultTerminal]);
@@ -134,11 +162,8 @@ export function Agents({
134
162
  }
135
163
  }
136
164
 
137
- if (
138
- inactiveAgentsResult.type === "success" &&
139
- inactiveAgentsResult.data
140
- ) {
141
- setInactiveAgents(inactiveAgentsResult.data);
165
+ if (inactiveAgentsResult && Array.isArray(inactiveAgentsResult)) {
166
+ setInactiveAgents(inactiveAgentsResult);
142
167
  }
143
168
  } catch (error) {
144
169
  console.error("Failed to load agents:", error);
@@ -160,9 +185,10 @@ export function Agents({
160
185
  agentId: string,
161
186
  ): Promise<AgentChat | null> => {
162
187
  try {
163
- const result = await getAgent(agentId);
164
- if (result.type === "success" && result.data) {
165
- return result.data;
188
+ const context = createAgentContext();
189
+ const result = await getAgent(agentId, context);
190
+ if (result && result.id) {
191
+ return result;
166
192
  }
167
193
  } catch (error) {
168
194
  console.error(`Failed to load messages for agent ${agentId}:`, error);
@@ -181,6 +207,17 @@ export function Agents({
181
207
  nextTerminalNumber.current++;
182
208
  };
183
209
 
210
+ const createNewAgent = (options: AiTerminalOptions) => {
211
+ const newTerminal: TerminalInstance = {
212
+ id: `terminal-${nextTerminalNumber.current}`,
213
+ title: `Agent ${nextTerminalNumber.current}`,
214
+ options: options,
215
+ };
216
+ setTerminals((prev) => [...prev, newTerminal]);
217
+ setActiveTerminalId(newTerminal.id);
218
+ nextTerminalNumber.current++;
219
+ };
220
+
184
221
  const closeTerminal = (terminalId: string) => {
185
222
  if (terminals.length <= 1) {
186
223
  // Don't close the last terminal
@@ -218,7 +255,17 @@ export function Agents({
218
255
  id: `agent-${agent.id}`,
219
256
  title: agent.name,
220
257
  agentId: agent.id,
221
- options: initialOptions,
258
+ options: {
259
+ ...initialOptions,
260
+ // Pass cost information from loaded agent
261
+ totalCost: agentWithMessages?.totalCost,
262
+ totalInputTokenCost: agentWithMessages?.totalInputTokenCost,
263
+ totalOutputTokenCost: agentWithMessages?.totalOutputTokenCost,
264
+ totalCachedTokenCost: agentWithMessages?.totalCachedInputTokenCost,
265
+ totalInputTokens: agentWithMessages?.totalInputTokens,
266
+ totalOutputTokens: agentWithMessages?.totalOutputTokens,
267
+ totalCachedTokens: agentWithMessages?.totalCachedInputTokens,
268
+ },
222
269
  messages: agentWithMessages?.messages
223
270
  ? convertAgentMessagesToTerminalMessages(agentWithMessages.messages)
224
271
  : [],
@@ -347,6 +394,8 @@ export function Agents({
347
394
  ...terminal.options,
348
395
  // Pass the pre-loaded messages to the terminal
349
396
  initialMessages: terminal.messages,
397
+ // Pass the agentId to maintain continuity
398
+ agentId: terminal.agentId,
350
399
  }}
351
400
  onAgentNameUpdate={(name) => {
352
401
  // Update the terminal title when agent name is updated
@@ -0,0 +1,209 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Sparkles, Send } from "lucide-react";
5
+ import {
6
+ Popover,
7
+ PopoverContent,
8
+ PopoverTrigger,
9
+ } from "../../components/ui/popover";
10
+ import { Button } from "../../components/ui/button";
11
+ import { useEditContext } from "../client/editContext";
12
+ import { AiTerminalOptions } from "./AiTerminal";
13
+ import { Component } from "../pageModel";
14
+
15
+ interface AiPromptPopoverProps {
16
+ children: React.ReactNode;
17
+ components?: Component[];
18
+ }
19
+
20
+ export function AiPromptPopover({
21
+ children,
22
+ components = [],
23
+ }: AiPromptPopoverProps) {
24
+ const [isOpen, setIsOpen] = useState(false);
25
+ const [prompt, setPrompt] = useState("");
26
+ const editContext = useEditContext();
27
+
28
+ const handleSubmit = async () => {
29
+ if (!prompt.trim()) return;
30
+
31
+ // Close the popover
32
+ setIsOpen(false);
33
+
34
+ // Create comprehensive AI options with component context
35
+ let finalPrompt = prompt.trim();
36
+ let hiddenSystemPrompt: string | undefined;
37
+
38
+ if (components.length > 0) {
39
+ // Create detailed component context for the agent
40
+ const componentInfo = components
41
+ .map((comp) => {
42
+ const componentType = comp.type || comp.name || "Unknown";
43
+ const componentId = comp.id;
44
+ const hasContent = comp.items && comp.items.length > 0;
45
+
46
+ return `- ${componentType} (ID: ${componentId})${hasContent ? " with data" : " (empty)"}`;
47
+ })
48
+ .join("\n");
49
+
50
+ finalPrompt = `I'm working with the following ${components.length === 1 ? "component" : "components"}:
51
+ ${componentInfo}
52
+
53
+ User request: ${prompt.trim()}`;
54
+
55
+ // Add hidden system prompt with component details for agent context
56
+ const componentDetails = components.map((comp) => ({
57
+ id: comp.id,
58
+ name: comp.name,
59
+ type: comp.type,
60
+ renderingId: comp.rendering?.id,
61
+ items: comp.items?.length || 0,
62
+ placeholder: comp.parentPlaceholder?.name,
63
+ isShared: comp.isShared,
64
+ }));
65
+
66
+ hiddenSystemPrompt = `Working with components: ${JSON.stringify(componentDetails, null, 2)}`;
67
+ }
68
+
69
+ // Switch to agents view first
70
+ editContext?.switchView("agents");
71
+
72
+ try {
73
+ // Execute the prompt directly with a new agent
74
+ await executePromptDirectly(finalPrompt, hiddenSystemPrompt);
75
+ } catch (error) {
76
+ console.error("Failed to execute prompt with new agent:", error);
77
+ // Fallback to the old method if the new one fails
78
+ const aiOptions: AiTerminalOptions = {
79
+ initialPrompt: finalPrompt,
80
+ hiddenSystemPrompt,
81
+ };
82
+ }
83
+
84
+ // Reset the prompt
85
+ setPrompt("");
86
+ };
87
+
88
+ const executePromptDirectly = async (
89
+ initialPrompt: string,
90
+ hiddenSystemPrompt?: string,
91
+ ) => {
92
+ if (!editContext) return;
93
+
94
+ // Generate a new agent ID
95
+ const newAgentId = crypto.randomUUID();
96
+
97
+ // Create the user message
98
+ const userMessage = {
99
+ id: crypto.randomUUID(),
100
+ content: initialPrompt,
101
+ role: "user",
102
+ name: "user",
103
+ };
104
+
105
+ // Create messages array with optional hidden system prompt
106
+ const messages = [
107
+ ...(hiddenSystemPrompt
108
+ ? [
109
+ {
110
+ role: "system",
111
+ name: "system",
112
+ content: hiddenSystemPrompt,
113
+ id: crypto.randomUUID(),
114
+ },
115
+ ]
116
+ : []),
117
+ userMessage,
118
+ ];
119
+
120
+ // Import the required functions
121
+ const { executePrompt } = await import("../services/aiService");
122
+ const { createEditorAiContext } = await import("./editorAiContext");
123
+
124
+ // Create AI context
125
+ const aiContext = createEditorAiContext({ editContext });
126
+
127
+ // Execute the prompt with the new agent ID
128
+ await executePrompt(messages, aiContext, {
129
+ sessionId: editContext.sessionId,
130
+ agentId: newAgentId,
131
+ addSelectedComponents: true,
132
+ profileId: "Editor", // Default to Editor profile
133
+ });
134
+
135
+ // The agents list will refresh automatically via websockets or periodic refresh
136
+ };
137
+
138
+ const handleKeyDown = (e: React.KeyboardEvent) => {
139
+ if (e.key === "Enter" && !e.shiftKey) {
140
+ e.preventDefault();
141
+ handleSubmit();
142
+ }
143
+ };
144
+
145
+ return (
146
+ <Popover
147
+ open={isOpen}
148
+ onOpenChange={setIsOpen}
149
+ enableIframeClickDetection={false}
150
+ >
151
+ <PopoverTrigger asChild>{children}</PopoverTrigger>
152
+ <PopoverContent
153
+ className="w-96 p-4"
154
+ side="bottom"
155
+ align="start"
156
+ onMouseDown={(e) => e.stopPropagation()}
157
+ onClick={(e) => e.stopPropagation()}
158
+ >
159
+ <div className="space-y-3">
160
+ <div className="flex items-center gap-2">
161
+ <Sparkles size={16} className="text-blue-500" />
162
+ <h3 className="font-medium">AI Assistant</h3>
163
+ </div>
164
+
165
+ <div className="space-y-2">
166
+ <label htmlFor="ai-prompt" className="text-sm text-gray-700">
167
+ What would you like AI to help you with?
168
+ {components.length > 0 && (
169
+ <span className="mt-1 block text-xs text-gray-500">
170
+ Working with {components.length} selected component
171
+ {components.length === 1 ? "" : "s"}
172
+ </span>
173
+ )}
174
+ </label>
175
+ <textarea
176
+ id="ai-prompt"
177
+ value={prompt}
178
+ onChange={(e) => setPrompt(e.target.value)}
179
+ onKeyDown={handleKeyDown}
180
+ placeholder="Enter your prompt here..."
181
+ className="w-full resize-none rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none"
182
+ rows={3}
183
+ autoFocus
184
+ />
185
+ </div>
186
+
187
+ <div className="flex justify-end gap-2">
188
+ <Button
189
+ variant="outline"
190
+ size="sm"
191
+ onClick={() => setIsOpen(false)}
192
+ >
193
+ Cancel
194
+ </Button>
195
+ <Button
196
+ size="sm"
197
+ onClick={handleSubmit}
198
+ disabled={!prompt.trim()}
199
+ className="flex items-center gap-1"
200
+ >
201
+ <Send size={14} />
202
+ Start Agent
203
+ </Button>
204
+ </div>
205
+ </div>
206
+ </PopoverContent>
207
+ </Popover>
208
+ );
209
+ }