@copilotkit/react-core 1.56.5-canary.1778052912 → 1.56.5-canary.1778068503

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/react-core",
3
- "version": "1.56.5-canary.1778052912",
3
+ "version": "1.56.5-canary.1778068503",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "ai",
@@ -73,11 +73,11 @@
73
73
  "untruncate-json": "^0.0.1",
74
74
  "use-stick-to-bottom": "^1.1.1",
75
75
  "zod-to-json-schema": "^3.24.5",
76
- "@copilotkit/a2ui-renderer": "1.56.5-canary.1778052912",
77
- "@copilotkit/web-inspector": "1.56.5-canary.1778052912",
78
- "@copilotkit/shared": "1.56.5-canary.1778052912",
79
- "@copilotkit/runtime-client-gql": "1.56.5-canary.1778052912",
80
- "@copilotkit/core": "1.56.5-canary.1778052912"
76
+ "@copilotkit/a2ui-renderer": "1.56.5-canary.1778068503",
77
+ "@copilotkit/core": "1.56.5-canary.1778068503",
78
+ "@copilotkit/runtime-client-gql": "1.56.5-canary.1778068503",
79
+ "@copilotkit/shared": "1.56.5-canary.1778068503",
80
+ "@copilotkit/web-inspector": "1.56.5-canary.1778068503"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@tailwindcss/cli": "^4.1.11",
@@ -8,11 +8,12 @@ import React, {
8
8
  } from "react";
9
9
  import { useVirtualizer } from "@tanstack/react-virtual";
10
10
  import { ScrollElementContext } from "./scroll-element-context";
11
- import { WithSlots, renderSlot, isReactComponentType } from "../../lib/slots";
11
+ import type { WithSlots } from "../../lib/slots";
12
+ import { renderSlot, isReactComponentType } from "../../lib/slots";
12
13
  import CopilotChatAssistantMessage from "./CopilotChatAssistantMessage";
13
14
  import CopilotChatUserMessage from "./CopilotChatUserMessage";
14
15
  import CopilotChatReasoningMessage from "./CopilotChatReasoningMessage";
15
- import {
16
+ import type {
16
17
  ActivityMessage,
17
18
  AssistantMessage,
18
19
  Message,
@@ -283,63 +284,25 @@ const MemoizedCustomMessage = React.memo(
283
284
  message: Message;
284
285
  position: "before" | "after";
285
286
  }) => React.ReactElement | null;
286
- stateSnapshot: unknown;
287
- numberOfMessagesInRun: number | undefined;
288
- isInLatestRun: boolean | undefined;
289
- isRunning: boolean;
287
+ stateSnapshot?: unknown;
290
288
  }) {
291
289
  return renderCustomMessage({ message, position });
292
290
  },
