@alpaca-editor/core 1.0.4103 → 1.0.4105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ui/context-menu.d.ts +1 -1
- package/dist/components/ui/context-menu.js +8 -8
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/config/config.js +8 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/editor/ContentTree.js +15 -16
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/ContextMenu.js +37 -6
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/MainLayout.js +1 -1
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/Terminal.js +2 -2
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/AgentHistory.js +1 -1
- package/dist/editor/ai/AgentTerminal.js +191 -15
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +9 -1
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -1
- package/dist/editor/ai/AiResponseMessage.js +4 -2
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/types.d.ts +25 -0
- package/dist/editor/ai/types.js +2 -0
- package/dist/editor/ai/types.js.map +1 -0
- package/dist/editor/client/EditorShell.js +31 -0
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +3 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/commands/agentCommands.d.ts +9 -0
- package/dist/editor/commands/agentCommands.js +30 -0
- package/dist/editor/commands/agentCommands.js.map +1 -0
- package/dist/editor/commands/itemCommands.js +14 -14
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/component-designer/aiContext.d.ts +1 -1
- package/dist/editor/context-menu/InsertMenu.d.ts +2 -1
- package/dist/editor/context-menu/InsertMenu.js +20 -15
- package/dist/editor/context-menu/InsertMenu.js.map +1 -1
- package/dist/editor/control-center/IndexOverview.js +3 -2
- package/dist/editor/control-center/IndexOverview.js.map +1 -1
- package/dist/editor/control-center/WebSocketMessages.js +3 -3
- package/dist/editor/control-center/WebSocketMessages.js.map +1 -1
- package/dist/editor/field-types/NameValueListEditor.d.ts +7 -0
- package/dist/editor/field-types/NameValueListEditor.js +99 -0
- package/dist/editor/field-types/NameValueListEditor.js.map +1 -0
- package/dist/editor/fieldTypes.d.ts +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js +2 -2
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
- package/dist/editor/menubar/ActiveUsers.js +3 -2
- package/dist/editor/menubar/ActiveUsers.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +47 -10
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +3 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +30 -4
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +44 -5
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/reviews/Comments.js +96 -35
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +22 -2
- package/dist/editor/services/agentService.js +26 -1
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +2 -1
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/Completions.js +2 -1
- package/dist/editor/sidebar/Completions.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.js +47 -90
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/ui/DragPreview.d.ts +13 -0
- package/dist/editor/ui/DragPreview.js +35 -0
- package/dist/editor/ui/DragPreview.js.map +1 -0
- package/dist/editor/ui/Icons.d.ts +2 -1
- package/dist/editor/ui/Icons.js +2 -2
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.js +2 -2
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +3 -15
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleTable.js +1 -1
- package/dist/editor/ui/SimpleTable.js.map +1 -1
- package/dist/editor/utils.d.ts +12 -1
- package/dist/editor/utils.js +60 -12
- package/dist/editor/utils.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +37 -9
- package/dist/tour/default-tour.js +34 -38
- package/dist/tour/default-tour.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/context-menu.tsx +31 -19
- package/src/config/config.tsx +9 -1
- package/src/config/types.ts +1 -0
- package/src/editor/ContentTree.tsx +13 -18
- package/src/editor/ContextMenu.tsx +112 -19
- package/src/editor/MainLayout.tsx +1 -1
- package/src/editor/Terminal.tsx +2 -2
- package/src/editor/ai/AgentHistory.tsx +2 -2
- package/src/editor/ai/AgentTerminal.tsx +234 -35
- package/src/editor/ai/Agents.tsx +14 -2
- package/src/editor/ai/AiResponseMessage.tsx +8 -6
- package/src/editor/ai/types.ts +27 -0
- package/src/editor/client/EditorShell.tsx +33 -0
- package/src/editor/client/editContext.ts +12 -1
- package/src/editor/commands/agentCommands.tsx +49 -0
- package/src/editor/commands/itemCommands.tsx +26 -14
- package/src/editor/component-designer/aiContext.ts +1 -1
- package/src/editor/context-menu/InsertMenu.tsx +64 -39
- package/src/editor/control-center/IndexOverview.tsx +3 -2
- package/src/editor/control-center/WebSocketMessages.tsx +3 -5
- package/src/editor/field-types/NameValueListEditor.tsx +197 -0
- package/src/editor/fieldTypes.ts +1 -1
- package/src/editor/media-selector/MediaFolderBrowser.tsx +2 -2
- package/src/editor/menubar/ActiveUsers.tsx +4 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +61 -13
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +82 -20
- package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +77 -24
- package/src/editor/reviews/Comments.tsx +123 -38
- package/src/editor/services/agentService.ts +53 -2
- package/src/editor/services/aiService.ts +3 -1
- package/src/editor/sidebar/Completions.tsx +2 -1
- package/src/editor/sidebar/GraphQL.tsx +50 -99
- package/src/editor/ui/DragPreview.tsx +44 -0
- package/src/editor/ui/Icons.tsx +3 -0
- package/src/editor/ui/ItemNameDialogNew.tsx +7 -3
- package/src/editor/ui/PerfectTree.tsx +2 -17
- package/src/editor/ui/SimpleTable.tsx +2 -2
- package/src/editor/utils.ts +73 -15
- package/src/revision.ts +2 -2
- package/src/tour/default-tour.tsx +34 -46
- package/dist/editor/ai/AiTerminal.d.ts +0 -47
- package/dist/editor/ai/AiTerminal.js +0 -300
- package/dist/editor/ai/AiTerminal.js.map +0 -1
- package/src/editor/ai/AiTerminal.tsx +0 -570
- package/src/editor/component-designer/ComponentDesignerAiTerminal.tsx_ +0 -11
|
@@ -5,22 +5,7 @@ import React, {
|
|
|
5
5
|
useCallback,
|
|
6
6
|
useLayoutEffect,
|
|
7
7
|
} from "react";
|
|
8
|
-
import {
|
|
9
|
-
Send,
|
|
10
|
-
Bot,
|
|
11
|
-
AlertCircle,
|
|
12
|
-
Loader2,
|
|
13
|
-
User,
|
|
14
|
-
X,
|
|
15
|
-
FileText,
|
|
16
|
-
Puzzle,
|
|
17
|
-
Type,
|
|
18
|
-
Plus,
|
|
19
|
-
MessageSquare,
|
|
20
|
-
Wand2,
|
|
21
|
-
Square,
|
|
22
|
-
ChevronDown,
|
|
23
|
-
} from "lucide-react";
|
|
8
|
+
import { Send, AlertCircle, Loader2, User, Wand2, Square } from "lucide-react";
|
|
24
9
|
import { DancingDots } from "./DancingDots";
|
|
25
10
|
import {
|
|
26
11
|
AgentChatMessage,
|
|
@@ -33,13 +18,15 @@ import {
|
|
|
33
18
|
AgentDetails,
|
|
34
19
|
updateAgentMetadata,
|
|
35
20
|
AgentMetadata,
|
|
21
|
+
updateAgentSettings,
|
|
22
|
+
updateAgentCostLimit,
|
|
36
23
|
} from "../services/agentService";
|
|
37
24
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
38
25
|
import { Textarea } from "../../components/ui/textarea";
|
|
39
26
|
import { Button } from "../../components/ui/button";
|
|
40
27
|
import { AiResponseMessage } from "./AiResponseMessage";
|
|
41
28
|
import { AgentCostDisplay } from "./AgentCostDisplay";
|
|
42
|
-
import { Message } from "./
|
|
29
|
+
import { Message } from "./types";
|
|
43
30
|
import { ContextInfoBar } from "./ContextInfoBar";
|
|
44
31
|
import { getComponentById } from "../componentTreeHelper";
|
|
45
32
|
import { Comment } from "../../types";
|
|
@@ -50,6 +37,8 @@ import {
|
|
|
50
37
|
PopoverContent,
|
|
51
38
|
PopoverTrigger,
|
|
52
39
|
} from "../../components/ui/popover";
|
|
40
|
+
import { SecretAgentIcon } from "../ui/Icons";
|
|
41
|
+
import { formatTime, formatDateTime } from "../utils";
|
|
53
42
|
|
|
54
43
|
// Simple user message component
|
|
55
44
|
const UserMessage = ({ message }: { message: AgentChatMessage }) => {
|
|
@@ -60,10 +49,10 @@ const UserMessage = ({ message }: { message: AgentChatMessage }) => {
|
|
|
60
49
|
</div>
|
|
61
50
|
<div className="min-w-0 flex-1 select-text">
|
|
62
51
|
<div className="mb-1 flex items-center gap-2">
|
|
63
|
-
<span className="text-
|
|
52
|
+
<span className="text-xs font-medium text-gray-900">You</span>
|
|
64
53
|
{message.createdDate && (
|
|
65
54
|
<span className="text-xs text-gray-400">
|
|
66
|
-
{new Date(message.createdDate)
|
|
55
|
+
{formatTime(new Date(message.createdDate))}
|
|
67
56
|
</span>
|
|
68
57
|
)}
|
|
69
58
|
</div>
|
|
@@ -316,6 +305,11 @@ export function AgentTerminal({
|
|
|
316
305
|
}, [messages]);
|
|
317
306
|
|
|
318
307
|
const [error, setError] = useState<string | null>(null);
|
|
308
|
+
const [costLimitExceeded, setCostLimitExceeded] = useState<{
|
|
309
|
+
totalCost: number;
|
|
310
|
+
costLimit: number;
|
|
311
|
+
initialCostLimit: number;
|
|
312
|
+
} | null>(null);
|
|
319
313
|
|
|
320
314
|
// Flag to track when we should create a new message
|
|
321
315
|
const shouldCreateNewMessage = useRef(false);
|
|
@@ -329,6 +323,12 @@ export function AgentTerminal({
|
|
|
329
323
|
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
|
330
324
|
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
|
331
325
|
|
|
326
|
+
// Cache mode/model changes made while the agent is still "new" (not yet persisted)
|
|
327
|
+
const pendingSettingsRef = useRef<{
|
|
328
|
+
modelName?: string | null;
|
|
329
|
+
mode?: "agent" | "ask";
|
|
330
|
+
} | null>(null);
|
|
331
|
+
|
|
332
332
|
// Auto-scroll to bottom when new messages arrive
|
|
333
333
|
const scrollToBottom = useCallback(() => {
|
|
334
334
|
const container = messagesContainerRef.current;
|
|
@@ -850,6 +850,24 @@ export function AgentTerminal({
|
|
|
850
850
|
break;
|
|
851
851
|
|
|
852
852
|
case "error":
|
|
853
|
+
// Detect cost limit exceeded
|
|
854
|
+
try {
|
|
855
|
+
const data: any = (message as any).data;
|
|
856
|
+
if (
|
|
857
|
+
message.error === "COST_LIMIT_EXCEEDED" ||
|
|
858
|
+
(data && data.kind === "costLimitExceeded")
|
|
859
|
+
) {
|
|
860
|
+
setCostLimitExceeded({
|
|
861
|
+
totalCost: Number(data?.totalCost) || 0,
|
|
862
|
+
costLimit: Number(data?.costLimit) || 0,
|
|
863
|
+
initialCostLimit: Number(data?.initialCostLimit) || 0,
|
|
864
|
+
});
|
|
865
|
+
setIsWaitingForResponse(false);
|
|
866
|
+
shouldCreateNewMessage.current = false;
|
|
867
|
+
resetDotsTimer();
|
|
868
|
+
break;
|
|
869
|
+
}
|
|
870
|
+
} catch {}
|
|
853
871
|
console.error("❌ Stream error:", message.error);
|
|
854
872
|
setError(message.error || "Stream error occurred");
|
|
855
873
|
setIsWaitingForResponse(false);
|
|
@@ -1188,17 +1206,23 @@ export function AgentTerminal({
|
|
|
1188
1206
|
}
|
|
1189
1207
|
}, [profiles, agent?.profileId]);
|
|
1190
1208
|
|
|
1191
|
-
// Update selected model when the active profile changes
|
|
1209
|
+
// Update selected model when the active profile or agent model changes
|
|
1192
1210
|
useEffect(() => {
|
|
1193
1211
|
if (!activeProfile) return;
|
|
1194
|
-
const
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1212
|
+
const agentModelName = agent?.model; // persisted as model NAME on server
|
|
1213
|
+
const models = activeProfile.models || [];
|
|
1214
|
+
let nextModelId: string | undefined = undefined;
|
|
1215
|
+
if (agentModelName) {
|
|
1216
|
+
const match = models.find(
|
|
1217
|
+
(m) => (m.name || "").toLowerCase() === agentModelName.toLowerCase(),
|
|
1218
|
+
);
|
|
1219
|
+
if (match) nextModelId = match.id;
|
|
1220
|
+
}
|
|
1221
|
+
if (!nextModelId) {
|
|
1222
|
+
nextModelId = activeProfile.defaultModelId || models[0]?.id;
|
|
1223
|
+
}
|
|
1200
1224
|
setSelectedModelId(nextModelId || undefined);
|
|
1201
|
-
}, [activeProfile?.id]);
|
|
1225
|
+
}, [activeProfile?.id, agent?.model]);
|
|
1202
1226
|
|
|
1203
1227
|
// Cleanup stream connection when component unmounts or agent changes
|
|
1204
1228
|
useEffect(() => {
|
|
@@ -1210,7 +1234,7 @@ export function AgentTerminal({
|
|
|
1210
1234
|
};
|
|
1211
1235
|
}, [agent?.id]);
|
|
1212
1236
|
|
|
1213
|
-
// Initialize mode from metadata
|
|
1237
|
+
// Initialize mode from metadata; fall back to agent.Mode from server
|
|
1214
1238
|
useEffect(() => {
|
|
1215
1239
|
try {
|
|
1216
1240
|
const metaMode = (agentMetadata as any)?.mode as
|
|
@@ -1219,9 +1243,16 @@ export function AgentTerminal({
|
|
|
1219
1243
|
| undefined;
|
|
1220
1244
|
if (metaMode === "agent" || metaMode === "ask") {
|
|
1221
1245
|
setMode(metaMode);
|
|
1246
|
+
return;
|
|
1222
1247
|
}
|
|
1223
1248
|
} catch {}
|
|
1224
|
-
|
|
1249
|
+
try {
|
|
1250
|
+
const serverMode = (agent as any)?.mode as string | undefined;
|
|
1251
|
+
if (serverMode === "agent" || serverMode === "ask") {
|
|
1252
|
+
setMode(serverMode);
|
|
1253
|
+
}
|
|
1254
|
+
} catch {}
|
|
1255
|
+
}, [agentMetadata, (agent as any)?.mode]);
|
|
1225
1256
|
|
|
1226
1257
|
const updateMode = useCallback(
|
|
1227
1258
|
async (nextMode: "agent" | "ask") => {
|
|
@@ -1266,6 +1297,26 @@ export function AgentTerminal({
|
|
|
1266
1297
|
}
|
|
1267
1298
|
}, [showDots, shouldAutoScroll, scrollToBottom]);
|
|
1268
1299
|
|
|
1300
|
+
// Persist any pending settings (mode/model) once an agent exists server-side
|
|
1301
|
+
const persistPendingSettingsIfNeeded = useCallback(async () => {
|
|
1302
|
+
try {
|
|
1303
|
+
if (!agent?.id) return;
|
|
1304
|
+
const pending = pendingSettingsRef.current;
|
|
1305
|
+
if (!pending) return;
|
|
1306
|
+
const payload: {
|
|
1307
|
+
model?: string | null;
|
|
1308
|
+
mode?: "agent" | "ask" | string | null;
|
|
1309
|
+
} = {};
|
|
1310
|
+
if (pending.modelName) payload.model = pending.modelName;
|
|
1311
|
+
if (pending.mode) payload.mode = pending.mode;
|
|
1312
|
+
if (Object.keys(payload).length === 0) return;
|
|
1313
|
+
await updateAgentSettings(agent.id, payload);
|
|
1314
|
+
pendingSettingsRef.current = null;
|
|
1315
|
+
} catch (e) {
|
|
1316
|
+
console.error("Failed to persist pending settings", e);
|
|
1317
|
+
}
|
|
1318
|
+
}, [agent?.id]);
|
|
1319
|
+
|
|
1269
1320
|
const handleSubmit = async () => {
|
|
1270
1321
|
if (!prompt.trim() || isSubmitting || !editContext) return;
|
|
1271
1322
|
|
|
@@ -1276,6 +1327,26 @@ export function AgentTerminal({
|
|
|
1276
1327
|
|
|
1277
1328
|
if (!agentId) return;
|
|
1278
1329
|
|
|
1330
|
+
// Optional context factory: invoke if configured and available, otherwise continue
|
|
1331
|
+
const factoryName = (agentMetadata as any)?.additionalData
|
|
1332
|
+
?.contextFactory as string | undefined;
|
|
1333
|
+
if (factoryName) {
|
|
1334
|
+
const factory = editContext.getContextFactory?.(factoryName);
|
|
1335
|
+
if (factory) {
|
|
1336
|
+
try {
|
|
1337
|
+
await Promise.resolve(factory());
|
|
1338
|
+
} catch (e: any) {
|
|
1339
|
+
console.warn(
|
|
1340
|
+
`Context factory '${factoryName}' failed: ${e?.message || String(e)}`,
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
} else {
|
|
1344
|
+
console.warn(
|
|
1345
|
+
`Context factory not found: ${factoryName}. Proceeding without it.`,
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1279
1350
|
// Add user message to local state immediately for better UX
|
|
1280
1351
|
const userMessage: AgentChatMessage = {
|
|
1281
1352
|
id: `user-${Date.now()}`,
|
|
@@ -1351,6 +1422,9 @@ export function AgentTerminal({
|
|
|
1351
1422
|
|
|
1352
1423
|
await startAgent(request);
|
|
1353
1424
|
|
|
1425
|
+
// If user changed mode/model while the agent was new, persist them now
|
|
1426
|
+
await persistPendingSettingsIfNeeded();
|
|
1427
|
+
|
|
1354
1428
|
// Save prompt to history
|
|
1355
1429
|
if (prompt.trim()) {
|
|
1356
1430
|
setPromptHistory((prev) => [
|
|
@@ -1433,6 +1507,26 @@ export function AgentTerminal({
|
|
|
1433
1507
|
const agentId = agent?.id;
|
|
1434
1508
|
if (!agentId) return;
|
|
1435
1509
|
|
|
1510
|
+
// Optional context factory: invoke if configured and available, otherwise continue
|
|
1511
|
+
const factoryName = (agentMetadata as any)?.additionalData
|
|
1512
|
+
?.contextFactory as string | undefined;
|
|
1513
|
+
if (factoryName) {
|
|
1514
|
+
const factory = editContext.getContextFactory?.(factoryName);
|
|
1515
|
+
if (factory) {
|
|
1516
|
+
try {
|
|
1517
|
+
await Promise.resolve(factory());
|
|
1518
|
+
} catch (e: any) {
|
|
1519
|
+
console.warn(
|
|
1520
|
+
`Context factory '${factoryName}' failed: ${e?.message || String(e)}`,
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
} else {
|
|
1524
|
+
console.warn(
|
|
1525
|
+
`Context factory not found: ${factoryName}. Proceeding without it.`,
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1436
1530
|
const userMessage: AgentChatMessage = {
|
|
1437
1531
|
id: `user-${Date.now()}`,
|
|
1438
1532
|
agentId,
|
|
@@ -1499,6 +1593,9 @@ export function AgentTerminal({
|
|
|
1499
1593
|
|
|
1500
1594
|
await startAgent(request);
|
|
1501
1595
|
|
|
1596
|
+
// If user changed mode/model while the agent was new, persist them now
|
|
1597
|
+
await persistPendingSettingsIfNeeded();
|
|
1598
|
+
|
|
1502
1599
|
await connectToStream();
|
|
1503
1600
|
} catch (err) {
|
|
1504
1601
|
console.error("Failed to submit quick message:", err);
|
|
@@ -2046,6 +2143,56 @@ export function AgentTerminal({
|
|
|
2046
2143
|
/>
|
|
2047
2144
|
);
|
|
2048
2145
|
|
|
2146
|
+
const renderCostLimitBanner = () => {
|
|
2147
|
+
if (!costLimitExceeded) return null;
|
|
2148
|
+
const { totalCost, costLimit, initialCostLimit } = costLimitExceeded;
|
|
2149
|
+
return (
|
|
2150
|
+
<div className="m-3 rounded border border-amber-300 bg-amber-50 p-3 text-xs text-amber-900">
|
|
2151
|
+
<div className="mb-2 flex items-center gap-2">
|
|
2152
|
+
<AlertCircle className="h-4 w-4 text-amber-500" strokeWidth={1} />
|
|
2153
|
+
<span>
|
|
2154
|
+
Cost limit exceeded. Spent ${totalCost.toFixed(4)} / $
|
|
2155
|
+
{costLimit.toFixed(4)}.
|
|
2156
|
+
</span>
|
|
2157
|
+
</div>
|
|
2158
|
+
<div className="flex gap-2">
|
|
2159
|
+
<button
|
|
2160
|
+
className="rounded border border-amber-300 bg-white px-2 py-1 hover:bg-amber-100"
|
|
2161
|
+
onClick={async () => {
|
|
2162
|
+
if (!agent?.id) return;
|
|
2163
|
+
try {
|
|
2164
|
+
await updateAgentCostLimit(agent.id, "remove");
|
|
2165
|
+
setCostLimitExceeded(null);
|
|
2166
|
+
// Reconnect to stream for next actions
|
|
2167
|
+
await connectToStream(agent);
|
|
2168
|
+
} catch (e) {
|
|
2169
|
+
console.error("Failed to remove cost limit", e);
|
|
2170
|
+
}
|
|
2171
|
+
}}
|
|
2172
|
+
>
|
|
2173
|
+
Continue and remove limit
|
|
2174
|
+
</button>
|
|
2175
|
+
<button
|
|
2176
|
+
className="rounded border border-amber-300 bg-white px-2 py-1 hover:bg-amber-100"
|
|
2177
|
+
onClick={async () => {
|
|
2178
|
+
if (!agent?.id) return;
|
|
2179
|
+
try {
|
|
2180
|
+
// Extend by initial cost limit amount
|
|
2181
|
+
await updateAgentCostLimit(agent.id, "extend");
|
|
2182
|
+
setCostLimitExceeded(null);
|
|
2183
|
+
await connectToStream(agent);
|
|
2184
|
+
} catch (e) {
|
|
2185
|
+
console.error("Failed to extend cost limit", e);
|
|
2186
|
+
}
|
|
2187
|
+
}}
|
|
2188
|
+
>
|
|
2189
|
+
Continue and extend limit
|
|
2190
|
+
</button>
|
|
2191
|
+
</div>
|
|
2192
|
+
</div>
|
|
2193
|
+
);
|
|
2194
|
+
};
|
|
2195
|
+
|
|
2049
2196
|
return (
|
|
2050
2197
|
<div className="flex h-full flex-col">
|
|
2051
2198
|
{/* Messages */}
|
|
@@ -2054,6 +2201,7 @@ export function AgentTerminal({
|
|
|
2054
2201
|
className="flex-1 overflow-y-auto"
|
|
2055
2202
|
onScroll={handleScroll}
|
|
2056
2203
|
>
|
|
2204
|
+
{renderCostLimitBanner()}
|
|
2057
2205
|
{error && (
|
|
2058
2206
|
<div className="m-4 rounded-lg border-l-4 border-red-500 bg-red-50 p-3">
|
|
2059
2207
|
<div className="flex items-start">
|
|
@@ -2072,9 +2220,10 @@ export function AgentTerminal({
|
|
|
2072
2220
|
{messages.length === 0 && !error && (
|
|
2073
2221
|
<div className="flex h-full items-center justify-center p-8">
|
|
2074
2222
|
<div className="text-center">
|
|
2075
|
-
<
|
|
2076
|
-
|
|
2223
|
+
<SecretAgentIcon
|
|
2224
|
+
size={48}
|
|
2077
2225
|
strokeWidth={1}
|
|
2226
|
+
className="mx-auto mb-4 text-gray-400"
|
|
2078
2227
|
/>
|
|
2079
2228
|
<h3 className="mb-2 text-lg font-medium text-gray-900">
|
|
2080
2229
|
Start a conversation
|
|
@@ -2186,9 +2335,36 @@ export function AgentTerminal({
|
|
|
2186
2335
|
<select
|
|
2187
2336
|
className="h-5 rounded border px-1.5 text-[10px] text-gray-500"
|
|
2188
2337
|
value={mode}
|
|
2189
|
-
onChange={(e) =>
|
|
2190
|
-
|
|
2191
|
-
|
|
2338
|
+
onChange={async (e) => {
|
|
2339
|
+
const nextMode = (e.target.value as "agent" | "ask") || "agent";
|
|
2340
|
+
// Optimistic UI update
|
|
2341
|
+
setMode(nextMode);
|
|
2342
|
+
const current = agentMetadata || ({} as AgentMetadata);
|
|
2343
|
+
const nextMeta: AgentMetadata = {
|
|
2344
|
+
...current,
|
|
2345
|
+
mode: nextMode,
|
|
2346
|
+
} as AgentMetadata;
|
|
2347
|
+
try {
|
|
2348
|
+
if (!agent?.id || agent.status === "new") {
|
|
2349
|
+
setAgentMetadata(nextMeta);
|
|
2350
|
+
// Cache until first start when agent is persisted
|
|
2351
|
+
pendingSettingsRef.current = {
|
|
2352
|
+
...(pendingSettingsRef.current || {}),
|
|
2353
|
+
mode: nextMode,
|
|
2354
|
+
};
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
await updateAgentSettings(agent.id, { mode: nextMode });
|
|
2358
|
+
setAgentMetadata(nextMeta);
|
|
2359
|
+
setAgent((prev) =>
|
|
2360
|
+
prev
|
|
2361
|
+
? { ...prev, metadata: JSON.stringify(nextMeta) }
|
|
2362
|
+
: prev,
|
|
2363
|
+
);
|
|
2364
|
+
} catch (e2) {
|
|
2365
|
+
console.error("Failed to persist mode change", e2);
|
|
2366
|
+
}
|
|
2367
|
+
}}
|
|
2192
2368
|
title="Mode"
|
|
2193
2369
|
aria-label="Mode"
|
|
2194
2370
|
data-testid="agent-mode-select"
|
|
@@ -2219,7 +2395,30 @@ export function AgentTerminal({
|
|
|
2219
2395
|
<select
|
|
2220
2396
|
className="h-5 rounded border px-1.5 text-[10px] text-gray-500"
|
|
2221
2397
|
value={selectedModelId || ""}
|
|
2222
|
-
onChange={(e) =>
|
|
2398
|
+
onChange={async (e) => {
|
|
2399
|
+
const nextId = e.target.value;
|
|
2400
|
+
setSelectedModelId(nextId);
|
|
2401
|
+
const modelName =
|
|
2402
|
+
activeProfile?.models?.find((m) => m.id === nextId)?.name ||
|
|
2403
|
+
"";
|
|
2404
|
+
// Update local agent state immediately for UX and to reflect in streaming stub
|
|
2405
|
+
setAgent((prev) =>
|
|
2406
|
+
prev ? { ...prev, model: modelName } : prev,
|
|
2407
|
+
);
|
|
2408
|
+
// Persist only for existing agents; otherwise cache until first start
|
|
2409
|
+
try {
|
|
2410
|
+
if (agent?.id && agent.status !== "new") {
|
|
2411
|
+
await updateAgentSettings(agent.id, { model: modelName });
|
|
2412
|
+
} else {
|
|
2413
|
+
pendingSettingsRef.current = {
|
|
2414
|
+
...(pendingSettingsRef.current || {}),
|
|
2415
|
+
modelName,
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
} catch (err) {
|
|
2419
|
+
console.error("Failed to persist agent model", err);
|
|
2420
|
+
}
|
|
2421
|
+
}}
|
|
2223
2422
|
title="Model"
|
|
2224
2423
|
aria-label="Model"
|
|
2225
2424
|
data-testid="agent-model-select"
|
package/src/editor/ai/Agents.tsx
CHANGED
|
@@ -502,7 +502,6 @@ export function Agents({ closeButton }: { closeButton?: React.ReactNode }) {
|
|
|
502
502
|
return (
|
|
503
503
|
<div className="flex h-full items-center justify-center">
|
|
504
504
|
<div className="w-72">
|
|
505
|
-
|
|
506
505
|
{loadingProfiles ? (
|
|
507
506
|
<div className="text-center text-xs text-gray-500">
|
|
508
507
|
Loading profiles...
|
|
@@ -670,7 +669,20 @@ export function Agents({ closeButton }: { closeButton?: React.ReactNode }) {
|
|
|
670
669
|
{agent.name}
|
|
671
670
|
</div>
|
|
672
671
|
<div className="text-xs text-gray-400">
|
|
673
|
-
{
|
|
672
|
+
{(() => {
|
|
673
|
+
try {
|
|
674
|
+
const {
|
|
675
|
+
formatDateTime,
|
|
676
|
+
} = require("../utils");
|
|
677
|
+
return formatDateTime(
|
|
678
|
+
new Date(agent.updatedDate),
|
|
679
|
+
);
|
|
680
|
+
} catch {
|
|
681
|
+
return new Date(
|
|
682
|
+
agent.updatedDate,
|
|
683
|
+
).toLocaleString();
|
|
684
|
+
}
|
|
685
|
+
})()}
|
|
674
686
|
</div>
|
|
675
687
|
</div>
|
|
676
688
|
<SimpleIconButton
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState, useEffect, useMemo } from "react";
|
|
2
|
+
import { formatTime } from "../utils";
|
|
2
3
|
|
|
3
4
|
import { useEditContext } from "../client/editContext";
|
|
4
5
|
import { EditOperation } from "../../types";
|
|
5
|
-
import { Message, ToolCall } from "./
|
|
6
|
+
import { Message, ToolCall } from "./types";
|
|
6
7
|
import { ToolCallDisplay } from "./ToolCallDisplay";
|
|
7
8
|
|
|
8
|
-
import { X
|
|
9
|
+
import { X } from "lucide-react";
|
|
9
10
|
import { Button } from "../../components/ui/button";
|
|
10
11
|
import { Checkbox } from "../../components/ui/checkbox";
|
|
12
|
+
import { SecretAgentIcon } from "../ui/Icons";
|
|
11
13
|
|
|
12
14
|
type QuickAction = {
|
|
13
15
|
id?: string;
|
|
@@ -464,15 +466,15 @@ export function AiResponseMessage({
|
|
|
464
466
|
return (
|
|
465
467
|
<div className="flex gap-3 p-4" data-testid="agent-message">
|
|
466
468
|
<div className="flex-shrink-0">
|
|
467
|
-
<
|
|
469
|
+
<SecretAgentIcon size={24} strokeWidth={1} className="text-gray-2" />
|
|
468
470
|
</div>
|
|
469
471
|
<div className="min-w-0 flex-1 select-text">
|
|
470
472
|
<div className="mb-1 flex items-center gap-2">
|
|
471
|
-
<span className="text-
|
|
473
|
+
<span className="text-dark text-xs font-medium">Agent</span>
|
|
472
474
|
|
|
473
475
|
{lastMessage && lastMessage.createdDate && (
|
|
474
476
|
<span className="text-xs text-gray-400">
|
|
475
|
-
{new Date(lastMessage.createdDate)
|
|
477
|
+
{formatTime(new Date(lastMessage.createdDate))}
|
|
476
478
|
</span>
|
|
477
479
|
)}
|
|
478
480
|
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ToolCall = {
|
|
2
|
+
id: string;
|
|
3
|
+
displayName?: string;
|
|
4
|
+
function: {
|
|
5
|
+
name: string;
|
|
6
|
+
arguments: string;
|
|
7
|
+
result?: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type Message = {
|
|
13
|
+
id: string;
|
|
14
|
+
content: string;
|
|
15
|
+
formattedContent?: string;
|
|
16
|
+
name: string;
|
|
17
|
+
role: string;
|
|
18
|
+
tool_calls?: ToolCall[];
|
|
19
|
+
tool_call_id?: string;
|
|
20
|
+
createdDate?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type AiContext = {
|
|
24
|
+
promptData: any;
|
|
25
|
+
endpoint?: string;
|
|
26
|
+
callback?: (response: any) => void;
|
|
27
|
+
};
|
|
@@ -1621,6 +1621,16 @@ export function EditorShell({
|
|
|
1621
1621
|
|
|
1622
1622
|
const editContext = useMemo<EditContextType>(() => {
|
|
1623
1623
|
// console.log('🔄 EditContext useMemo is being recalculated');
|
|
1624
|
+
// Simple registry for per-view context factories (e.g., GraphQL context)
|
|
1625
|
+
// Stored outside the EditContext object via ref to avoid re-renders
|
|
1626
|
+
if (!(globalThis as any).__editorContextFactoriesRef) {
|
|
1627
|
+
(globalThis as any).__editorContextFactoriesRef = {
|
|
1628
|
+
map: new Map<string, () => Promise<any> | any>(),
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
const factoriesRef = (globalThis as any).__editorContextFactoriesRef as {
|
|
1632
|
+
map: Map<string, () => Promise<any> | any>;
|
|
1633
|
+
};
|
|
1624
1634
|
const context = {
|
|
1625
1635
|
operations: operationsContext.ops,
|
|
1626
1636
|
itemsRepository,
|
|
@@ -2069,6 +2079,27 @@ export function EditorShell({
|
|
|
2069
2079
|
setCurrentWizardId,
|
|
2070
2080
|
favorites,
|
|
2071
2081
|
loadFavorites,
|
|
2082
|
+
// Context factory registry methods
|
|
2083
|
+
registerContextFactory: (
|
|
2084
|
+
name: string,
|
|
2085
|
+
factory: () => Promise<any> | any,
|
|
2086
|
+
) => {
|
|
2087
|
+
try {
|
|
2088
|
+
factoriesRef.map.set(name, factory);
|
|
2089
|
+
} catch {}
|
|
2090
|
+
},
|
|
2091
|
+
unregisterContextFactory: (name: string) => {
|
|
2092
|
+
try {
|
|
2093
|
+
factoriesRef.map.delete(name);
|
|
2094
|
+
} catch {}
|
|
2095
|
+
},
|
|
2096
|
+
getContextFactory: (name: string) => {
|
|
2097
|
+
try {
|
|
2098
|
+
return factoriesRef.map.get(name);
|
|
2099
|
+
} catch {
|
|
2100
|
+
return undefined;
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2072
2103
|
};
|
|
2073
2104
|
|
|
2074
2105
|
return context as unknown as EditContextType;
|
|
@@ -2126,6 +2157,7 @@ export function EditorShell({
|
|
|
2126
2157
|
revision,
|
|
2127
2158
|
comments,
|
|
2128
2159
|
setComments,
|
|
2160
|
+
availableCommentTags,
|
|
2129
2161
|
selectedComment,
|
|
2130
2162
|
setSelectedComment,
|
|
2131
2163
|
loadComments,
|
|
@@ -2141,6 +2173,7 @@ export function EditorShell({
|
|
|
2141
2173
|
setShowSuggestedEdits,
|
|
2142
2174
|
showSuggestedEditsDiff,
|
|
2143
2175
|
setShowSuggestedEditsDiff,
|
|
2176
|
+
showComments,
|
|
2144
2177
|
showResolvedComments,
|
|
2145
2178
|
setShowResolvedComments,
|
|
2146
2179
|
showComponentNavigator,
|
|
@@ -176,7 +176,9 @@ export type EditContextType = {
|
|
|
176
176
|
showToast: (message: string) => void;
|
|
177
177
|
sessionId: string;
|
|
178
178
|
openSplashScreen: () => void;
|
|
179
|
-
getComponentCommands: (
|
|
179
|
+
getComponentCommands: (
|
|
180
|
+
components: Component[],
|
|
181
|
+
) => Promise<ComponentCommand[]>;
|
|
180
182
|
selectMedia: ({
|
|
181
183
|
selectedIdPath,
|
|
182
184
|
mode,
|
|
@@ -372,6 +374,15 @@ export type EditContextType = {
|
|
|
372
374
|
loadFavorites: () => Promise<void>;
|
|
373
375
|
currentWizardId: string | null;
|
|
374
376
|
setCurrentWizardId: (wizardId: string | null) => void;
|
|
377
|
+
|
|
378
|
+
// Context factory registry (optional): panels can register named factories
|
|
379
|
+
// that produce structured context objects for agent prompts.
|
|
380
|
+
registerContextFactory?: (
|
|
381
|
+
name: string,
|
|
382
|
+
factory: () => Promise<any> | any,
|
|
383
|
+
) => void;
|
|
384
|
+
unregisterContextFactory?: (name: string) => void;
|
|
385
|
+
getContextFactory?: (name: string) => (() => Promise<any> | any) | undefined;
|
|
375
386
|
};
|
|
376
387
|
|
|
377
388
|
const EditContext = React.createContext<EditContextType | undefined>(undefined);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Command, CommandContext } from "./commands";
|
|
3
|
+
import { Wand2 } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
export type CreateAgentCommandData = {
|
|
6
|
+
profileName?: string;
|
|
7
|
+
profileId?: string;
|
|
8
|
+
contextFactory?: string;
|
|
9
|
+
initialPrompt?: string;
|
|
10
|
+
additionalData?: Record<string, any>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const createAgentCommand: Command<CreateAgentCommandData> = {
|
|
14
|
+
id: "createAgent",
|
|
15
|
+
label: "Start Agent",
|
|
16
|
+
icon: <Wand2 strokeWidth={1} className="text-violet-600" />,
|
|
17
|
+
execute: async (context: CommandContext<CreateAgentCommandData>) => {
|
|
18
|
+
const edit = context.editContext;
|
|
19
|
+
try {
|
|
20
|
+
edit.setShowAgentsPanel?.(true);
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
profileName,
|
|
24
|
+
profileId,
|
|
25
|
+
contextFactory,
|
|
26
|
+
initialPrompt,
|
|
27
|
+
additionalData,
|
|
28
|
+
} = context.data || {};
|
|
29
|
+
|
|
30
|
+
const metadata = {
|
|
31
|
+
profile: profileName,
|
|
32
|
+
additionalData: {
|
|
33
|
+
...(additionalData || {}),
|
|
34
|
+
profileName,
|
|
35
|
+
profileId,
|
|
36
|
+
contextFactory,
|
|
37
|
+
initialPrompt,
|
|
38
|
+
},
|
|
39
|
+
} as any;
|
|
40
|
+
|
|
41
|
+
window.dispatchEvent(
|
|
42
|
+
new CustomEvent("editor:addNewAgent", { detail: { metadata } }),
|
|
43
|
+
);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error("Failed to start agent", e);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
disabled: () => false,
|
|
49
|
+
};
|