@copilotkit/react-core 1.59.3 → 1.59.5

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.
@@ -234,9 +234,9 @@ const useCopilotChatConfiguration = () => {
234
234
 
235
235
  //#endregion
236
236
  //#region src/v2/lib/utils.ts
237
- const twMerge$7 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
237
+ const twMerge$8 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
238
238
  function cn(...inputs) {
239
- return twMerge$7((0, clsx.clsx)(inputs));
239
+ return twMerge$8((0, clsx.clsx)(inputs));
240
240
  }
241
241
 
242
242
  //#endregion
@@ -2954,210 +2954,110 @@ const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props
2954
2954
  };
2955
2955
 
2956
2956
  //#endregion
2957
- //#region src/v2/a2ui/A2UIMessageRenderer.tsx
2957
+ //#region src/v2/a2ui/A2UIRecoveryStates.tsx
2958
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).
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 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
- }
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
- * Renders the A2UI surface, or an error message if processing failed.
3050
- * Must be a child of A2UIProvider to access the error state.
3051
- */
3052
- function A2UISurfaceOrError({ surfaceId }) {
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
- * Processes A2UI operations into the provider's message processor.
3065
- * Must be a child of A2UIProvider to access the actions context.
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 SurfaceMessageProcessor({ surfaceId, operations }) {
3068
- const { processMessages, getSurface } = (0, _copilotkit_a2ui_renderer.useA2UIActions)();
3069
- const lastHashRef = (0, react.useRef)("");
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
- const hash = JSON.stringify(operations);
3072
- if (hash === lastHashRef.current) return;
3073
- lastHashRef.current = hash;
3074
- processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
3075
- }, [
3076
- processMessages,
3077
- getSurface,
3078
- surfaceId,
3079
- operations
3080
- ]);
3081
- return null;
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
- * Default loading component shown while an A2UI surface is generating.
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:flex cpk:flex-col cpk:gap-3 cpk:rounded-xl cpk:border cpk:border-gray-100 cpk:bg-gray-50/50 cpk:p-5",
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.jsxs)("div", {
3093
- className: "cpk:flex cpk:items-center cpk:gap-2",
3094
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
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:flex cpk:flex-col cpk:gap-2",
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)("style", { children: `
3117
- @keyframes cpk-a2ui-pulse {
3118
- 0%, 100% { opacity: 0.4; }
3119
- 50% { opacity: 1; }
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
- /**
3134
- * Tool name used by the dynamic A2UI generation secondary LLM.
3135
- * This renderer is auto-registered when A2UI is enabled.
3136
- */
3137
- const RENDER_A2UI_TOOL_NAME = "render_a2ui";
3138
3054
  /**
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", ... })`.
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.
3144
3058
  */
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: "Building interface"
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
+ }
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
+ }
3430
3539
  /**
3431
- * Registers the built-in `render_a2ui` tool call renderer via the props-based
3432
- * `setRenderToolCalls` mechanism (not `useRenderTool`).
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.
3433
3571
  *
3434
- * This ensures user-registered `useRenderTool({ name: "render_a2ui", ... })`
3435
- * hooks automatically override the built-in, since the merge logic in
3436
- * react-core.ts gives hook-based entries priority over prop-based entries.
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: ({ status, args: parameters }) => {
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,174 @@ 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
+ * Single-element three-stage design:
6205
+ * 1. **In-progress.** Glassmorphism pill chrome around a 270° arc icon
6206
+ * and the label. The arc has a single continuous visible stroke
6207
+ * (one `stroke-dasharray` dash + one gap, summing to the path
6208
+ * length) and the whole SVG rotates — so the viewer sees one
6209
+ * C-shaped arc spinning around the visual center.
6210
+ * 2. **Icon morph (~250 ms).** On status flip the single icon path
6211
+ * interpolates from the arc to a checkmark via CSS `d:` while the
6212
+ * dashed stroke transitions to solid (filling in the gap that was
6213
+ * the spinner's open portion). The SVG rotation animation is
6214
+ * removed; the snap back to identity is masked by the simultaneous
6215
+ * shape change. Chrome and text stay at full opacity throughout.
6216
+ * 3. **Settle (~400 ms, starts at +250 ms).** Chrome (background,
6217
+ * border, shadow, backdrop-blur) fades to zero opacity. The label
6218
+ * and icon stroke color transitions from saturated purple to a
6219
+ * true-neutral gray at 0.8 alpha — no hue cast, reads as "settled
6220
+ * history metadata." The label simultaneously skews to ~10° (a
6221
+ * transform-based italic feel that interpolates smoothly with the
6222
+ * color, rather than the discrete `font-style: italic` snap that
6223
+ * would cause a layout pop). The label text stays put — only its
6224
+ * color and slant change — so there is no "bump" where the brand
6225
+ * text disappears and reappears.
6226
+ *
6227
+ * Hard sequence: stage 3 has a 250 ms transition-delay so it waits
6228
+ * for stage 2 to finish. Total settle time ~650 ms in production.
6229
+ *
6230
+ * Both shapes are 3-segment cubic Bézier paths with matched command
6231
+ * structure (one `M` plus three `C`s), which is what makes the d
6232
+ * morph interpolate as a continuous shape change rather than snapping.
6233
+ *
6234
+ * The label is identical in both states (default "CopilotKit
6235
+ * Intelligence"). The static check icon carries the "done" semantic;
6236
+ * the color + slant transition does the "settle" work without needing
6237
+ * any wording change.
6238
+ *
6239
+ * Customize via the `intelligenceIndicator` slot on `CopilotChat`:
6240
+ * a className string restyles the wrapper, a props object tweaks
6241
+ * the default (`{ label }`), and a component replaces it entirely
6242
+ * with full control over visuals and timing.
6243
+ */
6244
+ function IntelligenceIndicatorView({ message, status, label, className, ...rest }) {
6245
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6246
+ className: (0, tailwind_merge.twMerge)("cpk-intelligence-indicator", className),
6247
+ role: "status",
6248
+ "aria-live": "polite",
6249
+ "data-testid": `cpk-intelligence-indicator-${message.id}`,
6250
+ "data-status": status,
6251
+ title: label,
6252
+ ...rest,
6253
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6254
+ className: "cpk-intelligence-indicator__chrome",
6255
+ "aria-hidden": "true"
6256
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6257
+ className: "cpk-intelligence-indicator__content",
6258
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
6259
+ className: "cpk-intelligence-indicator__icon",
6260
+ viewBox: "0 0 24 24",
6261
+ width: "14",
6262
+ height: "14",
6263
+ "aria-hidden": "true",
6264
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { className: "cpk-intelligence-indicator__icon-path" })
6265
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6266
+ className: "cpk-intelligence-indicator__label",
6267
+ children: label
6268
+ })]
6269
+ })]
6270
+ });
6271
+ }
6272
+
5744
6273
  //#endregion