293
291
  (prevProps, nextProps) => {
294
- // Skip the re-render unless an input that an authored renderer can
295
- // observe has changed. Each branch returns false to invalidate.
292
+ // Only re-render if the message or position changed
296
293
  if (prevProps.message.id !== nextProps.message.id) return false;
297
294
  if (prevProps.position !== nextProps.position) return false;
295
+ // Compare message content - for assistant messages this is a string, for others may differ
298
296
  if (prevProps.message.content !== nextProps.message.content) return false;
299
297
  if (prevProps.message.role !== nextProps.message.role) return false;
300
- // State snapshot custom renderers may read it. Deep-compare via
301
- // JSON.stringify (state-manager already deep-copies on each call, so
302
- // reference equality would never hit anyway).
298
+ // Compare state snapshot - custom renderers may depend on state
303
299
  if (
304
300
  JSON.stringify(prevProps.stateSnapshot) !==
305
301
  JSON.stringify(nextProps.stateSnapshot)
306
302
  )
307
303
  return false;
308
- // Re-render when this message's run grows. The runId-to-message
309
- // association is unidirectional in StateManager (only set, never
310
- // revoked except via clearAgentState/clearThreadState which wipe
311
- // wholesale), so in practice this fires only on growth as new messages
312
- // stream into the run. Slots in completed runs are unaffected — their
313
- // count is stable once the run has finished.
314
- if (prevProps.numberOfMessagesInRun !== nextProps.numberOfMessagesInRun)
315
- return false;
316
- // Re-render when this message's run stops being the latest run on the
317
- // thread — e.g. a new run starts and previously-completed messages need
318
- // to drop "is this the latest activity?" indicators. Only the slots
319
- // belonging to the run that just lost its "latest" status are
320
- // invalidated; the new run's slots and any older runs are unaffected.
321
- if (prevProps.isInLatestRun !== nextProps.isInLatestRun) return false;
322
- // Re-render when isRunning flips, but only for slots known to belong
323
- // to the latest run (`isInLatestRun === true`). Slots whose run is
324
- // older or whose run-id hasn't been recorded yet (undefined) are not
325
- // invalidated on isRunning changes — this preserves the perf guarantee
326
- // that completed messages skip re-renders during streaming.
327
- //
328
- // Renderers that gate solely on `agent.isRunning` and attach to a
329
- // message added before any run started (e.g. a user message inserted
330
- // before runAgent fires) won't observe the false→true transition for
331
- // the first run. In practice this is rare; the canonical pattern is
332
- // to gate on the slot's own runId membership (see
333
- // intelligence-indicator/__tests__/IntelligenceIndicator.e2e.test.tsx
334
- // for the recommended renderer shape).
335
- if (
336
- nextProps.isInLatestRun &&
337
- prevProps.isRunning !== nextProps.isRunning
338
- )
339
- return false;
340
- // Note: renderCustomMessage's reference is intentionally not compared —
341
- // it's a closure that changes frequently. The inputs above are the only
342
- // observable signals.
304
+ // Note: We don't compare renderCustomMessage function reference because it changes
305
+ // frequently. The message and state comparison is sufficient to determine if a re-render is needed.
343
306
  return true;
344
307
  },
345
308
  );
@@ -453,48 +416,6 @@ export function CopilotChatMessageView({
453
416
  return () => subscription.unsubscribe();
454
417
  }, [copilotkit]);
455
418
 
456
- // Build per-run metadata once per render, not once per message. The two
457
- // memo inputs MemoizedCustomMessage cares about (numberOfMessagesInRun and
458
- // isInLatestRun) are both derived from the same scan of `messages` →
459
- // `getRunIdForMessage`, so consolidating here turns an O(n²) per-render
460
- // pass (one O(n) helper call per message) into a single O(n) pass.
461
- //
462
- // Re-fires whenever messages, the active agent/thread, or copilotkit
463
- // changes. The internal messageToRun map can update without these props
464
- // changing (a runId association lands one tick after the message is
465
- // appended) — in that case the next external trigger refreshes. Note
466
- // the `MemoizedCustomMessage` gate at the `isInLatestRun &&` check is
467
- // strict-truthy — slots whose runId is still undefined during that
468
- // pre-association window will not re-render on isRunning flips. The
469
- // canonical pattern (see the IntelligenceIndicator e2e test) is to gate
470
- // the renderer on the slot's own runId membership rather than `isRunning`
471
- // alone, so the limitation doesn't bite typical use.
472
- const runMetadata = useMemo(() => {
473
- if (!config) return null;
474
- const runIdByMessageId = new Map<string, string>();
475
- for (const msg of messages) {
476
- const r = copilotkit.getRunIdForMessage(
477
- config.agentId,
478
- config.threadId,
479
- msg.id,
480
- );
481
- if (r) runIdByMessageId.set(msg.id, r);
482
- }
483
- const countByRunId = new Map<string, number>();
484
- for (const r of runIdByMessageId.values()) {
485
- countByRunId.set(r, (countByRunId.get(r) ?? 0) + 1);
486
- }
487
- let latestRunId: string | undefined;
488
- for (let i = messages.length - 1; i >= 0; i--) {
489
- const r = runIdByMessageId.get(messages[i]!.id);
490
- if (r) {
491
- latestRunId = r;
492
- break;
493
- }
494
- }
495
- return { runIdByMessageId, countByRunId, latestRunId };
496
- }, [messages, config?.agentId, config?.threadId, copilotkit]);
497
-
498
419
  // Helper to get state snapshot for a message (used for memoization)
