@copilotkit/react-core 1.59.4 → 1.59.5-canary.1781104893
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{copilotkit-D42EuTt0.d.mts → copilotkit-B83H_vWJ.d.mts} +67 -42
- package/dist/copilotkit-B83H_vWJ.d.mts.map +1 -0
- package/dist/{copilotkit-CtqalfG8.d.cts → copilotkit-CD3EqiJ4.d.cts} +67 -42
- package/dist/copilotkit-CD3EqiJ4.d.cts.map +1 -0
- package/dist/{copilotkit-LdQ8w20l.cjs → copilotkit-CWGR3Ict.cjs} +433 -246
- package/dist/copilotkit-CWGR3Ict.cjs.map +1 -0
- package/dist/{copilotkit-DoIlZQqa.mjs → copilotkit-DBOofUYQ.mjs} +433 -246
- package/dist/copilotkit-DBOofUYQ.mjs.map +1 -0
- package/dist/index.cjs +10 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +10 -7
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +350 -210
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/index.umd.js +432 -245
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +8 -8
- package/dist/copilotkit-CtqalfG8.d.cts.map +0 -1
- package/dist/copilotkit-D42EuTt0.d.mts.map +0 -1
- package/dist/copilotkit-DoIlZQqa.mjs.map +0 -1
- package/dist/copilotkit-LdQ8w20l.cjs.map +0 -1
|
@@ -2025,7 +2025,7 @@ function LicenseWarningBanner({ type, featureName, expiryDate, graceRemaining, o
|
|
|
2025
2025
|
severity: "warning",
|
|
2026
2026
|
message: `Your CopilotKit license expires in ${graceRemaining} day${graceRemaining !== 1 ? "s" : ""}. Please renew.`,
|
|
2027
2027
|
actionLabel: "Renew",
|
|
2028
|
-
actionUrl: "https://
|
|
2028
|
+
actionUrl: "https://dashboard.operations.copilotkit.ai",
|
|
2029
2029
|
onDismiss
|
|
2030
2030
|
});
|
|
2031
2031
|
case "expired": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerShell, {
|
|
@@ -2954,210 +2954,110 @@ const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props
|
|
|
2954
2954
|
};
|
|
2955
2955
|
|
|
2956
2956
|
//#endregion
|
|
2957
|
-
//#region src/v2/a2ui/
|
|
2957
|
+
//#region src/v2/a2ui/A2UIRecoveryStates.tsx
|
|
2958
2958
|
/**
|
|
2959
|
-
* The
|
|
2960
|
-
*
|
|
2959
|
+
* The pre-paint lifecycle fields the middleware stamps onto the `a2ui-surface`
|
|
2960
|
+
* activity content (alongside `a2ui_operations` on paint). `.passthrough()` keeps
|
|
2961
|
+
* `a2ui_operations` and any future fields intact.
|
|
2961
2962
|
*/
|
|
2962
|
-
const
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
lastContentRef.current = content;
|
|
2984
|
-
const incoming = content?.[A2UI_OPERATIONS_KEY];
|
|
2985
|
-
if (!content || !Array.isArray(incoming)) {
|
|
2986
|
-
setOperations([]);
|
|
2987
|
-
return;
|
|
2988
|
-
}
|
|
2989
|
-
setOperations(incoming);
|
|
2990
|
-
}, [content]);
|
|
2991
|
-
const groupedOperations = (0, react.useMemo)(() => {
|
|
2992
|
-
const groups = /* @__PURE__ */ new Map();
|
|
2993
|
-
for (const operation of operations) {
|
|
2994
|
-
const surfaceId = getOperationSurfaceId(operation) ?? _copilotkit_a2ui_renderer.DEFAULT_SURFACE_ID;
|
|
2995
|
-
if (!groups.has(surfaceId)) groups.set(surfaceId, []);
|
|
2996
|
-
groups.get(surfaceId).push(operation);
|
|
2997
|
-
}
|
|
2998
|
-
return groups;
|
|
2999
|
-
}, [operations]);
|
|
3000
|
-
if (!groupedOperations.size) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(loadingComponent ?? DefaultA2UILoading, {});
|
|
3001
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3002
|
-
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
|
|
3003
|
-
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactSurfaceHost, {
|
|
3004
|
-
surfaceId,
|
|
3005
|
-
operations: ops,
|
|
3006
|
-
theme,
|
|
3007
|
-
agent,
|
|
3008
|
-
copilotkit,
|
|
3009
|
-
catalog
|
|
3010
|
-
}, surfaceId))
|
|
3011
|
-
});
|
|
3012
|
-
}
|
|
3013
|
-
};
|
|
3014
|
-
}
|
|
3015
|
-
/**
|
|
3016
|
-
* Renders a single A2UI surface using the React renderer.
|
|
3017
|
-
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
|
|
3018
|
-
*/
|
|
3019
|
-
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog }) {
|
|
3020
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3021
|
-
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
|
|
3022
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
|
|
3023
|
-
onAction: (0, react.useCallback)(async (message) => {
|
|
3024
|
-
if (!agent) return;
|
|
3025
|
-
message.userAction;
|
|
3026
|
-
try {
|
|
3027
|
-
copilotkit.setProperties({
|
|
3028
|
-
...copilotkit.properties,
|
|
3029
|
-
a2uiAction: message
|
|
3030
|
-
});
|
|
3031
|
-
await copilotkit.runAgent({ agent });
|
|
3032
|
-
} finally {
|
|
3033
|
-
if (copilotkit.properties) {
|
|
3034
|
-
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
3035
|
-
copilotkit.setProperties(rest);
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
}, [agent, copilotkit]),
|
|
3039
|
-
theme,
|
|
3040
|
-
catalog,
|
|
3041
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
|
|
3042
|
-
surfaceId,
|
|
3043
|
-
operations
|
|
3044
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UISurfaceOrError, { surfaceId })]
|
|
3045
|
-
})
|
|
3046
|
-
});
|
|
2963
|
+
const A2UILifecycleFields = {
|
|
2964
|
+
status: zod.z.enum([
|
|
2965
|
+
"building",
|
|
2966
|
+
"retrying",
|
|
2967
|
+
"failed"
|
|
2968
|
+
]).optional(),
|
|
2969
|
+
attempt: zod.z.number().optional(),
|
|
2970
|
+
maxAttempts: zod.z.number().optional(),
|
|
2971
|
+
progressTokens: zod.z.number().optional(),
|
|
2972
|
+
error: zod.z.string().optional(),
|
|
2973
|
+
errors: zod.z.array(zod.z.any()).optional(),
|
|
2974
|
+
attempts: zod.z.array(zod.z.any()).optional(),
|
|
2975
|
+
debugExposure: zod.z.enum([
|
|
2976
|
+
"hidden",
|
|
2977
|
+
"collapsed",
|
|
2978
|
+
"verbose"
|
|
2979
|
+
]).optional()
|
|
2980
|
+
};
|
|
2981
|
+
/** Server-stamped debugExposure wins; else the client option; else "collapsed". */
|
|
2982
|
+
function resolveDebugExposure(content, optionDebugExposure) {
|
|
2983
|
+
return content?.debugExposure ?? optionDebugExposure;
|
|
3047
2984
|
}
|
|
3048
|
-
/**
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
const error = (0, _copilotkit_a2ui_renderer.useA2UIError)();
|
|
3054
|
-
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3055
|
-
className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
|
|
3056
|
-
children: ["A2UI render error: ", error]
|
|
3057
|
-
});
|
|
3058
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_a2ui_renderer.A2UIRenderer, {
|
|
3059
|
-
surfaceId,
|
|
3060
|
-
className: "cpk:flex cpk:flex-1"
|
|
2985
|
+
/** building: the generic skeleton + optional live token count. */
|
|
2986
|
+
function A2UIBuildingState({ content }) {
|
|
2987
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIGeneratingSkeleton, {
|
|
2988
|
+
label: "Building interface",
|
|
2989
|
+
tokens: typeof content?.progressTokens === "number" ? content.progressTokens : void 0
|
|
3061
2990
|
});
|
|
3062
2991
|
}
|
|
3063
2992
|
/**
|
|
3064
|
-
*
|
|
3065
|
-
*
|
|
2993
|
+
* retrying: stays the generic skeleton through fast/transient retries; only once
|
|
2994
|
+
* the retry is perceptible (after `showAfterMs`, or once `attempt` crosses
|
|
2995
|
+
* `showAfterAttempts`) does the sub-label reveal "Retrying generation… (N/M)".
|
|
3066
2996
|
*/
|
|
3067
|
-
function
|
|
3068
|
-
const
|
|
3069
|
-
const
|
|
2997
|
+
function A2UIRetryingState({ content, showAfterMs, showAfterAttempts, debugExposure }) {
|
|
2998
|
+
const attempt = typeof content?.attempt === "number" ? content.attempt : void 0;
|
|
2999
|
+
const maxAttempts = typeof content?.maxAttempts === "number" ? content.maxAttempts : void 0;
|
|
3000
|
+
const immediate = attempt !== void 0 && attempt >= showAfterAttempts;
|
|
3001
|
+
const [revealed, setRevealed] = (0, react.useState)(immediate);
|
|
3070
3002
|
(0, react.useEffect)(() => {
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3003
|
+
if (immediate) {
|
|
3004
|
+
setRevealed(true);
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
const timer = setTimeout(() => setRevealed(true), showAfterMs);
|
|
3008
|
+
return () => clearTimeout(timer);
|
|
3009
|
+
}, [immediate, showAfterMs]);
|
|
3010
|
+
const tokens = typeof content?.progressTokens === "number" ? content.progressTokens : void 0;
|
|
3011
|
+
if (!revealed) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIGeneratingSkeleton, {
|
|
3012
|
+
label: "Building interface",
|
|
3013
|
+
tokens
|
|
3014
|
+
});
|
|
3015
|
+
const label = attempt !== void 0 && maxAttempts !== void 0 ? `Retrying generation… (${attempt}/${maxAttempts} attempts)` : "Retrying generation…";
|
|
3016
|
+
const errors = Array.isArray(content?.errors) ? content.errors : [];
|
|
3017
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIGeneratingSkeleton, {
|
|
3018
|
+
label,
|
|
3019
|
+
tokens,
|
|
3020
|
+
children: debugExposure !== "hidden" && errors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIDebugDetails, {
|
|
3021
|
+
label: "validation issues",
|
|
3022
|
+
open: debugExposure === "verbose",
|
|
3023
|
+
payload: {
|
|
3024
|
+
attempt: content?.attempt,
|
|
3025
|
+
errors
|
|
3026
|
+
}
|
|
3027
|
+
})
|
|
3028
|
+
});
|
|
3082
3029
|
}
|
|
3083
|
-
/**
|
|
3084
|
-
|
|
3085
|
-
* Displays an animated shimmer skeleton.
|
|
3086
|
-
*/
|
|
3087
|
-
function DefaultA2UILoading() {
|
|
3030
|
+
/** failed: a clean hard-failure card that replaces the skeleton in place. */
|
|
3031
|
+
function A2UIRecoveryFailure({ content, debugExposure }) {
|
|
3088
3032
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3089
|
-
className: "cpk:
|
|
3090
|
-
style: { minHeight: 120 },
|
|
3033
|
+
className: "cpk:rounded-lg cpk:border cpk:border-amber-200 cpk:bg-amber-50 cpk:p-3 cpk:text-sm cpk:text-amber-800",
|
|
3091
3034
|
children: [
|
|
3092
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3093
|
-
className: "cpk:
|
|
3094
|
-
children:
|
|
3095
|
-
className: "cpk:h-3 cpk:w-3 cpk:rounded-full cpk:bg-gray-200",
|
|
3096
|
-
style: { animation: "cpk-a2ui-pulse 1.5s ease-in-out infinite" }
|
|
3097
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3098
|
-
className: "cpk:text-xs cpk:font-medium cpk:text-gray-400",
|
|
3099
|
-
children: "Generating UI..."
|
|
3100
|
-
})]
|
|
3035
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3036
|
+
className: "cpk:font-medium",
|
|
3037
|
+
children: "Couldn't generate the UI"
|
|
3101
3038
|
}),
|
|
3102
3039
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3103
|
-
className: "cpk:
|
|
3104
|
-
children:
|
|
3105
|
-
.8,
|
|
3106
|
-
.6,
|
|
3107
|
-
.4
|
|
3108
|
-
].map((width, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3109
|
-
className: "cpk:h-3 cpk:rounded cpk:bg-gray-200/70",
|
|
3110
|
-
style: {
|
|
3111
|
-
width: `${width * 100}%`,
|
|
3112
|
-
animation: `cpk-a2ui-pulse 1.5s ease-in-out ${i * .15}s infinite`
|
|
3113
|
-
}
|
|
3114
|
-
}, i))
|
|
3040
|
+
className: "cpk:mt-1 cpk:text-xs cpk:text-amber-700",
|
|
3041
|
+
children: "Something went wrong rendering this. You can keep chatting and try again."
|
|
3115
3042
|
}),
|
|
3116
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3043
|
+
debugExposure !== "hidden" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIDebugDetails, {
|
|
3044
|
+
label: "developer details",
|
|
3045
|
+
open: debugExposure === "verbose",
|
|
3046
|
+
payload: {
|
|
3047
|
+
error: content?.error,
|
|
3048
|
+
attempts: content?.attempts
|
|
3049
|
+
}
|
|
3050
|
+
})
|
|
3122
3051
|
]
|
|
3123
3052
|
});
|
|
3124
3053
|
}
|
|
3125
|
-
function getOperationSurfaceId(operation) {
|
|
3126
|
-
if (!operation || typeof operation !== "object") return null;
|
|
3127
|
-
if (typeof operation.surfaceId === "string") return operation.surfaceId;
|
|
3128
|
-
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
|
|
3129
|
-
}
|
|
3130
|
-
|
|
3131
|
-
//#endregion
|
|
3132
|
-
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
|
|
3133
3054
|
/**
|
|
3134
|
-
*
|
|
3135
|
-
*
|
|
3055
|
+
* Animated wireframe skeleton with a label, an optional live token count, and an
|
|
3056
|
+
* optional debug-detail slot below it. Pure CSS animation (no data dependency).
|
|
3057
|
+
* The `tokens` count drives a progressive reveal of skeleton rows.
|
|
3136
3058
|
*/
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
* Built-in progress indicator for dynamic A2UI generation.
|
|
3140
|
-
* Shows a skeleton wireframe that progressively reveals as tokens stream in.
|
|
3141
|
-
*
|
|
3142
|
-
* Registered automatically when A2UI is enabled. Users can override by
|
|
3143
|
-
* providing their own `useRenderTool({ name: "render_a2ui", ... })`.
|
|
3144
|
-
*/
|
|
3145
|
-
function A2UIProgressIndicator({ parameters }) {
|
|
3146
|
-
const lastRef = (0, react.useRef)({
|
|
3147
|
-
time: 0,
|
|
3148
|
-
tokens: 0
|
|
3149
|
-
});
|
|
3150
|
-
const now = Date.now();
|
|
3151
|
-
let { tokens } = lastRef.current;
|
|
3152
|
-
if (now - lastRef.current.time > 200) {
|
|
3153
|
-
const chars = JSON.stringify(parameters ?? {}).length;
|
|
3154
|
-
tokens = Math.round(chars / 4);
|
|
3155
|
-
lastRef.current = {
|
|
3156
|
-
time: now,
|
|
3157
|
-
tokens
|
|
3158
|
-
};
|
|
3159
|
-
}
|
|
3160
|
-
const phase = tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
|
|
3059
|
+
function A2UIGeneratingSkeleton({ label, tokens, children }) {
|
|
3060
|
+
const phase = tokens == null ? 3 : tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
|
|
3161
3061
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3162
3062
|
style: {
|
|
3163
3063
|
margin: "12px 0",
|
|
@@ -3365,8 +3265,8 @@ function A2UIProgressIndicator({ parameters }) {
|
|
|
3365
3265
|
color: "#a1a1aa",
|
|
3366
3266
|
letterSpacing: "0.025em"
|
|
3367
3267
|
},
|
|
3368
|
-
children:
|
|
3369
|
-
}), tokens > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3268
|
+
children: label
|
|
3269
|
+
}), typeof tokens === "number" && tokens > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3370
3270
|
style: {
|
|
3371
3271
|
fontSize: 11,
|
|
3372
3272
|
color: "#d4d4d8",
|
|
@@ -3379,6 +3279,7 @@ function A2UIProgressIndicator({ parameters }) {
|
|
|
3379
3279
|
]
|
|
3380
3280
|
})]
|
|
3381
3281
|
}),
|
|
3282
|
+
children,
|
|
3382
3283
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
|
|
3383
3284
|
@keyframes cpk-a2ui-fade {
|
|
3384
3285
|
0%, 100% { opacity: 1; }
|
|
@@ -3392,6 +3293,20 @@ function A2UIProgressIndicator({ parameters }) {
|
|
|
3392
3293
|
]
|
|
3393
3294
|
});
|
|
3394
3295
|
}
|
|
3296
|
+
function A2UIDebugDetails({ label, open, payload }) {
|
|
3297
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
3298
|
+
open,
|
|
3299
|
+
className: "cpk:mt-2 cpk:text-xs",
|
|
3300
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
|
|
3301
|
+
className: "cpk:cursor-pointer cpk:text-gray-500",
|
|
3302
|
+
children: label
|
|
3303
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
3304
|
+
className: "cpk:mt-1 cpk:overflow-auto cpk:rounded cpk:bg-gray-100 cpk:p-2 cpk:text-gray-700",
|
|
3305
|
+
style: { fontSize: 11 },
|
|
3306
|
+
children: JSON.stringify(payload, null, 2)
|
|
3307
|
+
})]
|
|
3308
|
+
});
|
|
3309
|
+
}
|
|
3395
3310
|
function Dot() {
|
|
3396
3311
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
3397
3312
|
width: 7,
|
|
@@ -3427,13 +3342,242 @@ function Row({ children, show, delay = 0 }) {
|
|
|
3427
3342
|
children
|
|
3428
3343
|
});
|
|
3429
3344
|
}
|
|
3345
|
+
|
|
3346
|
+
//#endregion
|
|
3347
|
+
//#region src/v2/a2ui/A2UIMessageRenderer.tsx
|
|
3348
|
+
/**
|
|
3349
|
+
* The container key used to wrap A2UI operations for explicit detection.
|
|
3350
|
+
* Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
|
|
3351
|
+
*/
|
|
3352
|
+
const A2UI_OPERATIONS_KEY = "a2ui_operations";
|
|
3353
|
+
let initialized = false;
|
|
3354
|
+
function ensureInitialized() {
|
|
3355
|
+
if (!initialized) {
|
|
3356
|
+
(0, _copilotkit_a2ui_renderer.initializeDefaultCatalog)();
|
|
3357
|
+
(0, _copilotkit_a2ui_renderer.injectStyles)();
|
|
3358
|
+
initialized = true;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* The `a2ui-surface` activity carries the WHOLE generative-UI lifecycle on one
|
|
3363
|
+
* stable messageId (OSS-162): pre-paint `status` ("building" | "retrying" |
|
|
3364
|
+
* "failed") with recovery detail, then `a2ui_operations` on paint. The states
|
|
3365
|
+
* swap in place, so the painted surface replaces the skeleton with no extra
|
|
3366
|
+
* coordination. `.passthrough()` preserves operations + any future fields.
|
|
3367
|
+
*/
|
|
3368
|
+
const A2UISurfaceContentSchema = zod.z.object({
|
|
3369
|
+
a2ui_operations: zod.z.array(zod.z.any()).optional(),
|
|
3370
|
+
...A2UILifecycleFields
|
|
3371
|
+
}).passthrough();
|
|
3372
|
+
function createA2UIMessageRenderer(options) {
|
|
3373
|
+
const { theme, catalog, loadingComponent, recovery } = options;
|
|
3374
|
+
const showAfterMs = recovery?.showAfterMs ?? 2e3;
|
|
3375
|
+
const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
|
|
3376
|
+
const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
|
|
3377
|
+
return {
|
|
3378
|
+
activityType: "a2ui-surface",
|
|
3379
|
+
content: A2UISurfaceContentSchema,
|
|
3380
|
+
render: ({ content, agent }) => {
|
|
3381
|
+
ensureInitialized();
|
|
3382
|
+
const [operations, setOperations] = (0, react.useState)([]);
|
|
3383
|
+
const { copilotkit } = useCopilotKit();
|
|
3384
|
+
const lastContentRef = (0, react.useRef)(null);
|
|
3385
|
+
(0, react.useEffect)(() => {
|
|
3386
|
+
if (content === lastContentRef.current) return;
|
|
3387
|
+
lastContentRef.current = content;
|
|
3388
|
+
const incoming = content?.[A2UI_OPERATIONS_KEY];
|
|
3389
|
+
if (!content || !Array.isArray(incoming)) {
|
|
3390
|
+
setOperations([]);
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
setOperations(incoming);
|
|
3394
|
+
}, [content]);
|
|
3395
|
+
const groupedOperations = (0, react.useMemo)(() => {
|
|
3396
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3397
|
+
for (const operation of operations) {
|
|
3398
|
+
const surfaceId = getOperationSurfaceId(operation) ?? _copilotkit_a2ui_renderer.DEFAULT_SURFACE_ID;
|
|
3399
|
+
if (!groups.has(surfaceId)) groups.set(surfaceId, []);
|
|
3400
|
+
groups.get(surfaceId).push(operation);
|
|
3401
|
+
}
|
|
3402
|
+
return groups;
|
|
3403
|
+
}, [operations]);
|
|
3404
|
+
const hasOps = groupedOperations.size > 0;
|
|
3405
|
+
const renderLifecycle = (c) => {
|
|
3406
|
+
const status = c?.status;
|
|
3407
|
+
const debugExposure = resolveDebugExposure(c, optionDebugExposure);
|
|
3408
|
+
if (status === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIRecoveryFailure, {
|
|
3409
|
+
content: c,
|
|
3410
|
+
debugExposure
|
|
3411
|
+
});
|
|
3412
|
+
if (status === "retrying") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIRetryingState, {
|
|
3413
|
+
content: c,
|
|
3414
|
+
showAfterMs,
|
|
3415
|
+
showAfterAttempts,
|
|
3416
|
+
debugExposure
|
|
3417
|
+
});
|
|
3418
|
+
if (loadingComponent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(loadingComponent, {});
|
|
3419
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuildingState, { content: c });
|
|
3420
|
+
};
|
|
3421
|
+
const lastLoaderContentRef = (0, react.useRef)(null);
|
|
3422
|
+
if (!(Array.isArray(content?.[A2UI_OPERATIONS_KEY]) && content[A2UI_OPERATIONS_KEY].length > 0)) lastLoaderContentRef.current = content;
|
|
3423
|
+
const [surfaceReady, setSurfaceReady] = (0, react.useState)(false);
|
|
3424
|
+
const readyRef = (0, react.useRef)(false);
|
|
3425
|
+
const markSurfaceReady = (0, react.useCallback)(() => {
|
|
3426
|
+
if (readyRef.current) return;
|
|
3427
|
+
readyRef.current = true;
|
|
3428
|
+
requestAnimationFrame(() => setSurfaceReady(true));
|
|
3429
|
+
}, []);
|
|
3430
|
+
(0, react.useEffect)(() => {
|
|
3431
|
+
if (!hasOps) {
|
|
3432
|
+
setSurfaceReady(false);
|
|
3433
|
+
readyRef.current = false;
|
|
3434
|
+
return;
|
|
3435
|
+
}
|
|
3436
|
+
const t = setTimeout(() => setSurfaceReady(true), 8e3);
|
|
3437
|
+
return () => clearTimeout(t);
|
|
3438
|
+
}, [hasOps]);
|
|
3439
|
+
if (!hasOps) return renderLifecycle(content);
|
|
3440
|
+
const surfaces = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3441
|
+
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
|
|
3442
|
+
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactSurfaceHost, {
|
|
3443
|
+
surfaceId,
|
|
3444
|
+
operations: ops,
|
|
3445
|
+
theme,
|
|
3446
|
+
agent,
|
|
3447
|
+
copilotkit,
|
|
3448
|
+
catalog,
|
|
3449
|
+
onReady: markSurfaceReady
|
|
3450
|
+
}, surfaceId))
|
|
3451
|
+
});
|
|
3452
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3453
|
+
style: { position: "relative" },
|
|
3454
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3455
|
+
"aria-hidden": !surfaceReady,
|
|
3456
|
+
style: surfaceReady ? void 0 : {
|
|
3457
|
+
position: "absolute",
|
|
3458
|
+
inset: 0,
|
|
3459
|
+
opacity: 0,
|
|
3460
|
+
pointerEvents: "none"
|
|
3461
|
+
},
|
|
3462
|
+
children: surfaces
|
|
3463
|
+
}), !surfaceReady && renderLifecycle(lastLoaderContentRef.current ?? content)]
|
|
3464
|
+
});
|
|
3465
|
+
}
|
|
3466
|
+
};
|
|
3467
|
+
}
|
|
3468
|
+
/**
|
|
3469
|
+
* Renders a single A2UI surface using the React renderer.
|
|
3470
|
+
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
|
|
3471
|
+
*/
|
|
3472
|
+
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onReady }) {
|
|
3473
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3474
|
+
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
|
|
3475
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
|
|
3476
|
+
onAction: (0, react.useCallback)(async (message) => {
|
|
3477
|
+
if (!agent) return;
|
|
3478
|
+
message.userAction;
|
|
3479
|
+
try {
|
|
3480
|
+
copilotkit.setProperties({
|
|
3481
|
+
...copilotkit.properties,
|
|
3482
|
+
a2uiAction: message
|
|
3483
|
+
});
|
|
3484
|
+
await copilotkit.runAgent({ agent });
|
|
3485
|
+
} finally {
|
|
3486
|
+
if (copilotkit.properties) {
|
|
3487
|
+
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
3488
|
+
copilotkit.setProperties(rest);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
}, [agent, copilotkit]),
|
|
3492
|
+
theme,
|
|
3493
|
+
catalog,
|
|
3494
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
|
|
3495
|
+
surfaceId,
|
|
3496
|
+
operations,
|
|
3497
|
+
onReady
|
|
3498
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UISurfaceOrError, { surfaceId })]
|
|
3499
|
+
})
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
/**
|
|
3503
|
+
* Renders the A2UI surface, or an error message if processing failed.
|
|
3504
|
+
* Must be a child of A2UIProvider to access the error state.
|
|
3505
|
+
*/
|
|
3506
|
+
function A2UISurfaceOrError({ surfaceId }) {
|
|
3507
|
+
const error = (0, _copilotkit_a2ui_renderer.useA2UIError)();
|
|
3508
|
+
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3509
|
+
className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
|
|
3510
|
+
children: ["A2UI render error: ", error]
|
|
3511
|
+
});
|
|
3512
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_a2ui_renderer.A2UIRenderer, {
|
|
3513
|
+
surfaceId,
|
|
3514
|
+
className: "cpk:flex cpk:flex-1"
|
|
3515
|
+
});
|
|
3516
|
+
}
|
|
3430
3517
|
/**
|
|
3431
|
-
*
|
|
3432
|
-
*
|
|
3518
|
+
* Processes A2UI operations into the provider's message processor.
|
|
3519
|
+
* Must be a child of A2UIProvider to access the actions context.
|
|
3520
|
+
*/
|
|
3521
|
+
function SurfaceMessageProcessor({ surfaceId, operations, onReady }) {
|
|
3522
|
+
const { processMessages, getSurface } = (0, _copilotkit_a2ui_renderer.useA2UIActions)();
|
|
3523
|
+
const lastHashRef = (0, react.useRef)("");
|
|
3524
|
+
(0, react.useEffect)(() => {
|
|
3525
|
+
const hash = JSON.stringify(operations);
|
|
3526
|
+
if (hash === lastHashRef.current) return;
|
|
3527
|
+
lastHashRef.current = hash;
|
|
3528
|
+
processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
|
|
3529
|
+
if (onReady && surfaceHasRenderableContent(operations)) onReady();
|
|
3530
|
+
}, [
|
|
3531
|
+
processMessages,
|
|
3532
|
+
getSurface,
|
|
3533
|
+
surfaceId,
|
|
3534
|
+
operations,
|
|
3535
|
+
onReady
|
|
3536
|
+
]);
|
|
3537
|
+
return null;
|
|
3538
|
+
}
|
|
3539
|
+
/**
|
|
3540
|
+
* Whether the surface's operations are enough to paint a visible card yet.
|
|
3541
|
+
* A data-bound surface references its data via `path` and renders nothing until
|
|
3542
|
+
* the data model has ≥1 value; a static surface (no path refs) paints from its
|
|
3543
|
+
* components alone. Used to time the loader→surface cross-over to actual content
|
|
3544
|
+
* arrival rather than a fixed delay. (OSS-162)
|
|
3545
|
+
*/
|
|
3546
|
+
function surfaceHasRenderableContent(operations) {
|
|
3547
|
+
const componentOps = operations.filter((o) => o?.updateComponents);
|
|
3548
|
+
if (!componentOps.length) return false;
|
|
3549
|
+
if (!JSON.stringify(componentOps).includes("\"path\"")) return true;
|
|
3550
|
+
return operations.some((o) => {
|
|
3551
|
+
const v = o?.updateDataModel?.value;
|
|
3552
|
+
if (!v || typeof v !== "object") return false;
|
|
3553
|
+
return Object.values(v).some((x) => Array.isArray(x) ? x.length > 0 : x !== null && x !== void 0 && x !== "");
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
function getOperationSurfaceId(operation) {
|
|
3557
|
+
if (!operation || typeof operation !== "object") return null;
|
|
3558
|
+
if (typeof operation.surfaceId === "string") return operation.surfaceId;
|
|
3559
|
+
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
//#endregion
|
|
3563
|
+
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
|
|
3564
|
+
/**
|
|
3565
|
+
* Tool name used by the dynamic A2UI generation secondary LLM.
|
|
3566
|
+
*/
|
|
3567
|
+
const RENDER_A2UI_TOOL_NAME = "render_a2ui";
|
|
3568
|
+
/**
|
|
3569
|
+
* Registers a no-op renderer for the `render_a2ui` tool call so its raw streamed
|
|
3570
|
+
* args are never surfaced in the transcript.
|
|
3571
|
+
*
|
|
3572
|
+
* The generation skeleton / retry / failure UX is NO LONGER owned here (OSS-162):
|
|
3573
|
+
* the A2UI middleware drives the whole lifecycle on the `a2ui-surface` activity
|
|
3574
|
+
* (one stable messageId, building → retrying → failed → painted), rendered in
|
|
3575
|
+
* place by `createA2UIMessageRenderer`. Owning a skeleton per tool call caused a
|
|
3576
|
+
* duplicate skeleton on retries / multi-call generations and a skeleton that
|
|
3577
|
+
* lingered after the surface painted — both fixed by retiring it here.
|
|
3433
3578
|
*
|
|
3434
|
-
*
|
|
3435
|
-
*
|
|
3436
|
-
* react-core.ts gives hook-based entries priority over prop-based entries.
|
|
3579
|
+
* Users can still override with their own `useRenderTool({ name: "render_a2ui" })`
|
|
3580
|
+
* (hook-based entries take priority over this prop-based registration).
|
|
3437
3581
|
*/
|
|
3438
3582
|
function A2UIBuiltInToolCallRenderer() {
|
|
3439
3583
|
const { copilotkit } = useCopilotKit();
|
|
@@ -3441,15 +3585,7 @@ function A2UIBuiltInToolCallRenderer() {
|
|
|
3441
3585
|
const renderer = defineToolCallRenderer({
|
|
3442
3586
|
name: RENDER_A2UI_TOOL_NAME,
|
|
3443
3587
|
args: zod.z.any(),
|
|
3444
|
-
render: (
|
|
3445
|
-
if (status === "complete") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
3446
|
-
const params = parameters;
|
|
3447
|
-
const items = params?.items;
|
|
3448
|
-
if (Array.isArray(items) && items.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
3449
|
-
const components = params?.components;
|
|
3450
|
-
if (Array.isArray(components) && components.length > 2) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
3451
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIProgressIndicator, { parameters });
|
|
3452
|
-
}
|
|
3588
|
+
render: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {})
|
|
3453
3589
|
});
|
|
3454
3590
|
const existing = copilotkit._renderToolCalls ?? [];
|
|
3455
3591
|
copilotkit.setRenderToolCalls([...existing.filter((rc) => rc.name !== RENDER_A2UI_TOOL_NAME), renderer]);
|
|
@@ -3586,7 +3722,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
|
|
|
3586
3722
|
if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
|
|
3587
3723
|
theme: a2ui?.theme ?? _copilotkit_a2ui_renderer.viewerTheme,
|
|
3588
3724
|
catalog: a2ui?.catalog,
|
|
3589
|
-
loadingComponent: a2ui?.loadingComponent
|
|
3725
|
+
loadingComponent: a2ui?.loadingComponent,
|
|
3726
|
+
recovery: a2ui?.recovery
|
|
3590
3727
|
}));
|
|
3591
3728
|
return renderers;
|
|
3592
3729
|
}, [
|
|
@@ -6064,40 +6201,28 @@ const ScrollElementContext = react.default.createContext(null);
|
|
|
6064
6201
|
* rendered by the {@link IntelligenceIndicator} brain and the default
|
|
6065
6202
|
* value for the `intelligenceIndicator` slot.
|
|
6066
6203
|
*
|
|
6067
|
-
*
|
|
6068
|
-
*
|
|
6069
|
-
*
|
|
6070
|
-
*
|
|
6071
|
-
* length) and the whole SVG rotates — so the viewer sees one
|
|
6072
|
-
* C-shaped arc spinning around the visual center.
|
|
6073
|
-
* 2. **Icon morph (~250 ms).** On status flip the single icon path
|
|
6074
|
-
* interpolates from the arc to a checkmark via CSS `d:` while the
|
|
6075
|
-
* dashed stroke transitions to solid (filling in the gap that was
|
|
6076
|
-
* the spinner's open portion). The SVG rotation animation is
|
|
6077
|
-
* removed; the snap back to identity is masked by the simultaneous
|
|
6078
|
-
* shape change. Chrome and text stay at full opacity throughout.
|
|
6079
|
-
* 3. **Settle (~400 ms, starts at +250 ms).** Chrome (background,
|
|
6080
|
-
* border, shadow, backdrop-blur) fades to zero opacity. The label
|
|
6081
|
-
* and icon stroke color transitions from saturated purple to a
|
|
6082
|
-
* true-neutral gray at 0.8 alpha — no hue cast, reads as "settled
|
|
6083
|
-
* history metadata." The label simultaneously skews to ~10° (a
|
|
6084
|
-
* transform-based italic feel that interpolates smoothly with the
|
|
6085
|
-
* color, rather than the discrete `font-style: italic` snap that
|
|
6086
|
-
* would cause a layout pop). The label text stays put — only its
|
|
6087
|
-
* color and slant change — so there is no "bump" where the brand
|
|
6088
|
-
* text disappears and reappears.
|
|
6089
|
-
*
|
|
6090
|
-
* Hard sequence: stage 3 has a 250 ms transition-delay so it waits
|
|
6091
|
-
* for stage 2 to finish. Total settle time ~650 ms in production.
|
|
6204
|
+
* Layout: a glassmorphism pill (the `__chrome` layer) wrapping an icon
|
|
6205
|
+
* and a label. The icon is two overlaid SVG paths — a spinner arc and a
|
|
6206
|
+
* checkmark — whose geometry lives in each path's `d` ATTRIBUTE so it
|
|
6207
|
+
* renders in every browser (the CSS `d:` property is Chrome-only).
|
|
6092
6208
|
*
|
|
6093
|
-
*
|
|
6094
|
-
*
|
|
6095
|
-
*
|
|
6209
|
+
* Two states, driven by the `data-status` attribute (see globals.css
|
|
6210
|
+
* for the exact timing):
|
|
6211
|
+
* 1. **In-progress.** The arc spins (CSS rotation) inside the pill and
|
|
6212
|
+
* the checkmark is hidden. Label + icon are a saturated purple.
|
|
6213
|
+
* 2. **Finished.** The arc fades out mid-spin while the checkmark draws
|
|
6214
|
+
* itself in upright (animated `stroke-dashoffset`); the pill chrome
|
|
6215
|
+
* fades away; and the label + icon settle from purple to a neutral
|
|
6216
|
+
* gray, with the label slanting slightly (a `transform: skewX`
|
|
6217
|
+
* faux-italic, so it interpolates with the color instead of snapping
|
|
6218
|
+
* and never reflows). The result reads as quiet "history metadata"
|
|
6219
|
+
* rather than an active spinner. The label text itself never changes
|
|
6220
|
+
* — the static check plus the color/slant shift carry the "done"
|
|
6221
|
+
* meaning, so no wording change is needed.
|
|
6096
6222
|
*
|
|
6097
|
-
*
|
|
6098
|
-
*
|
|
6099
|
-
*
|
|
6100
|
-
* any wording change.
|
|
6223
|
+
* All motion is gated behind `prefers-reduced-motion` (globals.css):
|
|
6224
|
+
* when reduced motion is requested the arc does not spin and the two
|
|
6225
|
+
* states swap instantly, without transitions.
|
|
6101
6226
|
*
|
|
6102
6227
|
* Customize via the `intelligenceIndicator` slot on `CopilotChat`:
|
|
6103
6228
|
* a className string restyles the wrapper, a props object tweaks
|
|
@@ -6118,13 +6243,21 @@ function IntelligenceIndicatorView({ message, status, label, className, ...rest
|
|
|
6118
6243
|
"aria-hidden": "true"
|
|
6119
6244
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6120
6245
|
className: "cpk-intelligence-indicator__content",
|
|
6121
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
6246
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
6122
6247
|
className: "cpk-intelligence-indicator__icon",
|
|
6123
6248
|
viewBox: "0 0 24 24",
|
|
6124
6249
|
width: "14",
|
|
6125
6250
|
height: "14",
|
|
6126
6251
|
"aria-hidden": "true",
|
|
6127
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
6252
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
6253
|
+
className: "cpk-intelligence-indicator__icon-arc",
|
|
6254
|
+
pathLength: 1,
|
|
6255
|
+
d: "M 12 3 C 17 3 21 7 21 12 C 21 17 17 21 12 21 C 7 21 3 17 3 12 C 3 7 7 3 12 3"
|
|
6256
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
6257
|
+
className: "cpk-intelligence-indicator__icon-check",
|
|
6258
|
+
pathLength: 1,
|
|
6259
|
+
d: "M 5 12.5 L 9 16.5 L 19 6.5"
|
|
6260
|
+
})]
|
|
6128
6261
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6129
6262
|
className: "cpk-intelligence-indicator__label",
|
|
6130
6263
|
children: label
|
|
@@ -6344,6 +6477,58 @@ function IntelligenceIndicator(props) {
|
|
|
6344
6477
|
//#endregion
|
|
6345
6478
|
//#region src/v2/components/chat/CopilotChatMessageView.tsx
|
|
6346
6479
|
/**
|
|
6480
|
+
* Builds a map of message.id → stable per-row React key for an entire message
|
|
6481
|
+
* list. A message's canonical `id` is not stable within a turn: some backends
|
|
6482
|
+
* re-key a message mid-stream (e.g. LangChain replaces its transient
|
|
6483
|
+
* `lc_run--…` streaming id with the provider's final `resp_…` id in the
|
|
6484
|
+
* MESSAGES_SNAPSHOT). Keying rows by `id` made React unmount/remount the row
|
|
6485
|
+
* on that swap — the visible HITL chat flash. Tool-call ids survive the
|
|
6486
|
+
* rename, so an assistant message anchored by a tool call is keyed by its
|
|
6487
|
+
* first tool-call id (`tc:<anchorToolCallId>`); everything else falls back to
|
|
6488
|
+
* `id`.
|
|
6489
|
+
*
|
|
6490
|
+
* Load-bearing assumption: this only helps when the first tool-call id itself
|
|
6491
|
+
* is stable across the rename; backends that re-key tool-call ids mid-stream
|
|
6492
|
+
* still remount.
|
|
6493
|
+
*
|
|
6494
|
+
* Collision rule: two distinct assistant messages can occasionally share a
|
|
6495
|
+
* first-tool-call id (e.g. due to upstream bugs or replayed state). First
|
|
6496
|
+
* occurrence (in list order) claims `tc:<id>`; later collisions fall back to
|
|
6497
|
+
* `message.id`. Every assigned key is checked against the claimed set, so
|
|
6498
|
+
* keys are unique even for pathological ids (e.g. a raw message id beginning
|
|
6499
|
+
* with "tc:"); collisions disambiguate with a deterministic numeric suffix.
|
|
6500
|
+
*
|
|
6501
|
+
* Precondition: callers must pass a deduplicated list (see
|
|
6502
|
+
* `deduplicateMessages`); duplicate message ids would silently overwrite map
|
|
6503
|
+
* entries.
|
|
6504
|
+
*
|
|
6505
|
+
* Order caveat: the collision rule is order-sensitive — if two messages
|
|
6506
|
+
* sharing a first tool-call id swap list positions across renders, the
|
|
6507
|
+
* claimant changes and both rows remount. Acceptable: that situation already
|
|
6508
|
+
* indicates an upstream id bug, and keys remain unique.
|
|
6509
|
+
*/
|
|
6510
|
+
function buildRowRenderKeys(messages) {
|
|
6511
|
+
const keys = /* @__PURE__ */ new Map();
|
|
6512
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
6513
|
+
for (const message of messages) {
|
|
6514
|
+
let candidate;
|
|
6515
|
+
if (message.role === "assistant") {
|
|
6516
|
+
const anchorToolCallId = message.toolCalls?.[0]?.id;
|
|
6517
|
+
if (anchorToolCallId) candidate = `tc:${anchorToolCallId}`;
|
|
6518
|
+
}
|
|
6519
|
+
let assigned = message.id;
|
|
6520
|
+
if (candidate && !claimed.has(candidate)) assigned = candidate;
|
|
6521
|
+
else if (claimed.has(assigned)) {
|
|
6522
|
+
let n = 2;
|
|
6523
|
+
while (claimed.has(`${assigned}:${n}`)) n += 1;
|
|
6524
|
+
assigned = `${assigned}:${n}`;
|
|
6525
|
+
}
|
|
6526
|
+
keys.set(message.id, assigned);
|
|
6527
|
+
claimed.add(assigned);
|
|
6528
|
+
}
|
|
6529
|
+
return keys;
|
|
6530
|
+
}
|
|
6531
|
+
/**
|
|
6347
6532
|
* Resolves a slot value into a { Component, slotProps } pair, handling the three
|
|
6348
6533
|
* slot forms: a component type, a className string, or a partial-props object.
|
|
6349
6534
|
*/
|
|
@@ -6522,6 +6707,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
6522
6707
|
return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
|
|
6523
6708
|
};
|
|
6524
6709
|
const deduplicatedMessages = (0, react.useMemo)(() => deduplicateMessages(messages), [messages]);
|
|
6710
|
+
const rowRenderKeys = (0, react.useMemo)(() => buildRowRenderKeys(deduplicatedMessages), [deduplicatedMessages]);
|
|
6525
6711
|
if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Merged ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
|
|
6526
6712
|
const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
|
|
6527
6713
|
const { Component: UserComponent, slotProps: userSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
|
|
@@ -6552,41 +6738,42 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
6552
6738
|
const renderMessageBlock = (message) => {
|
|
6553
6739
|
const elements = [];
|
|
6554
6740
|
const stateSnapshot = getStateSnapshotForMessage(message.id);
|
|
6741
|
+
const rowKey = rowRenderKeys.get(message.id) ?? message.id;
|
|
6555
6742
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
6556
6743
|
message,
|
|
6557
6744
|
position: "before",
|
|
6558
6745
|
renderCustomMessage,
|
|
6559
6746
|
stateSnapshot
|
|
6560
|
-
}, `${
|
|
6747
|
+
}, `${rowKey}-custom-before`));
|
|
6561
6748
|
if (message.role === "assistant") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedAssistantMessage, {
|
|
6562
6749
|
message,
|
|
6563
6750
|
messages,
|
|
6564
6751
|
isRunning,
|
|
6565
6752
|
AssistantMessageComponent: AssistantComponent,
|
|
6566
6753
|
slotProps: assistantSlotProps
|
|
6567
|
-
},
|
|
6754
|
+
}, rowKey));
|
|
6568
6755
|
else if (message.role === "user") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedUserMessage, {
|
|
6569
6756
|
message,
|
|
6570
6757
|
UserMessageComponent: UserComponent,
|
|
6571
6758
|
slotProps: userSlotProps
|
|
6572
|
-
},
|
|
6759
|
+
}, rowKey));
|
|
6573
6760
|
else if (message.role === "activity") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
|
|
6574
6761
|
message,
|
|
6575
6762
|
renderActivityMessage
|
|
6576
|
-
},
|
|
6763
|
+
}, rowKey));
|
|
6577
6764
|
else if (message.role === "reasoning") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
|
|
6578
6765
|
message,
|
|
6579
6766
|
messages,
|
|
6580
6767
|
isRunning,
|
|
6581
6768
|
ReasoningMessageComponent: ReasoningComponent,
|
|
6582
6769
|
slotProps: reasoningSlotProps
|
|
6583
|
-
},
|
|
6770
|
+
}, rowKey));
|
|
6584
6771
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
6585
6772
|
message,
|
|
6586
6773
|
position: "after",
|
|
6587
6774
|
renderCustomMessage,
|
|
6588
6775
|
stateSnapshot
|
|
6589
|
-
}, `${
|
|
6776
|
+
}, `${rowKey}-custom-after`));
|
|
6590
6777
|
const intelligenceTurnId = intelligenceTurnAnchors.get(message.id);
|
|
6591
6778
|
if (intelligenceTurnId !== void 0) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntelligenceIndicator, {
|
|
6592
6779
|
message,
|
|
@@ -6632,7 +6819,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
6632
6819
|
transform: `translateY(${virtualItem.start}px)`
|
|
6633
6820
|
},
|
|
6634
6821
|
children: renderMessageBlock(message)
|
|
6635
|
-
}, message.id);
|
|
6822
|
+
}, rowRenderKeys.get(message.id) ?? message.id);
|
|
6636
6823
|
})
|
|
6637
6824
|
}) : messageElements,
|
|
6638
6825
|
interruptElement,
|
|
@@ -9349,7 +9536,7 @@ const getErrorActions = (error) => {
|
|
|
9349
9536
|
} };
|
|
9350
9537
|
case _copilotkit_shared.CopilotKitErrorCode.UPGRADE_REQUIRED_ERROR: return { primary: {
|
|
9351
9538
|
label: "Upgrade",
|
|
9352
|
-
onClick: () => window.open("https://
|
|
9539
|
+
onClick: () => window.open("https://dashboard.operations.copilotkit.ai", "_blank", "noopener,noreferrer")
|
|
9353
9540
|
} };
|
|
9354
9541
|
default: return;
|
|
9355
9542
|
}
|
|
@@ -11024,4 +11211,4 @@ Object.defineProperty(exports, 'useToast', {
|
|
|
11024
11211
|
return useToast;
|
|
11025
11212
|
}
|
|
11026
11213
|
});
|
|
11027
|
-
//# sourceMappingURL=copilotkit-
|
|
11214
|
+
//# sourceMappingURL=copilotkit-CWGR3Ict.cjs.map
|