5745
6274
  //#region src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx
5746
6275
  /**
5747
6276
  * Grace window before showing the spinner. A matching tool call must
5748
6277
  * remain unresolved (no `tool`-role result message in `agent.messages`)
5749
- * for at least this long before the pill appears. This filters out
5750
- * history-replay flashes — during `connectAgent` replay, tool calls and
5751
- * their results arrive back-to-back in sub-millisecond bursts, so the
5752
- * timer is cancelled before it fires. Live runs cross the threshold
5753
- * easily because the tool actually has to execute.
6278
+ * for at least this long before the indicator transitions out of
6279
+ * `hidden`. This filters out history-replay flashes — during
6280
+ * `connectAgent` replay, tool calls and their results arrive
6281
+ * back-to-back in sub-millisecond bursts, so the timer is cancelled
6282
+ * before it fires. Live runs cross the threshold easily because the
6283
+ * tool actually has to execute.
5754
6284
  */
5755
6285
  const PENDING_THRESHOLD_MS = 100;
5756
- /** Hold the checkmark briefly before fading out. */
5757
- const CHECK_HOLD_MS = 800;
5758
6286
  /**
5759
- * Duration of the fade-out animation. Must match
5760
- * `cpk-intelligence-pill-fade-out` keyframes in `v2/styles/globals.css`.
6287
+ * Tool-name regex patterns that trigger the indicator. Matches any tool
6288
+ * name *containing* the Intelligence MCP server's canonical tool name, so
6289
+ * both the bare `copilotkit_knowledge_base_shell` and the namespaced
6290
+ * `mcp__<server>__copilotkit_knowledge_base_shell` form (emitted by
6291
+ * `@ag-ui/mcp-middleware`) light up the pill. If we add per-instance
6292
+ * customization later (e.g. a `CopilotKitProvider` prop or a runtime-info
6293
+ * field), this constant becomes the fallback.
5761
6294
  */
