@agent-native/toolkit 0.198.4 → 0.198.6

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 (96) hide show
  1. package/dist/app/agentkit/react/components.d.ts +6 -3
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +98 -68
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/styles.css +36 -24
  6. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  7. package/dist/app/chat/AgentKitAssistantChat.js +32 -15
  8. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  9. package/dist/app/chat/AgentPanel.d.ts +1 -1
  10. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  11. package/dist/app/chat/AgentPanel.js +11 -38
  12. package/dist/app/chat/AgentPanel.js.map +1 -1
  13. package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
  14. package/dist/app/chat/AgentSidebar.js +3 -11
  15. package/dist/app/chat/AgentSidebar.js.map +1 -1
  16. package/dist/app/chat/chat/tool-call-display.d.ts +2 -1
  17. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  18. package/dist/app/chat/chat/tool-call-display.js +83 -55
  19. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  20. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  21. package/dist/app/i18n/catalogs/ar-SA.js +1 -0
  22. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  23. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  24. package/dist/app/i18n/catalogs/de-DE.js +1 -0
  25. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  26. package/dist/app/i18n/catalogs/en-US.d.ts +1 -0
  27. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  28. package/dist/app/i18n/catalogs/en-US.js +1 -0
  29. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  30. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  31. package/dist/app/i18n/catalogs/es-ES.js +1 -0
  32. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  33. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  34. package/dist/app/i18n/catalogs/fr-FR.js +1 -0
  35. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  36. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  37. package/dist/app/i18n/catalogs/hi-IN.js +1 -0
  38. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  39. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  40. package/dist/app/i18n/catalogs/ja-JP.js +1 -0
  41. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  42. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  43. package/dist/app/i18n/catalogs/ko-KR.js +1 -0
  44. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  45. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  46. package/dist/app/i18n/catalogs/pt-BR.js +1 -0
  47. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  48. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/zh-CN.js +1 -0
  50. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  51. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/zh-TW.js +1 -0
  53. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  54. package/dist/composer/PromptComposer.d.ts +3 -0
  55. package/dist/composer/PromptComposer.d.ts.map +1 -1
  56. package/dist/composer/PromptComposer.js +2 -2
  57. package/dist/composer/PromptComposer.js.map +1 -1
  58. package/dist/composer/TiptapComposer.d.ts +4 -1
  59. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  60. package/dist/composer/TiptapComposer.js +32 -25
  61. package/dist/composer/TiptapComposer.js.map +1 -1
  62. package/dist/ui/sonner.js +1 -1
  63. package/dist/ui/sonner.js.map +1 -1
  64. package/package.json +3 -3
  65. package/src/app/agentkit/react/components.interaction.spec.tsx +93 -0
  66. package/src/app/agentkit/react/components.spec.tsx +1 -2
  67. package/src/app/agentkit/react/components.tsx +185 -132
  68. package/src/app/agentkit/react/composer-context.spec.tsx +7 -0
  69. package/src/app/agentkit/react/lifecycle.spec.tsx +556 -39
  70. package/src/app/agentkit/react/styles.css +36 -24
  71. package/src/app/chat/AgentKitAssistantChat.spec.tsx +60 -1
  72. package/src/app/chat/AgentKitAssistantChat.tsx +45 -14
  73. package/src/app/chat/AgentPanel.overlay.spec.tsx +28 -4
  74. package/src/app/chat/AgentPanel.tsx +51 -168
  75. package/src/app/chat/AgentSidebar.spec.tsx +19 -19
  76. package/src/app/chat/AgentSidebar.tsx +59 -94
  77. package/src/app/chat/chat/tool-call-display.spec.tsx +371 -15
  78. package/src/app/chat/chat/tool-call-display.tsx +154 -80
  79. package/src/app/i18n/catalogs/ar-SA.ts +1 -0
  80. package/src/app/i18n/catalogs/de-DE.ts +1 -0
  81. package/src/app/i18n/catalogs/en-US.ts +1 -0
  82. package/src/app/i18n/catalogs/es-ES.ts +1 -0
  83. package/src/app/i18n/catalogs/fr-FR.ts +1 -0
  84. package/src/app/i18n/catalogs/hi-IN.ts +1 -0
  85. package/src/app/i18n/catalogs/ja-JP.ts +1 -0
  86. package/src/app/i18n/catalogs/ko-KR.ts +1 -0
  87. package/src/app/i18n/catalogs/pt-BR.ts +1 -0
  88. package/src/app/i18n/catalogs/zh-CN.ts +1 -0
  89. package/src/app/i18n/catalogs/zh-TW.ts +1 -0
  90. package/src/app-shell/sidebar-overflow.spec.tsx +2 -1
  91. package/src/composer/PromptComposer.context.spec.tsx +28 -15
  92. package/src/composer/PromptComposer.tsx +7 -0
  93. package/src/composer/TiptapComposer.spec.ts +40 -6
  94. package/src/composer/TiptapComposer.tsx +89 -89
  95. package/src/ui/sonner.spec.ts +15 -0
  96. package/src/ui/sonner.tsx +1 -1
