@copilotkit/react-core 1.59.3 → 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-CEJz6krE.d.mts → copilotkit-B83H_vWJ.d.mts} +412 -64
- package/dist/copilotkit-B83H_vWJ.d.mts.map +1 -0
- package/dist/{copilotkit-D16eCFkt.d.cts → copilotkit-CD3EqiJ4.d.cts} +412 -64
- package/dist/copilotkit-CD3EqiJ4.d.cts.map +1 -0
- package/dist/{copilotkit-DhONbYmz.cjs → copilotkit-CWGR3Ict.cjs} +1002 -345
- package/dist/copilotkit-CWGR3Ict.cjs.map +1 -0
- package/dist/{copilotkit-BRNy5UvX.mjs → copilotkit-DBOofUYQ.mjs} +950 -335
- 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 +8 -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 +2 -2
- package/dist/v2/index.umd.js +956 -334
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +8 -8
- package/dist/copilotkit-BRNy5UvX.mjs.map +0 -1
- package/dist/copilotkit-CEJz6krE.d.mts.map +0 -1
- package/dist/copilotkit-D16eCFkt.d.cts.map +0 -1
- package/dist/copilotkit-DhONbYmz.cjs.map +0 -1
|
@@ -234,9 +234,9 @@ const useCopilotChatConfiguration = () => {
|
|
|
234
234
|
|
|
235
235
|
//#endregion
|
|
236
236
|
//#region src/v2/lib/utils.ts
|
|
237
|
-
const twMerge$
|
|
237
|
+
const twMerge$8 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
|
|
238
238
|
function cn(...inputs) {
|
|
239
|
-
return twMerge$
|
|
239
|
+
return twMerge$8((0, clsx.clsx)(inputs));
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
//#endregion
|
|
@@ -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/
|
|
2958
|
-
/**
|
|
2959
|
-
* The container key used to wrap A2UI operations for explicit detection.
|
|
2960
|
-
* Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
|
|
2961
|
-
*/
|
|
2962
|
-
const A2UI_OPERATIONS_KEY = "a2ui_operations";
|
|
2963
|
-
let initialized = false;
|
|
2964
|
-
function ensureInitialized() {
|
|
2965
|
-
if (!initialized) {
|
|
2966
|
-
(0, _copilotkit_a2ui_renderer.initializeDefaultCatalog)();
|
|
2967
|
-
(0, _copilotkit_a2ui_renderer.injectStyles)();
|
|
2968
|
-
initialized = true;
|
|
2969
|
-
}
|
|
2970
|
-
}
|
|
2971
|
-
function createA2UIMessageRenderer(options) {
|
|
2972
|
-
const { theme, catalog, loadingComponent } = options;
|
|
2973
|
-
return {
|
|
2974
|
-
activityType: "a2ui-surface",
|
|
2975
|
-
content: zod.z.any(),
|
|
2976
|
-
render: ({ content, agent }) => {
|
|
2977
|
-
ensureInitialized();
|
|
2978
|
-
const [operations, setOperations] = (0, react.useState)([]);
|
|
2979
|
-
const { copilotkit } = useCopilotKit();
|
|
2980
|
-
const lastContentRef = (0, react.useRef)(null);
|
|
2981
|
-
(0, react.useEffect)(() => {
|
|
2982
|
-
if (content === lastContentRef.current) return;
|
|
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
|
-
}
|
|
2957
|
+
//#region src/v2/a2ui/A2UIRecoveryStates.tsx
|
|
3015
2958
|
/**
|
|
3016
|
-
*
|
|
3017
|
-
*
|
|
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.
|
|
3018
2962
|
*/
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
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,
|
|
@@ -3415,25 +3330,254 @@ function Bar({ w, h, bg, anim, opacity, transition }) {
|
|
|
3415
3330
|
...transition ? { transition } : {}
|
|
3416
3331
|
} });
|
|
3417
3332
|
}
|
|
3418
|
-
function Row({ children, show, delay = 0 }) {
|
|
3419
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3420
|
-
style: {
|
|
3421
|
-
display: "flex",
|
|
3422
|
-
alignItems: "center",
|
|
3423
|
-
gap: 6,
|
|
3424
|
-
opacity: show ? 1 : 0,
|
|
3425
|
-
transition: `opacity 0.4s ${delay}s`
|
|
3426
|
-
},
|
|
3427
|
-
children
|
|
3333
|
+
function Row({ children, show, delay = 0 }) {
|
|
3334
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3335
|
+
style: {
|
|
3336
|
+
display: "flex",
|
|
3337
|
+
alignItems: "center",
|
|
3338
|
+
gap: 6,
|
|
3339
|
+
opacity: show ? 1 : 0,
|
|
3340
|
+
transition: `opacity 0.4s ${delay}s`
|
|
3341
|
+
},
|
|
3342
|
+
children
|
|
3343
|
+
});
|
|
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
|
+
}
|
|
3517
|
+
/**
|
|
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 !== "");
|
|
3428
3554
|
});
|
|
3429
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";
|
|
3430
3568
|
/**
|
|
3431
|
-
* Registers
|
|
3432
|
-
*
|
|
3569
|
+
* Registers a no-op renderer for the `render_a2ui` tool call so its raw streamed
|
|
3570
|
+
* args are never surfaced in the transcript.
|
|
3433
3571
|
*
|
|
3434
|
-
*
|
|
3435
|
-
*
|
|
3436
|
-
*
|
|
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.
|
|
3578
|
+
*
|
|
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
|
}, [
|
|
@@ -4811,6 +4948,175 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4811
4948
|
};
|
|
4812
4949
|
}
|
|
4813
4950
|
|
|
4951
|
+
//#endregion
|
|
4952
|
+
//#region src/v2/lib/record-annotation.ts
|
|
4953
|
+
/**
|
|
4954
|
+
* Low-level function that posts an arbitrary annotation to the CopilotKit
|
|
4955
|
+
* runtime's general annotation endpoint (`POST /annotate`).
|
|
4956
|
+
*
|
|
4957
|
+
* This is the single transport entry point for all annotation types. Higher-
|
|
4958
|
+
* level hooks (e.g. `useLearnFromUserAction`) build the `type`/`payload` pair
|
|
4959
|
+
* for their specific annotation shape and delegate the HTTP call here.
|
|
4960
|
+
*
|
|
4961
|
+
* The function uses the same transport as `useLearnFromUserAction`:
|
|
4962
|
+
* - `runtimeUrl` from `copilotkit.runtimeUrl` (BFF proxies to the platform)
|
|
4963
|
+
* - `headers` from `copilotkit.headers` (customer auth forwarded to BFF)
|
|
4964
|
+
* - `clientEventId` auto-generated via `randomUUID()` when omitted
|
|
4965
|
+
* - `userId` is resolved server-side by the runtime; the client never sends it
|
|
4966
|
+
* - Errors propagate to the caller (fire-and-propagate, not fire-and-forget)
|
|
4967
|
+
*
|
|
4968
|
+
* @param args - Transport dependencies plus annotation fields.
|
|
4969
|
+
* @returns The platform result containing the annotation row `id` and a
|
|
4970
|
+
* `duplicate` flag.
|
|
4971
|
+
* @throws When the network request fails or the runtime returns a non-2xx
|
|
4972
|
+
* status. Callers that want fire-and-forget behavior should `.catch`
|
|
4973
|
+
* at the call site.
|
|
4974
|
+
*/
|
|
4975
|
+
async function recordAnnotation(args) {
|
|
4976
|
+
const { runtimeUrl, headers, type, payload, threadId, occurredAt } = args;
|
|
4977
|
+
const body = {
|
|
4978
|
+
type,
|
|
4979
|
+
threadId,
|
|
4980
|
+
clientEventId: args.clientEventId ?? (0, _copilotkit_shared.randomUUID)(),
|
|
4981
|
+
...payload !== void 0 ? { payload } : {},
|
|
4982
|
+
...occurredAt !== void 0 ? { occurredAt } : {}
|
|
4983
|
+
};
|
|
4984
|
+
const response = await fetch(`${runtimeUrl}/annotate`, {
|
|
4985
|
+
method: "POST",
|
|
4986
|
+
headers: {
|
|
4987
|
+
"Content-Type": "application/json",
|
|
4988
|
+
...headers
|
|
4989
|
+
},
|
|
4990
|
+
body: JSON.stringify(body)
|
|
4991
|
+
});
|
|
4992
|
+
if (!response.ok) {
|
|
4993
|
+
const text = await response.text().catch(() => "");
|
|
4994
|
+
throw new Error(`recordAnnotation: request failed (${response.status})${text ? `: ${text}` : ""}`);
|
|
4995
|
+
}
|
|
4996
|
+
const text = await response.text();
|
|
4997
|
+
if (!text) throw new Error(`recordAnnotation: runtime ${runtimeUrl}/annotate returned ${response.status} with an empty body`);
|
|
4998
|
+
try {
|
|
4999
|
+
return JSON.parse(text);
|
|
5000
|
+
} catch {
|
|
5001
|
+
throw new Error(`recordAnnotation: runtime ${runtimeUrl}/annotate returned a non-JSON body (status ${response.status})`);
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
|
|
5005
|
+
//#endregion
|
|
5006
|
+
//#region src/v2/hooks/use-learn-from-user-action.tsx
|
|
5007
|
+
/**
|
|
5008
|
+
* Record a user UI interaction in the Intelligence platform's user-actions
|
|
5009
|
+
* stream. The platform's auto-curated knowledge base agent reads these
|
|
5010
|
+
* (alongside finished agent runs) and writes free-form Obsidian-flavored
|
|
5011
|
+
* markdown to `/project`, where any agent in the same project can later
|
|
5012
|
+
* read it via the `copilotkit_knowledge_base_shell` MCP tool.
|
|
5013
|
+
*
|
|
5014
|
+
* The hook returns a stable function. Calling it issues a request to the
|
|
5015
|
+
* customer's CopilotKit runtime (`POST ${runtimeUrl}/annotate`), which
|
|
5016
|
+
* resolves the Intel user from the BFF's auth and forwards to the
|
|
5017
|
+
* platform — the Intel API key never reaches the browser.
|
|
5018
|
+
*
|
|
5019
|
+
* If `clientEventId` is omitted `recordAnnotation` generates a UUID per call,
|
|
5020
|
+
* so a naive double-call (e.g. React 18 strict-mode double-mount, or a retry
|
|
5021
|
+
* after a network blip on a fresh Promise) is naturally safe. Supply your
|
|
5022
|
+
* own key when a single semantic event must remain idempotent across
|
|
5023
|
+
* multiple `learnFromUserAction(...)` calls.
|
|
5024
|
+
*
|
|
5025
|
+
* @example
|
|
5026
|
+
* ```tsx
|
|
5027
|
+
* import { useLearnFromUserAction } from "@copilotkit/react-core";
|
|
5028
|
+
*
|
|
5029
|
+
* function SettingsPage({ threadId }) {
|
|
5030
|
+
* const learnFromUserAction = useLearnFromUserAction();
|
|
5031
|
+
*
|
|
5032
|
+
* const onRename = (oldName: string, newName: string) => {
|
|
5033
|
+
* void learnFromUserAction({
|
|
5034
|
+
* threadId,
|
|
5035
|
+
* title: "Renamed project",
|
|
5036
|
+
* data: { previous: { name: oldName }, next: { name: newName } },
|
|
5037
|
+
* });
|
|
5038
|
+
* };
|
|
5039
|
+
* }
|
|
5040
|
+
* ```
|
|
5041
|
+
*/
|
|
5042
|
+
function useLearnFromUserAction() {
|
|
5043
|
+
const { copilotkit } = useCopilotKit();
|
|
5044
|
+
return (0, react.useCallback)(async (input) => {
|
|
5045
|
+
const runtimeUrl = copilotkit.runtimeUrl;
|
|
5046
|
+
if (!runtimeUrl) throw new Error("useLearnFromUserAction: runtimeUrl is not configured. Set it on <CopilotKitProvider runtimeUrl=...>.");
|
|
5047
|
+
const payload = {
|
|
5048
|
+
...input.title !== void 0 ? { title: input.title } : {},
|
|
5049
|
+
...input.description !== void 0 ? { description: input.description } : {},
|
|
5050
|
+
...input.data !== void 0 ? { data: input.data } : {}
|
|
5051
|
+
};
|
|
5052
|
+
return recordAnnotation({
|
|
5053
|
+
runtimeUrl,
|
|
5054
|
+
headers: copilotkit.headers ?? {},
|
|
5055
|
+
type: "user_action",
|
|
5056
|
+
payload: Object.keys(payload).length > 0 ? payload : void 0,
|
|
5057
|
+
threadId: input.threadId,
|
|
5058
|
+
clientEventId: input.clientEventId,
|
|
5059
|
+
occurredAt: input.occurredAt
|
|
5060
|
+
});
|
|
5061
|
+
}, [copilotkit]);
|
|
5062
|
+
}
|
|
5063
|
+
|
|
5064
|
+
//#endregion
|
|
5065
|
+
//#region src/v2/hooks/use-learn-from-user-action-in-current-thread.tsx
|
|
5066
|
+
/**
|
|
5067
|
+
* Record a user UI interaction against the **current chat's** thread. The
|
|
5068
|
+
* `threadId` is sourced from the surrounding
|
|
5069
|
+
* `<CopilotChatConfigurationProvider>` (the same provider `<CopilotChat>`,
|
|
5070
|
+
* `<CopilotSidebar>`, and friends set up), so callers in a chat-aware
|
|
5071
|
+
* subtree don't need to thread an id through manually.
|
|
5072
|
+
*
|
|
5073
|
+
* Throws on **call** (not on mount) when there is no chat-config provider
|
|
5074
|
+
* in scope — matches the "throw on call when runtimeUrl is missing"
|
|
5075
|
+
* behavior of {@link useLearnFromUserAction}. Mounting the hook in a branch
|
|
5076
|
+
* that never fires is harmless.
|
|
5077
|
+
*
|
|
5078
|
+
* The recorder does NOT accept a `threadId` override. If you need to
|
|
5079
|
+
* record against an explicit thread, use {@link useLearnFromUserAction}
|
|
5080
|
+
* directly — two hooks, two crisp contracts, no mode confusion.
|
|
5081
|
+
*
|
|
5082
|
+
* This hook always uses `config.threadId`, regardless of whether the
|
|
5083
|
+
* surrounding chat config minted it internally or received one from
|
|
5084
|
+
* the caller. Auto-minted threads simply mean the action lands under
|
|
5085
|
+
* a thread the platform never saw — the writer agent still distills
|
|
5086
|
+
* user-action-only threads (it does not require the thread to exist
|
|
5087
|
+
* in `cpki.threads`), so the loop keeps learning.
|
|
5088
|
+
*
|
|
5089
|
+
* @example
|
|
5090
|
+
* ```tsx
|
|
5091
|
+
* import { useLearnFromUserActionInCurrentThread } from "@copilotkit/react-core";
|
|
5092
|
+
*
|
|
5093
|
+
* function SettingsPanel() {
|
|
5094
|
+
* const learnFromUserAction = useLearnFromUserActionInCurrentThread();
|
|
5095
|
+
*
|
|
5096
|
+
* const onRename = (oldName: string, newName: string) => {
|
|
5097
|
+
* void learnFromUserAction({
|
|
5098
|
+
* title: "Renamed project",
|
|
5099
|
+
* data: { previous: { name: oldName }, next: { name: newName } },
|
|
5100
|
+
* });
|
|
5101
|
+
* };
|
|
5102
|
+
*
|
|
5103
|
+
* // ...
|
|
5104
|
+
* }
|
|
5105
|
+
* ```
|
|
5106
|
+
*/
|
|
5107
|
+
function useLearnFromUserActionInCurrentThread() {
|
|
5108
|
+
const config = useCopilotChatConfiguration();
|
|
5109
|
+
const learnFromUserAction = useLearnFromUserAction();
|
|
5110
|
+
return (0, react.useCallback)(async (input) => {
|
|
5111
|
+
const threadId = config?.threadId;
|
|
5112
|
+
if (!threadId) throw new Error("useLearnFromUserActionInCurrentThread: no CopilotChatConfigurationProvider in scope. Wrap the call site in <CopilotChat>, <CopilotSidebar>, or <CopilotChatConfigurationProvider>, or use `useLearnFromUserAction()` and pass `threadId` explicitly.");
|
|
5113
|
+
return learnFromUserAction({
|
|
5114
|
+
...input,
|
|
5115
|
+
threadId
|
|
5116
|
+
});
|
|
5117
|
+
}, [config?.threadId, learnFromUserAction]);
|
|
5118
|
+
}
|
|
5119
|
+
|
|
4814
5120
|
//#endregion
|
|
4815
5121
|
//#region src/v2/hooks/use-attachments.tsx
|
|
4816
5122
|
/**
|
|
@@ -4969,6 +5275,153 @@ function useAttachments({ config }) {
|
|
|
4969
5275
|
};
|
|
4970
5276
|
}
|
|
4971
5277
|
|
|
5278
|
+
//#endregion
|
|
5279
|
+
//#region src/v2/hooks/use-learning-containers.tsx
|
|
5280
|
+
/** The default learning containers value. Matches the backend default. */
|
|
5281
|
+
const DEFAULT_CONTAINERS = ["project"];
|
|
5282
|
+
/**
|
|
5283
|
+
* Declaratively keeps a thread's learning containers in sync by emitting
|
|
5284
|
+
* `set_learning_containers` annotations via the CopilotKit runtime annotate
|
|
5285
|
+
* endpoint (`POST ${runtimeUrl}/annotate`).
|
|
5286
|
+
*
|
|
5287
|
+
* **Emit rules:**
|
|
5288
|
+
* - On mount with `["project"]` (the backend default) → does NOT emit.
|
|
5289
|
+
* Absence of an annotation equals the default, so the round-trip is skipped.
|
|
5290
|
+
* - On mount with any other value → emits immediately.
|
|
5291
|
+
* - On any subsequent content change (including a switch back to
|
|
5292
|
+
* `["project"]`) → emits (a deliberate switch is always recorded).
|
|
5293
|
+
* - On unmount or threadId change → emits a reset to `["project"]`
|
|
5294
|
+
* so the backend is left in a clean state for the next consumer.
|
|
5295
|
+
* Changing `learningContainers` within the same thread does NOT reset the
|
|
5296
|
+
* thread; only the new value is emitted.
|
|
5297
|
+
*
|
|
5298
|
+
* Content-equality is evaluated via `JSON.stringify` so a fresh array literal
|
|
5299
|
+
* with the same items does NOT trigger a redundant emit.
|
|
5300
|
+
*
|
|
5301
|
+
* If `runtimeUrl` is absent, all emits are silently skipped.
|
|
5302
|
+
*
|
|
5303
|
+
* @example
|
|
5304
|
+
* ```tsx
|
|
5305
|
+
* function ThreadPane({ threadId, userScope }: Props) {
|
|
5306
|
+
* useLearningContainers({
|
|
5307
|
+
* threadId,
|
|
5308
|
+
* learningContainers: [userScope],
|
|
5309
|
+
* });
|
|
5310
|
+
* // ...
|
|
5311
|
+
* }
|
|
5312
|
+
* ```
|
|
5313
|
+
*/
|
|
5314
|
+
function useLearningContainers({ threadId, learningContainers }) {
|
|
5315
|
+
const { copilotkit } = useCopilotKit();
|
|
5316
|
+
/**
|
|
5317
|
+
* Tracks the last-synced container list so content-identical rerenders
|
|
5318
|
+
* (fresh array, same values) do not fire a redundant emit.
|
|
5319
|
+
* `null` = nothing synced yet (initial state or after a threadId reset).
|
|
5320
|
+
*/
|
|
5321
|
+
const lastSyncedRef = (0, react.useRef)(null);
|
|
5322
|
+
/** Guards the missing-runtimeUrl warning so it fires at most once per hook instance. */
|
|
5323
|
+
const warnedMissingUrlRef = (0, react.useRef)(false);
|
|
5324
|
+
const runtimeUrlRef = (0, react.useRef)(copilotkit.runtimeUrl);
|
|
5325
|
+
const headersRef = (0, react.useRef)(copilotkit.headers ?? {});
|
|
5326
|
+
runtimeUrlRef.current = copilotkit.runtimeUrl;
|
|
5327
|
+
headersRef.current = copilotkit.headers ?? {};
|
|
5328
|
+
const key = JSON.stringify(learningContainers);
|
|
5329
|
+
const defaultKey = JSON.stringify(DEFAULT_CONTAINERS);
|
|
5330
|
+
(0, react.useEffect)(() => {
|
|
5331
|
+
const runtimeUrl = copilotkit.runtimeUrl;
|
|
5332
|
+
const headers = copilotkit.headers ?? {};
|
|
5333
|
+
/**
|
|
5334
|
+
* Fire-and-forget emit; errors must not surface in render.
|
|
5335
|
+
* Failures are logged as warnings so they are diagnosable without
|
|
5336
|
+
* propagating into the React render cycle.
|
|
5337
|
+
*/
|
|
5338
|
+
const emit = (containers) => {
|
|
5339
|
+
if (!runtimeUrl) {
|
|
5340
|
+
if (!warnedMissingUrlRef.current) {
|
|
5341
|
+
warnedMissingUrlRef.current = true;
|
|
5342
|
+
console.warn("useLearningContainers: runtimeUrl not configured; learning-container sync disabled");
|
|
5343
|
+
}
|
|
5344
|
+
return;
|
|
5345
|
+
}
|
|
5346
|
+
recordAnnotation({
|
|
5347
|
+
runtimeUrl,
|
|
5348
|
+
headers,
|
|
5349
|
+
type: "set_learning_containers",
|
|
5350
|
+
payload: { containers },
|
|
5351
|
+
threadId
|
|
5352
|
+
}).catch((err) => {
|
|
5353
|
+
console.warn("useLearningContainers: failed to record set_learning_containers", err);
|
|
5354
|
+
});
|
|
5355
|
+
};
|
|
5356
|
+
if (lastSyncedRef.current === null) {
|
|
5357
|
+
if (key === defaultKey) {
|
|
5358
|
+
lastSyncedRef.current = learningContainers;
|
|
5359
|
+
return;
|
|
5360
|
+
}
|
|
5361
|
+
emit(learningContainers);
|
|
5362
|
+
lastSyncedRef.current = learningContainers;
|
|
5363
|
+
} else if (key !== JSON.stringify(lastSyncedRef.current)) {
|
|
5364
|
+
emit(learningContainers);
|
|
5365
|
+
lastSyncedRef.current = learningContainers;
|
|
5366
|
+
}
|
|
5367
|
+
}, [threadId, key]);
|
|
5368
|
+
(0, react.useEffect)(() => {
|
|
5369
|
+
const capturedThreadId = threadId;
|
|
5370
|
+
return () => {
|
|
5371
|
+
const capturedRuntimeUrl = runtimeUrlRef.current;
|
|
5372
|
+
const capturedHeaders = headersRef.current;
|
|
5373
|
+
if (capturedRuntimeUrl) recordAnnotation({
|
|
5374
|
+
runtimeUrl: capturedRuntimeUrl,
|
|
5375
|
+
headers: capturedHeaders,
|
|
5376
|
+
type: "set_learning_containers",
|
|
5377
|
+
payload: { containers: DEFAULT_CONTAINERS },
|
|
5378
|
+
threadId: capturedThreadId
|
|
5379
|
+
}).catch((err) => {
|
|
5380
|
+
console.warn("useLearningContainers: failed to record set_learning_containers", err);
|
|
5381
|
+
});
|
|
5382
|
+
lastSyncedRef.current = null;
|
|
5383
|
+
};
|
|
5384
|
+
}, [threadId]);
|
|
5385
|
+
}
|
|
5386
|
+
|
|
5387
|
+
//#endregion
|
|
5388
|
+
//#region src/v2/hooks/use-learning-containers-in-current-thread.tsx
|
|
5389
|
+
/**
|
|
5390
|
+
* Declaratively keeps the **current chat thread's** learning containers in
|
|
5391
|
+
* sync. The `threadId` is sourced from the surrounding
|
|
5392
|
+
* `<CopilotChatConfigurationProvider>` (the same provider `<CopilotChat>`,
|
|
5393
|
+
* `<CopilotSidebar>`, and friends set up), so callers in a chat-aware
|
|
5394
|
+
* subtree don't need to thread an id through manually.
|
|
5395
|
+
*
|
|
5396
|
+
* **Throws on render** when there is no chat-config provider in scope or
|
|
5397
|
+
* when the provider does not yet have an active `threadId`. Mount the hook
|
|
5398
|
+
* inside a subtree that is guaranteed to have a thread context.
|
|
5399
|
+
*
|
|
5400
|
+
* If you need to manage an explicit thread, use {@link useLearningContainers}
|
|
5401
|
+
* directly — two hooks, two crisp contracts, no mode confusion.
|
|
5402
|
+
*
|
|
5403
|
+
* @throws When no `CopilotChatConfigurationProvider` is in scope or when the
|
|
5404
|
+
* active `threadId` is absent/empty.
|
|
5405
|
+
*
|
|
5406
|
+
* @example
|
|
5407
|
+
* ```tsx
|
|
5408
|
+
* function ThreadPanel({ scope }: Props) {
|
|
5409
|
+
* useLearningContainersInCurrentThread({
|
|
5410
|
+
* learningContainers: [scope],
|
|
5411
|
+
* });
|
|
5412
|
+
* // ...
|
|
5413
|
+
* }
|
|
5414
|
+
* ```
|
|
5415
|
+
*/
|
|
5416
|
+
function useLearningContainersInCurrentThread({ learningContainers }) {
|
|
5417
|
+
const threadId = useCopilotChatConfiguration()?.threadId;
|
|
5418
|
+
if (!threadId) throw new Error("useLearningContainersInCurrentThread must be used within a thread context (no active threadId). Wrap the component in <CopilotChat>, <CopilotSidebar>, or <CopilotChatConfigurationProvider>, or use `useLearningContainers()` and pass `threadId` explicitly.");
|
|
5419
|
+
useLearningContainers({
|
|
5420
|
+
threadId,
|
|
5421
|
+
learningContainers
|
|
5422
|
+
});
|
|
5423
|
+
}
|
|
5424
|
+
|
|
4972
5425
|
//#endregion
|
|
4973
5426
|
//#region src/v2/components/chat/CopilotChatToolCallsView.tsx
|
|
4974
5427
|
function CopilotChatToolCallsView({ message, messages = [] }) {
|
|
@@ -5741,33 +6194,170 @@ CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
|
|
|
5741
6194
|
*/
|
|
5742
6195
|
const ScrollElementContext = react.default.createContext(null);
|
|
5743
6196
|
|
|
6197
|
+
//#endregion
|
|
6198
|
+
//#region src/v2/components/intelligence-indicator/IntelligenceIndicatorView.tsx
|
|
6199
|
+
/**
|
|
6200
|
+
* The presentational "CopilotKit Intelligence" face — the default
|
|
6201
|
+
* rendered by the {@link IntelligenceIndicator} brain and the default
|
|
6202
|
+
* value for the `intelligenceIndicator` slot.
|
|
6203
|
+
*
|
|
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).
|
|
6208
|
+
*
|
|
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.
|
|
6222
|
+
*
|
|
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.
|
|
6226
|
+
*
|
|
6227
|
+
* Customize via the `intelligenceIndicator` slot on `CopilotChat`:
|
|
6228
|
+
* a className string restyles the wrapper, a props object tweaks
|
|
6229
|
+
* the default (`{ label }`), and a component replaces it entirely
|
|
6230
|
+
* with full control over visuals and timing.
|
|
6231
|
+
*/
|
|
6232
|
+
function IntelligenceIndicatorView({ message, status, label, className, ...rest }) {
|
|
6233
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6234
|
+
className: (0, tailwind_merge.twMerge)("cpk-intelligence-indicator", className),
|
|
6235
|
+
role: "status",
|
|
6236
|
+
"aria-live": "polite",
|
|
6237
|
+
"data-testid": `cpk-intelligence-indicator-${message.id}`,
|
|
6238
|
+
"data-status": status,
|
|
6239
|
+
title: label,
|
|
6240
|
+
...rest,
|
|
6241
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6242
|
+
className: "cpk-intelligence-indicator__chrome",
|
|
6243
|
+
"aria-hidden": "true"
|
|
6244
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6245
|
+
className: "cpk-intelligence-indicator__content",
|
|
6246
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
6247
|
+
className: "cpk-intelligence-indicator__icon",
|
|
6248
|
+
viewBox: "0 0 24 24",
|
|
6249
|
+
width: "14",
|
|
6250
|
+
height: "14",
|
|
6251
|
+
"aria-hidden": "true",
|
|
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
|
+
})]
|
|
6261
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6262
|
+
className: "cpk-intelligence-indicator__label",
|
|
6263
|
+
children: label
|
|
6264
|
+
})]
|
|
6265
|
+
})]
|
|
6266
|
+
});
|
|
6267
|
+
}
|
|
6268
|
+
|
|
5744
6269
|
//#endregion
|
|
5745
6270
|
//#region src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx
|
|
5746
6271
|
/**
|
|
5747
6272
|
* Grace window before showing the spinner. A matching tool call must
|
|
5748
6273
|
* remain unresolved (no `tool`-role result message in `agent.messages`)
|
|
5749
|
-
* for at least this long before the
|
|
5750
|
-
* history-replay flashes — during
|
|
5751
|
-
*
|
|
5752
|
-
*
|
|
5753
|
-
*
|
|
6274
|
+
* for at least this long before the indicator transitions out of
|
|
6275
|
+
* `hidden`. This filters out history-replay flashes — during
|
|
6276
|
+
* `connectAgent` replay, tool calls and their results arrive
|
|
6277
|
+
* back-to-back in sub-millisecond bursts, so the timer is cancelled
|
|
6278
|
+
* before it fires. Live runs cross the threshold easily because the
|
|
6279
|
+
* tool actually has to execute.
|
|
5754
6280
|
*/
|
|
5755
6281
|
const PENDING_THRESHOLD_MS = 100;
|
|
5756
|
-
/** Hold the checkmark briefly before fading out. */
|
|
5757
|
-
const CHECK_HOLD_MS = 800;
|
|
5758
6282
|
/**
|
|
5759
|
-
*
|
|
5760
|
-
*
|
|
6283
|
+
* Tool-name regex patterns that trigger the indicator. Matches any tool
|
|
6284
|
+
* name *containing* the Intelligence MCP server's canonical tool name, so
|
|
6285
|
+
* both the bare `copilotkit_knowledge_base_shell` and the namespaced
|
|
6286
|
+
* `mcp__<server>__copilotkit_knowledge_base_shell` form (emitted by
|
|
6287
|
+
* `@ag-ui/mcp-middleware`) light up the pill. If we add per-instance
|
|
6288
|
+
* customization later (e.g. a `CopilotKitProvider` prop or a runtime-info
|
|
6289
|
+
* field), this constant becomes the fallback.
|
|
6290
|
+
*/
|
|
6291
|
+
const DEFAULT_TOOL_PATTERNS = [/copilotkit_knowledge_base_shell/];
|
|
6292
|
+
/**
|
|
6293
|
+
* Phase to start in when an indicator first mounts. A turn that is already
|
|
6294
|
+
* complete at mount jumps straight to `finished` — no `hidden` flash, no
|
|
6295
|
+
* spinner blip — which is what makes scrolled-back / replayed history render
|
|
6296
|
+
* its indicators directly in the finished state.
|
|
6297
|
+
*
|
|
6298
|
+
* Pure and timing-free on purpose: the grace window ({@link
|
|
6299
|
+
* PENDING_THRESHOLD_MS}) only controls *when* the live transition is applied;
|
|
6300
|
+
* these functions decide *what* it resolves to, so the decision can be unit
|
|
6301
|
+
* tested deterministically without any timers.
|
|
5761
6302
|
*/
|
|
5762
|
-
|
|
6303
|
+
function initialIndicatorPhase(turnComplete) {
|
|
6304
|
+
return turnComplete ? "finished" : "hidden";
|
|
6305
|
+
}
|
|
5763
6306
|
/**
|
|
5764
|
-
*
|
|
5765
|
-
*
|
|
5766
|
-
*
|
|
5767
|
-
*
|
|
6307
|
+
* Phase the grace window resolves to once it elapses:
|
|
6308
|
+
* - completed turn → `finished` (replay-flash suppression: a tool whose
|
|
6309
|
+
* result lands within the window skips the spinner entirely),
|
|
6310
|
+
* - a still-pending matching tool call → `spinner`,
|
|
6311
|
+
* - otherwise stay `hidden` (the matching tool call hasn't landed yet).
|
|
5768
6312
|
*/
|
|
5769
|
-
|
|
6313
|
+
function resolveGracePhase(turnComplete, hasPending) {
|
|
6314
|
+
if (turnComplete) return "finished";
|
|
6315
|
+
if (hasPending) return "spinner";
|
|
6316
|
+
return "hidden";
|
|
6317
|
+
}
|
|
5770
6318
|
const isMatchingToolCallName = (name) => typeof name === "string" && DEFAULT_TOOL_PATTERNS.some((p) => p.test(name));
|
|
6319
|
+
const messageHasMatchingToolCall = (m) => {
|
|
6320
|
+
if (m.role !== "assistant") return false;
|
|
6321
|
+
return (Array.isArray(m.toolCalls) ? m.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name));
|
|
6322
|
+
};
|
|
6323
|
+
/**
|
|
6324
|
+
* Stable turn id for the messages that precede the first user message (a turn
|
|
6325
|
+
* with no opening user message of its own). Used as the React key so the
|
|
6326
|
+
* indicator for that turn never collides with a real user-message id.
|
|
6327
|
+
*/
|
|
6328
|
+
const INTELLIGENCE_TURN_HEAD = "__cpk_turn_head__";
|
|
6329
|
+
/**
|
|
6330
|
+
* Map each Intelligence-using turn to its anchor message — the FIRST bash-using
|
|
6331
|
+
* assistant message of the turn — and a stable turn id (the id of the user
|
|
6332
|
+
* message that opened the turn, or {@link INTELLIGENCE_TURN_HEAD} for the
|
|
6333
|
+
* pre-first-user turn). Returns `Map<anchorMessageId, turnId>`.
|
|
6334
|
+
*
|
|
6335
|
+
* Anchoring to the FIRST (not last) bash-using message keeps the indicator
|
|
6336
|
+
* fixed in place for the whole turn: later bash steps don't reposition it, so
|
|
6337
|
+
* the spinner never abruptly jumps mid-turn (bug 1). `CopilotChatMessageView`
|
|
6338
|
+
* emits exactly one `IntelligenceIndicator` per entry, keyed by the turn id and
|
|
6339
|
+
* positioned at the anchor; the per-turn key also lets every past turn keep its
|
|
6340
|
+
* own indicator in scroll-back.
|
|
6341
|
+
*/
|
|
6342
|
+
function getIntelligenceTurnAnchors(messages) {
|
|
6343
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
6344
|
+
let turnId = INTELLIGENCE_TURN_HEAD;
|
|
6345
|
+
let anchorId = null;
|
|
6346
|
+
const commit = () => {
|
|
6347
|
+
if (anchorId !== null) anchors.set(anchorId, turnId);
|
|
6348
|
+
anchorId = null;
|
|
6349
|
+
};
|
|
6350
|
+
for (const m of messages) {
|
|
6351
|
+
if (m.role === "user") {
|
|
6352
|
+
commit();
|
|
6353
|
+
turnId = m.id;
|
|
6354
|
+
continue;
|
|
6355
|
+
}
|
|
6356
|
+
if (anchorId === null && messageHasMatchingToolCall(m)) anchorId = m.id;
|
|
6357
|
+
}
|
|
6358
|
+
commit();
|
|
6359
|
+
return anchors;
|
|
6360
|
+
}
|
|
5771
6361
|
/**
|
|
5772
6362
|
* "Tool-call-like" messages do NOT count as a real follow-up: tool
|
|
5773
6363
|
* result messages, assistant messages that carry tool calls, and
|
|
@@ -5786,45 +6376,51 @@ const isToolCallLikeMessage = (m) => {
|
|
|
5786
6376
|
return false;
|
|
5787
6377
|
};
|
|
5788
6378
|
/**
|
|
5789
|
-
* The "Using CopilotKit Intelligence"
|
|
5790
|
-
* `CopilotChatMessageView`
|
|
5791
|
-
*
|
|
5792
|
-
*
|
|
5793
|
-
*
|
|
6379
|
+
* The "Using CopilotKit Intelligence" indicator brain. Auto-mounted by
|
|
6380
|
+
* `CopilotChatMessageView` — once per Intelligence-using turn, at that
|
|
6381
|
+
* turn's anchor message and keyed by the turn id (see
|
|
6382
|
+
* {@link getIntelligenceTurnAnchors}). Callers do not register this
|
|
6383
|
+
* themselves. It owns the run subscription and the phase machine and
|
|
6384
|
+
* renders its swappable face via the `intelligenceIndicator` slot.
|
|
6385
|
+
*
|
|
6386
|
+
* Placement (which message anchors the turn) is decided by the view, so
|
|
6387
|
+
* this component does not self-gate its own placement; it only derives
|
|
6388
|
+
* in-progress/finished for the turn it was mounted on.
|
|
5794
6389
|
*
|
|
5795
6390
|
* Render gates (all must hold):
|
|
5796
6391
|
* 1. `copilotkit.intelligence !== undefined`
|
|
5797
|
-
* 2. The message is an assistant message with at least one
|
|
5798
|
-
* whose name matches {@link DEFAULT_TOOL_PATTERNS}
|
|
5799
|
-
* 3. The
|
|
5800
|
-
*
|
|
5801
|
-
*
|
|
5802
|
-
*
|
|
5803
|
-
*
|
|
5804
|
-
*
|
|
6392
|
+
* 2. The (anchor) message is an assistant message with at least one
|
|
6393
|
+
* tool call whose name matches {@link DEFAULT_TOOL_PATTERNS}.
|
|
6394
|
+
* 3. The phase machine is past `hidden`.
|
|
6395
|
+
*
|
|
6396
|
+
* Because the view keys each indicator by its turn id, the instance moves
|
|
6397
|
+
* with the anchor across a hand-off (no remount, no spinner restart), and
|
|
6398
|
+
* every prior Intelligence-using turn keeps its own persistent indicator
|
|
6399
|
+
* in chat history.
|
|
5805
6400
|
*
|
|
5806
6401
|
* Phase machine (per-instance, all timers local):
|
|
5807
|
-
* - Starts in `
|
|
5808
|
-
*
|
|
6402
|
+
* - Starts in `hidden`, unless the message mounts onto an
|
|
6403
|
+
* already-completed turn (no pending work, agent stopped or a
|
|
6404
|
+
* real follow-up already present), in which case the lazy
|
|
6405
|
+
* `useState` initializer starts directly in `finished`. This is
|
|
6406
|
+
* what avoids a "hidden flash" on history replay.
|
|
6407
|
+
* - `hidden → spinner` once a matching tool call has been pending
|
|
5809
6408
|
* (no `tool`-role result with a matching `toolCallId`) for
|
|
5810
6409
|
* {@link PENDING_THRESHOLD_MS}. Replay flashes (tool call + result
|
|
5811
6410
|
* in the same tick) never cross this threshold.
|
|
5812
|
-
* - `
|
|
6411
|
+
* - `hidden → finished` if after the grace window the turn is
|
|
6412
|
+
* already complete (no pending work AND
|
|
6413
|
+
* `sawRealFollowup || !agent.isRunning`). Handles very fast tools
|
|
6414
|
+
* whose result lands within the grace window.
|
|
6415
|
+
* - `spinner → finished` as soon as EITHER `agent.isRunning` flips
|
|
5813
6416
|
* false OR a non-tool-call-like message appears later in
|
|
5814
|
-
* `agent.messages` (i.e. the agent
|
|
5815
|
-
*
|
|
5816
|
-
* - `
|
|
5817
|
-
*
|
|
5818
|
-
*
|
|
5819
|
-
* Once `hidden`, the phase is sticky — a finished pill never re-spawns
|
|
5820
|
-
* on the same message. New runs mount fresh indicator instances on
|
|
5821
|
-
* their own assistant messages.
|
|
5822
|
-
*
|
|
5823
|
-
* The "exactly one pill at a time" guarantee is structural: only one
|
|
5824
|
-
* message satisfies the latest-matching-assistant gate at any moment.
|
|
6417
|
+
* `agent.messages` (i.e. the agent produced a "real" follow-up —
|
|
6418
|
+
* prose answer or a new user turn).
|
|
6419
|
+
* - `finished` is terminal: the indicator settles into its
|
|
6420
|
+
* persistent tag form and stays mounted.
|
|
5825
6421
|
*/
|
|
5826
6422
|
function IntelligenceIndicator(props) {
|
|
5827
|
-
const { message, agentId, label = "
|
|
6423
|
+
const { message, agentId, label = "CopilotKit Intelligence", intelligenceIndicator } = props;
|
|
5828
6424
|
const { copilotkit } = useCopilotKit();
|
|
5829
6425
|
const config = useCopilotChatConfiguration();
|
|
5830
6426
|
const { agent } = useAgent({
|
|
@@ -5844,90 +6440,95 @@ function IntelligenceIndicator(props) {
|
|
|
5844
6440
|
for (const m of agent.messages) if (m.role === "tool" && m.toolCallId) resolved.add(m.toolCallId);
|
|
5845
6441
|
return matchingToolCallIds.some((id) => !resolved.has(id));
|
|
5846
6442
|
}, [matchingToolCallIds, agent.messages]);
|
|
5847
|
-
const
|
|
6443
|
+
const turnComplete = (0, react.useMemo)(() => {
|
|
5848
6444
|
const idx = agent.messages.findIndex((m) => m.id === message.id);
|
|
5849
6445
|
if (idx < 0) return false;
|
|
5850
6446
|
for (let i = idx + 1; i < agent.messages.length; i += 1) if (!isToolCallLikeMessage(agent.messages[i])) return true;
|
|
5851
6447
|
return false;
|
|
5852
|
-
}, [agent.messages, message.id]);
|
|
5853
|
-
const [phase, setPhase] = (0, react.useState)(
|
|
6448
|
+
}, [agent.messages, message.id]) || !agent.isRunning;
|
|
6449
|
+
const [phase, setPhase] = (0, react.useState)(() => initialIndicatorPhase(turnComplete));
|
|
5854
6450
|
(0, react.useEffect)(() => {
|
|
5855
|
-
if (phase !== "
|
|
5856
|
-
|
|
5857
|
-
|
|
6451
|
+
if (phase !== "hidden") return void 0;
|
|
6452
|
+
const t = setTimeout(() => {
|
|
6453
|
+
setPhase(resolveGracePhase(turnComplete, hasPending));
|
|
6454
|
+
}, PENDING_THRESHOLD_MS);
|
|
5858
6455
|
return () => clearTimeout(t);
|
|
5859
|
-
}, [phase, hasPending]);
|
|
5860
|
-
(0, react.useEffect)(() => {
|
|
5861
|
-
if (phase !== "spinner") return void 0;
|
|
5862
|
-
if (!agent.isRunning || sawRealFollowup) setPhase("check");
|
|
5863
6456
|
}, [
|
|
5864
6457
|
phase,
|
|
5865
|
-
|
|
5866
|
-
|
|
6458
|
+
hasPending,
|
|
6459
|
+
turnComplete
|
|
5867
6460
|
]);
|
|
5868
6461
|
(0, react.useEffect)(() => {
|
|
5869
|
-
if (phase !== "
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
}, [phase]);
|
|
5873
|
-
(0, react.useEffect)(() => {
|
|
5874
|
-
if (phase !== "fading") return void 0;
|
|
5875
|
-
const t = setTimeout(() => setPhase("hidden"), FADE_OUT_ANIMATION_MS);
|
|
5876
|
-
return () => clearTimeout(t);
|
|
5877
|
-
}, [phase]);
|
|
6462
|
+
if (phase !== "spinner") return void 0;
|
|
6463
|
+
if (turnComplete) setPhase("finished");
|
|
6464
|
+
}, [phase, turnComplete]);
|
|
5878
6465
|
if (copilotkit.intelligence === void 0) return null;
|
|
5879
6466
|
if (!config) return null;
|
|
5880
|
-
if (phase === "
|
|
6467
|
+
if (phase === "hidden") return null;
|
|
5881
6468
|
if (message.role !== "assistant") return null;
|
|
5882
|
-
if (!(
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
if ((Array.isArray(m.toolCalls) ? m.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name))) {
|
|
5888
|
-
latestMatchingAssistantId = m.id;
|
|
5889
|
-
break;
|
|
5890
|
-
}
|
|
5891
|
-
}
|
|
5892
|
-
if (latestMatchingAssistantId !== message.id) return null;
|
|
5893
|
-
const showSpinner = phase === "spinner";
|
|
5894
|
-
const isFading = phase === "fading";
|
|
5895
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5896
|
-
className: "cpk-intelligence-pill" + (isFading ? " cpk-intelligence-pill--fading" : ""),
|
|
5897
|
-
role: "status",
|
|
5898
|
-
"aria-live": "polite",
|
|
5899
|
-
"aria-hidden": isFading || void 0,
|
|
5900
|
-
"data-testid": `cpk-intelligence-pill-${message.id}`,
|
|
5901
|
-
title: label,
|
|
5902
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
5903
|
-
className: "cpk-intelligence-pill__icon",
|
|
5904
|
-
viewBox: "0 0 24 24",
|
|
5905
|
-
width: "14",
|
|
5906
|
-
height: "14",
|
|
5907
|
-
"aria-hidden": "true",
|
|
5908
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
5909
|
-
cx: "12",
|
|
5910
|
-
cy: "12",
|
|
5911
|
-
r: "9",
|
|
5912
|
-
fill: "none",
|
|
5913
|
-
strokeWidth: "2.5",
|
|
5914
|
-
strokeLinecap: "round",
|
|
5915
|
-
className: "cpk-intelligence-pill__ring" + (showSpinner ? "" : " cpk-intelligence-pill__ring--done")
|
|
5916
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
5917
|
-
d: "M8 12.5l3 3 5-6",
|
|
5918
|
-
fill: "none",
|
|
5919
|
-
strokeWidth: "2.5",
|
|
5920
|
-
strokeLinecap: "round",
|
|
5921
|
-
strokeLinejoin: "round",
|
|
5922
|
-
className: "cpk-intelligence-pill__check" + (showSpinner ? "" : " cpk-intelligence-pill__check--shown")
|
|
5923
|
-
})]
|
|
5924
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
6469
|
+
if (!messageHasMatchingToolCall(message)) return null;
|
|
6470
|
+
return renderSlot(intelligenceIndicator, IntelligenceIndicatorView, {
|
|
6471
|
+
message,
|
|
6472
|
+
status: phase === "finished" ? "finished" : "in-progress",
|
|
6473
|
+
label
|
|
5925
6474
|
});
|
|
5926
6475
|
}
|
|
5927
6476
|
|
|
5928
6477
|
//#endregion
|
|
5929
6478
|
//#region src/v2/components/chat/CopilotChatMessageView.tsx
|
|
5930
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
|
+
/**
|
|
5931
6532
|
* Resolves a slot value into a { Component, slotProps } pair, handling the three
|
|
5932
6533
|
* slot forms: a component type, a className string, or a partial-props object.
|
|
5933
6534
|
*/
|
|
@@ -6074,7 +6675,7 @@ function deduplicateMessages(messages) {
|
|
|
6074
6675
|
return [...acc.values()];
|
|
6075
6676
|
}
|
|
6076
6677
|
const VIRTUALIZE_THRESHOLD = 50;
|
|
6077
|
-
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
|
|
6678
|
+
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, children, className, ...props }) {
|
|
6078
6679
|
const renderCustomMessage = useRenderCustomMessages();
|
|
6079
6680
|
const { renderActivityMessage } = useRenderActivityMessage();
|
|
6080
6681
|
const { copilotkit } = useCopilotKit();
|
|
@@ -6106,6 +6707,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
6106
6707
|
return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
|
|
6107
6708
|
};
|
|
6108
6709
|
const deduplicatedMessages = (0, react.useMemo)(() => deduplicateMessages(messages), [messages]);
|
|
6710
|
+
const rowRenderKeys = (0, react.useMemo)(() => buildRowRenderKeys(deduplicatedMessages), [deduplicatedMessages]);
|
|
6109
6711
|
if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Merged ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
|
|
6110
6712
|
const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
|
|
6111
6713
|
const { Component: UserComponent, slotProps: userSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
|
|
@@ -6132,48 +6734,52 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
6132
6734
|
if (!shouldVirtualize || !deduplicatedMessages.length) return;
|
|
6133
6735
|
virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
|
|
6134
6736
|
}, [shouldVirtualize, firstMessageId]);
|
|
6737
|
+
const intelligenceTurnAnchors = (0, react.useMemo)(() => getIntelligenceTurnAnchors(deduplicatedMessages), [deduplicatedMessages]);
|
|
6135
6738
|
const renderMessageBlock = (message) => {
|
|
6136
6739
|
const elements = [];
|
|
6137
6740
|
const stateSnapshot = getStateSnapshotForMessage(message.id);
|
|
6741
|
+
const rowKey = rowRenderKeys.get(message.id) ?? message.id;
|
|
6138
6742
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
6139
6743
|
message,
|
|
6140
6744
|
position: "before",
|
|
6141
6745
|
renderCustomMessage,
|
|
6142
6746
|
stateSnapshot
|
|
6143
|
-
}, `${
|
|
6747
|
+
}, `${rowKey}-custom-before`));
|
|
6144
6748
|
if (message.role === "assistant") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedAssistantMessage, {
|
|
6145
6749
|
message,
|
|
6146
6750
|
messages,
|
|
6147
6751
|
isRunning,
|
|
6148
6752
|
AssistantMessageComponent: AssistantComponent,
|
|
6149
6753
|
slotProps: assistantSlotProps
|
|
6150
|
-
},
|
|
6754
|
+
}, rowKey));
|
|
6151
6755
|
else if (message.role === "user") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedUserMessage, {
|
|
6152
6756
|
message,
|
|
6153
6757
|
UserMessageComponent: UserComponent,
|
|
6154
6758
|
slotProps: userSlotProps
|
|
6155
|
-
},
|
|
6759
|
+
}, rowKey));
|
|
6156
6760
|
else if (message.role === "activity") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
|
|
6157
6761
|
message,
|
|
6158
6762
|
renderActivityMessage
|
|
6159
|
-
},
|
|
6763
|
+
}, rowKey));
|
|
6160
6764
|
else if (message.role === "reasoning") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
|
|
6161
6765
|
message,
|
|
6162
6766
|
messages,
|
|
6163
6767
|
isRunning,
|
|
6164
6768
|
ReasoningMessageComponent: ReasoningComponent,
|
|
6165
6769
|
slotProps: reasoningSlotProps
|
|
6166
|
-
},
|
|
6770
|
+
}, rowKey));
|
|
6167
6771
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
6168
6772
|
message,
|
|
6169
6773
|
position: "after",
|
|
6170
6774
|
renderCustomMessage,
|
|
6171
6775
|
stateSnapshot
|
|
6172
|
-
}, `${
|
|
6173
|
-
|
|
6776
|
+
}, `${rowKey}-custom-after`));
|
|
6777
|
+
const intelligenceTurnId = intelligenceTurnAnchors.get(message.id);
|
|
6778
|
+
if (intelligenceTurnId !== void 0) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntelligenceIndicator, {
|
|
6174
6779
|
message,
|
|
6175
|
-
agentId: config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID
|
|
6176
|
-
|
|
6780
|
+
agentId: config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID,
|
|
6781
|
+
intelligenceIndicator
|
|
6782
|
+
}, `intelligence-${intelligenceTurnId}`));
|
|
6177
6783
|
return elements.filter(Boolean);
|
|
6178
6784
|
};
|
|
6179
6785
|
const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
|
|
@@ -6213,7 +6819,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
6213
6819
|
transform: `translateY(${virtualItem.start}px)`
|
|
6214
6820
|
},
|
|
6215
6821
|
children: renderMessageBlock(message)
|
|
6216
|
-
}, message.id);
|
|
6822
|
+
}, rowRenderKeys.get(message.id) ?? message.id);
|
|
6217
6823
|
})
|
|
6218
6824
|
}) : messageElements,
|
|
6219
6825
|
interruptElement,
|
|
@@ -6610,7 +7216,7 @@ function DropOverlay() {
|
|
|
6610
7216
|
})
|
|
6611
7217
|
});
|
|
6612
7218
|
}
|
|
6613
|
-
function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, isConnecting = false, hasExplicitThreadId = false, disclaimer, children, className, ...props }) {
|
|
7219
|
+
function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, isConnecting = false, hasExplicitThreadId = false, disclaimer, intelligenceIndicator, children, className, ...props }) {
|
|
6614
7220
|
const [inputContainerEl, setInputContainerEl] = (0, react.useState)(null);
|
|
6615
7221
|
const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
|
|
6616
7222
|
const [isResizing, setIsResizing] = (0, react.useState)(false);
|
|
@@ -6647,7 +7253,8 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
6647
7253
|
}, [inputContainerEl]);
|
|
6648
7254
|
const BoundMessageView = renderSlot(messageView, CopilotChatMessageView, {
|
|
6649
7255
|
messages,
|
|
6650
|
-
isRunning
|
|
7256
|
+
isRunning,
|
|
7257
|
+
intelligenceIndicator
|
|
6651
7258
|
});
|
|
6652
7259
|
const BoundInput = renderSlot(input, CopilotChatInput_default, {
|
|
6653
7260
|
onSubmitMessage,
|
|
@@ -7192,6 +7799,13 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7192
7799
|
resolvedAgentId,
|
|
7193
7800
|
hasExplicitThreadId
|
|
7194
7801
|
]);
|
|
7802
|
+
const waitForActiveRunToSettle = (0, react.useCallback)(async () => {
|
|
7803
|
+
if (agent.isRunning && (0, _copilotkit_core.isRunCompletionAware)(agent) && agent.activeRunCompletionPromise) try {
|
|
7804
|
+
await agent.activeRunCompletionPromise;
|
|
7805
|
+
} catch (error) {
|
|
7806
|
+
console.error("CopilotChat: in-flight run rejected while queuing send", error);
|
|
7807
|
+
}
|
|
7808
|
+
}, [agent]);
|
|
7195
7809
|
const onSubmitInput = (0, react.useCallback)(async (value) => {
|
|
7196
7810
|
if (selectedAttachmentsRef.current.some((a) => a.status === "uploading")) {
|
|
7197
7811
|
console.error("[CopilotKit] Cannot send while attachments are uploading (pre-await guard)");
|
|
@@ -7199,11 +7813,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7199
7813
|
return;
|
|
7200
7814
|
}
|
|
7201
7815
|
setInputValue("");
|
|
7202
|
-
|
|
7203
|
-
await agent.activeRunCompletionPromise;
|
|
7204
|
-
} catch (error) {
|
|
7205
|
-
console.error("CopilotChat: in-flight run rejected while queuing send", error);
|
|
7206
|
-
}
|
|
7816
|
+
await waitForActiveRunToSettle();
|
|
7207
7817
|
if (selectedAttachmentsRef.current.some((a) => a.status === "uploading")) {
|
|
7208
7818
|
console.error("[CopilotKit] Cannot send while attachments are uploading (post-await re-check)");
|
|
7209
7819
|
setTranscriptionError("Cannot send while attachments are uploading.");
|
|
@@ -7240,8 +7850,13 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7240
7850
|
} catch (error) {
|
|
7241
7851
|
console.error("CopilotChat: runAgent failed", error);
|
|
7242
7852
|
}
|
|
7243
|
-
}, [
|
|
7853
|
+
}, [
|
|
7854
|
+
agent,
|
|
7855
|
+
consumeAttachments,
|
|
7856
|
+
waitForActiveRunToSettle
|
|
7857
|
+
]);
|
|
7244
7858
|
const handleSelectSuggestion = (0, react.useCallback)(async (suggestion) => {
|
|
7859
|
+
await waitForActiveRunToSettle();
|
|
7245
7860
|
agent.addMessage({
|
|
7246
7861
|
id: (0, _copilotkit_shared.randomUUID)(),
|
|
7247
7862
|
role: "user",
|
|
@@ -7252,7 +7867,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7252
7867
|
} catch (error) {
|
|
7253
7868
|
console.error("CopilotChat: runAgent failed after selecting suggestion", error);
|
|
7254
7869
|
}
|
|
7255
|
-
}, [agent]);
|
|
7870
|
+
}, [agent, waitForActiveRunToSettle]);
|
|
7256
7871
|
const stopCurrentRun = (0, react.useCallback)(() => {
|
|
7257
7872
|
try {
|
|
7258
7873
|
copilotkit.stopAgent({ agent });
|
|
@@ -8921,7 +9536,7 @@ const getErrorActions = (error) => {
|
|
|
8921
9536
|
} };
|
|
8922
9537
|
case _copilotkit_shared.CopilotKitErrorCode.UPGRADE_REQUIRED_ERROR: return { primary: {
|
|
8923
9538
|
label: "Upgrade",
|
|
8924
|
-
onClick: () => window.open("https://
|
|
9539
|
+
onClick: () => window.open("https://dashboard.operations.copilotkit.ai", "_blank", "noopener,noreferrer")
|
|
8925
9540
|
} };
|
|
8926
9541
|
default: return;
|
|
8927
9542
|
}
|
|
@@ -10320,12 +10935,24 @@ Object.defineProperty(exports, 'DefaultOpenIcon', {
|
|
|
10320
10935
|
return DefaultOpenIcon;
|
|
10321
10936
|
}
|
|
10322
10937
|
});
|
|
10938
|
+
Object.defineProperty(exports, 'INTELLIGENCE_TURN_HEAD', {
|
|
10939
|
+
enumerable: true,
|
|
10940
|
+
get: function () {
|
|
10941
|
+
return INTELLIGENCE_TURN_HEAD;
|
|
10942
|
+
}
|
|
10943
|
+
});
|
|
10323
10944
|
Object.defineProperty(exports, 'IntelligenceIndicator', {
|
|
10324
10945
|
enumerable: true,
|
|
10325
10946
|
get: function () {
|
|
10326
10947
|
return IntelligenceIndicator;
|
|
10327
10948
|
}
|
|
10328
10949
|
});
|
|
10950
|
+
Object.defineProperty(exports, 'IntelligenceIndicatorView', {
|
|
10951
|
+
enumerable: true,
|
|
10952
|
+
get: function () {
|
|
10953
|
+
return IntelligenceIndicatorView;
|
|
10954
|
+
}
|
|
10955
|
+
});
|
|
10329
10956
|
Object.defineProperty(exports, 'MCPAppsActivityContentSchema', {
|
|
10330
10957
|
enumerable: true,
|
|
10331
10958
|
get: function () {
|
|
@@ -10398,6 +11025,12 @@ Object.defineProperty(exports, 'defineToolCallRenderer', {
|
|
|
10398
11025
|
return defineToolCallRenderer;
|
|
10399
11026
|
}
|
|
10400
11027
|
});
|
|
11028
|
+
Object.defineProperty(exports, 'getIntelligenceTurnAnchors', {
|
|
11029
|
+
enumerable: true,
|
|
11030
|
+
get: function () {
|
|
11031
|
+
return getIntelligenceTurnAnchors;
|
|
11032
|
+
}
|
|
11033
|
+
});
|
|
10401
11034
|
Object.defineProperty(exports, 'shouldShowDevConsole', {
|
|
10402
11035
|
enumerable: true,
|
|
10403
11036
|
get: function () {
|
|
@@ -10500,6 +11133,30 @@ Object.defineProperty(exports, 'useInterrupt', {
|
|
|
10500
11133
|
return useInterrupt;
|
|
10501
11134
|
}
|
|
10502
11135
|
});
|
|
11136
|
+
Object.defineProperty(exports, 'useLearnFromUserAction', {
|
|
11137
|
+
enumerable: true,
|
|
11138
|
+
get: function () {
|
|
11139
|
+
return useLearnFromUserAction;
|
|
11140
|
+
}
|
|
11141
|
+
});
|
|
11142
|
+
Object.defineProperty(exports, 'useLearnFromUserActionInCurrentThread', {
|
|
11143
|
+
enumerable: true,
|
|
11144
|
+
get: function () {
|
|
11145
|
+
return useLearnFromUserActionInCurrentThread;
|
|
11146
|
+
}
|
|
11147
|
+
});
|
|
11148
|
+
Object.defineProperty(exports, 'useLearningContainers', {
|
|
11149
|
+
enumerable: true,
|
|
11150
|
+
get: function () {
|
|
11151
|
+
return useLearningContainers;
|
|
11152
|
+
}
|
|
11153
|
+
});
|
|
11154
|
+
Object.defineProperty(exports, 'useLearningContainersInCurrentThread', {
|
|
11155
|
+
enumerable: true,
|
|
11156
|
+
get: function () {
|
|
11157
|
+
return useLearningContainersInCurrentThread;
|
|
11158
|
+
}
|
|
11159
|
+
});
|
|
10503
11160
|
Object.defineProperty(exports, 'useRenderActivityMessage', {
|
|
10504
11161
|
enumerable: true,
|
|
10505
11162
|
get: function () {
|
|
@@ -10554,4 +11211,4 @@ Object.defineProperty(exports, 'useToast', {
|
|
|
10554
11211
|
return useToast;
|
|
10555
11212
|
}
|
|
10556
11213
|
});
|
|
10557
|
-
//# sourceMappingURL=copilotkit-
|
|
11214
|
+
//# sourceMappingURL=copilotkit-CWGR3Ict.cjs.map
|