@parhelia/core 0.1.12515 → 0.1.12523

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 (61) hide show
  1. package/dist/config/config.js +13 -0
  2. package/dist/config/config.js.map +1 -1
  3. package/dist/config/types.d.ts +9 -0
  4. package/dist/config/types.js.map +1 -1
  5. package/dist/editor/MainLayout.js +2 -1
  6. package/dist/editor/MainLayout.js.map +1 -1
  7. package/dist/editor/ai/AgentTerminal.js +171 -40
  8. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  9. package/dist/editor/ai/AiResponseMessage.d.ts +9 -1
  10. package/dist/editor/ai/AiResponseMessage.js +2 -2
  11. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  12. package/dist/editor/ai/ToolCallDisplay.d.ts +9 -1
  13. package/dist/editor/ai/ToolCallDisplay.js +9 -2
  14. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  15. package/dist/editor/ai/dialogs/browserBoundCapture.js +18 -3
  16. package/dist/editor/ai/dialogs/browserBoundCapture.js.map +1 -1
  17. package/dist/editor/client/operations.js +5 -3
  18. package/dist/editor/client/operations.js.map +1 -1
  19. package/dist/editor/content-tree/index.d.ts +3 -0
  20. package/dist/editor/content-tree/index.js +4 -0
  21. package/dist/editor/content-tree/index.js.map +1 -0
  22. package/dist/editor/context-menu/InsertMenu.js +46 -23
  23. package/dist/editor/context-menu/InsertMenu.js.map +1 -1
  24. package/dist/editor/hooks/useNavigationPanelLogic.js +6 -1
  25. package/dist/editor/hooks/useNavigationPanelLogic.js.map +1 -1
  26. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +80 -13
  27. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  28. package/dist/editor/page-viewer/MiniMap.d.ts +1 -1
  29. package/dist/editor/page-viewer/MiniMap.js +18 -6
  30. package/dist/editor/page-viewer/MiniMap.js.map +1 -1
  31. package/dist/editor/reviews/SuggestedEdit.js +31 -3
  32. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  33. package/dist/editor/reviews/SuggestionDisplayPopover.js +31 -5
  34. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  35. package/dist/editor/services/agentService.d.ts +14 -0
  36. package/dist/editor/services/agentService.js +14 -0
  37. package/dist/editor/services/agentService.js.map +1 -1
  38. package/dist/editor/services/aiService.d.ts +1 -0
  39. package/dist/editor/services/aiService.js.map +1 -1
  40. package/dist/editor/services/contentService.d.ts +1 -0
  41. package/dist/editor/services/contentService.js.map +1 -1
  42. package/dist/editor/sidebar/ComponentTree.js +1 -36
  43. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  44. package/dist/editor/sidebar/NavigationPanelItem.js +5 -4
  45. package/dist/editor/sidebar/NavigationPanelItem.js.map +1 -1
  46. package/dist/editor/sidebar/SidebarPanel.js +10 -3
  47. package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
  48. package/dist/editor/sidebar/Validation.js +22 -12
  49. package/dist/editor/sidebar/Validation.js.map +1 -1
  50. package/dist/editor/ui/Splitter.js +2 -2
  51. package/dist/editor/ui/Splitter.js.map +1 -1
  52. package/dist/revision.d.ts +2 -2
  53. package/dist/revision.js +2 -2
  54. package/dist/task-board/components/TaskAttachmentsSection.js +2 -1
  55. package/dist/task-board/components/TaskAttachmentsSection.js.map +1 -1
  56. package/dist/task-board/components/TaskDetailPanel.js +10 -7
  57. package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
  58. package/dist/task-board/components/WizardTaskDetailsPanel.js +3 -2
  59. package/dist/task-board/components/WizardTaskDetailsPanel.js.map +1 -1
  60. package/dist/types.d.ts +1 -0
  61. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
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, ArrowLeft, DollarSign, ExternalLink, Settings2, Target, X, Plus, } from "lucide-react";
4
- import { getAgent, startAgent, claimAgentBrowser, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, } from "../services/agentService";
4
+ import { getAgent, startAgent, claimAgentBrowser, assignAgentSkill, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, revokeAgentSkill, } from "../services/agentService";
5
5
  import { parseAgentStatus } from "../services/agentStatus";