5762
- const FADE_OUT_ANIMATION_MS = 480;
6295
+ const DEFAULT_TOOL_PATTERNS = [/copilotkit_knowledge_base_shell/];
5763
6296
  /**
5764
- * Tool-name regex patterns that trigger the indicator. Currently
5765
- * hardcoded to the Intelligence MCP server's canonical tool name. If
5766
- * we add per-instance customization later (e.g. a `CopilotKitProvider`
5767
- * prop or a runtime-info field), this constant becomes the fallback.
6297
+ * Phase to start in when an indicator first mounts. A turn that is already
6298
+ * complete at mount jumps straight to `finished` no `hidden` flash, no
6299
+ * spinner blip — which is what makes scrolled-back / replayed history render
6300
+ * its indicators directly in the finished state.
6301
+ *
6302
+ * Pure and timing-free on purpose: the grace window ({@link
6303
+ * PENDING_THRESHOLD_MS}) only controls *when* the live transition is applied;
6304
+ * these functions decide *what* it resolves to, so the decision can be unit
6305
+ * tested deterministically without any timers.
5768
6306
  */
5769
- const DEFAULT_TOOL_PATTERNS = [/^copilotkit_knowledge_base_shell$/];
6307
+ function initialIndicatorPhase(turnComplete) {
6308
+ return turnComplete ? "finished" : "hidden";
6309
+ }
6310
+ /**
6311
+ * Phase the grace window resolves to once it elapses:
6312
+ * - completed turn → `finished` (replay-flash suppression: a tool whose
6313
+ * result lands within the window skips the spinner entirely),
6314
+ * - a still-pending matching tool call → `spinner`,
6315
+ * - otherwise stay `hidden` (the matching tool call hasn't landed yet).
6316
+ */
6317
+ function resolveGracePhase(turnComplete, hasPending) {
6318
+ if (turnComplete) return "finished";
6319
+ if (hasPending) return "spinner";
6320
+ return "hidden";
6321
+ }
5770
6322
  const isMatchingToolCallName = (name) => typeof name === "string" && DEFAULT_TOOL_PATTERNS.some((p) => p.test(name));
