@copilotkit/react-core 1.9.2-next.8 → 1.9.2-next.9

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 (99) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/{chunk-CCESTGAM.mjs → chunk-3OQM3NEK.mjs} +2 -2
  3. package/dist/{chunk-T4ZKC4X4.mjs → chunk-3YHYWAHK.mjs} +2 -2
  4. package/dist/{chunk-ISYBUDL4.mjs → chunk-55QZ2SVJ.mjs} +4 -5
  5. package/dist/chunk-55QZ2SVJ.mjs.map +1 -0
  6. package/dist/chunk-57K2ZJ5F.mjs +348 -0
  7. package/dist/chunk-57K2ZJ5F.mjs.map +1 -0
  8. package/dist/{chunk-ZHEEHGLS.mjs → chunk-ADIITPD2.mjs} +8 -5
  9. package/dist/{chunk-ZHEEHGLS.mjs.map → chunk-ADIITPD2.mjs.map} +1 -1
  10. package/dist/{chunk-S4BOATBG.mjs → chunk-EF5BNM34.mjs} +2 -2
  11. package/dist/{chunk-I4JPQECN.mjs → chunk-EXU7GWLC.mjs} +4 -4
  12. package/dist/{chunk-7G6RR4HE.mjs → chunk-FXK6RQIN.mjs} +2 -2
  13. package/dist/{chunk-JXF732XG.mjs → chunk-G7LYGERN.mjs} +80 -13
  14. package/dist/chunk-G7LYGERN.mjs.map +1 -0
  15. package/dist/{chunk-VJCHRQ7Q.mjs → chunk-JDEWNLNP.mjs} +37 -4
  16. package/dist/chunk-JDEWNLNP.mjs.map +1 -0
  17. package/dist/{chunk-JHIZ5HAI.mjs → chunk-NQVCZQ5T.mjs} +3 -3
  18. package/dist/{chunk-VF6UPRKM.mjs → chunk-OF4SZTLL.mjs} +3 -3
  19. package/dist/{chunk-QQZLIEXK.mjs → chunk-SJJNFYGQ.mjs} +3 -3
  20. package/dist/{chunk-RN3ZRHI7.mjs → chunk-WOGURSAL.mjs} +5 -5
  21. package/dist/chunk-YAF2LATQ.mjs +310 -0
  22. package/dist/chunk-YAF2LATQ.mjs.map +1 -0
  23. package/dist/components/copilot-provider/copilot-messages.js +35 -2
  24. package/dist/components/copilot-provider/copilot-messages.js.map +1 -1
  25. package/dist/components/copilot-provider/copilot-messages.mjs +2 -2
  26. package/dist/components/copilot-provider/copilotkit.js +289 -234
  27. package/dist/components/copilot-provider/copilotkit.js.map +1 -1
  28. package/dist/components/copilot-provider/copilotkit.mjs +7 -7
  29. package/dist/components/copilot-provider/index.js +289 -234
  30. package/dist/components/copilot-provider/index.js.map +1 -1
  31. package/dist/components/copilot-provider/index.mjs +7 -7
  32. package/dist/components/error-boundary/error-boundary.js +135 -146
  33. package/dist/components/error-boundary/error-boundary.js.map +1 -1
  34. package/dist/components/error-boundary/error-boundary.mjs +4 -4
  35. package/dist/components/error-boundary/error-utils.js.map +1 -1
  36. package/dist/components/error-boundary/error-utils.mjs +2 -2
  37. package/dist/components/index.js +289 -234
  38. package/dist/components/index.js.map +1 -1
  39. package/dist/components/index.mjs +7 -7
  40. package/dist/components/toast/toast-provider.js +118 -85
  41. package/dist/components/toast/toast-provider.js.map +1 -1
  42. package/dist/components/toast/toast-provider.mjs +1 -1
  43. package/dist/components/usage-banner.js +135 -146
  44. package/dist/components/usage-banner.js.map +1 -1
  45. package/dist/components/usage-banner.mjs +1 -1
  46. package/dist/hooks/index.js +75 -10
  47. package/dist/hooks/index.js.map +1 -1
  48. package/dist/hooks/index.mjs +14 -14
  49. package/dist/hooks/use-chat.js +72 -10
  50. package/dist/hooks/use-chat.js.map +1 -1
  51. package/dist/hooks/use-chat.mjs +4 -4
  52. package/dist/hooks/use-coagent-state-render.js.map +1 -1
  53. package/dist/hooks/use-coagent-state-render.mjs +2 -2
  54. package/dist/hooks/use-coagent.js +75 -10
  55. package/dist/hooks/use-coagent.js.map +1 -1
  56. package/dist/hooks/use-coagent.mjs +10 -10
  57. package/dist/hooks/use-copilot-action.js.map +1 -1
  58. package/dist/hooks/use-copilot-action.mjs +3 -3
  59. package/dist/hooks/use-copilot-authenticated-action.js.map +1 -1
  60. package/dist/hooks/use-copilot-authenticated-action.mjs +4 -4
  61. package/dist/hooks/use-copilot-chat.js +72 -10
  62. package/dist/hooks/use-copilot-chat.js.map +1 -1
  63. package/dist/hooks/use-copilot-chat.mjs +9 -9
  64. package/dist/hooks/use-copilot-runtime-client.js +1 -1
  65. package/dist/hooks/use-copilot-runtime-client.js.map +1 -1
  66. package/dist/hooks/use-copilot-runtime-client.mjs +2 -2
  67. package/dist/hooks/use-langgraph-interrupt.js +72 -10
  68. package/dist/hooks/use-langgraph-interrupt.js.map +1 -1
  69. package/dist/hooks/use-langgraph-interrupt.mjs +10 -10
  70. package/dist/index.js +363 -243
  71. package/dist/index.js.map +1 -1
  72. package/dist/index.mjs +15 -15
  73. package/dist/lib/copilot-task.mjs +8 -8
  74. package/dist/lib/index.mjs +8 -8
  75. package/dist/utils/extract.mjs +7 -7
  76. package/dist/utils/index.mjs +7 -7
  77. package/package.json +3 -3
  78. package/src/components/copilot-provider/copilot-messages.tsx +42 -3
  79. package/src/components/toast/toast-provider.tsx +49 -24
  80. package/src/components/usage-banner.tsx +144 -147
  81. package/src/hooks/use-chat.ts +106 -6
  82. package/src/hooks/use-coagent.ts +5 -0
  83. package/src/hooks/use-copilot-runtime-client.ts +2 -39
  84. package/dist/chunk-HD2GE3DK.mjs +0 -359
  85. package/dist/chunk-HD2GE3DK.mjs.map +0 -1
  86. package/dist/chunk-ISYBUDL4.mjs.map +0 -1
  87. package/dist/chunk-JXF732XG.mjs.map +0 -1
  88. package/dist/chunk-VJCHRQ7Q.mjs.map +0 -1
  89. package/dist/chunk-VRXANACV.mjs +0 -277
  90. package/dist/chunk-VRXANACV.mjs.map +0 -1
  91. /package/dist/{chunk-CCESTGAM.mjs.map → chunk-3OQM3NEK.mjs.map} +0 -0
  92. /package/dist/{chunk-T4ZKC4X4.mjs.map → chunk-3YHYWAHK.mjs.map} +0 -0
  93. /package/dist/{chunk-S4BOATBG.mjs.map → chunk-EF5BNM34.mjs.map} +0 -0
  94. /package/dist/{chunk-I4JPQECN.mjs.map → chunk-EXU7GWLC.mjs.map} +0 -0
  95. /package/dist/{chunk-7G6RR4HE.mjs.map → chunk-FXK6RQIN.mjs.map} +0 -0
  96. /package/dist/{chunk-JHIZ5HAI.mjs.map → chunk-NQVCZQ5T.mjs.map} +0 -0
  97. /package/dist/{chunk-VF6UPRKM.mjs.map → chunk-OF4SZTLL.mjs.map} +0 -0
  98. /package/dist/{chunk-QQZLIEXK.mjs.map → chunk-SJJNFYGQ.mjs.map} +0 -0
  99. /package/dist/{chunk-RN3ZRHI7.mjs.map → chunk-WOGURSAL.mjs.map} +0 -0
