@copilotkit/vue 2.0.0-rc.26 → 2.0.0-rc.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.mjs +2 -2
  3. package/dist/use-render-activity-message-C7b4K-TN.cjs +113 -0
  4. package/dist/use-render-activity-message-C7b4K-TN.cjs.map +1 -0
  5. package/dist/{use-render-activity-message-BVZr0Z-G.js → use-render-activity-message-Dpqx4DE8.js} +5686 -5983
  6. package/dist/use-render-activity-message-Dpqx4DE8.js.map +1 -0
  7. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +3 -6
  8. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
  9. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  10. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
  11. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts.map +1 -1
  12. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +1 -16
  13. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  14. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  15. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  16. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  17. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  18. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
  19. package/dist/v2/components/chat/CopilotThreadsDrawer.vue.d.ts.map +1 -1
  20. package/dist/v2/components/chat/message-feedback.d.ts +20 -0
  21. package/dist/v2/components/chat/message-feedback.d.ts.map +1 -1
  22. package/dist/v2/hooks/use-configure-suggestions.d.ts.map +1 -1
  23. package/dist/v2/hooks/use-default-render-tool.d.ts +2 -7
  24. package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
  25. package/dist/v2/hooks/use-frontend-tool.d.ts +2 -2
  26. package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -1
  27. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  28. package/dist/v2/hooks/use-suggestions.d.ts.map +1 -1
  29. package/dist/v2/hooks/use-threads.d.ts +6 -15
  30. package/dist/v2/hooks/use-threads.d.ts.map +1 -1
  31. package/dist/v2/index.cjs +1 -1
  32. package/dist/v2/index.mjs +1 -1
  33. package/dist/v2/lib/vue-core.d.ts +16 -2
  34. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  35. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  36. package/package.json +8 -8
  37. package/src/__tests__/build-entries.test.ts +3 -1
  38. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +4 -19
  39. package/src/v2/components/chat/CopilotChat.vue +50 -218
  40. package/src/v2/components/chat/CopilotChatAssistantMessage.vue +28 -141
  41. package/src/v2/components/chat/CopilotChatFeedbackCommentPopover.vue +11 -76
  42. package/src/v2/components/chat/CopilotChatInput.vue +82 -269
  43. package/src/v2/components/chat/CopilotChatMessageView.vue +14 -64
  44. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +25 -52
  45. package/src/v2/components/chat/CopilotChatView.vue +92 -135
  46. package/src/v2/components/chat/CopilotPopupViewInternal.vue +18 -58
  47. package/src/v2/components/chat/CopilotSidebarViewInternal.vue +22 -45
  48. package/src/v2/components/chat/CopilotThreadsDrawer.vue +6 -3
  49. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +43 -0
  50. package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.ts +43 -1
  51. package/src/v2/components/chat/message-feedback.ts +33 -1
  52. package/src/v2/hooks/use-configure-suggestions.ts +3 -4
  53. package/src/v2/hooks/use-default-render-tool.ts +7 -68
  54. package/src/v2/hooks/use-frontend-tool.ts +10 -20
  55. package/src/v2/hooks/use-render-activity-message.ts +4 -2
  56. package/src/v2/hooks/use-suggestions.ts +9 -4
  57. package/src/v2/hooks/use-threads.ts +6 -15
  58. package/src/v2/lib/vue-core.ts +19 -2
  59. package/src/v2/providers/CopilotKitProvider.vue +107 -184
  60. package/src/v2/providers/__tests__/CopilotKitProvider.a2ui.test.ts +18 -0
  61. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +35 -2
  62. package/dist/use-render-activity-message-8p2HwK2y.cjs +0 -114
  63. package/dist/use-render-activity-message-8p2HwK2y.cjs.map +0 -1
  64. package/dist/use-render-activity-message-BVZr0Z-G.js.map +0 -1
@@ -1,14 +1,19 @@
1
1
  <script setup lang="ts">