6323
+ const messageHasMatchingToolCall = (m) => {
6324
+ if (m.role !== "assistant") return false;
6325
+ return (Array.isArray(m.toolCalls) ? m.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name));
6326
+ };
6327
+ /**
6328
+ * Stable turn id for the messages that precede the first user message (a turn
6329
+ * with no opening user message of its own). Used as the React key so the
6330
+ * indicator for that turn never collides with a real user-message id.
6331
+ */
6332
+ const INTELLIGENCE_TURN_HEAD = "__cpk_turn_head__";
6333
+ /**
6334
+ * Map each Intelligence-using turn to its anchor message — the FIRST bash-using
6335
+ * assistant message of the turn — and a stable turn id (the id of the user
6336
+ * message that opened the turn, or {@link INTELLIGENCE_TURN_HEAD} for the
6337
+ * pre-first-user turn). Returns `Map<anchorMessageId, turnId>`.
6338
+ *
6339
+ * Anchoring to the FIRST (not last) bash-using message keeps the indicator
6340
+ * fixed in place for the whole turn: later bash steps don't reposition it, so
6341
+ * the spinner never abruptly jumps mid-turn (bug 1). `CopilotChatMessageView`
6342
+ * emits exactly one `IntelligenceIndicator` per entry, keyed by the turn id and
6343
+ * positioned at the anchor; the per-turn key also lets every past turn keep its
6344
+ * own indicator in scroll-back.
6345
+ */
6346
+ function getIntelligenceTurnAnchors(messages) {
6347
+ const anchors = /* @__PURE__ */ new Map();
6348
+ let turnId = INTELLIGENCE_TURN_HEAD;
6349
+ let anchorId = null;
6350
+ const commit = () => {
6351
+ if (anchorId !== null) anchors.set(anchorId, turnId);
6352
+ anchorId = null;
6353
+ };
6354
+ for (const m of messages) {
6355
+ if (m.role === "user") {
6356
+ commit();
6357
+ turnId = m.id;
6358
+ continue;
6359
+ }
6360
+ if (anchorId === null && messageHasMatchingToolCall(m)) anchorId = m.id;
6361
+ }
6362
+ commit();
6363
+ return anchors;
6364
+ }
5771
6365
  /**
5772
6366
  * "Tool-call-like" messages do NOT count as a real follow-up: tool
5773
6367
  * result messages, assistant messages that carry tool calls, and
@@ -5786,45 +6380,51 @@ const isToolCallLikeMessage = (m) => {
5786
6380
  return false;
5787
6381
  };
5788
6382
  /**
5789
- * The "Using CopilotKit Intelligence" pill. Auto-mounted by
5790
- * `CopilotChatMessageView` for every message slot when
5791
- * `copilotkit.intelligence` is configured callers do not register
5792
- * this themselves. Self-gates so only the canonical message renders a
5793
- * pill.
6383
+ * The "Using CopilotKit Intelligence" indicator brain. Auto-mounted by
6384
+ * `CopilotChatMessageView` once per Intelligence-using turn, at that
6385
+ * turn's anchor message and keyed by the turn id (see
6386
+ * {@link getIntelligenceTurnAnchors}). Callers do not register this
6387
+ * themselves. It owns the run subscription and the phase machine and
6388
+ * renders its swappable face via the `intelligenceIndicator` slot.
6389
+ *
6390
+ * Placement (which message anchors the turn) is decided by the view, so
6391
+ * this component does not self-gate its own placement; it only derives
6392
+ * in-progress/finished for the turn it was mounted on.
5794
6393
  *
5795
6394
  * Render gates (all must hold):
5796
6395
  * 1. `copilotkit.intelligence !== undefined`
5797
- * 2. The message is an assistant message with at least one tool call
5798
- * whose name matches {@link DEFAULT_TOOL_PATTERNS}
5799
- * 3. The message is the *latest* such matching-assistant message in
5800
- * `agent.messages` — tool-result messages and prose-only assistant
5801
- * messages don't invalidate the slot, so the pill stays
5802
- * continuously through a multi-step tool chain.
5803
- * 4. The phase machine is past `idle` (the pending-grace timer fired)
5804
- * and not yet `hidden`.
6396
+ * 2. The (anchor) message is an assistant message with at least one
6397
+ * tool call whose name matches {@link DEFAULT_TOOL_PATTERNS}.
6398
+ * 3. The phase machine is past `hidden`.
6399
+ *
6400
+ * Because the view keys each indicator by its turn id, the instance moves
6401
+ * with the anchor across a hand-off (no remount, no spinner restart), and
6402
+ * every prior Intelligence-using turn keeps its own persistent indicator
6403
+ * in chat history.
5805
6404
  *
5806
6405
  * Phase machine (per-instance, all timers local):
5807
- * - Starts in `idle` nothing rendered.
5808
- * - `idle spinner` once a matching tool call has been pending
6406
+ * - Starts in `hidden`, unless the message mounts onto an
6407
+ * already-completed turn (no pending work, agent stopped or a
6408
+ * real follow-up already present), in which case the lazy
6409
+ * `useState` initializer starts directly in `finished`. This is
6410
+ * what avoids a "hidden flash" on history replay.
6411
+ * - `hidden → spinner` once a matching tool call has been pending
5809
6412
  * (no `tool`-role result with a matching `toolCallId`) for
5810
6413
  * {@link PENDING_THRESHOLD_MS}. Replay flashes (tool call + result
5811
6414
  * in the same tick) never cross this threshold.
5812
- * - `spinnercheck` as soon as EITHER `agent.isRunning` flips
6415
+ * - `hiddenfinished` if after the grace window the turn is
6416
+ * already complete (no pending work AND
6417
+ * `sawRealFollowup || !agent.isRunning`). Handles very fast tools
6418
+ * whose result lands within the grace window.
6419
+ * - `spinner → finished` as soon as EITHER `agent.isRunning` flips
5813
6420
  * false OR a non-tool-call-like message appears later in
5814
- * `agent.messages` (i.e. the agent has produced a "real"
5815
- * follow-up — prose answer or a new user turn).
5816
- * - `check fading` after {@link CHECK_HOLD_MS}.
5817
- * - `fading hidden` after {@link FADE_OUT_ANIMATION_MS}.
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.
6421
+ * `agent.messages` (i.e. the agent produced a "real" follow-up —
6422
+ * prose answer or a new user turn).
6423
+ * - `finished` is terminal: the indicator settles into its
6424
+ * persistent tag form and stays mounted.
5825
6425
  */
