@alpaca-editor/core 1.0.4192 → 1.0.4194
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/agents-view/AgentCard.js +1 -1
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentsView.js +5 -7
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/config/config.js +14 -7
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ItemInfo.js +3 -3
- package/dist/editor/ItemInfo.js.map +1 -1
- package/dist/editor/QuickItemSwitcher.js +1 -1
- package/dist/editor/QuickItemSwitcher.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +1 -7
- package/dist/editor/ai/AgentTerminal.js +382 -256
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +84 -198
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -3
- package/dist/editor/ai/AiResponseMessage.js +12 -65
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +1 -2
- package/dist/editor/ai/ToolCallDisplay.js +5 -13
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/client/EditorShell.js +5 -6
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +4 -6
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +0 -2
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/control-center/About.js +1 -1
- package/dist/editor/control-center/About.js.map +1 -1
- package/dist/editor/control-center/AllAgentsPanel.js +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +0 -1
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +1 -1
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/PageWizard.js +3 -3
- package/dist/page-wizard/PageWizard.js.map +1 -1
- package/dist/page-wizard/WizardSteps.js +1 -1
- package/dist/page-wizard/WizardSteps.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/OpenPage.js +6 -10
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/splash-screen/RecentPages.js +2 -2
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/splash-screen/SplashScreen.js +1 -1
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +12 -244
- package/package.json +1 -1
- package/src/agents-view/AgentCard.tsx +6 -1
- package/src/agents-view/AgentsView.tsx +30 -18
- package/src/config/config.tsx +17 -8
- package/src/editor/ItemInfo.tsx +2 -3
- package/src/editor/QuickItemSwitcher.tsx +1 -1
- package/src/editor/ai/AgentTerminal.tsx +649 -544
- package/src/editor/ai/Agents.tsx +250 -464
- package/src/editor/ai/AiResponseMessage.tsx +29 -134
- package/src/editor/ai/ToolCallDisplay.tsx +4 -18
- package/src/editor/client/EditorShell.tsx +6 -9
- package/src/editor/client/hooks/useSocketMessageHandler.ts +7 -6
- package/src/editor/commands/componentCommands.tsx +0 -1
- package/src/editor/control-center/About.tsx +2 -2
- package/src/editor/control-center/AllAgentsPanel.tsx +1 -1
- package/src/editor/field-types/MultiLineText.tsx +1 -1
- package/src/editor/services/aiService.ts +0 -2
- package/src/editor/sidebar/Validation.tsx +1 -1
- package/src/index.ts +0 -5
- package/src/page-wizard/PageWizard.tsx +3 -3
- package/src/page-wizard/WizardSteps.tsx +1 -1
- package/src/revision.ts +2 -2
- package/src/splash-screen/OpenPage.tsx +4 -12
- package/src/splash-screen/RecentPages.tsx +61 -58
- package/src/splash-screen/SplashScreen.tsx +1 -1
- package/styles.css +0 -20
- package/dist/components/ui/PlaceholderInput.d.ts +0 -41
- package/dist/components/ui/PlaceholderInput.js +0 -160
- package/dist/components/ui/PlaceholderInput.js.map +0 -1
- package/dist/components/ui/PlaceholderInputTypes.d.ts +0 -41
- package/dist/components/ui/PlaceholderInputTypes.js +0 -48
- package/dist/components/ui/PlaceholderInputTypes.js.map +0 -1
- package/dist/components/ui/PlaceholderItemSelector.d.ts +0 -7
- package/dist/components/ui/PlaceholderItemSelector.js +0 -154
- package/dist/components/ui/PlaceholderItemSelector.js.map +0 -1
- package/dist/editor/ai/MediaImage.d.ts +0 -6
- package/dist/editor/ai/MediaImage.js +0 -38
- package/dist/editor/ai/MediaImage.js.map +0 -1
- package/dist/splash-screen/ModernSplashScreen.d.ts +0 -8
- package/dist/splash-screen/ModernSplashScreen.js +0 -92
- package/dist/splash-screen/ModernSplashScreen.js.map +0 -1
- package/dist/splash-screen/ParheliaAssistantChat.d.ts +0 -8
- package/dist/splash-screen/ParheliaAssistantChat.js +0 -155
- package/dist/splash-screen/ParheliaAssistantChat.js.map +0 -1
- package/dist/splash-screen/RecentAgents.d.ts +0 -7
- package/dist/splash-screen/RecentAgents.js +0 -76
- package/dist/splash-screen/RecentAgents.js.map +0 -1
- package/src/components/ui/PlaceholderInput.tsx +0 -290
- package/src/components/ui/PlaceholderInputTypes.tsx +0 -97
- package/src/components/ui/PlaceholderItemSelector.tsx +0 -253
- package/src/editor/ai/MediaImage.tsx +0 -75
- package/src/splash-screen/ModernSplashScreen.tsx +0 -229
- package/src/splash-screen/ParheliaAssistantChat.tsx +0 -273
- package/src/splash-screen/RecentAgents.tsx +0 -151
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useEffect, useState, useRef, useCallback, useLayoutEffect, useMemo,
|
|
2
|
+
import React, { useEffect, useState, useRef, useCallback, useLayoutEffect, useMemo, } from "react";
|
|
3
3
|
import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, } from "lucide-react";
|
|
4
4
|
import { DancingDots } from "./DancingDots";
|
|
5
|
-
import { getAgent, startAgent, updateAgentSettings, updateAgentCostLimit, cancelAgent, canonicalizeAgentMetadata, } from "../services/agentService";
|
|
5
|
+
import { getAgent, startAgent, updateAgentContext, updateAgentSettings, updateAgentCostLimit, cancelAgent, canonicalizeAgentMetadata, } from "../services/agentService";
|
|
6
6
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
7
7
|
import { Textarea } from "../../components/ui/textarea";
|
|
8
8
|
import { Button } from "../../components/ui/button";
|
|
9
|
-
import { PlaceholderInput } from "../../components/ui/PlaceholderInput";
|
|
10
9
|
import { AiResponseMessage } from "./AiResponseMessage";
|
|
11
10
|
import { AgentCostDisplay } from "./AgentCostDisplay";
|
|
12
11
|
import { ContextInfoBar } from "./ContextInfoBar";
|
|
@@ -440,12 +439,6 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
440
439
|
id: agentMessage.id,
|
|
441
440
|
content: agentMessage.content,
|
|
442
441
|
formattedContent: agentMessage.content
|
|
443
|
-
?.replace(/^######\s+(.+)$/gm, "<h6>$1</h6>")
|
|
444
|
-
?.replace(/^#####\s+(.+)$/gm, "<h5>$1</h5>")
|
|
445
|
-
?.replace(/^####\s+(.+)$/gm, "<h4>$1</h4>")
|
|
446
|
-
?.replace(/^###\s+(.+)$/gm, "<h3>$1</h3>")
|
|
447
|
-
?.replace(/^##\s+(.+)$/gm, "<h2>$1</h2>")
|
|
448
|
-
?.replace(/^#\s+(.+)$/gm, "<h1>$1</h1>")
|
|
449
442
|
?.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
|
450
443
|
?.replace(/\n/g, "<br/>"),
|
|
451
444
|
name: agentMessage.name,
|
|
@@ -475,7 +468,7 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
475
468
|
// interface AgentTerminalProps {
|
|
476
469
|
// agentStub: Agent;
|
|
477
470
|
// }
|
|
478
|
-
export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true,
|
|
471
|
+
export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true, }) {
|
|
479
472
|
const editContext = useEditContext();
|
|
480
473
|
const fieldsContext = useFieldsEditContext();
|
|
481
474
|
const [agent, setAgent] = useState(undefined);
|
|
@@ -485,7 +478,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
485
478
|
const [isLoading, setIsLoading] = useState(false);
|
|
486
479
|
const [isConnecting, setIsConnecting] = useState(false);
|
|
487
480
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
488
|
-
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
489
481
|
const [agentMetadata, setAgentMetadata] = useState(null);
|
|
490
482
|
// Generate a stable clientSessionId per component instance for stream deduplication
|
|
491
483
|
const clientSessionIdRef = useRef(null);
|
|
@@ -658,12 +650,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
658
650
|
setIsVoiceSupported(false);
|
|
659
651
|
}
|
|
660
652
|
}, []);
|
|
661
|
-
// Auto-focus terminal input on mount
|
|
662
|
-
useEffect(() => {
|
|
663
|
-
if (textareaRef.current) {
|
|
664
|
-
textareaRef.current.focus();
|
|
665
|
-
}
|
|
666
|
-
}, []);
|
|
667
653
|
// Start voice recognition
|
|
668
654
|
const startVoice = useCallback(() => {
|
|
669
655
|
try {
|
|
@@ -1192,6 +1178,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1192
1178
|
const loadAgent = useCallback(async () => {
|
|
1193
1179
|
try {
|
|
1194
1180
|
if (agentStub.status === "new") {
|
|
1181
|
+
// Set agent ID immediately for new agents
|
|
1182
|
+
window.currentAgentId = agentStub.id;
|
|
1183
|
+
// Set currentAgentId for new agent
|
|
1195
1184
|
// Derive initial profile from provided metadata if present
|
|
1196
1185
|
const initialProfileIdFromMeta = (() => {
|
|
1197
1186
|
try {
|
|
@@ -1376,6 +1365,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1376
1365
|
const merged = mergeMessagesById(dbMessages, prevMessages);
|
|
1377
1366
|
return merged;
|
|
1378
1367
|
});
|
|
1368
|
+
// Set agent ID for existing agents too
|
|
1369
|
+
window.currentAgentId = agentData.id;
|
|
1379
1370
|
// Check if cost limit was exceeded (detect from existing messages)
|
|
1380
1371
|
try {
|
|
1381
1372
|
const costLimitMessage = (agentData.messages || []).find((msg) => msg.role === "assistant" &&
|
|
@@ -1887,19 +1878,18 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1887
1878
|
return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
|
|
1888
1879
|
}, []);
|
|
1889
1880
|
// Profiles are provided by parent component (Agents). No local loading here.
|
|
1890
|
-
// Select active profile based on agent.profileId or
|
|
1881
|
+
// Select active profile based on agent.profileId or default to first
|
|
1891
1882
|
useEffect(() => {
|
|
1892
1883
|
if (!profiles || profiles.length === 0)
|
|
1893
1884
|
return;
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
? (profiles.find((p) => p.id === profileIdToUse) ?? profiles[0])
|
|
1885
|
+
const candidate = agent?.profileId
|
|
1886
|
+
? (profiles.find((p) => p.id === agent?.profileId) ??
|
|
1887
|
+
profiles[0])
|
|
1898
1888
|
: profiles[0];
|
|
1899
1889
|
if (candidate && (!activeProfile || activeProfile.id !== candidate.id)) {
|
|
1900
1890
|
setActiveProfile(candidate);
|
|
1901
1891
|
}
|
|
1902
|
-
}, [profiles, agent?.profileId
|
|
1892
|
+
}, [profiles, agent?.profileId]);
|
|
1903
1893
|
// Update selected model when the active profile or agent model changes
|
|
1904
1894
|
useEffect(() => {
|
|
1905
1895
|
if (!activeProfile)
|
|
@@ -1976,13 +1966,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1976
1966
|
}
|
|
1977
1967
|
}, [agent?.id]);
|
|
1978
1968
|
const handleSubmit = async () => {
|
|
1979
|
-
if (isSubmitting || !editContext)
|
|
1969
|
+
if (!prompt.trim() || isSubmitting || !editContext)
|
|
1980
1970
|
return;
|
|
1981
1971
|
try {
|
|
1982
1972
|
setIsSubmitting(true);
|
|
1983
1973
|
setError(null);
|
|
1984
|
-
|
|
1985
|
-
const agentId = agent?.id || agentStub.id;
|
|
1974
|
+
const agentId = agent?.id;
|
|
1986
1975
|
if (!agentId)
|
|
1987
1976
|
return;
|
|
1988
1977
|
// Optional context factory: invoke if configured and available, otherwise continue
|
|
@@ -2006,7 +1995,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2006
1995
|
// It will be added when we receive the agent:user:message broadcast from the server
|
|
2007
1996
|
// This ensures all tabs (including the sending tab) have the same messageId from the database
|
|
2008
1997
|
const request = {
|
|
2009
|
-
agentId:
|
|
1998
|
+
agentId: agent.id,
|
|
2010
1999
|
message: prompt.trim(),
|
|
2011
2000
|
sessionId: editContext.sessionId,
|
|
2012
2001
|
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
@@ -2163,35 +2152,232 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2163
2152
|
setIsSubmitting(false);
|
|
2164
2153
|
}
|
|
2165
2154
|
};
|
|
2166
|
-
//
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
},
|
|
2155
|
+
// Context info bar helpers - must be declared before any early returns to keep hooks order stable
|
|
2156
|
+
const removeContextKey = useCallback(async (key, index) => {
|
|
2157
|
+
if (!agent?.id)
|
|
2158
|
+
return;
|
|
2159
|
+
const current = agentMetadata || {};
|
|
2160
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2161
|
+
const currentWithoutContext = { ...current };
|
|
2162
|
+
delete currentWithoutContext.context;
|
|
2163
|
+
const next = {
|
|
2164
|
+
...currentWithoutContext,
|
|
2165
|
+
additionalData: {
|
|
2166
|
+
...(current.additionalData || {}),
|
|
2167
|
+
context: {
|
|
2168
|
+
...((current.additionalData &&
|
|
2169
|
+
current.additionalData.context) ||
|
|
2170
|
+
{}),
|
|
2171
|
+
},
|
|
2172
|
+
},
|
|
2173
|
+
};
|
|
2174
|
+
if (next.additionalData && next.additionalData.context) {
|
|
2175
|
+
const ctx = next.additionalData.context;
|
|
2176
|
+
if (key === "items" && typeof index === "number" && ctx.items) {
|
|
2177
|
+
ctx.items.splice(index, 1);
|
|
2178
|
+
if (ctx.items.length === 0)
|
|
2179
|
+
delete ctx.items;
|
|
2180
|
+
}
|
|
2181
|
+
else if (key === "componentIds" &&
|
|
2182
|
+
typeof index === "number" &&
|
|
2183
|
+
ctx.componentIds) {
|
|
2184
|
+
ctx.componentIds.splice(index, 1);
|
|
2185
|
+
if (ctx.componentIds.length === 0)
|
|
2186
|
+
delete ctx.componentIds;
|
|
2187
|
+
}
|
|
2188
|
+
else if (key === "field") {
|
|
2189
|
+
delete ctx.field;
|
|
2190
|
+
}
|
|
2191
|
+
else if (key === "comment") {
|
|
2192
|
+
delete ctx.comment;
|
|
2193
|
+
}
|
|
2184
2194
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
+
try {
|
|
2196
|
+
// For new (not yet persisted) agents, only update local state
|
|
2197
|
+
if (agent.status === "new") {
|
|
2198
|
+
setAgentMetadata(next);
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
// Persisted agents: update server and local cache
|
|
2202
|
+
await updateAgentContext(agent.id, next);
|
|
2203
|
+
setAgentMetadata(next);
|
|
2204
|
+
setAgent((prev) => prev ? { ...prev, metadata: JSON.stringify(next) } : prev);
|
|
2205
|
+
}
|
|
2206
|
+
catch (e) {
|
|
2207
|
+
console.error("Failed to update agent metadata", e);
|
|
2208
|
+
}
|
|
2209
|
+
}, [agent?.id, agentMetadata]);
|
|
2210
|
+
const addPagesToContext = async () => {
|
|
2211
|
+
if (!agent?.id || !editContext?.currentItemDescriptor)
|
|
2212
|
+
return;
|
|
2213
|
+
// Add the current page (currentItemDescriptor represents the current page)
|
|
2214
|
+
// Note: Currently only supports adding the current page. To support multiple pages,
|
|
2215
|
+
// we'd need a page selection mechanism in the UI (e.g., a page picker dialog)
|
|
2216
|
+
const item = editContext.currentItemDescriptor;
|
|
2217
|
+
const pageToAdd = {
|
|
2218
|
+
id: item.id,
|
|
2219
|
+
language: item.language,
|
|
2220
|
+
version: item.version,
|
|
2221
|
+
name: editContext.contentEditorItem?.name,
|
|
2222
|
+
path: undefined,
|
|
2223
|
+
};
|
|
2224
|
+
const current = agentMetadata || {};
|
|
2225
|
+
const currentPages = current.additionalData?.context?.items || [];
|
|
2226
|
+
// Check if this page is already in context
|
|
2227
|
+
const existingPageIds = new Set(currentPages.map((p) => `${p.id}-${p.language}-${p.version}`));
|
|
2228
|
+
if (existingPageIds.has(`${pageToAdd.id}-${pageToAdd.language}-${pageToAdd.version}`)) {
|
|
2229
|
+
return; // Page already exists
|
|
2230
|
+
}
|
|
2231
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2232
|
+
const currentWithoutContext = { ...current };
|
|
2233
|
+
delete currentWithoutContext.context;
|
|
2234
|
+
const next = {
|
|
2235
|
+
...currentWithoutContext,
|
|
2236
|
+
additionalData: {
|
|
2237
|
+
...(current.additionalData || {}),
|
|
2238
|
+
context: {
|
|
2239
|
+
...((current.additionalData &&
|
|
2240
|
+
current.additionalData.context) ||
|
|
2241
|
+
{}),
|
|
2242
|
+
items: [...currentPages, pageToAdd],
|
|
2243
|
+
},
|
|
2244
|
+
},
|
|
2245
|
+
};
|
|
2246
|
+
try {
|
|
2247
|
+
if (agent.status === "new") {
|
|
2248
|
+
setAgentMetadata(next);
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
await updateAgentContext(agent.id, next);
|
|
2252
|
+
setAgentMetadata(next);
|
|
2253
|
+
setAgent((prev) => prev ? { ...prev, metadata: JSON.stringify(next) } : prev);
|
|
2254
|
+
}
|
|
2255
|
+
catch (e) {
|
|
2256
|
+
console.error("Failed to update agent metadata (add pages)", e);
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
const addSelectedComponentsToContext = async () => {
|
|
2260
|
+
if (!agent?.id || !editContext?.selection?.length)
|
|
2261
|
+
return;
|
|
2262
|
+
const current = agentMetadata || {};
|
|
2263
|
+
const currentComponentIds = current.additionalData?.context?.componentIds ||
|
|
2264
|
+
[];
|
|
2265
|
+
// Merge with existing components, avoiding duplicates
|
|
2266
|
+
const existingIds = new Set(currentComponentIds);
|
|
2267
|
+
const newComponentIds = editContext.selection.filter((id) => !existingIds.has(id));
|
|
2268
|
+
if (newComponentIds.length === 0)
|
|
2269
|
+
return; // No new components to add
|
|
2270
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2271
|
+
const currentWithoutContext = { ...current };
|
|
2272
|
+
delete currentWithoutContext.context;
|
|
2273
|
+
const next = {
|
|
2274
|
+
...currentWithoutContext,
|
|
2275
|
+
additionalData: {
|
|
2276
|
+
...(current.additionalData || {}),
|
|
2277
|
+
context: {
|
|
2278
|
+
...((current.additionalData &&
|
|
2279
|
+
current.additionalData.context) ||
|
|
2280
|
+
{}),
|
|
2281
|
+
componentIds: [...currentComponentIds, ...newComponentIds],
|
|
2282
|
+
},
|
|
2283
|
+
},
|
|
2284
|
+
};
|
|
2285
|
+
try {
|
|
2286
|
+
if (agent.status === "new") {
|
|
2287
|
+
setAgentMetadata(next);
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
await updateAgentContext(agent.id, next);
|
|
2291
|
+
setAgentMetadata(next);
|
|
2292
|
+
setAgent((prev) => prev ? { ...prev, metadata: JSON.stringify(next) } : prev);
|
|
2293
|
+
}
|
|
2294
|
+
catch (e) {
|
|
2295
|
+
console.error("Failed to update agent metadata (add components)", e);
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
const addComponentIdsToContext = async (ids) => {
|
|
2299
|
+
if (!agent?.id || !ids?.length)
|
|
2300
|
+
return;
|
|
2301
|
+
const current = agentMetadata || {};
|
|
2302
|
+
const currentComponentIds = current.additionalData?.context?.componentIds ||
|
|
2303
|
+
[];
|
|
2304
|
+
// Merge with existing components, avoiding duplicates
|
|
2305
|
+
const existingIds = new Set(currentComponentIds);
|
|
2306
|
+
const newComponentIds = ids.filter((id) => !!id && !existingIds.has(id));
|
|
2307
|
+
if (newComponentIds.length === 0)
|
|
2308
|
+
return;
|
|
2309
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2310
|
+
const currentWithoutContext = { ...current };
|
|
2311
|
+
delete currentWithoutContext.context;
|
|
2312
|
+
const next = {
|
|
2313
|
+
...currentWithoutContext,
|
|
2314
|
+
additionalData: {
|
|
2315
|
+
...(current.additionalData || {}),
|
|
2316
|
+
context: {
|
|
2317
|
+
...((current.additionalData &&
|
|
2318
|
+
current.additionalData.context) ||
|
|
2319
|
+
{}),
|
|
2320
|
+
componentIds: [...currentComponentIds, ...newComponentIds],
|
|
2321
|
+
},
|
|
2322
|
+
},
|
|
2323
|
+
};
|
|
2324
|
+
try {
|
|
2325
|
+
if (agent.status === "new") {
|
|
2326
|
+
setAgentMetadata(next);
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2329
|
+
await updateAgentContext(agent.id, next);
|
|
2330
|
+
setAgentMetadata(next);
|
|
2331
|
+
setAgent((prev) => prev ? { ...prev, metadata: JSON.stringify(next) } : prev);
|
|
2332
|
+
}
|
|
2333
|
+
catch (e) {
|
|
2334
|
+
console.error("Failed to update agent metadata (add components)", e);
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
const addPagesToContextFromItems = async (items) => {
|
|
2338
|
+
if (!agent?.id || !items?.length)
|
|
2339
|
+
return;
|
|
2340
|
+
const current = agentMetadata || {};
|
|
2341
|
+
const currentPages = current.additionalData?.context?.items || [];
|
|
2342
|
+
const existingPageIds = new Set(currentPages.map((p) => `${p.id}-${p.language}-${p.version}`));
|
|
2343
|
+
const pagesToAdd = items
|
|
2344
|
+
.filter((it) => !!it?.id)
|
|
2345
|
+
.map((it) => ({
|
|
2346
|
+
id: it.id,
|
|
2347
|
+
language: it.language,
|
|
2348
|
+
version: it.version,
|
|
2349
|
+
}))
|
|
2350
|
+
.filter((p) => !existingPageIds.has(`${p.id}-${p.language}-${p.version}`));
|
|
2351
|
+
if (pagesToAdd.length === 0)
|
|
2352
|
+
return;
|
|
2353
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2354
|
+
const currentWithoutContext = { ...current };
|
|
2355
|
+
delete currentWithoutContext.context;
|
|
2356
|
+
const next = {
|
|
2357
|
+
...currentWithoutContext,
|
|
2358
|
+
additionalData: {
|
|
2359
|
+
...(current.additionalData || {}),
|
|
2360
|
+
context: {
|
|
2361
|
+
...((current.additionalData &&
|
|
2362
|
+
current.additionalData.context) ||
|
|
2363
|
+
{}),
|
|
2364
|
+
items: [...currentPages, ...pagesToAdd],
|
|
2365
|
+
},
|
|
2366
|
+
},
|
|
2367
|
+
};
|
|
2368
|
+
try {
|
|
2369
|
+
if (agent.status === "new") {
|
|
2370
|
+
setAgentMetadata(next);
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
await updateAgentContext(agent.id, next);
|
|
2374
|
+
setAgentMetadata(next);
|
|
2375
|
+
setAgent((prev) => prev ? { ...prev, metadata: JSON.stringify(next) } : prev);
|
|
2376
|
+
}
|
|
2377
|
+
catch (e) {
|
|
2378
|
+
console.error("Failed to update agent metadata (add items/pages)", e);
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2195
2381
|
// Resolve display names when metadata or editor state changes
|
|
2196
2382
|
useEffect(() => {
|
|
2197
2383
|
const metaCtx = agentMetadata;
|
|
@@ -2365,11 +2551,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2365
2551
|
}
|
|
2366
2552
|
}, children: "Extend limit and continue" }) })] }));
|
|
2367
2553
|
};
|
|
2368
|
-
return (_jsxs("div", { className:
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2554
|
+
return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error && (_jsx("div", { className: "m-4 rounded-lg border-l-4 border-red-500 bg-red-50 p-3 select-text", children: _jsxs("div", { className: "flex items-start", children: [_jsx(AlertCircle, { className: "mt-0.5 h-5 w-5 text-red-400", strokeWidth: 1 }), _jsxs("div", { className: "ml-3", children: [_jsx("p", { className: "text-sm font-medium text-red-800", children: "Error" }), _jsx("p", { className: "mt-1 text-sm text-red-700", children: error })] })] }) })), messages.length === 0 && !error && (_jsx("div", { className: "flex h-full items-center justify-center p-8", children: _jsxs("div", { className: "max-w-prose text-center", children: [activeProfile?.svgIcon ? (_jsx("div", { className: "mx-auto mb-4 flex h-24 w-24 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
|
|
2555
|
+
__html: activeProfile.svgIcon,
|
|
2556
|
+
} })) : (_jsx(SecretAgentIcon, { size: 96, strokeWidth: 1, className: "mx-auto mb-4 text-gray-400" })), activeProfile?.greetingMessage ? (_jsx("div", { className: "prose prose-sm mx-auto text-center", dangerouslySetInnerHTML: {
|
|
2557
|
+
__html: activeProfile.greetingMessage,
|
|
2558
|
+
} })) : (_jsxs(_Fragment, { children: [_jsx("h3", { className: "mb-2 text-lg font-medium text-gray-900", children: "Start a conversation" }), _jsx("p", { className: "mb-4 text-sm text-gray-500", children: "Send a message to begin working with your AI agent." }), _jsx("div", { className: "text-xs text-gray-400", children: "Your agent can help with content editing, research, and automation tasks." })] }))] }) })), _jsx("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: groupConsecutiveMessages(messages).map((group, groupIndex) => {
|
|
2373
2559
|
if (group.type === "user" && group.messages[0]) {
|
|
2374
2560
|
// Render user message
|
|
2375
2561
|
return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
|
|
@@ -2389,23 +2575,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2389
2575
|
return null;
|
|
2390
2576
|
}
|
|
2391
2577
|
const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
|
|
2392
|
-
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isSubmitting && !isConnecting, editOperations: [], error: error || undefined, profileSvgIcon: activeProfile?.svgIcon,
|
|
2578
|
+
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isSubmitting && !isConnecting, editOperations: [], error: error || undefined, profileSvgIcon: activeProfile?.svgIcon, onQuickAction: (action) => {
|
|
2393
2579
|
const text = (action.prompt ||
|
|
2394
2580
|
action.value ||
|
|
2395
2581
|
action.label ||
|
|
2396
2582
|
"").trim();
|
|
2397
2583
|
if (!text)
|
|
2398
2584
|
return;
|
|
2399
|
-
// Check if text contains placeholders ({placeholder} or <placeholder>)
|
|
2400
|
-
const hasPlaceholders = /\{([^}]+)\}|<([^>]+)>/.test(text);
|
|
2401
|
-
if (hasPlaceholders) {
|
|
2402
|
-
// Show placeholder input
|
|
2403
|
-
setActivePlaceholderInput({
|
|
2404
|
-
text,
|
|
2405
|
-
behavior: action.behavior,
|
|
2406
|
-
});
|
|
2407
|
-
return;
|
|
2408
|
-
}
|
|
2409
2585
|
if (action.behavior === "compose") {
|
|
2410
2586
|
setPrompt(text);
|
|
2411
2587
|
setInputPlaceholder(action.placeholder ||
|
|
@@ -2431,204 +2607,154 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2431
2607
|
sendQuickMessage(text);
|
|
2432
2608
|
} }, groupIndex));
|
|
2433
2609
|
}
|
|
2434
|
-
}) }), _jsx("div", { className: showDots ? "visible" : "invisible", children: _jsx(DancingDots, {}) }), renderCostLimitBanner(), _jsx("div", { ref: messagesEndRef })] }),
|
|
2435
|
-
// Placeholder Input (from quick actions)
|
|
2436
|
-
_jsx(PlaceholderInput, { text: activePlaceholderInput.text, showButtons: false, onComplete: (filledText) => {
|
|
2437
|
-
setActivePlaceholderInput(null);
|
|
2438
|
-
if (activePlaceholderInput.behavior === "compose") {
|
|
2439
|
-
setPrompt(filledText);
|
|
2440
|
-
setInputPlaceholder("Review and edit, then press Enter to send");
|
|
2441
|
-
if (textareaRef.current) {
|
|
2442
|
-
try {
|
|
2443
|
-
textareaRef.current.focus();
|
|
2444
|
-
const v = textareaRef.current.value || "";
|
|
2445
|
-
textareaRef.current.selectionStart = v.length;
|
|
2446
|
-
textareaRef.current.selectionEnd = v.length;
|
|
2447
|
-
}
|
|
2448
|
-
catch { }
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
else {
|
|
2452
|
-
// Submit behavior or default
|
|
2453
|
-
if (isExecuting) {
|
|
2454
|
-
try {
|
|
2455
|
-
handleStop();
|
|
2456
|
-
}
|
|
2457
|
-
catch { }
|
|
2458
|
-
}
|
|
2459
|
-
sendQuickMessage(filledText);
|
|
2460
|
-
}
|
|
2461
|
-
}, onCancel: () => {
|
|
2462
|
-
setActivePlaceholderInput(null);
|
|
2463
|
-
} })) : prompt && /\{([^}]+)\}|<([^>]+)>/.test(prompt) ? (
|
|
2464
|
-
// Template mode: show PlaceholderInput when prompt contains placeholders
|
|
2465
|
-
_jsx(PlaceholderInput, { text: prompt, showButtons: false, onComplete: (filledText) => {
|
|
2466
|
-
setPrompt(filledText);
|
|
2467
|
-
// Auto-submit after filling placeholders
|
|
2468
|
-
if (filledText.trim()) {
|
|
2469
|
-
if (isExecuting) {
|
|
2470
|
-
try {
|
|
2471
|
-
handleStop();
|
|
2472
|
-
}
|
|
2473
|
-
catch { }
|
|
2474
|
-
}
|
|
2475
|
-
sendQuickMessage(filledText);
|
|
2476
|
-
}
|
|
2477
|
-
}, onCancel: () => {
|
|
2478
|
-
setPrompt("");
|
|
2479
|
-
setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
2480
|
-
} })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, value: prompt, onChange: (e) => {
|
|
2610
|
+
}) }), _jsx("div", { className: showDots ? "visible" : "invisible", children: _jsx(DancingDots, {}) }), renderCostLimitBanner(), _jsx("div", { ref: messagesEndRef })] }), renderContextInfoBar(), _jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata }), _jsxs("div", { className: "border-t border-gray-200 p-4", children: [_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, value: prompt, onChange: (e) => {
|
|
2481
2611
|
setPrompt(e.target.value);
|
|
2482
2612
|
// Reset history index when user starts typing
|
|
2483
2613
|
if (currentHistoryIndex !== -1) {
|
|
2484
2614
|
setCurrentHistoryIndex(-1);
|
|
2485
2615
|
}
|
|
2486
|
-
}, onKeyDown: handleKeyPress, placeholder: inputPlaceholder, className: "h-[80px] flex-1 resize-none overflow-y-auto text-xs", "data-testid": "agent-terminal-prompt", disabled: isSubmitting }) })
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2616
|
+
}, onKeyDown: handleKeyPress, placeholder: inputPlaceholder, className: "h-[80px] flex-1 resize-none overflow-y-auto text-xs", "data-testid": "agent-terminal-prompt", disabled: isSubmitting }) }), _jsxs("div", { className: "flex items-stretch justify-between gap-2", children: [_jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-start gap-2", children: [_jsxs(Tooltip, { delayDuration: 400, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("select", { className: `h-5 rounded border px-1.5 text-[10px] ${mode === "read-only"
|
|
2617
|
+
? "border-green-300 bg-green-50 text-green-700"
|
|
2618
|
+
: mode === "supervised"
|
|
2619
|
+
? "border-amber-300 bg-amber-50 text-amber-700"
|
|
2620
|
+
: "border-red-300 bg-red-50 text-red-700"}`, value: mode, onChange: async (e) => {
|
|
2621
|
+
const nextMode = e.target.value || "supervised";
|
|
2622
|
+
// Optimistic UI update
|
|
2623
|
+
setMode(nextMode);
|
|
2624
|
+
const current = agentMetadata || {};
|
|
2625
|
+
const nextMeta = {
|
|
2626
|
+
...current,
|
|
2627
|
+
mode: nextMode,
|
|
2628
|
+
};
|
|
2629
|
+
try {
|
|
2630
|
+
if (!agent?.id || agent.status === "new") {
|
|
2631
|
+
setAgentMetadata(nextMeta);
|
|
2632
|
+
// Cache until first start when agent is persisted
|
|
2633
|
+
pendingSettingsRef.current = {
|
|
2634
|
+
...(pendingSettingsRef.current || {}),
|
|
2497
2635
|
mode: nextMode,
|
|
2498
2636
|
};
|
|
2499
|
-
|
|
2500
|
-
if (!agent?.id || agent.status === "new") {
|
|
2501
|
-
setAgentMetadata(nextMeta);
|
|
2502
|
-
// Cache until first start when agent is persisted
|
|
2503
|
-
pendingSettingsRef.current = {
|
|
2504
|
-
...(pendingSettingsRef.current || {}),
|
|
2505
|
-
mode: nextMode,
|
|
2506
|
-
};
|
|
2507
|
-
return;
|
|
2508
|
-
}
|
|
2509
|
-
await updateAgentSettings(agent.id, {
|
|
2510
|
-
mode: nextMode,
|
|
2511
|
-
});
|
|
2512
|
-
setAgentMetadata(nextMeta);
|
|
2513
|
-
setAgent((prev) => prev
|
|
2514
|
-
? { ...prev, metadata: JSON.stringify(nextMeta) }
|
|
2515
|
-
: prev);
|
|
2516
|
-
}
|
|
2517
|
-
catch (e2) {
|
|
2518
|
-
console.error("Failed to persist mode change", e2);
|
|
2519
|
-
}
|
|
2520
|
-
}, title: "Mode", "aria-label": "Mode", "data-testid": "agent-mode-select", children: [_jsx("option", { value: "supervised", children: "Supervised" }), _jsx("option", { value: "autonomous", children: "Autonomous" }), _jsx("option", { value: "read-only", children: "Read-Only" })] }) }), _jsx(TooltipContent, { side: "top", sideOffset: 6, children: _jsxs("div", { className: "max-w-[320px] space-y-1", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold text-green-500", children: "Read-Only" }), ": Limited tool access as configured by the profile (Ask Mode Tools)."] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold text-amber-500", children: "Supervised" }), ": Full tool access, but writes are limited to pages/items in the current context. Creating new items or updating existing items outside the current context requires explicit approval."] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold text-red-500", children: "Autonomous" }), ": Full tool access; can write across the site/project only limited by user permissions."] })] }) })] }), profiles?.length > 0 && (_jsx("select", { className: "h-5 rounded border px-1.5 text-[10px] text-gray-500", value: activeProfile?.id || "", onChange: async (e) => {
|
|
2521
|
-
const nextProfile = profiles.find((x) => x.id === e.target.value);
|
|
2522
|
-
if (!nextProfile)
|
|
2523
|
-
return;
|
|
2524
|
-
setActiveProfile(nextProfile);
|
|
2525
|
-
try {
|
|
2526
|
-
if (agent?.id && agent.status !== "new") {
|
|
2527
|
-
await updateAgentSettings(agent.id, {
|
|
2528
|
-
profileId: nextProfile.id,
|
|
2529
|
-
profileName: nextProfile.name,
|
|
2530
|
-
});
|
|
2531
|
-
}
|
|
2532
|
-
else {
|
|
2533
|
-
// cache until first start
|
|
2534
|
-
pendingSettingsRef.current = {
|
|
2535
|
-
...(pendingSettingsRef.current || {}),
|
|
2536
|
-
// we cache profile by updating local metadata
|
|
2537
|
-
};
|
|
2538
|
-
setAgentMetadata((current) => {
|
|
2539
|
-
const next = { ...(current || {}) };
|
|
2540
|
-
next.profile = nextProfile.name;
|
|
2541
|
-
next.additionalData = {
|
|
2542
|
-
...(next.additionalData || {}),
|
|
2543
|
-
profileId: nextProfile.id,
|
|
2544
|
-
profileName: nextProfile.name,
|
|
2545
|
-
};
|
|
2546
|
-
return next;
|
|
2547
|
-
});
|
|
2548
|
-
}
|
|
2549
|
-
// reflect in local agent stub so tabs and titles can use it if needed
|
|
2550
|
-
setAgent((prev) => prev
|
|
2551
|
-
? {
|
|
2552
|
-
...prev,
|
|
2553
|
-
metadata: JSON.stringify({
|
|
2554
|
-
...(agentMetadata || {}),
|
|
2555
|
-
profile: nextProfile.name,
|
|
2556
|
-
additionalData: {
|
|
2557
|
-
...(agentMetadata
|
|
2558
|
-
?.additionalData || {}),
|
|
2559
|
-
profileId: nextProfile.id,
|
|
2560
|
-
profileName: nextProfile.name,
|
|
2561
|
-
},
|
|
2562
|
-
}),
|
|
2637
|
+
return;
|
|
2563
2638
|
}
|
|
2564
|
-
:
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
}, title: "Profile", "aria-label": "Profile", "data-testid": "agent-profile-select", children: profiles.map((p) => (_jsx("option", { value: p.id, children: p.name }, p.id))) })), activeProfile?.models?.length ? (_jsx("select", { className: "h-5 rounded border px-1.5 text-[10px] text-gray-500", value: selectedModelId || "", onChange: async (e) => {
|
|
2570
|
-
const nextId = e.target.value;
|
|
2571
|
-
setSelectedModelId(nextId);
|
|
2572
|
-
const modelName = activeProfile?.models?.find((m) => m.id === nextId)
|
|
2573
|
-
?.name || "";
|
|
2574
|
-
// Update local agent state immediately for UX and to reflect in streaming stub
|
|
2575
|
-
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
2576
|
-
// Persist only for existing agents; otherwise cache until first start
|
|
2577
|
-
try {
|
|
2578
|
-
if (agent?.id && agent.status !== "new") {
|
|
2579
|
-
await updateAgentSettings(agent.id, {
|
|
2580
|
-
model: modelName,
|
|
2581
|
-
});
|
|
2639
|
+
await updateAgentSettings(agent.id, { mode: nextMode });
|
|
2640
|
+
setAgentMetadata(nextMeta);
|
|
2641
|
+
setAgent((prev) => prev
|
|
2642
|
+
? { ...prev, metadata: JSON.stringify(nextMeta) }
|
|
2643
|
+
: prev);
|
|
2582
2644
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
...(pendingSettingsRef.current || {}),
|
|
2586
|
-
modelName,
|
|
2587
|
-
};
|
|
2645
|
+
catch (e2) {
|
|
2646
|
+
console.error("Failed to persist mode change", e2);
|
|
2588
2647
|
}
|
|
2648
|
+
}, title: "Mode", "aria-label": "Mode", "data-testid": "agent-mode-select", children: [_jsx("option", { value: "supervised", children: "Supervised" }), _jsx("option", { value: "autonomous", children: "Autonomous" }), _jsx("option", { value: "read-only", children: "Read-Only" })] }) }), _jsx(TooltipContent, { side: "top", sideOffset: 6, children: _jsxs("div", { className: "max-w-[320px] space-y-1", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold text-green-500", children: "Read-Only" }), ": Limited tool access as configured by the profile (Ask Mode Tools)."] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold text-amber-500", children: "Supervised" }), ": Full tool access, but writes are limited to pages/items in the current context. Creating new items or updating existing items outside the current context requires explicit approval."] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold text-red-500", children: "Autonomous" }), ": Full tool access; can write across the site/project only limited by user permissions."] })] }) })] }), profiles?.length > 0 && (_jsx("select", { className: "h-5 rounded border px-1.5 text-[10px] text-gray-500", value: activeProfile?.id || "", onChange: async (e) => {
|
|
2649
|
+
const nextProfile = profiles.find((x) => x.id === e.target.value);
|
|
2650
|
+
if (!nextProfile)
|
|
2651
|
+
return;
|
|
2652
|
+
setActiveProfile(nextProfile);
|
|
2653
|
+
try {
|
|
2654
|
+
if (agent?.id && agent.status !== "new") {
|
|
2655
|
+
await updateAgentSettings(agent.id, {
|
|
2656
|
+
profileId: nextProfile.id,
|
|
2657
|
+
profileName: nextProfile.name,
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2660
|
+
else {
|
|
2661
|
+
// cache until first start
|
|
2662
|
+
pendingSettingsRef.current = {
|
|
2663
|
+
...(pendingSettingsRef.current || {}),
|
|
2664
|
+
// we cache profile by updating local metadata
|
|
2665
|
+
};
|
|
2666
|
+
setAgentMetadata((current) => {
|
|
2667
|
+
const next = { ...(current || {}) };
|
|
2668
|
+
next.profile = nextProfile.name;
|
|
2669
|
+
next.additionalData = {
|
|
2670
|
+
...(next.additionalData || {}),
|
|
2671
|
+
profileId: nextProfile.id,
|
|
2672
|
+
profileName: nextProfile.name,
|
|
2673
|
+
};
|
|
2674
|
+
return next;
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
// reflect in local agent stub so tabs and titles can use it if needed
|
|
2678
|
+
setAgent((prev) => prev
|
|
2679
|
+
? {
|
|
2680
|
+
...prev,
|
|
2681
|
+
metadata: JSON.stringify({
|
|
2682
|
+
...(agentMetadata || {}),
|
|
2683
|
+
profile: nextProfile.name,
|
|
2684
|
+
additionalData: {
|
|
2685
|
+
...(agentMetadata?.additionalData ||
|
|
2686
|
+
{}),
|
|
2687
|
+
profileId: nextProfile.id,
|
|
2688
|
+
profileName: nextProfile.name,
|
|
2689
|
+
},
|
|
2690
|
+
}),
|
|
2589
2691
|
}
|
|
2590
|
-
|
|
2591
|
-
console.error("Failed to persist agent model", err);
|
|
2592
|
-
}
|
|
2593
|
-
}, title: "Model", "aria-label": "Model", "data-testid": "agent-model-select", children: activeProfile.models.map((m) => (_jsx("option", { value: m.id, children: m.name }, m.id))) })) : null, activeProfile?.prompts?.length ? (_jsxs(Popover, { open: showPredefined, onOpenChange: setShowPredefined, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { className: "rounded p-1 hover:bg-gray-100", onClick: () => { }, title: "Predefined prompts", "aria-label": "Predefined prompts", children: _jsx(Wand2, { className: "h-3 w-3", strokeWidth: 1 }) }) }), _jsx(PopoverContent, { className: "w-64 p-0", align: "start", children: _jsx("div", { className: "max-h-56 overflow-y-auto p-2", children: activeProfile.prompts.map((p, index) => (_jsx("div", { className: "cursor-pointer rounded p-1.5 text-xs text-gray-700 hover:bg-gray-100", onClick: () => {
|
|
2594
|
-
setPrompt(p.prompt);
|
|
2595
|
-
setShowPredefined(false);
|
|
2596
|
-
if (textareaRef.current)
|
|
2597
|
-
textareaRef.current.focus();
|
|
2598
|
-
}, children: p.title }, index))) }) })] })) : null] }), _jsxs("div", { className: "flex items-center gap-1 self-end", children: [isVoiceSupported ? (_jsx(Button, { onClick: toggleVoice, size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", title: isListening ? "Stop voice input" : "Start voice input", "aria-label": isListening ? "Stop voice input" : "Start voice input", "aria-pressed": isListening, children: isListening ? (_jsx(MicOff, { className: "size-3", strokeWidth: 1 })) : (_jsx(Mic, { className: "size-3", strokeWidth: 1 })) })) : null, _jsx(Button, { onClick: isExecuting ? handleStop : handleSubmit, disabled: !isExecuting && !prompt.trim(), size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", title: isExecuting ? "Stop" : "Send", "aria-label": isExecuting ? "Stop" : "Send", "data-testid": "agent-send-stop-button", "data-executing": isExecuting ? "true" : "false", children: isExecuting ? (_jsx(Square, { className: "size-3", strokeWidth: 1 })) : (_jsx(Send, { className: "size-3", strokeWidth: 1 })) })] })] }), _jsxs("div", { className: "mt-1 flex items-center gap-2 text-[10px] text-gray-500", children: [_jsx(AgentCostDisplay, { totalTokens: liveTotals
|
|
2599
|
-
? {
|
|
2600
|
-
input: liveTotals.input,
|
|
2601
|
-
output: liveTotals.output,
|
|
2602
|
-
cached: liveTotals.cached,
|
|
2603
|
-
cacheWrite: liveTotals.cacheWrite ?? 0,
|
|
2604
|
-
inputCost: liveTotals.inputCost,
|
|
2605
|
-
outputCost: liveTotals.outputCost,
|
|
2606
|
-
cachedCost: liveTotals.cachedCost,
|
|
2607
|
-
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
2608
|
-
totalCost: liveTotals.totalCost,
|
|
2692
|
+
: prev);
|
|
2609
2693
|
}
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
//
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2694
|
+
catch (err) {
|
|
2695
|
+
console.error("Failed to persist agent profile", err);
|
|
2696
|
+
}
|
|
2697
|
+
}, title: "Profile", "aria-label": "Profile", "data-testid": "agent-profile-select", children: profiles.map((p) => (_jsx("option", { value: p.id, children: p.name }, p.id))) })), activeProfile?.models?.length ? (_jsx("select", { className: "h-5 rounded border px-1.5 text-[10px] text-gray-500", value: selectedModelId || "", onChange: async (e) => {
|
|
2698
|
+
const nextId = e.target.value;
|
|
2699
|
+
setSelectedModelId(nextId);
|
|
2700
|
+
const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name ||
|
|
2701
|
+
"";
|
|
2702
|
+
// Update local agent state immediately for UX and to reflect in streaming stub
|
|
2703
|
+
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
2704
|
+
// Persist only for existing agents; otherwise cache until first start
|
|
2705
|
+
try {
|
|
2706
|
+
if (agent?.id && agent.status !== "new") {
|
|
2707
|
+
await updateAgentSettings(agent.id, { model: modelName });
|
|
2622
2708
|
}
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2709
|
+
else {
|
|
2710
|
+
pendingSettingsRef.current = {
|
|
2711
|
+
...(pendingSettingsRef.current || {}),
|
|
2712
|
+
modelName,
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
catch (err) {
|
|
2717
|
+
console.error("Failed to persist agent model", err);
|
|
2718
|
+
}
|
|
2719
|
+
}, title: "Model", "aria-label": "Model", "data-testid": "agent-model-select", children: activeProfile.models.map((m) => (_jsx("option", { value: m.id, children: m.name }, m.id))) })) : null, activeProfile?.prompts?.length ? (_jsxs(Popover, { open: showPredefined, onOpenChange: setShowPredefined, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { className: "rounded p-1 hover:bg-gray-100", onClick: () => { }, title: "Predefined prompts", "aria-label": "Predefined prompts", children: _jsx(Wand2, { className: "h-3 w-3", strokeWidth: 1 }) }) }), _jsx(PopoverContent, { className: "w-64 p-0", align: "start", children: _jsx("div", { className: "max-h-56 overflow-y-auto p-2", children: activeProfile.prompts.map((p, index) => (_jsx("div", { className: "cursor-pointer rounded p-1.5 text-xs text-gray-700 hover:bg-gray-100", onClick: () => {
|
|
2720
|
+
setPrompt(p.prompt);
|
|
2721
|
+
setShowPredefined(false);
|
|
2722
|
+
if (textareaRef.current)
|
|
2723
|
+
textareaRef.current.focus();
|
|
2724
|
+
}, children: p.title }, index))) }) })] })) : null] }), _jsxs("div", { className: "flex items-center gap-1 self-end", children: [isVoiceSupported ? (_jsx(Button, { onClick: toggleVoice, size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", title: isListening ? "Stop voice input" : "Start voice input", "aria-label": isListening ? "Stop voice input" : "Start voice input", "aria-pressed": isListening, children: isListening ? (_jsx(MicOff, { className: "size-3", strokeWidth: 1 })) : (_jsx(Mic, { className: "size-3", strokeWidth: 1 })) })) : null, _jsx(Button, { onClick: isExecuting ? handleStop : handleSubmit, disabled: !isExecuting && !prompt.trim(), size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", title: isExecuting ? "Stop" : "Send", "aria-label": isExecuting ? "Stop" : "Send", "data-testid": "agent-send-stop-button", "data-executing": isExecuting ? "true" : "false", children: isExecuting ? (_jsx(Square, { className: "size-3", strokeWidth: 1 })) : (_jsx(Send, { className: "size-3", strokeWidth: 1 })) })] })] }), _jsxs("div", { className: "mt-1 flex items-center gap-2 text-[10px] text-gray-500", children: [_jsx(AgentCostDisplay, { totalTokens: liveTotals
|
|
2725
|
+
? {
|
|
2726
|
+
input: liveTotals.input,
|
|
2727
|
+
output: liveTotals.output,
|
|
2728
|
+
cached: liveTotals.cached,
|
|
2729
|
+
cacheWrite: liveTotals.cacheWrite ?? 0,
|
|
2730
|
+
inputCost: liveTotals.inputCost,
|
|
2731
|
+
outputCost: liveTotals.outputCost,
|
|
2732
|
+
cachedCost: liveTotals.cachedCost,
|
|
2733
|
+
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
2734
|
+
totalCost: liveTotals.totalCost,
|
|
2735
|
+
}
|
|
2736
|
+
: totalTokens, costLimit: effectiveCostLimit }), (() => {
|
|
2737
|
+
try {
|
|
2738
|
+
const s = window.__agentContextWindowStatus;
|
|
2739
|
+
if (!s || !s.contextWindowTokens)
|
|
2740
|
+
return null;
|
|
2741
|
+
const pct = typeof s.contextUsedPercent === "number"
|
|
2742
|
+
? `${s.contextUsedPercent.toFixed(1)}%`
|
|
2743
|
+
: undefined;
|
|
2744
|
+
// Helper function to format tokens as "k"
|
|
2745
|
+
const formatTokens = (tokens) => {
|
|
2746
|
+
if (tokens >= 1000) {
|
|
2747
|
+
return `${(tokens / 1000).toFixed(1)}k`;
|
|
2631
2748
|
}
|
|
2632
|
-
|
|
2749
|
+
return tokens.toString();
|
|
2750
|
+
};
|
|
2751
|
+
if (!pct)
|
|
2752
|
+
return null;
|
|
2753
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "cursor-help rounded border border-gray-200 bg-gray-50 px-2 py-0.5", children: ["Context: ", pct] }) }), _jsx(TooltipContent, { side: "top", sideOffset: 6, children: _jsxs("div", { className: "max-w-[320px] space-y-1 text-xs", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Model:" }), " ", s.model, s.normalizedModel && ` (${s.normalizedModel})`] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Context window:" }), " ", formatTokens(s.estimatedInputTokens || 0), " /", " ", formatTokens(s.contextWindowTokens), " tokens"] }), typeof s.maxCompletionTokens === "number" && (_jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Max completion:" }), " ", formatTokens(s.maxCompletionTokens), " tokens"] })), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Used:" }), " ", pct] })] }) })] }));
|
|
2754
|
+
}
|
|
2755
|
+
catch {
|
|
2756
|
+
return null;
|
|
2757
|
+
}
|
|
2758
|
+
})()] })] })] }));
|
|
2633
2759
|
}
|
|
2634
2760
|
//# sourceMappingURL=AgentTerminal.js.map
|