2
2
  import {
3
3
  computed,
4
- nextTick,
5
4
  onBeforeUnmount,
6
5
  onMounted,
7
6
  ref,
8
7
  useAttrs,
9
8
  watch,
10
9
  } from "vue";
11
- import { ɵcreateDockedSidebar, ɵdimensionToCss } from "@copilotkit/core";
10
+ import {
11
+ ɵSIDEBAR_DEFAULT_WIDTH,
12
+ ɵSIDEBAR_TRANSITION_MS,
13
+ ɵcreateDockedSidebar,
14
+ ɵdimensionToCss,
15
+ ɵwatchElementWidth,
16
+ } from "@copilotkit/core";
12
17
  import type { ɵDockedSidebar } from "@copilotkit/core";
13
18
  import { CopilotChatDefaultLabels } from "../../providers/types";
14
19
  import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
@@ -32,9 +37,6 @@ import type {
32
37
 
33
38
  defineOptions({ inheritAttrs: false });
34
39
 
35
- const DEFAULT_SIDEBAR_WIDTH = 480;
36
- const SIDEBAR_TRANSITION_MS = 260;
37
-
38
40
  const props = withDefaults(
39
41
  defineProps<Omit<CopilotSidebarViewProps, "defaultOpen">>(),
40
42
  {
@@ -98,9 +100,8 @@ const config = useCopilotChatConfiguration();
98
100
 
99
101
  const sidebarRef = ref<HTMLElement | null>(null);
100
102
  const measuredSidebarWidth = ref<number | string>(
101
- props.width ?? DEFAULT_SIDEBAR_WIDTH,
103
+ props.width ?? ɵSIDEBAR_DEFAULT_WIDTH,
102
104
  );
103
- let resizeObserver: ResizeObserver | null = null;
104
105
 
105
106
  const isSidebarOpen = computed(() => config.value?.isModalOpen ?? false);
106
107
  const resolvedSidebarWidth = computed(
@@ -130,7 +131,7 @@ const asideStyle = computed(
130
131
  }) as Record<string, string>,
131
132
  );
132
133
  const widthCss = computed(() =>
133
- ɵdimensionToCss(resolvedSidebarWidth.value, DEFAULT_SIDEBAR_WIDTH),
134
+ ɵdimensionToCss(resolvedSidebarWidth.value, ɵSIDEBAR_DEFAULT_WIDTH),
134
135
  );
135
136
 
136
137
  // The page makes room on the docked edge (desktop only).
@@ -142,17 +143,6 @@ watch(
142
143
  },
143
144
  );
144
145
 
145
- function updateMeasuredWidth() {
146
- if (props.width !== undefined || !sidebarRef.value) {
147
- return;
148
- }
149
-
150
- const nextWidth = sidebarRef.value.getBoundingClientRect().width;
151
- if (nextWidth > 0) {
152
- measuredSidebarWidth.value = nextWidth;
153
- }
154
- }
155
-
156
146
  function closeSidebar() {
157
147
  config.value?.setModalOpen?.(false);
158
148
  }
@@ -226,48 +216,35 @@ const chatViewEventProps = computed(() => {
226
216
  return listeners;
227
217
  });
228
218
 
219
+ // Without a `width` prop, the page margin follows the width the CSS gives
220
+ // the sidebar.
229
221
  watch(
230
- () => props.width,
231
- (nextWidth) => {
232
- if (nextWidth !== undefined) {
233
- measuredSidebarWidth.value = nextWidth;
234
- return;
235
- }
236
- updateMeasuredWidth();
222
+ [sidebarRef, () => props.width],
223
+ ([element, width], _old, onCleanup) => {
224
+ if (width !== undefined || !element) return;
225
+ onCleanup(
226
+ ɵwatchElementWidth(element, (next) => {
227
+ measuredSidebarWidth.value = next;
228
+ }),
229
+ );
237
230
  },
231
+ { flush: "post" },
238
232
  );
239
233
 
240
- onMounted(async () => {
234
+ onMounted(() => {
241
235
  dock = ɵcreateDockedSidebar(document, {
242
- transitionMs: SIDEBAR_TRANSITION_MS,
236
+ transitionMs: ɵSIDEBAR_TRANSITION_MS,
243
237
  });
244
238
  dock.set({
245
239
  open: isSidebarOpen.value,
246
240
  position: props.position,
247
241
  width: widthCss.value,
248
242
  });
249
- await nextTick();
250
- updateMeasuredWidth();
251
-
252
- if (
253
- props.width !== undefined ||
254
- !sidebarRef.value ||
255
- typeof ResizeObserver === "undefined"
256
- ) {
257
- return;
258
- }
259
-
260
- resizeObserver = new ResizeObserver(() => {
261
- updateMeasuredWidth();
262
- });
263
- resizeObserver.observe(sidebarRef.value);
264
243
  });
265
244
 
266
245
  onBeforeUnmount(() => {
267
246
  dock?.dispose();
268
247
  dock = undefined;
269
- resizeObserver?.disconnect();
270
- resizeObserver = null;
271
248
  });
272
249
  </script>
273
250
 
@@ -21,10 +21,10 @@ import type {
21
21
  // lands and is published; declared locally here because the built element types
22
22
  // in this worktree predate it.
23
23
  type CollapseChangeDetail = { collapsed: boolean };
24
- import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
25
24
  import {
26
25
  ɵcreateThreadsDrawerActions,
27
26
  ɵgetThreadsDrawerElementProps,
27
+ ɵresolveAgentId,
28
28
  ɵresolveThreadsLicense,
29
29
  ɵtoDrawerThread,
30
30
  } from "@copilotkit/core";
@@ -82,8 +82,11 @@ const threadsLicense = computed(() => ɵresolveThreadsLicense(license.value));
82
82
  const licensed = computed(() => threadsLicense.value.licensed);
83
83
  const licensePending = computed(() => threadsLicense.value.licensePending);
84
84
 
85
- const resolvedAgentId = computed(
86
- () => props.agentId ?? config.value?.agentId ?? DEFAULT_AGENT_ID,
85
+ const resolvedAgentId = computed(() =>
86
+ ɵresolveAgentId({
87
+ agentId: props.agentId,
88
+ chatAgentId: config.value?.agentId,
89
+ }),
87
90
  );
88
91
  const activeThreadId = computed(() => config.value?.threadId ?? null);
89
92
 
@@ -1218,4 +1218,47 @@ describe("CopilotChatInput", () => {
1218
1218
  expect(input.value).toBe("部分");
1219
1219
  });
1220
1220
  });
1221
+
1222
+ // `onSubmitMessage` is both a declared prop (to detect a listener) and the
1223
+ // `submit-message` emit. A template `@submit-message` listener must enable
1224
+ // sending and fire once per send.
1225
+ describe("@submit-message listener", () => {
1226
+ function renderWithTemplateListener(onSubmit?: (value: string) => void) {
1227
+ const Host = defineComponent({
1228
+ components: { CopilotChatConfigurationProvider, CopilotChatInput },
1229
+ setup() {
1230
+ return { TEST_THREAD_ID, onSubmit };
1231
+ },
1232
+ template: onSubmit
1233
+ ? `
1234
+ <CopilotChatConfigurationProvider :thread-id="TEST_THREAD_ID">
1235
+ <CopilotChatInput model-value="hello" @submit-message="onSubmit" />
1236
+ </CopilotChatConfigurationProvider>
1237
+ `
1238
+ : `
1239
+ <CopilotChatConfigurationProvider :thread-id="TEST_THREAD_ID">
1240
+ <CopilotChatInput model-value="hello" />
1241
+ </CopilotChatConfigurationProvider>
1242
+ `,
1243
+ });
1244
+ return render(Host);
1245
+ }
1246
+
1247
+ it("enables the send button and fires the listener once per send", async () => {
1248
+ const onSubmit = vi.fn();
1249
+ renderWithTemplateListener(onSubmit);
1250
+
1251
+ expect(getSendButton().disabled).toBe(false);
1252
+ await fireEvent.click(getSendButton());
1253
+
1254
+ expect(onSubmit).toHaveBeenCalledTimes(1);
1255
+ expect(onSubmit).toHaveBeenCalledWith("hello");
1256
+ });
1257
+
1258
+ it("keeps the send button disabled without a listener", () => {
1259
+ renderWithTemplateListener();
1260
+
1261
+ expect(getSendButton().disabled).toBe(true);
1262
+ });
1263
+ });
1221
1264
  });
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { defineComponent, h, provide, ref } from "vue";
3
- import { render, waitFor } from "@testing-library/vue";
3
+ import { fireEvent, render, waitFor } from "@testing-library/vue";
4
4
  import type { Message } from "@ag-ui/core";
5
5
  import CopilotKitProvider from "../../../providers/CopilotKitProvider.vue";
6
6
  import CopilotChatConfigurationProvider from "../../../providers/CopilotChatConfigurationProvider.vue";
@@ -75,6 +75,10 @@ async function waitForMount(screen: ReturnType<typeof render>) {
75
75
  await screen.findByTestId("copilot-chat-view");
76
76
  }
77
77
 
78
+ function rect(top: number, bottom: number) {
79
+ return { top, bottom, left: 0, right: 0, width: 0, height: bottom - top };
80
+ }
81
+
78
82
  describe("CopilotChatView pin-to-send mode", () => {
79
83
  it("renders the pin-to-send spacer element when autoScroll='pin-to-send'", async () => {
80
84
  const screen = renderView("pin-to-send");
@@ -163,4 +167,42 @@ describe("CopilotChatView pin-to-send mode", () => {
163
167
  expect(spacer.getAttribute("data-pin-to-send-spacer")).not.toBeNull(),
164
168
  );
165
169
  });
170
+ // The spacer under the messages is not content: the scroll-to-bottom
171
+ // button follows the end of the messages, as in React.
172
+ describe("scroll-to-bottom button", () => {
173
+ async function setup(contentBottom: number) {
174
+ const screen = renderView("pin-to-send");
175
+ await waitForMount(screen);
176
+ const scrollElement = screen.getByTestId("copilot-chat-view-scroll");
177
+ const contentElement = screen.getByTestId("copilot-scroll-content");
178
+ // The spacer makes the scroll height far larger than the view.
179
+ Object.defineProperty(scrollElement, "scrollHeight", {
180
+ configurable: true,
181
+ get: () => 2000,
182
+ });
183
+ Object.defineProperty(scrollElement, "clientHeight", {
184
+ configurable: true,
185
+ get: () => 400,
186
+ });
187
+ scrollElement.getBoundingClientRect = () => rect(0, 400) as DOMRect;
188
+ contentElement.getBoundingClientRect = () =>
189
+ rect(-200, contentBottom) as DOMRect;
190
+ await fireEvent.scroll(scrollElement);
191
+ return screen;
192
+ }
193
+
194
+ it("stays hidden when only the spacer is below the view", async () => {
195
+ const screen = await setup(390);
196
+ expect(
197
+ screen.queryByTestId("copilot-chat-view-scroll-to-bottom"),
198
+ ).toBeNull();
199
+ });
200
+
201
+ it("shows when messages sit below the view", async () => {
202
+ const screen = await setup(700);
203
+ expect(
204
+ await screen.findByTestId("copilot-chat-view-scroll-to-bottom"),
205
+ ).toBeDefined();
206
+ });
207
+ });
166
208
  });
@@ -114,6 +114,26 @@ export interface FeedbackComment {
114
114
  readonly close: ɵFeedbackCommentController["close"];
115
115
  /** Moves focus back to the thumb without opening the comment again. */
116
116
  readonly returnFocus: (target: HTMLElement | null) => void;
117
+ /** The chosen thumb the popover points at. */
118
+ readonly anchor: ComputedRef<HTMLElement | null>;
119
+ /** A thumb click; returns whether the app's handler should run. */
120
+ readonly rate: ɵFeedbackCommentController["rate"];
121
+ /** The attributes and handlers of one thumb. */
122
+ readonly thumb: ɵFeedbackCommentController["thumb"];
123
+ /** Points at the chosen thumb when the click gave no element. */
124
+ readonly findAnchor: ɵFeedbackCommentController["findAnchor"];
125
+ /** Closes a comment whose rating changed or whose toolbar hid. */
126
+ readonly sync: ɵFeedbackCommentController["sync"];
127
+ /** Moves focus into the field when a keyboard rating asked for it. */
128
+ readonly focusField: ɵFeedbackCommentController["focusField"];
129
+ /** Saves, closes, and returns focus to the thumb. */
130
+ readonly submit: ɵFeedbackCommentController["submit"];
131
+ /** Escape in the popover; returns whether to stop the event. */
132
+ readonly popoverEscape: ɵFeedbackCommentController["popoverEscape"];
133
+ /** Tab out of the popover, back into the page's order. */
134
+ readonly popoverKeyDown: ɵFeedbackCommentController["popoverKeyDown"];
135
+ /** Focus left the comment form. */
136
+ readonly popoverFocusLeave: ɵFeedbackCommentController["popoverFocusLeave"];
117
137
  }
118
138
 
119
139
  /**
@@ -124,8 +144,10 @@ export interface FeedbackComment {
124
144
  export function useFeedbackComment(
125
145
  controls: Ref<MessageFeedbackControls | null>,
126
146
  ): FeedbackComment {
147
+ const input = shallowRef<HTMLInputElement | null>(null);
127
148
  const controller = new ɵFeedbackCommentController({
128
149
  getControls: () => controls.value,
150
+ getInput: () => input.value,
129
151
  });
130
152
  const state = shallowRef(controller.getSnapshot());
131
153
  const unsubscribe = controller.subscribe(() => {
@@ -145,12 +167,22 @@ export function useFeedbackComment(
145
167
  }),
146
168
  focusRequest: computed(() => state.value.focusRequest),
147
169
  takeFocusRequest: controller.takeFocusRequest,
148
- input: shallowRef<HTMLInputElement | null>(null),
170
+ input,
149
171
  show: controller.show,
150
172
  enter: controller.enter,
151
173
  leave: controller.leave,
152
174
  setEditing: controller.setEditing,
153
175
  close: controller.close,
154
176
  returnFocus: controller.returnFocus,
177
+ anchor: computed(() => state.value.anchor),
178
+ rate: controller.rate,
179
+ thumb: controller.thumb,
180
+ findAnchor: controller.findAnchor,
181
+ sync: controller.sync,
182
+ focusField: controller.focusField,
183
+ submit: controller.submit,
184
+ popoverEscape: controller.popoverEscape,
185
+ popoverKeyDown: controller.popoverKeyDown,
186
+ popoverFocusLeave: controller.popoverFocusLeave,
155
187
  };
156
188
  }
@@ -2,8 +2,7 @@ import { computed, watch } from "vue";
2
2
  import type { WatchSource } from "vue";
3
3
  import { useCopilotKit } from "../providers/useCopilotKit";
4
4
  import { useCopilotChatConfiguration } from "../providers/useCopilotChatConfiguration";
5
- import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
6
- import { ɵbindSuggestionsConfig } from "@copilotkit/core";
5
+ import { ɵbindSuggestionsConfig, ɵresolveAgentId } from "@copilotkit/core";
7
6
  import type { ɵSuggestionsConfigInput } from "@copilotkit/core";
8
7
 
9
8
  /**
@@ -27,8 +26,8 @@ export function useConfigureSuggestions(
27
26
  ): void {
28
27
  const { copilotkit } = useCopilotKit();
29
28
  const chatConfig = useCopilotChatConfiguration();
30
- const chatAgentId = computed(
31
- () => chatConfig.value?.agentId ?? DEFAULT_AGENT_ID,
29
+ const chatAgentId = computed(() =>
30
+ ɵresolveAgentId({ chatAgentId: chatConfig.value?.agentId }),
32
31
  );
33
32
  const extraDeps = deps ?? [];
34
33
 
@@ -2,84 +2,23 @@ import { defineComponent, h, inject, ref } from "vue";
2
2
  import type { WatchSource } from "vue";
3
3
  import type { Component, VNodeChild } from "vue";
4
4
  import {
5
+ ɵadaptDefaultToolCallRenderProps as adaptRendererProps,
5
6
  ɵgetToolCallBadge,
6
7
  ɵnormalizeToolCallStatus,
7
8
  ɵstringifyToolCallAttr,
8
9
  ɵstringifyToolCallPayload,
9
10
  } from "@copilotkit/core";
10
- import type { ToolCallStatus } from "@copilotkit/core";
11
+ import type {
12
+ ɵDefaultToolCallRenderProps,
13
+ ɵRawToolCallRenderProps,
14
+ } from "@copilotkit/core";
11
15
  import { useRenderTool } from "./use-render-tool";
12
16
  import { ToolCallGroupContextKey } from "../components/chat/tool-call-display-context";
13
17
  import { ToolCallRow } from "../components/chat/tool-call-group-parts";
14
18
  import { useChatLabels } from "../components/chat/use-chat-labels";
15
19
 
16
- type DefaultRenderProps = {
17
- name: string;
18
- toolCallId: string;
19
- parameters: unknown;
20
- status: "inProgress" | "executing" | "complete";
21
- result: string | undefined;
22
- };
23
-
24
- /**
25
- * Module-level dedup set so an unknown status value only emits a console
26
- * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
27
- * would log on every re-render (potentially many per second).
28
- */
29
- const warnedUnknownStatuses = new Set<string>();
30
-
31
- /**
32
- * Map a {@link ToolCallStatus} enum value to the documented string-union
33
- * status the {@link DefaultRenderProps} contract exposes. Unknown / future
34
- * enum members log a warning (once per distinct value) and fall back to
35
- * `"inProgress"`.
36
- */
37
- function mapToolCallStatus(
38
- status: ToolCallStatus,
39
- ): DefaultRenderProps["status"] {
40
- const normalized = ɵnormalizeToolCallStatus(status);
41
- if (normalized !== "unknown") return normalized;
42
- const key = String(status);
43
- if (!warnedUnknownStatuses.has(key)) {
44
- warnedUnknownStatuses.add(key);
45
- console.warn(
46
- `[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`,
47
- );
48
- }
49
- return "inProgress";
50
- }
51
-
52
- /**
53
- * Convert framework-internal raw renderer props (`args`, enum status) to the
54
- * documented DefaultRenderProps shape. Idempotent on already-documented input
55
- * — if the caller passes `parameters` and a string-union `status`, those win.
56
- */
57
- type AdaptInput = {
58
- name?: unknown;
59
- toolCallId?: unknown;
60
- args?: unknown;
61
- parameters?: unknown;
62
- status?: unknown;
63
- result?: unknown;
64
- };
65
-
66
- function adaptRendererProps(raw: AdaptInput): DefaultRenderProps {
67
- const parameters = raw.parameters !== undefined ? raw.parameters : raw.args;
68
- const rawStatus = raw.status;
69
- const status: DefaultRenderProps["status"] =
70
- rawStatus === "inProgress" ||
71
- rawStatus === "executing" ||
72
- rawStatus === "complete"
73
- ? rawStatus
74
- : mapToolCallStatus(rawStatus as ToolCallStatus);
75
- return {
76
- name: raw.name as string,
77
- toolCallId: raw.toolCallId as string,
78
- parameters,
79
- status,
80
- result: raw.result as string | undefined,
81
- };
82
- }
20
+ type DefaultRenderProps = ɵDefaultToolCallRenderProps;
21
+ type AdaptInput = ɵRawToolCallRenderProps;
83
22
 
84
23
  const DefaultToolCallRenderer = defineComponent({
85
24
  props: {
@@ -1,4 +1,5 @@
1
1
  import { watch } from "vue";
2
+ import { ɵregisterFrontendTools } from "@copilotkit/core";
2
3
  import type { WatchSource } from "vue";
3
4
  import { useCopilotKit } from "../providers/useCopilotKit";
4
5
  import type { VueFrontendTool } from "../types";
@@ -14,8 +15,8 @@ const EMPTY_DEPS: WatchSource<unknown>[] = [];
14
15
  *
15
16
  * A tool with the same name and `agentId` as a tool that is already
16
17
  * registered (for example a provider `frontendTools` entry) takes its place
17
- * while this scope is active. When the scope is disposed, the earlier tool is
18
- * active again.
18
+ * while this scope is active, with a console warning. When the scope is
19
+ * disposed, the earlier tool is active again.
19
20
  *
20
21
  * @example
21
22
  * ```ts
@@ -45,26 +46,15 @@ export function useFrontendTool<T extends Record<string, unknown>>(
45
46
  ],
46
47
  (_newValues, _old, onCleanup) => {
47
48
  const core = copilotkit.value;
48
- const name = tool.name;
49
-
50
49
  // Registrations stack in core: this one shadows an earlier tool with the
51
50
  // same name and agentId (a provider tool, or another component's), and
52
- // unregistering it brings that tool back.
53
- const unregister = core.addTool(tool);
54
-
55
- // The render function is registered even when tool.parameters is
56
- // undefined — tools like HITL confirm dialogs have no parameters
57
- // but still need their UI rendered in the chat.
58
- if (tool.render) {
59
- core.addHookRenderToolCall({
60
- name,
61
- args: tool.parameters,
62
- agentId: tool.agentId,
63
- render: tool.render,
64
- } as VueToolCallRenderer);
65
- }
66
-
67
- onCleanup(unregister);
51
+ // unregistering it brings that tool back. The renderer stays registered
52
+ // so past calls still render in the chat history.
53
+ onCleanup(
54
+ ɵregisterFrontendTools(core, [tool], (renderer) =>
55
+ core.addHookRenderToolCall(renderer as VueToolCallRenderer),
56
+ ),
57
+ );
68
58
  },
69
59
  { immediate: true },
70
60
  );
@@ -5,8 +5,8 @@ import {
5
5
  ɵdescribeActivityRendererFailure,
6
6
  ɵfindActivityRenderer,
7
7
  ɵmatchActivityRenderer,
8
+ ɵresolveAgentId,
8
9
  } from "@copilotkit/core";
9
- import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
10
10
  import { useCopilotKit } from "../providers/useCopilotKit";
11
11
  import { useCopilotChatConfiguration } from "../providers/useCopilotChatConfiguration";
12
12
  import type { VueActivityMessageRendererProps } from "../types";
@@ -23,7 +23,9 @@ import type { VueActivityMessageRendererProps } from "../types";
23
23
  export function useRenderActivityMessage() {
24
24
  const { copilotkit } = useCopilotKit();
25
25
  const config = useCopilotChatConfiguration();
26
- const agentId = computed(() => config.value?.agentId ?? DEFAULT_AGENT_ID);
26
+ const agentId = computed(() =>
27
+ ɵresolveAgentId({ chatAgentId: config.value?.agentId }),
28
+ );
27
29
 
28
30
  function findRenderer(activityType: string) {
29
31
  return ɵfindActivityRenderer(copilotkit.value.renderActivityMessages, {
@@ -1,13 +1,15 @@
1
1
  import { computed, ref, toValue, watch } from "vue";
2
2
  import type { MaybeRefOrGetter, Ref } from "vue";
3
- import { ɵsubscribeToAgentSuggestions } from "@copilotkit/core";
3
+ import {
4
+ ɵresolveAgentId,
5
+ ɵsubscribeToAgentSuggestions,
6
+ } from "@copilotkit/core";
4
7
  import type {
5
8
  CopilotKitCoreGetSuggestionsResult,
6
9
  Suggestion,
7
10
  } from "@copilotkit/core";
8
11
  import { useCopilotKit } from "../providers/useCopilotKit";
9
12
  import { useCopilotChatConfiguration } from "../providers/useCopilotChatConfiguration";
10
- import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
11
13
 
12
14
  export interface UseSuggestionsOptions {
13
15
  agentId?: MaybeRefOrGetter<string | undefined>;
@@ -38,8 +40,11 @@ export function useSuggestions(
38
40
  ): UseSuggestionsResult {
39
41
  const { copilotkit } = useCopilotKit();
40
42
  const config = useCopilotChatConfiguration();
41
- const resolvedAgentId = computed(
42
- () => toValue(options.agentId) ?? config.value?.agentId ?? DEFAULT_AGENT_ID,
43
+ const resolvedAgentId = computed(() =>
44
+ ɵresolveAgentId({
45
+ agentId: toValue(options.agentId),
46
+ chatAgentId: config.value?.agentId,
47
+ }),
43
48
  );
44
49
 
45
50
  const suggestions = ref<Suggestion[]>([]);
@@ -1,23 +1,14 @@
1
1
  import { computed, onScopeDispose, shallowRef, toValue, watch } from "vue";
2
2
  import type { MaybeRefOrGetter, Ref } from "vue";
3
3
  import { ɵThreadsController, ɵcreateThreadStore } from "@copilotkit/core";
4
+ import type { ɵThreadSummary } from "@copilotkit/core";
4
5
  import { useCopilotKit } from "../providers/useCopilotKit";
5
6
 
6
- export interface Thread {
7
- id: string;
8
- agentId: string;
9
- name: string | null;
10
- archived: boolean;
11
- createdAt: string;
12
- updatedAt: string;
13
- /**
14
- * ISO-8601 timestamp of the most recent agent run on this thread. Absent
15
- * when the thread has never been run. Prefer this over `updatedAt` for
16
- * user-facing "last activity" displays — it is not bumped by metadata-only
17
- * actions like rename or archive.
18
- */
19
- lastRunAt?: string;
20
- }
7
+ /**
8
+ * One thread as the hook returns it. This is core's thread summary, the same
9
+ * type React and Angular expose, so the adapters cannot drift apart.
10
+ */
11
+ export type Thread = ɵThreadSummary;
21
12
 
22
13
  export interface UseThreadsInput {
23
14
  agentId: MaybeRefOrGetter<string>;
@@ -86,12 +86,18 @@ export class CopilotKitCoreVue extends CopilotKitCore {
86
86
  this._renderers.setCustom(renderers);
87
87
  }
88
88
 
89
- /** The renderers the provider was given, without hook renderers. */
89
+ /**
90
+ * The renderers the provider was given, without hook or built-in
91
+ * renderers. Nothing in CopilotKit reads it now; it stays for app code.
92
+ */
90
93
  get propRenderToolCalls() {
91
94
  return this._renderers.propToolCalls;
92
95
  }
93
96
 
94
- /** Prop and hook renderers merged; a hook renderer wins over a prop one. */
97
+ /**
98
+ * Prop, hook and built-in renderers merged. For one name and agentId, a
99
+ * hook renderer wins over a prop one, and both win over a built-in one.
100
+ */
95
101
  get renderToolCalls() {
96
102
  return this._renderers.toolCalls;
97
103
  }
@@ -112,6 +118,17 @@ export class CopilotKitCoreVue extends CopilotKitCore {
112
118
  this._renderers.removeHookToolCall(name, agentId);
113
119
  }
114
120
 
121
+ /**
122
+ * @internal Add a renderer for a tool a built-in feature owns (A2UI,
123
+ * generative UI). Prop and hook renderers with the same name and agent win
124
+ * over it, and `setRenderToolCalls` never removes it.
125
+ *
126
+ * @returns A function that removes the renderer.
127
+ */
128
+ ɵaddBuiltInRenderToolCall(entry: VueToolCallRenderer): () => void {
129
+ return this._renderers.addBuiltInToolCall(entry);
130
+ }
131
+
115
132
  private notifyRenderersChanged(kind: ɵRendererKind): void {
116
133
  void this.notifySubscribers((subscriber) => {
117
134
  const vueSubscriber = subscriber as CopilotKitCoreVueSubscriber;