5826
6426
  function IntelligenceIndicator(props) {
5827
- const { message, agentId, label = "Using CopilotKit Intelligence" } = props;
6427
+ const { message, agentId, label = "CopilotKit Intelligence", intelligenceIndicator } = props;
5828
6428
  const { copilotkit } = useCopilotKit();
5829
6429
  const config = useCopilotChatConfiguration();
5830
6430
  const { agent } = useAgent({
@@ -5844,84 +6444,37 @@ function IntelligenceIndicator(props) {
5844
6444
  for (const m of agent.messages) if (m.role === "tool" && m.toolCallId) resolved.add(m.toolCallId);
5845
6445
  return matchingToolCallIds.some((id) => !resolved.has(id));
5846
6446
  }, [matchingToolCallIds, agent.messages]);
5847
- const sawRealFollowup = (0, react.useMemo)(() => {
6447
+ const turnComplete = (0, react.useMemo)(() => {
5848
6448
  const idx = agent.messages.findIndex((m) => m.id === message.id);
5849
6449
  if (idx < 0) return false;
5850
6450
  for (let i = idx + 1; i < agent.messages.length; i += 1) if (!isToolCallLikeMessage(agent.messages[i])) return true;
5851
6451
  return false;
5852
- }, [agent.messages, message.id]);
5853
- const [phase, setPhase] = (0, react.useState)("idle");
6452
+ }, [agent.messages, message.id]) || !agent.isRunning;
6453
+ const [phase, setPhase] = (0, react.useState)(() => initialIndicatorPhase(turnComplete));
5854
6454
  (0, react.useEffect)(() => {
5855
- if (phase !== "idle") return void 0;
5856
- if (!hasPending) return void 0;
5857
- const t = setTimeout(() => setPhase("spinner"), PENDING_THRESHOLD_MS);
6455
+ if (phase !== "hidden") return void 0;
6456
+ const t = setTimeout(() => {
6457
+ setPhase(resolveGracePhase(turnComplete, hasPending));
6458
+ }, PENDING_THRESHOLD_MS);
5858
6459
  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
6460
  }, [
5864
6461
  phase,
5865
- agent.isRunning,
5866
- sawRealFollowup
6462
+ hasPending,
6463
+ turnComplete
5867
6464
  ]);
5868
6465
  (0, react.useEffect)(() => {
5869
- if (phase !== "check") return void 0;
5870
- const t = setTimeout(() => setPhase("fading"), CHECK_HOLD_MS);
5871
- return () => clearTimeout(t);
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]);
6466
+ if (phase !== "spinner") return void 0;
6467
+ if (turnComplete) setPhase("finished");
6468
+ }, [phase, turnComplete]);
5878
6469
  if (copilotkit.intelligence === void 0) return null;
5879
6470
  if (!config) return null;
5880
- if (phase === "idle" || phase === "hidden") return null;
6471
+ if (phase === "hidden") return null;
5881
6472
  if (message.role !== "assistant") return null;
5882
- if (!(Array.isArray(message.toolCalls) ? message.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name))) return null;
5883
- let latestMatchingAssistantId;
5884
- for (let i = agent.messages.length - 1; i >= 0; i -= 1) {
5885
- const m = agent.messages[i];
5886
- if (m.role !== "assistant") continue;
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 })]
6473
+ if (!messageHasMatchingToolCall(message)) return null;
6474
+ return renderSlot(intelligenceIndicator, IntelligenceIndicatorView, {
6475
+ message,
6476
+ status: phase === "finished" ? "finished" : "in-progress",
6477
+ label
5925
6478
  });
5926
6479
  }
5927
6480
 
@@ -6074,7 +6627,7 @@ function deduplicateMessages(messages) {
6074
6627
  return [...acc.values()];
6075
6628
  }
6076
6629
  const VIRTUALIZE_THRESHOLD = 50;