6
6
  import { useEditContext, useFieldsEditContext } from "../client/editContext";
7
7
  import { localStorageService } from "../services/localStorageService";
@@ -1087,6 +1087,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1087
1087
  const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
1088
1088
  const [skillsLoading, setSkillsLoading] = useState(false);
1089
1089
  const [skillsError, setSkillsError] = useState(null);
1090
+ const [skillActionError, setSkillActionError] = useState(null);
1090
1091
  const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
1091
1092
  const [availableTools, setAvailableTools] = useState([]);
1092
1093
  const [operationAllowances, setOperationAllowances] = useState({
@@ -1289,9 +1290,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1289
1290
  .filter((id) => id.length > 0);
1290
1291
  }, [activeProfile?.preloadSkills]);
1291
1292
  const autoAssignedSkillIds = useMemo(() => {
1292
- const all = isNewAgent
1293
- ? [...preloadedSkillIds, ...backendAssignedSkillIds]
1294
- : backendAssignedSkillIds;
1293
+ const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
1294
+ const all = backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
1295
1295
  const seen = new Set();
1296
1296
  const unique = [];
1297
1297
  for (const id of all) {
@@ -1302,7 +1302,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1302
1302
  unique.push(id);
1303
1303
  }
1304
1304
  return unique;
1305
- }, [backendAssignedSkillIds, isNewAgent, preloadedSkillIds]);
1305
+ }, [backendAssignedSkillIds, preloadedSkillIds]);
1306
1306
  const selectedSkillIds = useMemo(() => {
1307
1307
  const all = [...autoAssignedSkillIds, ...metadataSelectedSkillIds];
1308
1308
  const seen = new Set();
@@ -1444,7 +1444,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1444
1444
  return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
1445
1445
  }, [availableSkills, listedProfileSkillIdSet]);
1446
1446
  const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
1447
- const availableSkillIdSet = useMemo(() => new Set(availableSkills.map((skill) => skill.id.toLowerCase())), [availableSkills]);
1447
+ const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
1448
+ .map((skill) => String(skill?.id || "").toLowerCase())
1449
+ .filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
1448
1450
  const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
1449
1451
  const selectedSkills = useMemo(() => selectedSkillIds
1450
1452
  .map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
@@ -1508,20 +1510,23 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1508
1510
  return meta;
1509
1511
  }
1510
1512
  }, []);