@@ -21,8 +21,8 @@ const defaultIcons: Record<Severity, JSX.Element> = {
21
21
  [Severity.CRITICAL]: (
22
22
  <svg
23
23
  viewBox="0 0 24 24"
24
- width="18"
25
- height="18"
24
+ width="16"
25
+ height="16"
26
26
  stroke="currentColor"
27
27
  strokeWidth="2.5"
28
28
  fill="none"
@@ -37,8 +37,8 @@ const defaultIcons: Record<Severity, JSX.Element> = {
37
37
  [Severity.WARNING]: (
38
38
  <svg
39
39
  viewBox="0 0 24 24"
40
- width="18"
41
- height="18"
40
+ width="16"
41
+ height="16"
42
42
  stroke="currentColor"
43
43
  strokeWidth="2.5"
44
44
  fill="none"
@@ -53,8 +53,8 @@ const defaultIcons: Record<Severity, JSX.Element> = {
53
53
  [Severity.INFO]: (
54
54
  <svg
55
55
  viewBox="0 0 24 24"
56
- width="18"
57
- height="18"
56
+ width="16"
57
+ height="16"
58
58
  stroke="currentColor"
59
59
  strokeWidth="2.5"
60
60
  fill="none"
@@ -79,23 +79,46 @@ export function UsageBanner({
79
79
  return null;
80
80
  }
81
81
 
82
- // Parse markdown links from message and clean it up
82
+ // Enhanced message parsing to clean up technical details
83
83
  const parseMessage = (rawMessage: string) => {
84
- // Extract markdown links: [text](url)
85
- const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
86
- const matches = Array.from(rawMessage.matchAll(linkRegex));
84
+ // console.log("Raw message:", rawMessage); // Debug
85
+
86
+ // Super aggressive cleaning - handle common error patterns first
87
+ if (
88
+ rawMessage.toLowerCase().includes("authentication") ||
89
+ rawMessage.toLowerCase().includes("api key")
90
+ ) {
91
+ return "Authentication failed. Please check your API key.";
92
+ }
93
+
94
+ if (rawMessage.toLowerCase().includes("rate limit")) {
95
+ return "Rate limit exceeded. Please try again later.";
96
+ }
97
+
98
+ if (rawMessage.toLowerCase().includes("checkpointer")) {
99
+ return "Agent configuration error. Please check your setup.";
100
+ }
101
+
102
+ // For any other error, extract just the main error type
103
+ let cleanMessage = rawMessage;
87
104
 
88
- if (matches.length > 0) {
89
- // Remove "See more:" and markdown links from the main message
90
- let cleanMessage = rawMessage
91
- .replace(/\.\s*See more:\s*\[([^\]]+)\]\(([^)]+)\)/g, ".")
92
- .replace(/See more:\s*\[([^\]]+)\]\(([^)]+)\)/g, "")
93
- .trim();
105
+ // Remove everything after the first " - " or ":" followed by technical details
106
+ cleanMessage = cleanMessage.split(" - ")[0];
107
+ cleanMessage = cleanMessage.split(": Error code")[0];
108
+ cleanMessage = cleanMessage.split(": 401")[0];
109
+ cleanMessage = cleanMessage.split(": 403")[0];
110
+ cleanMessage = cleanMessage.split(": 404")[0];
111
+ cleanMessage = cleanMessage.split(": 500")[0];
94
112
 
95
- return cleanMessage;
113
+ // Remove "See more" links
114
+ cleanMessage = cleanMessage.replace(/See more:.*$/g, "").trim();
115
+
116
+ // If still too technical, just show a generic message
117
+ if (cleanMessage.includes("{") || cleanMessage.includes("'") || cleanMessage.length > 60) {
118
+ return "Configuration error. Please check your setup.";
96
119
  }
97
120
 
98
- return rawMessage;
121
+ return cleanMessage || "An error occurred. Please check your configuration.";
99
122
  };
100
123
 
101
124
  const cleanMessage = parseMessage(message);
@@ -103,7 +126,7 @@ export function UsageBanner({
103
126
 
104
127
  const themeConfigs = {
105
128
  [Severity.INFO]: {
106
- bg: "linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%)",
129
+ bg: "rgba(239, 246, 255, 0.95)",
107
130
  border: "#93c5fd",
108
131
  text: "#1e40af",
109
132
  icon: "#3b82f6",
@@ -111,7 +134,7 @@ export function UsageBanner({
111
134
  primaryBtnHover: "#2563eb",
112
135
  },
113
136
  [Severity.WARNING]: {
114
- bg: "linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%)",
137
+ bg: "rgba(255, 251, 235, 0.95)",
115
138
  border: "#fbbf24",
116
139
  text: "#92400e",
117
140
  icon: "#f59e0b",
@@ -119,7 +142,7 @@ export function UsageBanner({
119
142
  primaryBtnHover: "#d97706",
120
143
  },
121
144
  [Severity.CRITICAL]: {
122
- bg: "linear-gradient(135deg, #fef2f2 0%, #fecaca 100%)",
145
+ bg: "rgba(254, 242, 242, 0.95)",
123
146
  border: "#f87171",
124
147
  text: "#991b1b",
125
148
  icon: "#ef4444",
@@ -134,13 +157,13 @@ export function UsageBanner({
134
157
  <div
135
158
  style={{
136
159
  position: "fixed",
137
- bottom: "20px",
160
+ bottom: "24px",
138
161
  left: "50%",
139
162
  transform: "translateX(-50%)",
140
- maxWidth: "min(95vw, 680px)",
141
- width: "100%",
163
+ width: "400px",
164
+ maxWidth: "90vw",
142
165
  zIndex: 10000,
143
- animation: "bannerSlideIn 0.4s cubic-bezier(0.16, 1, 0.3, 1)",
166
+ animation: "bannerSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1)",
144
167
  }}
145
168
  >
146
169
  <style>
@@ -148,28 +171,29 @@ export function UsageBanner({
148
171
  @keyframes bannerSlideIn {
149
172
  from {
150
173
  opacity: 0;
151
- transform: translateX(-50%) translateY(10px);
174
+ transform: translateX(-50%) translateY(20px);
175
+ scale: 0.95;
152
176
  }
153
177
  to {
154
178
  opacity: 1;
155
179
  transform: translateX(-50%) translateY(0);
180
+ scale: 1;
156
181
  }
157
182
  }
158
183
  `}
159
184
  </style>
160
185
  <div
161
186
  style={{
162
- display: "flex",
163
- alignItems: "flex-start",
164
- gap: "14px",
165
- borderRadius: "16px",
187
+ borderRadius: "12px",
166
188
  border: `1px solid ${themeConfig.border}`,
167
189
  background: themeConfig.bg,
168
- padding: "18px 20px",
169
- boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)",
190
+ padding: "14px",
191
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08)",
170
192
  position: "relative",
171
- backdropFilter: "blur(10px)",
172
- WebkitBackdropFilter: "blur(10px)",
193
+ backdropFilter: "blur(12px)",
194
+ WebkitBackdropFilter: "blur(12px)",
195
+ boxSizing: "border-box",
196
+ overflow: "hidden",
173
197
  }}
174
198
  >
175
199
  {/* Close button */}
@@ -178,144 +202,117 @@ export function UsageBanner({
178
202
  onClick={onClose}
179
203
  style={{
180
204
  position: "absolute",
181
- top: "12px",
182
- right: "12px",
183
- background: "rgba(255, 255, 255, 0.8)",
205
+ top: "8px",
206
+ right: "8px",
207
+ background: "rgba(255, 255, 255, 0.9)",
184
208
  border: "none",
185
209
  color: themeConfig.text,
186
210
  cursor: "pointer",
187
- fontSize: "18px",
211
+ fontSize: "16px",
188
212
  lineHeight: "1",
189
- padding: "6px",
190
- borderRadius: "8px",
191
- opacity: 0.7,
192
- transition: "all 0.2s ease",
213
+ padding: "4px",
214
+ borderRadius: "4px",
215
+ width: "20px",
216
+ height: "20px",
193
217
  display: "flex",
194
218
  alignItems: "center",
195
219
  justifyContent: "center",
196
- width: "28px",
197
- height: "28px",
198
220
  }}
199
221
  title="Close"
200
- onMouseOver={(e) => {
201
- e.currentTarget.style.opacity = "1";
202
- e.currentTarget.style.background = "rgba(255, 255, 255, 1)";
203
- e.currentTarget.style.transform = "scale(1.05)";
204
- }}
205
- onMouseOut={(e) => {
206
- e.currentTarget.style.opacity = "0.7";
207
- e.currentTarget.style.background = "rgba(255, 255, 255, 0.8)";
208
- e.currentTarget.style.transform = "scale(1)";
209
- }}
210
222
  >
211
223
  ×
212
224
  </button>
213
225
  )}
214
226
 
215
- {/* Icon */}
227
+ {/* Message */}
216
228
  <div
217
229
  style={{
218
- color: themeConfig.icon,
219
- flexShrink: 0,
220
- marginTop: "1px",
221
- padding: "6px",
222
- borderRadius: "10px",
223
- background: "rgba(255, 255, 255, 0.7)",
224
- display: "flex",
225
- alignItems: "center",
226
- justifyContent: "center",
230
+ fontSize: "14px",
231
+ fontWeight: 500,
232
+ color: themeConfig.text,
233
+ lineHeight: "1.4",
234
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
235
+ paddingRight: onClose ? "30px" : "0",
236
+ marginBottom: actions ? "12px" : "0",
237
+ wordBreak: "break-word",
238
+ overflow: "hidden",
239
+ textOverflow: "ellipsis",
240
+ display: "-webkit-box",
241
+ WebkitLineClamp: 2,
242
+ WebkitBoxOrient: "vertical",
227
243
  }}
228
244
  >
229
- {Icon}
245
+ {cleanMessage}
230
246
  </div>
231
247
 
232
- {/* Content */}
233
- <div style={{ flex: 1, paddingRight: onClose ? "40px" : "0" }}>
234
- {/* Message */}
248
+ {/* Actions */}
249
+ {actions && (
235
250
  <div
236
251
  style={{
237
- fontSize: "15px",
238
- fontWeight: 600,
239
- color: themeConfig.text,
240
- lineHeight: "1.5",
241
- marginBottom: actions ? "12px" : "0",
242
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
252
+ display: "flex",
253
+ gap: "8px",
254
+ flexWrap: "wrap",
243
255
  }}
244
256
  >
245
- {cleanMessage}
257
+ {actions.secondary && (
258
+ <button
259
+ onClick={actions.secondary.onClick}
260
+ style={{
261
+ borderRadius: "8px",
262
+ padding: "6px 12px",
263
+ fontSize: "13px",
264
+ fontWeight: 500,
265
+ color: themeConfig.text,
266
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
267
+ border: `1px solid ${themeConfig.border}`,
268
+ cursor: "pointer",
269
+ transition: "all 0.2s ease",
270
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
271
+ }}
272
+ onMouseOver={(e) => {
273
+ e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 1)";
274
+ e.currentTarget.style.transform = "translateY(-1px)";
275
+ }}
276
+ onMouseOut={(e) => {
277
+ e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
278
+ e.currentTarget.style.transform = "translateY(0)";
279
+ }}
280
+ >
281
+ {actions.secondary.label}
282
+ </button>
283
+ )}
284
+ {actions.primary && (
285
+ <button
286
+ onClick={actions.primary.onClick}
287
+ style={{
288
+ borderRadius: "8px",
289
+ padding: "6px 12px",
290
+ fontSize: "13px",
291
+ fontWeight: 600,
292
+ color: "#fff",
293
+ backgroundColor: themeConfig.primaryBtn,
294
+ border: "none",
295
+ cursor: "pointer",
296
+ transition: "all 0.2s ease",
297
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
298
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
299
+ }}
300
+ onMouseOver={(e) => {
301
+ e.currentTarget.style.backgroundColor = themeConfig.primaryBtnHover;
302
+ e.currentTarget.style.transform = "translateY(-1px)";
303
+ e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.2)";
304
+ }}
305
+ onMouseOut={(e) => {
306
+ e.currentTarget.style.backgroundColor = themeConfig.primaryBtn;
307
+ e.currentTarget.style.transform = "translateY(0)";
308
+ e.currentTarget.style.boxShadow = "0 2px 8px rgba(0, 0, 0, 0.15)";
309
+ }}
310
+ >
311
+ {actions.primary.label}
312
+ </button>
313
+ )}
246
314
  </div>
247
-
248
- {/* Actions */}
249
- {actions && (
250
- <div
251
- style={{
252
- display: "flex",
253
- gap: "10px",
254
- flexWrap: "wrap",
255
- }}
256
- >
257
- {actions.secondary && (
258
- <button
259
- onClick={actions.secondary.onClick}
260
- style={{
261
- borderRadius: "10px",
262
- padding: "8px 16px",
263
- fontSize: "14px",
264
- fontWeight: 500,
265
- color: themeConfig.text,
266
- backgroundColor: "rgba(255, 255, 255, 0.8)",
267
- border: `1.5px solid ${themeConfig.border}`,
268
- cursor: "pointer",
269
- transition: "all 0.2s ease",
270
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
271
- }}
272
- onMouseOver={(e) => {
273
- e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 1)";
274
- e.currentTarget.style.transform = "translateY(-1px)";
275
- e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.15)";
276
- }}
277
- onMouseOut={(e) => {
278
- e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.8)";
279
- e.currentTarget.style.transform = "translateY(0)";
280
- e.currentTarget.style.boxShadow = "none";
281
- }}
282
- >
283
- {actions.secondary.label}
284
- </button>
285
- )}
286
- {actions.primary && (
287
- <button
288
- onClick={actions.primary.onClick}
289
- style={{
290
- borderRadius: "10px",
291
- padding: "8px 16px",
292
- fontSize: "14px",
293
- fontWeight: 600,
294
- color: "#fff",
295
- backgroundColor: themeConfig.primaryBtn,
296
- border: "none",
297
- cursor: "pointer",
298
- transition: "all 0.2s ease",
299
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
300
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
301
- }}
302
- onMouseOver={(e) => {
303
- e.currentTarget.style.backgroundColor = themeConfig.primaryBtnHover;
304
- e.currentTarget.style.transform = "translateY(-1px)";
305
- e.currentTarget.style.boxShadow = "0 6px 16px rgba(0, 0, 0, 0.2)";
306
- }}
307
- onMouseOut={(e) => {
308
- e.currentTarget.style.backgroundColor = themeConfig.primaryBtn;
309
- e.currentTarget.style.transform = "translateY(0)";
310
- e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.15)";
311
- }}
312
- >
313
- {actions.primary.label}
314
- </button>
315
- )}
316
- </div>
317
- )}
318
- </div>
315
+ )}
319
316
  </div>
320
317
  </div>
321
318
  );
@@ -5,6 +5,8 @@ import {
5
5
  CoAgentStateRenderHandler,
6
6
  randomId,
7
7
  parseJson,
8
+ CopilotKitError,
9
+ CopilotKitErrorCode,
8
10
  } from "@copilotkit/shared";
9
11
  import {
10
12
  Message,
@@ -39,6 +41,7 @@ import { AgentSession } from "../context/copilot-context";
39
41
  import { useCopilotRuntimeClient } from "./use-copilot-runtime-client";
40
42
  import { useCopilotContext } from "../context/copilot-context";
41
43
  import { useAsyncCallback, useErrorToast } from "../components/error-boundary/error-utils";
44
+ import { useToast } from "../components/toast/toast-provider";
42
45
  import {
43
46
  LangGraphInterruptAction,
44
47
  LangGraphInterruptActionSetter,
@@ -218,6 +221,41 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
218
221
  } = options;
219
222
  const runChatCompletionRef = useRef<(previousMessages: Message[]) => Promise<Message[]>>();
220
223
  const addErrorToast = useErrorToast();
224
+ const { setBannerError } = useToast();
225
+
226
+ // Get onTrace from context since it's not part of copilotConfig
227
+ const { onTrace } = useCopilotContext();
228
+
229
+ // Add tracing functionality to use-chat
230
+ const traceUIError = async (error: CopilotKitError, originalError?: any) => {
231
+ // Just check if onTrace and publicApiKey are defined
232
+ if (!onTrace || !copilotConfig?.publicApiKey) return;
233
+
234
+ try {
235
+ const traceEvent = {
236
+ type: "error" as const,
237
+ timestamp: Date.now(),
238
+ context: {
239
+ source: "ui" as const,
240
+ request: {
241
+ operation: "useChatCompletion",
242
+ url: copilotConfig.chatApiEndpoint,
243
+ startTime: Date.now(),
244
+ },
245
+ technical: {
246
+ environment: "browser",
247
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
248
+ stackTrace: originalError instanceof Error ? originalError.stack : undefined,
249
+ },
250
+ },
251
+ error,
252
+ };
253
+
254
+ await onTrace(traceEvent);
255
+ } catch (traceError) {
256
+ console.error("Error in use-chat onTrace handler:", traceError);
257
+ }
258
+ };
221
259
  // We need to keep a ref of coagent states and session because of renderAndWait - making sure
222
260
  // the latest state is sent to the API
223
261
  // This is a workaround and needs to be addressed in the future
@@ -453,29 +491,91 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
453
491
  filterAdjacentAgentStateMessages(rawMessagesResponse),
454
492
  );
455
493
 
456
- if (messages.length === 0) {
457
- continue;
458
- }
459
-
460
494
  newMessages = [];
461
495
 
496
+ // Handle error statuses BEFORE checking if there are messages
497
+ // (errors can come in chunks with no messages)
498
+
462
499
  // request failed, display error message and quit
463
500
  if (
464
501
  value.generateCopilotResponse.status?.__typename === "FailedResponseStatus" &&
465
502
  value.generateCopilotResponse.status.reason === "GUARDRAILS_VALIDATION_FAILED"
466
503
  ) {
504
+ const guardrailsReason =
505
+ value.generateCopilotResponse.status.details?.guardrailsReason || "";
506
+
467
507
  newMessages = [
468
508
  new TextMessage({
469
509
  role: MessageRole.Assistant,
470
- content: value.generateCopilotResponse.status.details?.guardrailsReason || "",
510
+ content: guardrailsReason,
471
511
  }),
472
512
  ];
513
+
514
+ // Trace guardrails validation failure
515
+ const guardrailsError = new CopilotKitError({
516
+ message: `Guardrails validation failed: ${guardrailsReason}`,
517
+ code: CopilotKitErrorCode.MISUSE,
518
+ });
519
+ await traceUIError(guardrailsError, {
520
+ statusReason: value.generateCopilotResponse.status.reason,
521
+ statusDetails: value.generateCopilotResponse.status.details,
522
+ });
523
+
473
524
  setMessages([...previousMessages, ...newMessages]);
474
525
  break;
475
526
  }
476
527
 
528
+ // Handle UNKNOWN_ERROR failures (like authentication errors) by routing to banner error system
529
+ if (
530
+ value.generateCopilotResponse.status?.__typename === "FailedResponseStatus" &&
531
+ value.generateCopilotResponse.status.reason === "UNKNOWN_ERROR"
532
+ ) {
533
+ const errorMessage =
534
+ value.generateCopilotResponse.status.details?.description ||
535
+ "An unknown error occurred";
536
+
537
+ // Try to extract original error information from the response details
538
+ const statusDetails = value.generateCopilotResponse.status.details;
539
+ const originalError = statusDetails?.originalError || statusDetails?.error;
540
+
541
+ // Extract structured error information if available (prioritize top-level over extensions)
542
+ const originalCode = originalError?.code || originalError?.extensions?.code;
543
+ const originalSeverity = originalError?.severity || originalError?.extensions?.severity;
544
+ const originalVisibility =
545
+ originalError?.visibility || originalError?.extensions?.visibility;
546
+
547
+ // Use the original error code if available, otherwise default to NETWORK_ERROR
548
+ let errorCode = CopilotKitErrorCode.NETWORK_ERROR;
549
+ if (originalCode && Object.values(CopilotKitErrorCode).includes(originalCode)) {
550
+ errorCode = originalCode;
551
+ }
552
+
553
+ // Create a structured CopilotKitError preserving original error information
554
+ const structuredError = new CopilotKitError({
555
+ message: errorMessage,
556
+ code: errorCode,
557
+ severity: originalSeverity,
558
+ visibility: originalVisibility,
559
+ });
560
+
561
+ // Display the error in the banner
562
+ setBannerError(structuredError);
563
+
564
+ // Trace the error for debugging/observability
565
+ await traceUIError(structuredError, {
566
+ statusReason: value.generateCopilotResponse.status.reason,
567
+ statusDetails: value.generateCopilotResponse.status.details,
568
+ originalErrorCode: originalCode,
569
+ preservedStructure: !!originalCode,
570
+ });
571
+
572
+ // Stop processing and break from the loop
573
+ setIsLoading(false);
574
+ break;
575
+ }
576
+
477
577
  // add messages to the chat
478
- else {
578
+ else if (messages.length > 0) {
479
579
  newMessages = [...messages];
480
580
 
481
581
  for (const message of messages) {
@@ -278,6 +278,11 @@ export function useCoAgent<T = any>(options: UseCoagentOptions<T>): UseCoagentRe
278
278
  agentName: name,
279
279
  });
280
280
 
281
+ // Runtime client handles errors automatically via handleGQLErrors
282
+ if (result.error) {
283
+ return; // Don't process data on error
284
+ }
285
+
281
286
  const newState = result.data?.loadAgentState?.state;
282
287
  if (newState === lastLoadedState.current) return;
283
288
 
@@ -32,6 +32,7 @@ export const useCopilotRuntimeClient = (options: CopilotRuntimeClientHookOptions
32
32
 
33
33
  // Helper function to trace UI errors
34
34
  const traceUIError = async (error: CopilotKitError, originalError?: any) => {
35
+ // Just check if onTrace and publicApiKey are defined
35
36
  if (!onTrace || !runtimeOptions.publicApiKey) return;
36
37
 
37
38
  try {
@@ -46,14 +47,13 @@ export const useCopilotRuntimeClient = (options: CopilotRuntimeClientHookOptions
46
47
  startTime: Date.now(),
47
48
  },
48
49
  technical: {
49
- environment: process.env.NODE_ENV,
50
+ environment: "browser",
50
51
  userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
51
52
  stackTrace: originalError instanceof Error ? originalError.stack : undefined,
52
53
  },
53
54
  },
54
55
  error,
55
56
  };
56
-
57
57
  await onTrace(traceEvent);
58
58
  } catch (traceError) {
59
59
  console.error("Error in onTrace handler:", traceError);
@@ -79,7 +79,6 @@ export const useCopilotRuntimeClient = (options: CopilotRuntimeClientHookOptions
79
79
  return;
80
80
  }
81
81
 
82
- // Respect showDevConsole setting for ALL errors
83
82
  if (!isDev) {
84
83
  console.error("CopilotKit Error (hidden in production):", gqlError.message);
85
84
  return;
@@ -148,42 +147,6 @@ export const useCopilotRuntimeClient = (options: CopilotRuntimeClientHookOptions
148
147
  return runtimeClient;
149
148
  };
150
149
 
151
- // Helper to determine if error should show as banner based on structured error system
152
- function shouldShowAsBanner(gqlError: GraphQLError): boolean {
153
- const extensions = gqlError.extensions;
154
- if (!extensions) return false;
155
-
156
- // Primary: Check error code and use structured config
157
- const code = extensions.code as CopilotKitErrorCode;
158
- if (code && ERROR_CONFIG[code]?.visibility === ErrorVisibility.BANNER) {
159
- return true;
160
- }
161
-
162
- // Fallback: Check for API key errors which should always be banners
163
- const errorMessage = gqlError.message.toLowerCase();
164
- if (
165
- errorMessage.includes("api key") ||
166
- errorMessage.includes("401") ||
167
- errorMessage.includes("unauthorized") ||
168
- errorMessage.includes("authentication") ||
169
- errorMessage.includes("incorrect api key")
170
- ) {
171
- return true;
172
- }
173
-
174
- // Legacy: Check by stack trace for discovery errors (for backward compatibility)
175
- const originalError = extensions.originalError as any;
176
- if (originalError?.stack) {
177
- return (
178
- originalError.stack.includes("CopilotApiDiscoveryError") ||
179
- originalError.stack.includes("CopilotKitRemoteEndpointDiscoveryError") ||
180
- originalError.stack.includes("CopilotKitAgentDiscoveryError")
181
- );
182
- }
183
-
184
- return false;
185
- }
186
-
187
150
  // Create appropriate structured error from GraphQL error
188
151
  function createStructuredError(gqlError: GraphQLError): CopilotKitError | null {
189
152
  const extensions = gqlError.extensions;