@@ -30,7 +30,6 @@ import { cn } from "@agent-native/toolkit/utils";
30
30
  import type { ToolCallMessagePartProps } from "@assistant-ui/react";
31
31
  import {
32
32
  IconAlertTriangle,
33
- IconCircleX,
34
33
  IconCheck,
35
34
  IconChevronRight,
36
35
  IconCopy,
@@ -630,6 +629,7 @@ export function ToolCallDisplay({
630
629
  mcpApp,
631
630
  chatUI,
632
631
  isRunning,
632
+ isError,
633
633
  outcome,
634
634
  structuredMeta,
635
635
  activity,
@@ -646,6 +646,7 @@ export function ToolCallDisplay({
646
646
  mcpApp?: AgentMcpAppPayload;
647
647
  chatUI?: ActionChatUIConfig;
648
648
  isRunning: boolean;
649
+ isError?: boolean;
649
650
  outcome?: "unknown";
650
651
  structuredMeta?: Record<string, unknown>;
651
652
  activity?: boolean;
@@ -670,16 +671,17 @@ export function ToolCallDisplay({
670
671
  const isDelegatedAgentCall =
671
672
  toolName === "call-agent" || toolName.startsWith("agent:");
672
673
  const effectiveIsRunning =
673
- isRunning ||
674
- (isDelegatedAgentCall &&
675
- isToolCallActive({
676
- type: "tool-call",
677
- toolName,
678
- result,
679
- outcome,
680
- activity,
681
- structuredMeta,
682
- }));
674
+ !isError &&
675
+ (isRunning ||
676
+ (isDelegatedAgentCall &&
677
+ isToolCallActive({
678
+ type: "tool-call",
679
+ toolName,
680
+ result,
681
+ outcome,
682
+ activity,
683
+ structuredMeta,
684
+ })));
683
685
  const showActiveTail = isActiveTail ?? isLatestRunning;
684
686
  const toolKind = structuredMeta?.toolKind as string | undefined;
685
687
  const wrapToolDisplay = (children: React.ReactNode) => (
@@ -694,7 +696,7 @@ export function ToolCallDisplay({
694
696
  {children}
695
697
  </ToolActivityPresentation>
696
698
  );
697
- if (toolKind === "bash") {
699
+ if (!isError && toolKind === "bash") {
698
700
  return wrapToolDisplay(
699
701
  <BashCell
700
702
  meta={
@@ -705,7 +707,7 @@ export function ToolCallDisplay({
705
707
  />,
706
708
  );
707
709
  }
708
- if (toolKind === "edit") {
710
+ if (!isError && toolKind === "edit") {
709
711
  return wrapToolDisplay(
710
712
  <EditCell
711
713
  meta={
@@ -715,7 +717,7 @@ export function ToolCallDisplay({
715
717
  />,
716
718
  );
717
719
  }
718
- if (toolKind === "write") {
720
+ if (!isError && toolKind === "write") {
719
721
  return wrapToolDisplay(
720
722
  <WriteCell
721
723
  meta={
@@ -735,6 +737,7 @@ export function ToolCallDisplay({
735
737
  mcpApp={mcpApp}
736
738
  chatUI={chatUI}
737
739
  isRunning={effectiveIsRunning}
740
+ isError={isError}
738
741
  outcome={outcome}
739
742
  isActiveTail={showActiveTail}
740
743
  structuredMeta={structuredMeta}
@@ -756,6 +759,7 @@ function ToolCallDisplayGeneric({
756
759
  mcpApp,
757
760
  chatUI,
758
761
  isRunning,
762
+ isError = false,
759
763
  outcome,
760
764
  isActiveTail,
761
765
  structuredMeta,
@@ -771,6 +775,7 @@ function ToolCallDisplayGeneric({
771
775
  mcpApp?: AgentMcpAppPayload;
772
776
  chatUI?: ActionChatUIConfig;
773
777
  isRunning: boolean;
778
+ isError?: boolean;
774
779
  outcome?: "unknown";
775
780
  isActiveTail: boolean;
776
781
  structuredMeta?: Record<string, unknown>;
@@ -788,20 +793,23 @@ function ToolCallDisplayGeneric({
788
793
  const suppressInlineOpenApp = React.useContext(SuppressInlineOpenAppContext);
789
794
  const isRawCallAgent = toolName === "call-agent";
790
795
  const isAgentCall = toolName.startsWith("agent:") || isRawCallAgent;
791
- const [expanded, setExpanded] = useState(isAgentCall);
796
+ const [expanded, setExpanded] = useState(false);
792
797
  const [outputOpen, setOutputOpen] = useState(false);
793
798
  const agentName = toolName.startsWith("agent:")
794
799
  ? toolName.slice(6)
795
800
  : typeof args.agent === "string"
796
801
  ? args.agent
797
802
  : null;
798
- const isAgentError = isAgentCall && result === "Error calling agent";
803
+ const isAgentError =
804
+ isAgentCall && (isError || result === "Error calling agent");
799
805
  const isUnknownOutcome = !isRunning && outcome === "unknown";
800
- const agentStreamText = isRawCallAgent
801
- ? (result ?? "")
802
- : isAgentCall
803
- ? (argsText ?? "")
804
- : "";
806
+ const agentStreamText = isAgentError
807
+ ? ""
808
+ : isRawCallAgent
809
+ ? (result ?? "")
810
+ : isAgentCall
811
+ ? (argsText ?? "")
812
+ : "";
805
813
  const agentActivity = structuredMeta?.agentActivity as
806
814
  | A2AAgentActivitySnapshot
807
815
  | undefined;
@@ -811,7 +819,7 @@ function ToolCallDisplayGeneric({
811
819
  const hasStreamText = agentStreamText.length > 0;
812
820
  const hasArgs = !isAgentCall && Object.keys(args).length > 0;
813
821
 
814
- if (toolName === "connect-builder" && result) {
822
+ if (!isError && toolName === "connect-builder" && result) {
815
823
  try {
816
824
  const parsed = JSON.parse(result);
817
825
  if (parsed?.kind === "connect-builder-card") {
@@ -832,7 +840,7 @@ function ToolCallDisplayGeneric({
832
840
  }
833
841
  }
834
842
 
835
- if (toolName === "connect-file-storage" && result) {
843
+ if (!isError && toolName === "connect-file-storage" && result) {
836
844
  try {
837
845
  const parsed = JSON.parse(result);
838
846
  if (parsed?.kind === "connect-file-storage-card") {
@@ -845,6 +853,7 @@ function ToolCallDisplayGeneric({
845
853
  }
846
854
 
847
855
  if (
856
+ !isError &&
848
857
  toolName === "agent-teams" &&
849
858
  (args as Record<string, string>)?.action === "spawn" &&
850
859
  result
@@ -898,11 +907,14 @@ function ToolCallDisplayGeneric({
898
907
  };
899
908
  const skipRegistryRenderer =
900
909
  !isAgentCall && isBuiltinDataWidgetActionRenderer(nativeToolContext);
901
- const NativeToolRenderer = isAgentCall
902
- ? null
903
- : (resolveBuiltinActionChatRenderer(nativeToolContext) ??
904
- (skipRegistryRenderer ? null : resolveToolRenderer(nativeToolContext)) ??
905
- resolveBuiltinFallbackToolRenderer(nativeToolContext));
910
+ const NativeToolRenderer =
911
+ isError || isAgentCall
912
+ ? null
913
+ : (resolveBuiltinActionChatRenderer(nativeToolContext) ??
914
+ (skipRegistryRenderer
915
+ ? null
916
+ : resolveToolRenderer(nativeToolContext)) ??
917
+ resolveBuiltinFallbackToolRenderer(nativeToolContext));
906
918
  if (NativeToolRenderer) {
907
919
  return (
908
920
  <ActionChatUiSurface
@@ -930,15 +942,13 @@ function ToolCallDisplayGeneric({
930
942
  const displayName = isAgentCall
931
943
  ? isRunning
932
944
  ? t("agentChat.tool.askingAgent", { agent: agentName })
933
- : isAgentError
934
- ? t("agentChat.tool.askingAgentFailed", { agent: agentName })
935
- : t("agentChat.tool.askedAgent", { agent: agentName })
945
+ : t("agentChat.tool.askedAgent", { agent: agentName })
936
946
  : toolLabel(t, toolName);
937
947
  const rowContext = isAgentCall ? null : resolveToolCallRowContext(args);
938
948
 
939
949
  const canExpand = isAgentCall
940
950
  ? hasStreamText
941
- : hasArgs || result !== undefined;
951
+ : hasArgs || result !== undefined || isError;
942
952
  const isExpanded = isAgentCall ? hasStreamText && expanded : expanded;
943
953
  const integration = resolveToolIntegration(toolName, args);
944
954
  const ToolIcon = resolveToolIcon(toolName);
@@ -954,6 +964,11 @@ function ToolCallDisplayGeneric({
954
964
  responseText={agentStreamText}
955
965
  isRunning={isRunning}
956
966
  isError={isAgentError}
967
+ errorText={
968
+ isAgentError
969
+ ? result || t("agentChat.tool.failedWithoutDetails")
970
+ : undefined
971
+ }
957
972
  durationMs={
958
973
  typeof structuredMeta?.agentDurationMs === "number"
959
974
  ? structuredMeta.agentDurationMs
@@ -988,8 +1003,6 @@ function ToolCallDisplayGeneric({
988
1003
  <IntegrationToolBadge integration={integration} />
989
1004
  ) : isRunning ? (
990
1005
  <CubeLoader aria-hidden="true" className="size-3.5" />
991
- ) : isAgentError ? (
992
- <IconCircleX className="size-3.5 text-destructive" />
993
1006
  ) : isUnknownOutcome ? (
994
1007
  <IconAlertTriangle className="size-3.5 text-muted-foreground" />
995
1008
  ) : (
@@ -1052,7 +1065,11 @@ function ToolCallDisplayGeneric({
1052
1065
  ) : null}
1053
1066
  </button>
1054
1067
  <AnimatedCollapse
1055
- open={isExpanded && !isAgentCall && (hasArgs || result !== undefined)}
1068
+ open={
1069
+ isExpanded &&
1070
+ !isAgentCall &&
1071
+ (hasArgs || result !== undefined || isError)
1072
+ }
1056
1073
  >
1057
1074
  <div className={cn("mt-1 space-y-2", !embeddedInWorkSummary && "pl-5")}>
1058
1075
  {inputPayload && (
@@ -1061,7 +1078,11 @@ function ToolCallDisplayGeneric({
1061
1078
  lang={inputPayload.lang}
1062
1079
  />
1063
1080
  )}
1064
- {resultPayload && (
1081
+ {isError ? (
1082
+ <div className="max-h-56 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/70 px-2.5 py-1 text-xs text-muted-foreground">
1083
+ {result || t("agentChat.tool.failedWithoutDetails")}
1084
+ </div>
1085
+ ) : resultPayload ? (
1065
1086
  <ToolOutputPopover
1066
1087
  open={outputOpen}
1067
1088
  onOpenChange={setOutputOpen}
@@ -1078,7 +1099,7 @@ function ToolCallDisplayGeneric({
1078
1099
  <IconCode className="size-3.5" />
1079
1100
  </button>
1080
1101
  </ToolOutputPopover>
1081
- )}
1102
+ ) : null}
1082
1103
  </div>
1083
1104
  </AnimatedCollapse>
1084
1105
  {isUnknownOutcome && (
@@ -1112,6 +1133,7 @@ function AgentCallCell({
1112
1133
  responseText,
1113
1134
  isRunning,
1114
1135
  isError,
1136
+ errorText,
1115
1137
  durationMs,
1116
1138
  }: {
1117
1139
  agentName: string;
@@ -1121,34 +1143,40 @@ function AgentCallCell({
1121
1143
  responseText: string;
1122
1144
  isRunning: boolean;
1123
1145
  isError: boolean;
1146
+ errorText?: string;
1124
1147
  durationMs?: number;
1125
1148
  }) {
1126
1149
  const t = useT();
1127
1150
  const formatDuration = useLocalizedWorkedDuration();
1128
- const [open, setOpen] = useState(true);
1151
+ const [open, setOpen] = useState(!isError);
1152
+ useLayoutEffect(() => {
1153
+ if (isError) setOpen(false);
1154
+ }, [isError]);
1129
1155
  const responseKey = toolCallId ?? agentName;
1156
+ const activeReasoning = isRunning ? (activity?.reasoning ?? []) : [];
1130
1157
  const toolCount = activity?.toolCalls?.length ?? 0;
1131
1158
  const segments = activity?.response ?? [];
1132
- const inlineSegments =
1133
- responseText && !isRunning ? segments.slice(0, toolCount) : segments;
1134
- const finalText =
1135
- responseText || (inlineSegments.length ? "" : activity?.responseText);
1136
- const work =
1137
- activity?.reasoning?.length || toolCount || inlineSegments.length;
1159
+ const inlineSegments = isError
1160
+ ? []
1161
+ : responseText && !isRunning
1162
+ ? segments.slice(0, toolCount)
1163
+ : segments;
1164
+ const finalText = isError
1165
+ ? ""
1166
+ : responseText || (inlineSegments.length ? "" : activity?.responseText);
1167
+ const work = activeReasoning.length || toolCount || inlineSegments.length;
1138
1168
  const workItemCount = Math.max(
1139
- activity?.reasoning?.length ?? 0,
1169
+ activeReasoning.length,
1140
1170
  toolCount,
1141
1171
  inlineSegments.length,
1142
1172
  );
1143
1173
  const label = isRunning
1144
1174
  ? t("agentChat.tool.askingAgent", { agent: agentName })
1145
- : isError
1146
- ? t("agentChat.tool.askingAgentFailed", { agent: agentName })
1147
- : t("agentChat.tool.askedAgent", { agent: agentName });
1175
+ : t("agentChat.tool.askedAgent", { agent: agentName });
1148
1176
  const workContent = work ? (
1149
1177
  <div className="space-y-1">
1150
1178
  {Array.from({ length: workItemCount }, (_, index) => {
1151
- const reasoningText = activity?.reasoning?.[index];
1179
+ const reasoningText = activeReasoning[index];
1152
1180
  const segment = inlineSegments[index];
1153
1181
  const tool = activity?.toolCalls?.[index];
1154
1182
  return (
@@ -1158,11 +1186,11 @@ function AgentCallCell({
1158
1186
  text={reasoningText}
1159
1187
  isStreaming={
1160
1188
  isRunning &&
1161
- activity.activePhase === "reasoning" &&
1162
- index === activity.reasoning.length - 1
1189
+ activity?.activePhase === "reasoning" &&
1190
+ index === activeReasoning.length - 1
1163
1191
  }
1164
- defaultOpen={index === activity.reasoning.length - 1}
1165
- collapseWhenReplaced={index < activity.toolCalls.length}
1192
+ defaultOpen={index === activeReasoning.length - 1}
1193
+ collapseWhenReplaced={index < toolCount}
1166
1194
  />
1167
1195
  )}
1168
1196
  {segment && (
@@ -1183,7 +1211,9 @@ function AgentCallCell({
1183
1211
  <AgentActivityToolCallRow
1184
1212
  tool={tool}
1185
1213
  isActiveTail={
1186
- isRunning && index === activity.toolCalls.length - 1
1214
+ isRunning &&
1215
+ tool.status === "running" &&
1216
+ index === activity.toolCalls.length - 1
1187
1217
  }
1188
1218
  />
1189
1219
  )}
@@ -1215,8 +1245,6 @@ function AgentCallCell({
1215
1245
  >
1216
1246
  {isRunning ? (
1217
1247
  <CubeLoader aria-hidden="true" className="size-3.5" />
1218
- ) : isError ? (
1219
- <IconCircleX className="size-3.5 text-destructive" />
1220
1248
  ) : (
1221
1249
  <IconChevronRight
1222
1250
  className={cn("size-3.5 transition-transform", open && "rotate-90")}
@@ -1260,6 +1288,11 @@ function AgentCallCell({
1260
1288
  />
1261
1289
  </div>
1262
1290
  )}
1291
+ {isError && errorText ? (
1292
+ <div className="max-h-56 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/70 px-2.5 py-1 text-xs text-muted-foreground">
1293
+ {errorText}
1294
+ </div>
1295
+ ) : null}
1263
1296
  </div>
1264
1297
  </AnimatedCollapse>
1265
1298
  </div>
@@ -1273,13 +1306,46 @@ function AgentActivityToolCallRow({
1273
1306
  tool: A2AAgentActivityToolCall;
1274
1307
  isActiveTail: boolean;
1275
1308
  }) {
1309
+ const [open, setOpen] = useState(false);
1276
1310
  const t = useT();
1277
1311
  const isRunning = tool.status === "running";
1312
+ const failureDetails =
1313
+ tool.status === "failed"
1314
+ ? tool.result?.trim()
1315
+ ? tool.result
1316
+ : t("agentChat.tool.failedWithoutDetails")
1317
+ : undefined;
1278
1318
  const integration = resolveToolIntegration(
1279
1319
  tool.name,
1280
1320
  tool.input ? parseJsonText(tool.input) : undefined,
1281
1321
  );
1282
1322
  const ToolIcon = resolveToolIcon(tool.name);
1323
+ const row = (
1324
+ <>
1325
+ <span
1326
+ className={cn(
1327
+ "flex shrink-0 items-center justify-center",
1328
+ integration ? "size-5" : "size-4",
1329
+ )}
1330
+ >
1331
+ {integration ? (
1332
+ <IntegrationToolBadge integration={integration} />
1333
+ ) : isRunning ? (
1334
+ <CubeLoader aria-hidden="true" className="size-3.5" />
1335
+ ) : (
1336
+ <ToolIcon className="size-3.5" />
1337
+ )}
1338
+ </span>
1339
+ <span
1340
+ className={cn(
1341
+ "min-w-0 truncate font-normal",
1342
+ isActiveTail && "agent-running-shimmer",
1343
+ )}
1344
+ >
1345
+ {toolLabel(t, tool.name)}
1346
+ </span>
1347
+ </>
1348
+ );
1283
1349
 
1284
1350
  return (
1285
1351
  <ToolActivityPresentation
@@ -1288,30 +1354,34 @@ function AgentActivityToolCallRow({
1288
1354
  toolCallId={tool.id}
1289
1355
  suppressLongRunningHint
1290
1356
  >
1291
- <div className="agent-kit-density my-0.5 flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-muted-foreground">
1292
- <span
1293
- className={cn(
1294
- "flex shrink-0 items-center justify-center",
1295
- integration ? "size-5" : "size-4",
1296
- )}
1297
- >
1298
- {integration ? (
1299
- <IntegrationToolBadge integration={integration} />
1300
- ) : isRunning ? (
1301
- <CubeLoader aria-hidden="true" className="size-3.5" />
1302
- ) : (
1303
- <ToolIcon className="size-3.5" />
1304
- )}
1305
- </span>
1306
- <span
1307
- className={cn(
1308
- "min-w-0 truncate font-normal",
1309
- isActiveTail && "agent-running-shimmer",
1310
- )}
1311
- >
1312
- {toolLabel(t, tool.name)}
1313
- </span>
1314
- </div>
1357
+ {tool.status === "failed" ? (
1358
+ <div>
1359
+ <button
1360
+ type="button"
1361
+ onClick={() => setOpen((value) => !value)}
1362
+ aria-expanded={open}
1363
+ className="agent-kit-density my-0.5 flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-muted-foreground transition-colors hover:text-foreground"
1364
+ >
1365
+ {row}
1366
+ <IconChevronRight
1367
+ aria-hidden="true"
1368
+ className={cn(
1369
+ "size-3.5 transition-transform",
1370
+ open && "rotate-90",
1371
+ )}
1372
+ />
1373
+ </button>
1374
+ <AnimatedCollapse open={open}>
1375
+ <div className="max-h-56 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/70 px-2.5 py-1 text-xs text-muted-foreground">
1376
+ {failureDetails}
1377
+ </div>
1378
+ </AnimatedCollapse>
1379
+ </div>
1380
+ ) : (
1381
+ <div className="agent-kit-density my-0.5 flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-muted-foreground">
1382
+ {row}
1383
+ </div>
1384
+ )}
1315
1385
  </ToolActivityPresentation>
1316
1386
  );
1317
1387
  }
@@ -1369,6 +1439,7 @@ export function ToolCallFallback({
1369
1439
  structuredMeta={rest.structuredMeta}
1370
1440
  activity={rest.activity}
1371
1441
  isRunning={isRunning}
1442
+ isError={rest.isError}
1372
1443
  outcome={rest.outcome}
1373
1444
  isActiveTail={rest.isActiveTail}
1374
1445
  isLatestRunning={rest.isLatestRunning}
@@ -1399,6 +1470,7 @@ export function ReconnectStreamMessage({
1399
1470
  const latestActiveToolIndex = content.reduce(
1400
1471
  (latestIndex, part, index) =>
1401
1472
  part.type === "tool-call" &&
1473
+ !part.isError &&
1402
1474
  !isCallAgentToolCallShadowed(content, index) &&
1403
1475
  (chatRunning || (allowActivitySpinner && part.activity === true))
1404
1476
  ? index
@@ -1440,12 +1512,14 @@ export function ReconnectStreamMessage({
1440
1512
  argsText={part.argsText}
1441
1513
  args={part.args}
1442
1514
  result={part.result}
1515
+ isError={part.isError}
1443
1516
  mcpApp={part.mcpApp}
1444
1517
  chatUI={part.chatUI}
1445
1518
  structuredMeta={part.structuredMeta}
1446
1519
  activity={part.activity}
1447
1520
  outcome={part.outcome}
1448
1521
  isRunning={
1522
+ !part.isError &&
1449
1523
  part.result === undefined &&
1450
1524
  (chatRunning || (allowActivitySpinner && part.activity === true))
1451
1525
  }
@@ -1260,6 +1260,7 @@ const messages: ToolkitAgentChatTranslation = {
1260
1260
  "tool.askingAgent": "جارٍ سؤال {{agent}}...",
1261
1261
  "tool.elapsed": "انقضت {{duration}}",
1262
1262
  "tool.askingAgentFailed": "حدث خطأ أثناء سؤال {{agent}}",
1263
+ "tool.failedWithoutDetails": "لا تتوفر تفاصيل للخطأ.",
1263
1264
  "tool.input": "الإدخال",
1264
1265
  "tool.inputWithLabel": "الإدخال - {{label}}",
1265
1266
  "tool.interrupted":
@@ -896,6 +896,7 @@ const messages: ToolkitAgentChatTranslation = {
896
896
  "tool.askingAgent": "{{agent}} wird gefragt...",
897
897
  "tool.elapsed": "{{duration}} vergangen",
898
898
  "tool.askingAgentFailed": "Fehler beim Fragen von {{agent}}",
899
+ "tool.failedWithoutDetails": "Keine Fehlerdetails verfügbar.",
899
900
  "tool.input": "Eingabe",
900
901
  "tool.inputWithLabel": "Eingabe – {{label}}",
901
902
  "tool.interrupted":
@@ -1249,6 +1249,7 @@ const messages = {
1249
1249
  "tool.askingAgent": "Asking {{agent}}...",
1250
1250
  "tool.elapsed": "{{duration}} elapsed",
1251
1251
  "tool.askingAgentFailed": "Error asking {{agent}}",
1252
+ "tool.failedWithoutDetails": "No error details are available.",
1252
1253
  "tool.input": "Input",
1253
1254
  "tool.inputWithLabel": "Input - {{label}}",
1254
1255
  "tool.interrupted":
@@ -892,6 +892,7 @@ const messages: ToolkitAgentChatTranslation = {
892
892
  "tool.askingAgent": "Consultando a {{agent}}...",
893
893
  "tool.elapsed": "{{duration}} transcurridos",
894
894
  "tool.askingAgentFailed": "Error al consultar a {{agent}}",
895
+ "tool.failedWithoutDetails": "No hay detalles de error disponibles.",
895
896
  "tool.input": "Entrada",
896
897
  "tool.inputWithLabel": "Entrada - {{label}}",
897
898
  "tool.interrupted":
@@ -898,6 +898,7 @@ const messages: ToolkitAgentChatTranslation = {
898
898
  "tool.askingAgent": "Consultation de {{agent}}...",
899
899
  "tool.elapsed": "Temps écoulé : {{duration}}",
900
900
  "tool.askingAgentFailed": "Erreur lors de la consultation de {{agent}}",
901
+ "tool.failedWithoutDetails": "Aucun détail d’erreur disponible.",
901
902
  "tool.input": "Entrée",
902
903
  "tool.inputWithLabel": "Entrée - {{label}}",
903
904
  "tool.interrupted":
@@ -1229,6 +1229,7 @@ const messages: ToolkitAgentChatTranslation = {
1229
1229
  "tool.askingAgent": "{{agent}} से पूछा जा रहा है...",
1230
1230
  "tool.elapsed": "{{duration}} बीत चुके",
1231
1231
  "tool.askingAgentFailed": "{{agent}} से पूछते समय त्रुटि हुई",
1232
+ "tool.failedWithoutDetails": "त्रुटि का कोई विवरण उपलब्ध नहीं है।",
1232
1233
  "tool.input": "इनपुट",
1233
1234
  "tool.inputWithLabel": "इनपुट - {{label}}",
1234
1235
  "tool.interrupted":
@@ -1265,6 +1265,7 @@ const messages: ToolkitAgentChatTranslation = {
1265
1265
  "tool.askingAgent": "{{agent}} に問い合わせ中...",
1266
1266
  "tool.elapsed": "{{duration}} 経過",
1267
1267
  "tool.askingAgentFailed": "{{agent}} への問い合わせ中にエラーが発生しました",
1268
+ "tool.failedWithoutDetails": "エラーの詳細はありません。",
1268
1269
  "tool.input": "入力",
1269
1270
  "tool.inputWithLabel": "入力 - {{label}}",
1270
1271
  "tool.interrupted":
@@ -1244,6 +1244,7 @@ const messages: ToolkitAgentChatTranslation = {
1244
1244
  "tool.askingAgent": "{{agent}}에게 요청 중...",
1245
1245
  "tool.elapsed": "{{duration}} 경과",
1246
1246
  "tool.askingAgentFailed": "{{agent}}에게 요청하는 중 오류 발생",
1247
+ "tool.failedWithoutDetails": "오류 세부 정보가 없습니다.",
1247
1248
  "tool.input": "입력",
1248
1249
  "tool.inputWithLabel": "입력 - {{label}}",
1249
1250
  "tool.interrupted":
@@ -884,6 +884,7 @@ const messages: ToolkitAgentChatTranslation = {
884
884
  "tool.askingAgent": "Consultando {{agent}}...",
885
885
  "tool.elapsed": "{{duration}} decorridos",
886
886
  "tool.askingAgentFailed": "Erro ao consultar {{agent}}",
887
+ "tool.failedWithoutDetails": "Nenhum detalhe do erro disponível.",
887
888
  "tool.input": "Entrada",
888
889
  "tool.inputWithLabel": "Entrada - {{label}}",
889
890
  "tool.interrupted":
@@ -1178,6 +1178,7 @@ const messages: ToolkitAgentChatTranslation = {
1178
1178
  "tool.askingAgent": "正在询问 {{agent}}...",
1179
1179
  "tool.elapsed": "已用 {{duration}}",
1180
1180
  "tool.askingAgentFailed": "询问 {{agent}} 时出错",
1181
+ "tool.failedWithoutDetails": "没有可用的错误详情。",
1181
1182
  "tool.input": "输入",
1182
1183
  "tool.inputWithLabel": "输入 - {{label}}",
1183
1184
  "tool.interrupted":
@@ -1182,6 +1182,7 @@ const messages: ToolkitAgentChatTranslation = {
1182
1182
  "tool.askingAgent": "正在詢問 {{agent}}...",
1183
1183
  "tool.elapsed": "已耗時 {{duration}}",
1184
1184
  "tool.askingAgentFailed": "詢問 {{agent}} 時發生錯誤",
1185
+ "tool.failedWithoutDetails": "沒有可用的錯誤詳細資料。",
1185
1186
  "tool.input": "輸入",
1186
1187
  "tool.inputWithLabel": "輸入 - {{label}}",
1187
1188
  "tool.interrupted":
@@ -33,8 +33,9 @@ describe("AppSidebar overflow affordances", () => {
33
33
  });
34
34
 
35
35
  afterEach(async () => {
36
+ // Radix debounces ResizeObserver callbacks; flush them before happy-dom teardown.
36
37
  await act(async () => {
37
- await new Promise((resolve) => setTimeout(resolve, 20));
38
+ await new Promise((resolve) => setTimeout(resolve, 50));
38
39
  });
39
40
  act(() => root.unmount());
40
41
  container.remove();