6077
- function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
6630
+ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, children, className, ...props }) {
6078
6631
  const renderCustomMessage = useRenderCustomMessages();
6079
6632
  const { renderActivityMessage } = useRenderActivityMessage();
6080
6633
  const { copilotkit } = useCopilotKit();
@@ -6132,6 +6685,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
6132
6685
  if (!shouldVirtualize || !deduplicatedMessages.length) return;
6133
6686
  virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
6134
6687
  }, [shouldVirtualize, firstMessageId]);
6688
+ const intelligenceTurnAnchors = (0, react.useMemo)(() => getIntelligenceTurnAnchors(deduplicatedMessages), [deduplicatedMessages]);
6135
6689
  const renderMessageBlock = (message) => {
6136
6690
  const elements = [];
6137
6691
  const stateSnapshot = getStateSnapshotForMessage(message.id);
@@ -6170,10 +6724,12 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
6170
6724
  renderCustomMessage,
6171
6725
  stateSnapshot
6172
6726
  }, `${message.id}-custom-after`));
6173
- if (copilotkit.intelligence !== void 0 && message.role === "assistant") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntelligenceIndicator, {
6727
+ const intelligenceTurnId = intelligenceTurnAnchors.get(message.id);
6728
+ if (intelligenceTurnId !== void 0) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntelligenceIndicator, {
6174
6729
  message,
6175
- agentId: config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID
6176
- }, `${message.id}-intelligence`));
6730
+ agentId: config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID,
6731
+ intelligenceIndicator
6732
+ }, `intelligence-${intelligenceTurnId}`));
6177
6733
  return elements.filter(Boolean);
6178
6734
  };
6179
6735
  const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
@@ -6610,7 +7166,7 @@ function DropOverlay() {
6610
7166
  })
6611
7167
  });
6612
7168
  }
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 }) {
7169
+ 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
7170
  const [inputContainerEl, setInputContainerEl] = (0, react.useState)(null);
6615
7171
  const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
6616
7172
  const [isResizing, setIsResizing] = (0, react.useState)(false);
@@ -6647,7 +7203,8 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6647
7203
  }, [inputContainerEl]);
6648
7204
  const BoundMessageView = renderSlot(messageView, CopilotChatMessageView, {
6649
7205
  messages,
6650
- isRunning
7206
+ isRunning,
7207
+ intelligenceIndicator
6651
7208
  });
6652
7209
  const BoundInput = renderSlot(input, CopilotChatInput_default, {
6653
7210
  onSubmitMessage,
@@ -7192,6 +7749,13 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7192
7749
  resolvedAgentId,
7193
7750
  hasExplicitThreadId
7194
7751
  ]);