499
420
  const getStateSnapshotForMessage = (messageId: string): unknown => {
500
421
  if (!config) return undefined;
@@ -619,14 +540,6 @@ export function CopilotChatMessageView({
619
540
  const renderMessageBlock = (message: Message): React.ReactElement[] => {
620
541
  const elements: (React.ReactElement | null | undefined)[] = [];
621
542
  const stateSnapshot = getStateSnapshotForMessage(message.id);
622
- const messageRunId = runMetadata?.runIdByMessageId.get(message.id);
623
- const numberOfMessagesInRun = messageRunId
624
- ? runMetadata?.countByRunId.get(messageRunId)
625
- : undefined;
626
- const isInLatestRun =
627
- messageRunId === undefined
628
- ? undefined
629
- : messageRunId === runMetadata?.latestRunId;
630
543
 
631
544
  if (renderCustomMessage) {
632
545
  elements.push(
@@ -636,9 +549,6 @@ export function CopilotChatMessageView({
636
549
  position="before"
637
550
  renderCustomMessage={renderCustomMessage}
638
551
  stateSnapshot={stateSnapshot}
639
- numberOfMessagesInRun={numberOfMessagesInRun}
640
- isInLatestRun={isInLatestRun}
641
- isRunning={isRunning}
642
552
  />,
643
553
  );
644
554
  }
@@ -692,24 +602,18 @@ export function CopilotChatMessageView({
692
602
  position="after"
693
603
  renderCustomMessage={renderCustomMessage}
694
604
  stateSnapshot={stateSnapshot}
695
- numberOfMessagesInRun={numberOfMessagesInRun}
696
- isInLatestRun={isInLatestRun}
697
- isRunning={isRunning}
698
605
  />,
699
606
  );
700
607
  }
701
608
 
702
609
  // Auto-mount the IntelligenceIndicator on assistant message slots
703
610
  // when the runtime is in intelligence mode. The component self-gates
704
- // further (last in run, latest run, matching tool call) so only one
705
- // pill renders at a time — but mounting it only for assistant
706
- // messages avoids spinning up a `useAgent` subscription, a 200 ms
707
- // poll interval, and four effects on every user/reasoning/activity
708
- // slot just to have it return null at the role gate.
709
- if (
710
- copilotkit.intelligence !== undefined &&
711
- message.role === "assistant"
712
- ) {
611
+ // further (latest matching-assistant slot, pending tool-call grace
612
+ // window) so only one pill renders at a time — mounting only for
613
+ // assistant messages avoids the per-slot `useAgent` subscription
614
+ // and four effects on user/reasoning/activity slots that would just
615
+ // return null at the role gate anyway.
616
+ if (copilotkit.intelligence !== undefined && message.role === "assistant") {
713
617
  elements.push(
714
618
  <IntelligenceIndicator
715
619
  key={`${message.id}-intelligence`}
@@ -313,13 +313,13 @@ describe("CopilotChat activity message rendering", () => {
313
313
  //
314
314
  // The trap this catches is a refactor where the renderer pipeline keys
315
315
  // on something other than `config.agentId` (e.g. for proxied
316
- // registrations, accidentally keying on `proxy.remoteAgentId`). The
316
+ // registrations, accidentally keying on `proxy.runtimeAgentId`). The
317
317
  // assertion shape — "two agents in the registry, the renderer must
318
318
  // receive the one matching config.agentId" — is generic to that bug
319
319
  // class. The proxy-routing-specific behavior (registering a
320
- // ProxiedCopilotRuntimeAgent with `remoteAgentId !== agentId` and
320
+ // ProxiedCopilotRuntimeAgent with `runtimeAgentId !== agentId` and
321
321
  // verifying `getAgent(agentId)` returns the proxy, not the agent at
322
- // remoteAgentId) is covered by `core-register-proxied-agent.test.ts`
322
+ // runtimeAgentId) is covered by `core-register-proxied-agent.test.ts`
323
323
  // at the registry level.
324
324
  const localAgent = new MockStepwiseAgent();
325
325
  localAgent.agentId = "chat-1";