@parhelia/core 0.1.12496 → 0.1.12517

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 (159) hide show
  1. package/dist/agents-view/AgentCard.js +20 -19
  2. package/dist/agents-view/AgentCard.js.map +1 -1
  3. package/dist/agents-view/AgentsInbox.js +2 -13
  4. package/dist/agents-view/AgentsInbox.js.map +1 -1
  5. package/dist/agents-view/AgentsView.js +7 -55
  6. package/dist/agents-view/AgentsView.js.map +1 -1
  7. package/dist/agents-view/AgentsWorkspaceView.js +2 -11
  8. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  9. package/dist/components/ui/copy-button.d.ts +2 -1
  10. package/dist/components/ui/copy-button.js +2 -2
  11. package/dist/components/ui/copy-button.js.map +1 -1
  12. package/dist/components/ui/paste-button.d.ts +2 -1
  13. package/dist/components/ui/paste-button.js +2 -2
  14. package/dist/components/ui/paste-button.js.map +1 -1
  15. package/dist/config/config.js +40 -0
  16. package/dist/config/config.js.map +1 -1
  17. package/dist/config/types.d.ts +23 -5
  18. package/dist/config/types.js.map +1 -1
  19. package/dist/editor/ContentTree.js +36 -4
  20. package/dist/editor/ContentTree.js.map +1 -1
  21. package/dist/editor/FieldListField.js +4 -4
  22. package/dist/editor/FieldListField.js.map +1 -1
  23. package/dist/editor/FieldListFieldWithFallbacks.js +23 -2
  24. package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
  25. package/dist/editor/GlobalMenuBar.js +1 -1
  26. package/dist/editor/GlobalMenuBar.js.map +1 -1
  27. package/dist/editor/ItemInfo.js +36 -1
  28. package/dist/editor/ItemInfo.js.map +1 -1
  29. package/dist/editor/MainLayout.d.ts +0 -2
  30. package/dist/editor/MainLayout.js +0 -1
  31. package/dist/editor/MainLayout.js.map +1 -1
  32. package/dist/editor/Titlebar.js +2 -2
  33. package/dist/editor/Titlebar.js.map +1 -1
  34. package/dist/editor/ai/AgentStatusBadge.d.ts +0 -5
  35. package/dist/editor/ai/AgentStatusBadge.js +57 -71
  36. package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
  37. package/dist/editor/ai/AgentTerminal.js +88 -90
  38. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  39. package/dist/editor/ai/Agents.js +8 -63
  40. package/dist/editor/ai/Agents.js.map +1 -1
  41. package/dist/editor/ai/InlineAiDialog.js +1 -6
  42. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  43. package/dist/editor/ai/ToolCallDisplay.js +152 -63
  44. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  45. package/dist/editor/ai/useAgentStatus.js +12 -85
  46. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  47. package/dist/editor/client/EditorShell.js +49 -59
  48. package/dist/editor/client/EditorShell.js.map +1 -1
  49. package/dist/editor/client/editContext.d.ts +3 -15
  50. package/dist/editor/client/editContext.js.map +1 -1
  51. package/dist/editor/client/hooks/useEditorUrlSync.js +1 -2
  52. package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
  53. package/dist/editor/client/hooks/useSocketMessageHandler.js +19 -6
  54. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  55. package/dist/editor/client/operations.js +20 -6
  56. package/dist/editor/client/operations.js.map +1 -1
  57. package/dist/editor/client/ui/EditorChrome.d.ts +0 -4
  58. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  59. package/dist/editor/client/waitForEditOperationTerminal.d.ts +8 -3
  60. package/dist/editor/client/waitForEditOperationTerminal.js +5 -1
  61. package/dist/editor/client/waitForEditOperationTerminal.js.map +1 -1
  62. package/dist/editor/commands/itemCommands.js +5 -0
  63. package/dist/editor/commands/itemCommands.js.map +1 -1
  64. package/dist/editor/content-tree/IndicatorSettings.d.ts +11 -0
  65. package/dist/editor/content-tree/IndicatorSettings.js +60 -0
  66. package/dist/editor/content-tree/IndicatorSettings.js.map +1 -0
  67. package/dist/editor/content-tree/TreeOptions.d.ts +6 -0
  68. package/dist/editor/content-tree/TreeOptions.js +8 -0
  69. package/dist/editor/content-tree/TreeOptions.js.map +1 -0
  70. package/dist/editor/content-tree/TreeSettingsMenu.d.ts +2 -0
  71. package/dist/editor/content-tree/TreeSettingsMenu.js +30 -0
  72. package/dist/editor/content-tree/TreeSettingsMenu.js.map +1 -0
  73. package/dist/editor/content-tree/index.d.ts +3 -0
  74. package/dist/editor/content-tree/index.js +4 -0
  75. package/dist/editor/content-tree/index.js.map +1 -0
  76. package/dist/editor/hooks/useNavigationPanelLogic.js +2 -6
  77. package/dist/editor/hooks/useNavigationPanelLogic.js.map +1 -1
  78. package/dist/editor/manualActionEvents.d.ts +8 -0
  79. package/dist/editor/manualActionEvents.js +48 -0
  80. package/dist/editor/manualActionEvents.js.map +1 -0
  81. package/dist/editor/menubar/PageSelector.js +9 -12
  82. package/dist/editor/menubar/PageSelector.js.map +1 -1
  83. package/dist/editor/menubar/WorkflowButton.js +23 -23
  84. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  85. package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
  86. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  87. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +3 -9
  88. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +225 -71
  89. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  90. package/dist/editor/notifications/WatchButton.js +2 -2
  91. package/dist/editor/notifications/WatchButton.js.map +1 -1
  92. package/dist/editor/page-viewer/EditorForm.js +2 -0
  93. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  94. package/dist/editor/page-viewer/PageViewer.js +8 -2
  95. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  96. package/dist/editor/reviews/CreateReviewDialog.js +0 -2
  97. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  98. package/dist/editor/reviews/SuggestedEdit.js +31 -3
  99. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  100. package/dist/editor/reviews/SuggestionDisplayPopover.js +31 -5
  101. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  102. package/dist/editor/services/agentService.d.ts +3 -2
  103. package/dist/editor/services/agentService.js.map +1 -1
  104. package/dist/editor/services/agentStatus.d.ts +12 -0
  105. package/dist/editor/services/agentStatus.js +59 -0
  106. package/dist/editor/services/agentStatus.js.map +1 -0
  107. package/dist/editor/settings/SettingsView.js +4 -4
  108. package/dist/editor/settings/SettingsView.js.map +1 -1
  109. package/dist/editor/settings/index/useIndexStatus.js +1 -1
  110. package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
  111. package/dist/editor/settings/panels/JavaScriptToolConfigPanel.js +1 -1
  112. package/dist/editor/settings/panels/JavaScriptToolConfigPanel.js.map +1 -1
  113. package/dist/editor/sidebar/ComponentTree.js +201 -40
  114. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  115. package/dist/editor/sidebar/MainContentTree.js +3 -2
  116. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  117. package/dist/editor/sidebar/MorePanelsButton.js +1 -1
  118. package/dist/editor/sidebar/MorePanelsButton.js.map +1 -1
  119. package/dist/editor/sidebar/NavigationPanelItem.js +2 -2
  120. package/dist/editor/sidebar/NavigationPanelItem.js.map +1 -1
  121. package/dist/editor/sidebar/SidebarPanel.js +20 -4
  122. package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
  123. package/dist/editor/sidebar/SidebarStack.js +1 -0
  124. package/dist/editor/sidebar/SidebarStack.js.map +1 -1
  125. package/dist/editor/sidebar/Workbox.js +53 -3
  126. package/dist/editor/sidebar/Workbox.js.map +1 -1
  127. package/dist/editor/tree-indicators/GutterContext.d.ts +4 -0
  128. package/dist/editor/tree-indicators/GutterContext.js +23 -0
  129. package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
  130. package/dist/editor/tree-indicators/index.d.ts +0 -1
  131. package/dist/editor/tree-indicators/index.js +0 -1
  132. package/dist/editor/tree-indicators/index.js.map +1 -1
  133. package/dist/editor/tree-indicators/types.d.ts +1 -1
  134. package/dist/editor/ui/HomeButton.js +1 -1
  135. package/dist/editor/ui/HomeButton.js.map +1 -1
  136. package/dist/editor/ui/ItemNameDialogNew.d.ts +2 -0
  137. package/dist/editor/ui/ItemNameDialogNew.js +17 -7
  138. package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
  139. package/dist/editor/ui/ItemSearch.js +7 -11
  140. package/dist/editor/ui/ItemSearch.js.map +1 -1
  141. package/dist/editor/ui/SimpleTabs.js +33 -16
  142. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  143. package/dist/editor/ui/Splitter.js +1 -1
  144. package/dist/editor/ui/Splitter.js.map +1 -1
  145. package/dist/revision.d.ts +2 -2
  146. package/dist/revision.js +2 -2
  147. package/dist/setup/wizard/steps/AddModelDialog.js +3 -2
  148. package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
  149. package/dist/task-board/components/AssignAgentDialog.js +0 -8
  150. package/dist/task-board/components/AssignAgentDialog.js.map +1 -1
  151. package/dist/task-board/components/ProjectAgentsPanel.js +1 -26
  152. package/dist/task-board/components/ProjectAgentsPanel.js.map +1 -1
  153. package/dist/task-board/components/TaskAgentPanel.js +2 -6
  154. package/dist/task-board/components/TaskAgentPanel.js.map +1 -1
  155. package/dist/types.d.ts +2 -13
  156. package/package.json +1 -1
  157. package/dist/editor/tree-indicators/GutterSelector.d.ts +0 -5
  158. package/dist/editor/tree-indicators/GutterSelector.js +0 -91
  159. package/dist/editor/tree-indicators/GutterSelector.js.map +0 -1
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
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
4
  import { getAgent, startAgent, claimAgentBrowser, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, } from "../services/agentService";