1511
- const updateSelectedSkillIds = useCallback(async (skillIds) => {
1512
- if (!agent?.id)
1513
+ const getSkillActionErrorMessage = useCallback((error) => {
1514
+ const message = error instanceof Error && error.message.trim()
1515
+ ? error.message.trim()
1516
+ : "Failed to update skill";
1517
+ if (message.includes("Skill is not available for this agent profile")) {
1518
+ return "This skill cannot be added for the current agent profile.";
1519
+ }
1520
+ return message;
1521
+ }, []);
1522
+ const clearLegacySelectedSkillsFromMetadata = useCallback(async () => {
1523
+ const legacySkillIds = metadataSelectedSkillIds;
1524
+ if (!agent?.id || legacySkillIds.length === 0) {
1513
1525
  return;
1526
+ }
1514
1527
  const current = agentMetadata || {};
1515
1528
  const currentAdditionalData = current.additionalData || {};
1516
- const nextAdditionalData = {
1517
- ...currentAdditionalData,
1518
- };
1519
- if (skillIds.length > 0) {
1520
- nextAdditionalData.skillIds = skillIds;
1521
- }
1522
- else {
1523
- delete nextAdditionalData.skillIds;
1524
- }
1529
+ const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
1525
1530
  const next = {
1526
1531
  ...current,
1527
1532
  };
@@ -1532,26 +1537,86 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1532
1537
  delete next.additionalData;
1533
1538
  }
1534
1539
  try {
1535
- if (agent.status === "new") {
1536
- setAgentMetadata(next);
1537
- return;
1538
- }
1539
1540
  await updateAgentContext(agent.id, next);
1540
1541
  setAgentMetadata(next);
1541
1542
  setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
1542
1543
  }
1543
1544
  catch (e) {
1544
- console.error("Failed to update selected skills", e);
1545
+ console.error("Failed to clear legacy selected skills", e);
1545
1546
  }
1546
- }, [agent?.id, agent?.status, agentMetadata, setAgent, setAgentMetadata]);
1547
+ }, [
1548
+ agent?.id,
1549
+ agentMetadata,
1550
+ metadataSelectedSkillIds,
1551
+ setAgent,
1552
+ setAgentMetadata,
1553
+ ]);
1547
1554
  const handleAddSkill = useCallback(async (skillId) => {
1548
1555
  if (selectedSkillSet.has(skillId.toLowerCase()))
1549
1556
  return;
1550
- await updateSelectedSkillIds([...metadataSelectedSkillIds, skillId]);
1551
- }, [metadataSelectedSkillIds, selectedSkillSet, updateSelectedSkillIds]);
1557
+ if (!agent?.id)
1558
+ return;
1559
+ try {
1560
+ setSkillActionError(null);
1561
+ await assignAgentSkill({ agentId: agent.id, skillId });
1562
+ setAgent((prev) => {
1563
+ if (!prev)
1564
+ return prev;
1565
+ const currentAssigned = Array.isArray(prev.assignedSkillIds)
1566
+ ? prev.assignedSkillIds
1567
+ : [];
1568
+ if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
1569
+ return prev;
1570
+ }
1571
+ return {
1572
+ ...prev,
1573
+ assignedSkillIds: [...currentAssigned, skillId],
1574
+ };
1575
+ });
1576
+ await clearLegacySelectedSkillsFromMetadata();
1577
+ return true;
1578
+ }
1579
+ catch (e) {
1580
+ setSkillActionError(getSkillActionErrorMessage(e));
1581
+ return false;
1582
+ }
1583
+ }, [
1584
+ agent?.id,
1585
+ assignAgentSkill,
1586
+ clearLegacySelectedSkillsFromMetadata,
1587
+ getSkillActionErrorMessage,
1588
+ setAgent,
1589
+ selectedSkillSet,
1590
+ ]);
1552
1591
  const handleRemoveSkill = useCallback(async (skillId) => {
1553
- await updateSelectedSkillIds(metadataSelectedSkillIds.filter((id) => id.toLowerCase() !== skillId.toLowerCase()));
1554
- }, [metadataSelectedSkillIds, updateSelectedSkillIds]);
1592
+ if (!agent?.id)
1593
+ return;
1594
+ try {
1595
+ setSkillActionError(null);
1596
+ await revokeAgentSkill({ agentId: agent.id, skillId });
1597
+ setAgent((prev) => {
1598
+ if (!prev)
1599
+ return prev;
1600
+ const currentAssigned = Array.isArray(prev.assignedSkillIds)
1601
+ ? prev.assignedSkillIds
1602
+ : [];
1603
+ return {
1604
+ ...prev,
1605
+ assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
1606
+ };
1607
+ });
1608
+ await clearLegacySelectedSkillsFromMetadata();
1609
+ }
1610
+ catch (e) {
1611
+ setSkillActionError(getSkillActionErrorMessage(e));
1612
+ }
1613
+ }, [
1614
+ agent?.id,
1615
+ clearLegacySelectedSkillsFromMetadata,
1616
+ getSkillActionErrorMessage,
1617
+ revokeAgentSkill,
1618
+ setAgent,
1619
+ ]);
1555
1620
  const handleOpenProfileSettings = useCallback(async () => {
1556
1621
  if (!editContext || !activeProfile?.id)
1557
1622
  return;
@@ -2007,6 +2072,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2007
2072
  // If no messageId is provided, we'll use the last assistant message or create a new one
2008
2073
  let messageId = message.data?.messageId;
2009
2074
  if (!messageId && agentData?.id) {
2075
+ console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
2076
+ agentId: agentData.id,
2077
+ isIncremental: message.data?.isIncremental,
2078
+ previousContentLength: message.data?.previousContentLength,
2079
+ totalContentLength: message.data?.totalContentLength,
2080
+ });
2010
2081
  // For backward compatibility: if no messageId, find or create the current streaming message
2011
2082
  // This handles cases where the backend doesn't send messageId
2012
2083
  const currentMessages = messagesRef.current;
@@ -2107,6 +2178,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2107
2178
  const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
2108
2179
  if (existingMessageIndex === -1) {
2109
2180
  // Message doesn't exist - create new streaming message
2181
+ const previousContentLength = message.data?.previousContentLength || 0;
2182
+ if (message.data?.isIncremental && previousContentLength > 0) {
2183
+ console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
2184
+ messageId,
2185
+ previousContentLength,
2186
+ totalContentLength: message.data?.totalContentLength,
2187
+ deltaLength: (message.data?.deltaContent || "").length,
2188
+ });
2189
+ }
2110
2190
  const newStreamMessage = createNewStreamMessage(messageId, agentData);