7752
+ const waitForActiveRunToSettle = (0, react.useCallback)(async () => {
7753
+ if (agent.isRunning && (0, _copilotkit_core.isRunCompletionAware)(agent) && agent.activeRunCompletionPromise) try {
7754
+ await agent.activeRunCompletionPromise;
7755
+ } catch (error) {
7756
+ console.error("CopilotChat: in-flight run rejected while queuing send", error);
7757
+ }
7758
+ }, [agent]);
7195
7759
  const onSubmitInput = (0, react.useCallback)(async (value) => {
7196
7760
  if (selectedAttachmentsRef.current.some((a) => a.status === "uploading")) {
7197
7761
  console.error("[CopilotKit] Cannot send while attachments are uploading (pre-await guard)");
@@ -7199,11 +7763,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7199
7763
  return;
7200
7764
  }
7201
7765
  setInputValue("");
7202
- if (agent.isRunning && "activeRunCompletionPromise" in agent && agent.activeRunCompletionPromise) try {
7203
- await agent.activeRunCompletionPromise;
7204
- } catch (error) {
7205
- console.error("CopilotChat: in-flight run rejected while queuing send", error);
7206
- }
7766
+ await waitForActiveRunToSettle();
7207
7767
  if (selectedAttachmentsRef.current.some((a) => a.status === "uploading")) {
7208
7768
  console.error("[CopilotKit] Cannot send while attachments are uploading (post-await re-check)");
7209
7769
  setTranscriptionError("Cannot send while attachments are uploading.");
@@ -7240,8 +7800,13 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7240
7800
  } catch (error) {
7241
7801
  console.error("CopilotChat: runAgent failed", error);
7242
7802
  }
7243
- }, [agent, consumeAttachments]);
7803
+ }, [
7804
+ agent,
7805
+ consumeAttachments,
7806
+ waitForActiveRunToSettle
7807
+ ]);
7244
7808
  const handleSelectSuggestion = (0, react.useCallback)(async (suggestion) => {
7809
+ await waitForActiveRunToSettle();
7245
7810
  agent.addMessage({
7246
7811
  id: (0, _copilotkit_shared.randomUUID)(),
7247
7812
  role: "user",
@@ -7252,7 +7817,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7252
7817
  } catch (error) {
7253
7818
  console.error("CopilotChat: runAgent failed after selecting suggestion", error);
7254
7819
  }
7255
- }, [agent]);
7820
+ }, [agent, waitForActiveRunToSettle]);
7256
7821
  const stopCurrentRun = (0, react.useCallback)(() => {
7257
7822
  try {
7258
7823
  copilotkit.stopAgent({ agent });
@@ -10320,12 +10885,24 @@ Object.defineProperty(exports, 'DefaultOpenIcon', {
10320
10885
  return DefaultOpenIcon;
10321
10886
  }
10322
10887
  });
10888
+ Object.defineProperty(exports, 'INTELLIGENCE_TURN_HEAD', {
10889
+ enumerable: true,
10890
+ get: function () {
10891
+ return INTELLIGENCE_TURN_HEAD;
10892
+ }
10893
+ });
10323
10894
  Object.defineProperty(exports, 'IntelligenceIndicator', {
10324
10895
  enumerable: true,
10325
10896
  get: function () {
10326
10897
  return IntelligenceIndicator;
10327
10898
  }
10328
10899
  });
10900
+ Object.defineProperty(exports, 'IntelligenceIndicatorView', {
10901
+ enumerable: true,
10902
+ get: function () {
10903
+ return IntelligenceIndicatorView;
10904
+ }
10905
+ });
10329
10906
  Object.defineProperty(exports, 'MCPAppsActivityContentSchema', {
10330
10907
  enumerable: true,
10331
10908
  get: function () {
@@ -10398,6 +10975,12 @@ Object.defineProperty(exports, 'defineToolCallRenderer', {
10398
10975
  return defineToolCallRenderer;
10399
10976
  }
10400
10977
  });
10978
+ Object.defineProperty(exports, 'getIntelligenceTurnAnchors', {
10979
+ enumerable: true,
10980
+ get: function () {
10981
+ return getIntelligenceTurnAnchors;
10982
+ }
10983
+ });
10401
10984
  Object.defineProperty(exports, 'shouldShowDevConsole', {
10402
10985
  enumerable: true,
10403
10986
  get: function () {
@@ -10500,6 +11083,30 @@ Object.defineProperty(exports, 'useInterrupt', {
10500
11083
  return useInterrupt;
10501
11084
  }
10502
11085
  });
11086
+ Object.defineProperty(exports, 'useLearnFromUserAction', {
11087
+ enumerable: true,
11088
+ get: function () {
11089
+ return useLearnFromUserAction;
11090
+ }
11091
+ });
11092
+ Object.defineProperty(exports, 'useLearnFromUserActionInCurrentThread', {
11093
+ enumerable: true,
11094
+ get: function () {
11095
+ return useLearnFromUserActionInCurrentThread;
11096
+ }
11097
+ });
11098
+ Object.defineProperty(exports, 'useLearningContainers', {
11099
+ enumerable: true,
11100
+ get: function () {
11101
+ return useLearningContainers;
11102
+ }
11103
+ });
11104
+ Object.defineProperty(exports, 'useLearningContainersInCurrentThread', {
11105
+ enumerable: true,
11106
+ get: function () {
11107
+ return useLearningContainersInCurrentThread;
11108
+ }
11109
+ });
10503
11110
  Object.defineProperty(exports, 'useRenderActivityMessage', {
10504
11111
  enumerable: true,
10505
11112
  get: function () {
@@ -10554,4 +11161,4 @@ Object.defineProperty(exports, 'useToast', {
10554
11161
  return useToast;
10555
11162
  }
10556
11163
  });
10557
- //# sourceMappingURL=copilotkit-DhONbYmz.cjs.map
11164
+ //# sourceMappingURL=copilotkit-CFfEVdV4.cjs.map