5
+ import { parseAgentStatus } from "../services/agentStatus";
5
6
  import { useEditContext, useFieldsEditContext } from "../client/editContext";
6
7
  import { localStorageService } from "../services/localStorageService";
7
8
  import { Textarea } from "../../components/ui/textarea";
@@ -28,6 +29,19 @@ import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
28
29
  import { SimpleTabs } from "../ui/SimpleTabs";
29
30
  import { Splitter } from "../ui/Splitter";
30
31
  import { ScrollingContentTree } from "../ScrollingContentTree";
32
+ import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
33
+ const userMessageMarkdownComponents = {
34
+ h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm font-semibold leading-5 text-gray-900" })),
35
+ h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] font-semibold leading-5 text-gray-900" })),
36
+ h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] font-semibold leading-5 text-gray-900" })),
37
+ h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] font-medium leading-5 text-gray-800" })),
38
+ p: (props) => _jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" }),
39
+ ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
40
+ ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
41
+ li: (props) => _jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" }),
42
+ pre: (props) => (_jsx("pre", { ...props, className: "my-2 overflow-auto rounded-md bg-slate-100 px-3 py-2 text-[11px] leading-4 text-slate-700" })),
43
+ code: ({ inline, className, ...props }) => inline ? (_jsx("code", { ...props, className: "rounded bg-slate-100 px-1 py-0.5 text-[11px] text-slate-700" })) : (_jsx("code", { ...props, className: className })),
44
+ };
31
45
  function buildPlaceholderAgentDetails(agentStub) {
32
46
  const now = new Date().toISOString();
33
47
  const updated = agentStub.updatedDate || now;
@@ -178,7 +192,7 @@ function toUserFacingAgentErrorMessage(value) {
178
192
  return cleaned;
179
193
  }
180
194
  function isAgentErrorStatusValue(status) {
181
- return status === "error" || status === 4;
195
+ return status === "error";
182
196
  }
183
197
  // Simple user message component
184
198
  const UserMessage = ({ message }) => {
@@ -191,7 +205,7 @@ const UserMessage = ({ message }) => {
191
205
  const triggerContent = triggerMatch?.[2] || "";
192
206
  const isTriggerMessage = triggerName.length > 0;
193
207
  if (isTriggerMessage) {
194
- return (_jsx("div", { className: "px-4 py-2", children: _jsxs("div", { className: "text-[11px]", children: [_jsxs("button", { type: "button", onClick: () => setIsTriggerExpanded((expanded) => !expanded), className: "text-theme-secondary hover:bg-theme-hover flex w-full items-center gap-2 rounded-md border-l-2 border-cyan-200 px-2 py-1 text-left transition-colors", "data-testid": "trigger-message-toggle", "data-expanded": isTriggerExpanded ? "true" : "false", children: [_jsx(Target, { className: "h-3.5 w-3.5 shrink-0", strokeWidth: 1.5 }), _jsxs("span", { className: "truncate font-medium", children: ["Trigger: ", triggerName] }), message.createdDate && (_jsx("span", { className: "ml-1 shrink-0 text-[10px] text-gray-400", children: formatTime(new Date(message.createdDate)) })), _jsx("span", { className: "ml-auto shrink-0", children: isTriggerExpanded ? (_jsx(ChevronUp, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) })] }), isTriggerExpanded && (_jsx("div", { className: "mt-1 border-l-2 border-cyan-100 pl-[1.35rem] text-[11px] text-gray-600", children: triggerContent }))] }) }));
208
+ return (_jsx("div", { className: "px-4 py-2", children: _jsxs("div", { className: "text-[11px]", children: [_jsxs("button", { type: "button", onClick: () => setIsTriggerExpanded((expanded) => !expanded), className: "text-theme-secondary hover:bg-theme-hover flex w-full items-center gap-2 rounded-md border-l-2 border-cyan-200 px-2 py-1 text-left transition-colors", "data-testid": "trigger-message-toggle", "data-expanded": isTriggerExpanded ? "true" : "false", children: [_jsx(Target, { className: "h-3.5 w-3.5 shrink-0", strokeWidth: 1.5 }), _jsxs("span", { className: "truncate font-medium", children: ["Trigger: ", triggerName] }), message.createdDate && (_jsx("span", { className: "ml-1 shrink-0 text-[10px] text-gray-400", children: formatTime(new Date(message.createdDate)) })), _jsx("span", { className: "ml-auto shrink-0", children: isTriggerExpanded ? (_jsx(ChevronUp, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) })] }), isTriggerExpanded && (_jsx("div", { className: "mt-1 border-l-2 border-cyan-100 pl-[1.35rem] text-[11px] text-gray-600", children: _jsx(MarkdownDisplay, { source: triggerContent, components: userMessageMarkdownComponents }) }))] }) }));
195
209
  }
196
210
  // Parse source agent name from content if it starts with "[From ...]:"
197
211
  // Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
@@ -219,7 +233,7 @@ const UserMessage = ({ message }) => {
219
233
  message.sourceAgent?.name;
220
234
  }
221
235
  const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
222
- return (_jsxs("div", { className: "flex gap-3 p-4", children: [_jsx("div", { className: "shrink-0", children: _jsx(User, { className: "text-theme-secondary h-5 w-5", strokeWidth: 1 }) }), _jsxs("div", { className: "min-w-0 flex-1 select-text", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-[12px] font-medium text-gray-900", children: displayName }), message.createdDate && (_jsx("span", { className: "text-[12px] text-gray-400", "data-testid": "user-message-timestamp", "data-timestamp": message.createdDate, children: formatTime(new Date(message.createdDate)) }))] }), _jsx("div", { className: "prose prose max-w-none text-[12px] text-gray-700 select-text", children: displayContent })] })] }));
236
+ return (_jsxs("div", { className: "flex gap-3 p-4", children: [_jsx("div", { className: "shrink-0", children: _jsx(User, { className: "text-theme-secondary h-5 w-5", strokeWidth: 1 }) }), _jsxs("div", { className: "min-w-0 flex-1 select-text", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-[12px] font-medium text-gray-900", children: displayName }), message.createdDate && (_jsx("span", { className: "text-[12px] text-gray-400", "data-testid": "user-message-timestamp", "data-timestamp": message.createdDate, children: formatTime(new Date(message.createdDate)) }))] }), _jsx("div", { className: "prose prose max-w-none text-[12px] text-gray-700 select-text", children: _jsx(MarkdownDisplay, { source: displayContent, components: userMessageMarkdownComponents }) })] })] }));
223
237
  };
224
238
  const HeartbeatMessage = ({ message }) => {
225
239
  return (_jsx("div", { className: "px-4 py-2", "data-testid": "agent-heartbeat-message", children: _jsxs("div", { className: "flex items-center gap-2 rounded-md border border-sky-100 bg-sky-50/80 px-3 py-2 text-[11px] text-sky-700", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", strokeWidth: 1.5 }), _jsx("span", { className: "min-w-0 flex-1", children: message.content }), message.createdDate && (_jsx("span", { className: "shrink-0 text-[10px] text-sky-500", children: formatTime(new Date(message.createdDate)) }))] }) }));
@@ -463,12 +477,9 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
463
477
  const [isExpanded, setIsExpanded] = useState(true);
464
478
  // First try to get todos from agent metadata (real-time updates)
465
479
  // Server sends additionalData.todoList directly via contextChanged status
466
- // Also check top-level todoList for backward compatibility with stored contexts
467
480
  const metadataTodos = (() => {
468
481
  try {
469
- // Check both additionalData.todoList and top-level todoList (from [JsonExtensionData] serialization)
470
- const todoList = agentMetadata?.additionalData?.todoList ||
471
- agentMetadata?.todoList;
482
+ const todoList = agentMetadata?.additionalData?.todoList;
472
483
  if (todoList?.items && Array.isArray(todoList.items)) {
473
484
  const rawItems = todoList.items
474
485
  .map((item, idx) => ({
@@ -1061,11 +1072,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1061
1072
  value === "supervised") {
1062
1073
  return value;
1063
1074
  }
1064
- // Backend task/spawned agents persist raw "agent", which behaves like
1065
- // autonomous in the frontend's current 3-mode model.
1066
- if (value === "agent") {
1067
- return "autonomous";
1068
- }
1069
1075
  return null;
1070
1076
  };
1071
1077
  const [mode, setMode] = useState("supervised");
@@ -1108,8 +1114,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1108
1114
  const hasTodoContent = useMemo(() => {
1109
1115
  const metadataTodos = (() => {
1110
1116
  try {
1111
- const todoList = agentMetadata?.additionalData?.todoList ||
1112
- agentMetadata?.todoList;
1117
+ const todoList = agentMetadata?.additionalData?.todoList;
1113
1118
  if (todoList?.items && Array.isArray(todoList.items)) {
1114
1119
  const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
1115
1120
  return raw.length > 0;
@@ -1257,9 +1262,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1257
1262
  label: m.name,
1258
1263
  })) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
1259
1264
  const metadataSelectedSkillIds = useMemo(() => {
1260
- const rawSkillIds = agentMetadata?.additionalData?.skillIds ??
1261
- agentMetadata?.skillIds ??
1262
- [];
1265
+ const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
1263
1266
  if (!Array.isArray(rawSkillIds)) {
1264
1267
  return [];
1265
1268
  }
@@ -1682,7 +1685,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1682
1685
  // Auto-focus terminal input on mount
1683
1686
  useEffect(() => {
1684
1687
  if (textareaRef.current) {
1685
- textareaRef.current.focus();
1688
+ try {
1689
+ textareaRef.current.focus({ preventScroll: true });
1690
+ }
1691
+ catch {
1692
+ textareaRef.current.focus();
1693
+ }
1686
1694
  }
1687
1695
  }, []);
1688
1696
  // Start voice recognition
@@ -1999,6 +2007,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1999
2007
  // If no messageId is provided, we'll use the last assistant message or create a new one
2000
2008
  let messageId = message.data?.messageId;
2001
2009
  if (!messageId && agentData?.id) {
2010
+ console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
2011
+ agentId: agentData.id,
2012
+ isIncremental: message.data?.isIncremental,
2013
+ previousContentLength: message.data?.previousContentLength,
2014
+ totalContentLength: message.data?.totalContentLength,
2015
+ });
2002
2016
  // For backward compatibility: if no messageId, find or create the current streaming message
2003
2017
  // This handles cases where the backend doesn't send messageId
2004
2018
  const currentMessages = messagesRef.current;
@@ -2012,7 +2026,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2012
2026
  // If the agent isn't currently running (e.g., we switched tabs after the run
2013
2027
  // completed), skip creating a new streaming message to avoid duplicates.
2014
2028
  const currentAgentStatus = (agentData || agent)?.status;
2015
- const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
2029
+ const isAgentRunning = currentAgentStatus === "running";
2016
2030
  if (!isAgentRunning) {
2017
2031
  return;
2018
2032
  }
@@ -2099,6 +2113,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2099
2113
  const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
2100
2114
  if (existingMessageIndex === -1) {
2101
2115
  // Message doesn't exist - create new streaming message
2116
+ const previousContentLength = message.data?.previousContentLength || 0;
2117
+ if (message.data?.isIncremental && previousContentLength > 0) {
2118
+ console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
2119
+ messageId,
2120
+ previousContentLength,
2121
+ totalContentLength: message.data?.totalContentLength,
2122
+ deltaLength: (message.data?.deltaContent || "").length,
2123
+ });
2124
+ }
2102
2125
  const newStreamMessage = createNewStreamMessage(messageId, agentData);
2103
2126
  // Set the content for the new message
2104
2127
  const updatedNewMessage = { ...newStreamMessage };
@@ -2121,8 +2144,21 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2121
2144
  return prev;
2122
2145
  // Check if existing content is already longer than what we're trying to stream
2123
2146
  const currentContentLength = existingMessage.content?.length || 0;
2147
+ const previousContentLength = message.data?.previousContentLength || 0;
2124
2148
  const totalContentLength = message.data?.totalContentLength || 0;
2125
- if (currentContentLength >= totalContentLength &&
2149
+ if (message.data?.isIncremental &&
2150
+ previousContentLength !== currentContentLength &&
2151
+ (previousContentLength > 0 || currentContentLength > 0)) {
2152
+ console.warn("[AgentTerminal] Content chunk length mismatch", {
2153
+ messageId,
2154
+ previousContentLength,
2155
+ currentContentLength,
2156
+ totalContentLength,
2157
+ deltaLength: (message.data?.deltaContent || "").length,
2158
+ });
2159
+ }
2160
+ if (message.data?.isIncremental &&
2161
+ currentContentLength >= totalContentLength &&
2126
2162
  totalContentLength > 0) {
2127
2163
  return prev;
2128
2164
  }
@@ -2151,6 +2187,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2151
2187
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2152
2188
  let toolCallMessageId = message.data?.messageId;
2153
2189
  if (!toolCallMessageId) {
2190
+ console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
2191
+ toolCallId,
2192
+ toolName: message.data?.name || message.data?.displayName,
2193
+ });
2154
2194
  const current = messagesRef.current;
2155
2195
  const lastStreaming = [...current]
2156
2196
  .reverse()
@@ -2314,6 +2354,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2314
2354
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2315
2355
  let resultMessageId = message.data?.messageId;
2316
2356
  if (!resultMessageId) {
2357
+ console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
2358
+ toolCallId: resultToolCallId,
2359
+ toolName: message.data?.functionName || message.data?.displayName,
2360
+ });
2317
2361
  const current = messagesRef.current;
2318
2362
  const lastStreaming = [...current]
2319
2363
  .reverse()
@@ -2819,29 +2863,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2819
2863
  if (!contextJson)
2820
2864
  return null;
2821
2865
  const parsedContext = JSON.parse(contextJson);
2822
- // Context is stored as flat structure with top-level properties.
2823
- // Due to C# [JsonExtensionData], AdditionalData entries can be serialized at top level.
2824
- // Normalize well-known extension keys back under additionalData for consistent UI behavior.
2825
2866
  if (parsedContext && typeof parsedContext === "object") {
2826
- const normalized = { ...parsedContext };
2827
- const additionalData = {
2828
- ...(normalized.additionalData || {}),
2829
- };
2830
- let additionalDataUpdated = false;
2831
- // If todoList is at top level but not in additionalData, move it
2832
- if (normalized.todoList && !normalized.additionalData?.todoList) {
2833
- additionalData.todoList = normalized.todoList;
2834
- additionalDataUpdated = true;
2835
- }
2836
- // Planner/task skill selections may also be flattened from JsonExtensionData.
2837
- if (normalized.skillIds && !normalized.additionalData?.skillIds) {
2838
- additionalData.skillIds = normalized.skillIds;
2839
- additionalDataUpdated = true;
2840
- }
2841
- if (additionalDataUpdated) {
2842
- normalized.additionalData = additionalData;
2843
- }
2844
- return normalized;
2867
+ return parsedContext;
2845
2868
  }
2846
2869
  return null;
2847
2870
  }
@@ -2927,7 +2950,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2927
2950
  return;
2928
2951
  }
2929
2952
  // Check if cost limit exceeded based on status or cost values
2930
- const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
2953
+ const statusIndicatesLimit = agent.status === "costLimitReached";
2931
2954
  // Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
2932
2955
  const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
2933
2956
  const costExceedsLimit = agent.costLimit &&
@@ -3294,12 +3317,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3294
3317
  // Route based on statusData.state
3295
3318
  try {
3296
3319
  // Normalize various status shapes and handle Cancelled uniformly
3297
- const normalizedStatus = statusData?.state ||
3298
- statusData?.status;
3299
- if (normalizedStatus === "Cancelled" ||
3300
- normalizedStatus === "canceled" ||
3301
- normalizedStatus === "stopped" ||
3302
- normalizedStatus === "Stopped") {
3320
+ const normalizedStatus = parseAgentStatus(statusData?.state) ||
3321
+ parseAgentStatus(statusData?.status);
3322
+ if (normalizedStatus === "idle") {
3303
3323
  // Stop indicators and mark any in-progress streaming messages as completed
3304
3324
  clearHeartbeatMessages();
3305
3325
  setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
@@ -3437,8 +3457,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3437
3457
  return;
3438
3458
  }
3439
3459
  // Handle waiting states explicitly
3440
- if (statusData?.state === "WaitingForApproval" ||
3441
- statusData?.state === "waitingForApproval") {
3460
+ if (normalizedStatus === "waitingForApproval") {
3442
3461
  setPendingBrowserCaptureDialogType(null);
3443
3462
  setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
3444
3463
  setIsConnecting(false);
@@ -3446,8 +3465,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3446
3465
  setIsAgentThinking(false);
3447
3466
  return;
3448
3467
  }
3449
- if (statusData?.state === "WaitingForInput" ||
3450
- statusData?.state === "waitingForInput") {
3468
+ if (normalizedStatus === "waitingForInput") {
3451
3469
  const dialogType = typeof statusData?.dialogType === "string"
3452
3470
  ? statusData.dialogType
3453
3471
  : null;
@@ -3470,7 +3488,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3470
3488
  setIsAgentThinking(false);
3471
3489
  return;
3472
3490
  }
3473
- if (statusData?.state === "CostLimitReached") {
3491
+ if (normalizedStatus === "costLimitReached") {
3474
3492
  setPendingBrowserCaptureDialogType(null);
3475
3493
  const totalCost = Number(statusData.totalCost) || 0;
3476
3494
  const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
@@ -3518,8 +3536,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3518
3536
  return;
3519
3537
  }
3520
3538
  // Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
3521
- if (normalizedStatus === "completed" ||
3522
- normalizedStatus === "Completed") {
3539
+ if (normalizedStatus === "completed") {
3523
3540
  // Reset deduplication for the next run
3524
3541
  lastSeqRef.current = 0;
3525
3542
  clearHeartbeatMessages();
@@ -3545,21 +3562,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3545
3562
  setIsAgentThinking(false);
3546
3563
  return;
3547
3564
  }
3548
- // Handle "Idle" state - agent has finished processing
3549
- if (normalizedStatus === "idle" || normalizedStatus === "Idle") {
3550
- // Update agent status to idle
3551
- clearHeartbeatMessages();
3552
- setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
3553
- setIsWaitingForResponse(false);
3554
- isWaitingRef.current = false;
3555
- setIsConnecting(false);
3556
- shouldCreateNewMessage.current = false;
3557
- setIsAgentThinking(false);
3558
- return;
3559
- }
3560
3565
  // Handle "Running" state - agent is actively processing
3561
- if (normalizedStatus === "running" ||
3562
- normalizedStatus === "Running") {
3566
+ if (normalizedStatus === "running") {
3563
3567
  // Update agent status to running
3564
3568
  setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
3565
3569
  setIsWaitingForResponse(true);
@@ -3568,7 +3572,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3568
3572
  return;
3569
3573
  }
3570
3574
  // Handle "Error" state
3571
- if (normalizedStatus === "error" || normalizedStatus === "Error") {
3575
+ if (normalizedStatus === "error") {
3572
3576
  const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
3573
3577
  clearHeartbeatMessages();
3574
3578
  setAgent((prev) => prev
@@ -3840,9 +3844,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3840
3844
  }
3841
3845
  setTimeout(() => {
3842
3846
  if (request.dialogType === "questionnaire") {
3843
- inlineDialogContainerRef.current?.scrollIntoView({
3844
- block: "start",
3845
- });
3847
+ scrollToBottomRefForDialogs.current?.();
3846
3848
  return;
3847
3849
  }
3848
3850
  scrollToBottomRefForDialogs.current?.();
@@ -3910,11 +3912,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3910
3912
  // Use case-insensitive comparison for GUID matching (backend may return different casing)
3911
3913
  const normalizedProfileId = profileIdToUse?.toLowerCase();
3912
3914
  const candidate = normalizedProfileId
3913
- ? (profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId) ??
3914
- profiles[0])
3915
- : profiles[0];
3916
- if (!candidate)
3915
+ ? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
3916
+ : undefined;
3917
+ if (!candidate) {
3918
+ setActiveProfile(undefined);
3917
3919
  return;
3920
+ }
3918
3921
  // Keep active profile in sync whenever the matching entry in `profiles` changes —
3919
3922
  // not only when the profile id changes. Otherwise availableSkills (and similar fields)
3920
3923
  // that are merged in the parent after async loads never update (e.g. Template Builder
@@ -3936,7 +3939,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3936
3939
  return prev;
3937
3940
  return candidate;
3938
3941
  });
3939
- }, [profiles, agent?.profileId, agentStub.profileId]);
3942
+ }, [profiles, agent?.id, agent?.profileId, agentStub.id, agentStub.profileId]);
3940
3943
  // Clear queued prompts when agent changes or is new;
3941
3944
  // initial fetch is handled by loadAgent() for better performance and reliability
3942
3945
  useEffect(() => {
@@ -4265,8 +4268,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4265
4268
  const response = await startAgent(request);
4266
4269
  console.log("[AgentTerminal] startAgent response:", response);
4267
4270
  // Check if prompt was queued (agent was already running)
4268
- const wasQueued = response.message?.toLowerCase().includes("queued") ||
4269
- response.status === "Queued";
4271
+ const wasQueued = response.message?.toLowerCase().includes("queued");
4270
4272
  if (wasQueued) {
4271
4273
  // Prompt was queued - show a brief notification but don't set waiting state
4272
4274
  // The prompt will be processed when the agent becomes idle
@@ -5221,11 +5223,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5221
5223
  };
5222
5224
  const renderErrorBanner = () => {
5223
5225
  const currentAgent = agent || agentStub;
5224
- const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
5226
+ const isErrorStatus = currentAgent?.status === "error";
5225
5227
  const errorMessage = (isErrorStatus ? currentAgent?.statusMessage : null) || error;
5226
5228
  if (!errorMessage)
5227
5229
  return null;
5228
- return (_jsx("div", { className: "m-3 rounded border border-red-300 bg-red-50 p-3 text-[11px] text-red-900", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-red-500", strokeWidth: 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "mb-1 font-semibold", children: "Agent Error" }), _jsx("div", { className: "text-red-800", children: errorMessage })] })] }) }));
5230
+ return (_jsx("div", { className: "m-3 rounded border border-red-300 bg-red-50 p-3 text-[11px] text-red-900", "data-testid": "agent-error-banner", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-red-500", strokeWidth: 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "mb-1 font-semibold", children: "Agent Error" }), _jsx("div", { className: "text-red-800", children: errorMessage })] })] }) }));
5229
5231
  };
5230
5232
  const renderBrowserClaimBanner = (variant = "inline") => {
5231
5233
  if (!agent?.id || !editContext?.sessionId)
@@ -5254,12 +5256,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5254
5256
  ? "Take over browser control"
5255
5257
  : "Attach to this browser" })) })] }) }));
5256
5258
  };
5257
- const fixedBrowserClaimBanner = isClaimedByCurrentSession
5258
- ? renderBrowserClaimBanner("fixed")
5259
- : null;
5260
- const inlineBrowserClaimBanner = !isClaimedByCurrentSession
5261
- ? renderBrowserClaimBanner("inline")
5262
- : null;
5259
+ const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
5260
+ const inlineBrowserClaimBanner = null;
5263
5261
  useEffect(() => {
5264
5262
  if (agent?.status !== "waitingForInput") {
5265
5263
  setPendingBrowserCaptureDialogType(null);
@@ -5438,7 +5436,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5438
5436
  })()
5439
5437
  : null;
5440
5438
  const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
5441
- const fullModeUpperContent = (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [fixedBrowserClaimBanner, _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error && !isAgentErrorStatusValue((agent || agentStub)?.status) && (_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-[11px] font-medium text-red-800", children: "Error" }), _jsx("p", { className: "mt-1 text-[11px] text-red-700", children: error })] })] }) })), messages.length === 0 && !error && !hideGreeting && (_jsx("div", { className: "flex h-full items-center justify-center", children: !activeProfile ? (_jsx(Loader2, { className: "mx-auto h-8 w-8 animate-spin text-gray-400" })) : (_jsx(AgentGreeting, { profile: activeProfile, onPromptClick: (p) => {
5439
+ const fullModeUpperContent = (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [fixedBrowserClaimBanner, _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [messages.length === 0 && !error && !hideGreeting && (_jsx("div", { className: "flex h-full items-center justify-center", children: !activeProfile ? (_jsx(Loader2, { className: "mx-auto h-8 w-8 animate-spin text-gray-400" })) : (_jsx(AgentGreeting, { profile: activeProfile, onPromptClick: (p) => {
5442
5440
  setPrompt(p);
5443
5441
  // Use setTimeout to ensure state is updated before submission
5444
5442
  setTimeout(() => {