2111
2191
  // Set the content for the new message
2112
2192
  const updatedNewMessage = { ...newStreamMessage };
@@ -2129,8 +2209,21 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2129
2209
  return prev;
2130
2210
  // Check if existing content is already longer than what we're trying to stream
2131
2211
  const currentContentLength = existingMessage.content?.length || 0;
2212
+ const previousContentLength = message.data?.previousContentLength || 0;
2132
2213
  const totalContentLength = message.data?.totalContentLength || 0;
2133
- if (currentContentLength >= totalContentLength &&
2214
+ if (message.data?.isIncremental &&
2215
+ previousContentLength !== currentContentLength &&
2216
+ (previousContentLength > 0 || currentContentLength > 0)) {
2217
+ console.warn("[AgentTerminal] Content chunk length mismatch", {
2218
+ messageId,
2219
+ previousContentLength,
2220
+ currentContentLength,
2221
+ totalContentLength,
2222
+ deltaLength: (message.data?.deltaContent || "").length,
2223
+ });
2224
+ }
2225
+ if (message.data?.isIncremental &&
2226
+ currentContentLength >= totalContentLength &&
2134
2227
  totalContentLength > 0) {
2135
2228
  return prev;
2136
2229
  }
@@ -2159,6 +2252,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2159
2252
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2160
2253
  let toolCallMessageId = message.data?.messageId;
2161
2254
  if (!toolCallMessageId) {
2255
+ console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
2256
+ toolCallId,
2257
+ toolName: message.data?.name || message.data?.displayName,
2258
+ });
2162
2259
  const current = messagesRef.current;
2163
2260
  const lastStreaming = [...current]
2164
2261
  .reverse()
@@ -2322,6 +2419,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2322
2419
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2323
2420
  let resultMessageId = message.data?.messageId;
2324
2421
  if (!resultMessageId) {
2422
+ console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
2423
+ toolCallId: resultToolCallId,
2424
+ toolName: message.data?.functionName || message.data?.displayName,
2425
+ });
2325
2426
  const current = messagesRef.current;
2326
2427
  const lastStreaming = [...current]
2327
2428
  .reverse()
@@ -5196,9 +5297,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5196
5297
  const renderBrowserClaimBanner = (variant = "inline") => {
5197
5298
  if (!agent?.id || !editContext?.sessionId)
5198
5299
  return null;
5199
- if (!isClaimedByCurrentSession &&
5200
- !isClaimedByAnotherBrowser &&
5201
- !isPendingBrowserCaptureWait) {
5300
+ if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
5301
+ return null;
5302
+ }
5303
+ if (isPendingBrowserCaptureWait) {
5202
5304
  return null;
5203
5305
  }
5204
5306
  const label = isClaimedByCurrentSession
@@ -5222,6 +5324,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5222
5324
  };
5223
5325
  const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
5224
5326
  const inlineBrowserClaimBanner = null;
5327
+ const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
5328
+ ? {
5329
+ toolNames: ["capture-page-screenshot", "capture-page-dom"],
5330
+ label: isClaimedByAnotherBrowser
5331
+ ? "Attached in another browser"
5332
+ : "No browser attached",
5333
+ description: isClaimedByAnotherBrowser
5334
+ ? "This capture request is waiting in another browser. Take over browser control here to continue."
5335
+ : "This capture request is waiting for a browser attachment. Attach this browser to continue.",
5336
+ actionLabel: isClaimedByAnotherBrowser
5337
+ ? "Take over browser control"
5338
+ : "Attach to this browser",
5339
+ isPending: isBrowserClaimMutationPending,
5340
+ onAction: () => {
5341
+ void handleClaimBrowser(isClaimedByAnotherBrowser);
5342
+ },
5343
+ }
5344
+ : null;
5225
5345
  useEffect(() => {
5226
5346
  if (agent?.status !== "waitingForInput") {
5227
5347
  setPendingBrowserCaptureDialogType(null);
@@ -5291,7 +5411,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5291
5411
  __html: sanitizeSvg(activeProfile.svgIcon),
5292
5412
  } })) : (_jsx(SecretAgentIcon, { size: 64, strokeWidth: 1, className: "text-gray-400" })), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400" })] })] }) })), inlineBrowserClaimBanner, renderErrorBanner(), inlineDialog ? (inlineDialog) : latestSummaryAssistantGroup ? (_jsx("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: _jsx(AiResponseMessage, { messages: summaryMessages, finished: !latestSummaryAssistantGroup.isLastGroup || !isExecuting, editOperations: summaryOperations, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
5293
5413
  activeProfile?.displayTitle ||
5294
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
5414
+ activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
5295
5415
  const text = (action.prompt ||
5296
5416
  action.value ||
5297
5417
  action.label ||
@@ -5444,7 +5564,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5444
5564
  const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
5445
5565
  return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
5446
5566
  activeProfile?.displayTitle ||
5447
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
5567
+ activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
5448
5568
  const text = (action.prompt ||
5449
5569
  action.value ||
5450
5570
  action.label ||
@@ -5785,29 +5905,40 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5785
5905
  catch (err) {
5786
5906
  console.error("Failed to persist agent model", err);
5787
5907
  }
5788
- } })] })) : null, _jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-1 text-[11px] font-medium text-gray-700", children: [_jsx(Target, { className: "h-3 w-3", strokeWidth: 1 }), "Skills"] }), _jsxs(Popover, { open: showSkillPicker, onOpenChange: setShowSkillPicker, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { size: "xs", variant: "outline", className: "h-5 rounded border px-1.5 text-[10px] text-gray-600", "data-testid": "agent-skill-picker-trigger", children: [_jsx(Plus, { className: "mr-1 h-3 w-3", strokeWidth: 1.5 }), "Add"] }) }), _jsx(PopoverContent, { className: "w-88 p-2", align: "end", side: "bottom", children: _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-[11px] font-medium text-gray-700", children: "Select a skill" }), _jsxs("div", { className: "relative h-56 rounded border border-gray-200 bg-gray-50", children: [_jsx(ScrollingContentTree, { rootItemIds: skillRootIds, selectedItemId: selectedSkillIds[selectedSkillIds.length - 1] || undefined, expandedItemId: selectedSkillIds[selectedSkillIds.length - 1] || skillRootIds[0], scrollToSelected: true, hideRootNodes: false, onSelectionChange: (selection) => {
5908
+ } })] })) : null, _jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-1 text-[11px] font-medium text-gray-700", children: [_jsx(Target, { className: "h-3 w-3", strokeWidth: 1 }), "Skills"] }), _jsxs(Popover, { open: showSkillPicker, onOpenChange: (open) => {
5909
+ setShowSkillPicker(open);
5910
+ if (open) {
5911
+ setSkillActionError(null);
5912
+ }
5913
+ }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { size: "xs", variant: "outline", className: "h-5 rounded border px-1.5 text-[10px] text-gray-600", "data-testid": "agent-skill-picker-trigger", children: [_jsx(Plus, { className: "mr-1 h-3 w-3", strokeWidth: 1.5 }), "Add"] }) }), _jsx(PopoverContent, { className: "w-88 p-2", align: "end", side: "bottom", children: _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-[11px] font-medium text-gray-700", children: "Select a skill" }), _jsxs("div", { className: "relative h-56 rounded border border-gray-200 bg-gray-50", children: [_jsx(ScrollingContentTree, { rootItemIds: skillRootIds, selectedItemId: selectedSkillIds[selectedSkillIds.length - 1] || undefined, expandedItemId: selectedSkillIds[selectedSkillIds.length - 1] || skillRootIds[0], scrollToSelected: true, hideRootNodes: false, onSelectionChange: (selection) => {
5789
5914
  const selected = selection[0];
5790
5915
  if (!selected?.id)
5791
5916
  return;
5917
+ setSkillActionError(null);
5792
5918
  if (selectableTemplateIdSet.size > 0 &&
5793
5919
  (!selected.templateId ||
5794
5920
  !selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
5795
5921
  return;
5796
5922
  }
5797
- if (!availableSkillIdSet.has(selected.id.toLowerCase())) {
5923
+ if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
5924
+ setSkillActionError("This skill cannot be added for the current agent profile.");
5798
5925
  return;
5799
5926
  }
5800
- void handleAddSkill(selected.id);
5801
- setShowSkillPicker(false);
5927
+ void (async () => {
5928
+ const added = await handleAddSkill(selected.id);
5929
+ if (added) {
5930
+ setShowSkillPicker(false);
5931
+ }
5932
+ })();
5802
5933
  } }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
5803
5934
  !skillsError &&
5804
- profileFilteredSkills.length > 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "Click a skill item in the tree to add it." })), skillsError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillsError })), !skillsLoading &&
5935
+ profileFilteredSkills.length > 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "Click a skill item in the tree to add it." })), skillsError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillsError })), !skillsError && skillActionError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillActionError })), !skillsLoading &&
5805
5936
  !skillsError &&
5806
5937
  skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
5807
5938
  !skillsError &&
5808
5939
  profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
5809
- ? "All available/allowed skills are selected"
5810
- : "No available or allowed skills for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
5940
+ ? "All addable skills are selected"
5941
+ : "No skills can be added for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
5811
5942
  const skill = selectedSkills.find((s) => s.id === skillId);
5812
5943
  return (_jsxs("div", { className: "inline-flex items-center gap-1 rounded-full border border-gray-200 bg-gray-100 px-1.5 py-0.5 text-[10px] text-gray-700", children: [_jsx("span", { children: skill?.name || skillId }), _jsx("button", { type: "button", className: "rounded p-0.5 text-gray-500 hover:bg-gray-200 hover:text-gray-700", title: "Open skill item", "aria-label": `Open ${skill?.name || skillId}`, onClick: () => {
5813
5944
  void handleOpenSkillItem(skillId);