@copilotkit/vue 2.0.0-rc.7 → 2.0.0-rc.8

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 (124) hide show
  1. package/dist/index.cjs +1 -73
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.mjs +292 -16947
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/use-render-activity-message-C5LCG9a9.js +16451 -0
  6. package/dist/use-render-activity-message-C5LCG9a9.js.map +1 -0
  7. package/dist/use-render-activity-message-CPzguixR.cjs +74 -0
  8. package/dist/use-render-activity-message-CPzguixR.cjs.map +1 -0
  9. package/dist/v2/components/LicenseWarningBanner.vue.d.ts.map +1 -1
  10. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  11. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  12. package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
  13. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +6 -270
  14. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  15. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +1 -1
  16. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  17. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  18. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +1 -1
  19. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
  20. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  21. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +1 -1
  22. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  23. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  24. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -1
  25. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +0 -1
  26. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  27. package/dist/v2/components/chat/index.d.ts +0 -1
  28. package/dist/v2/components/chat/index.d.ts.map +1 -1
  29. package/dist/v2/components/chat/tool-call-display-context.d.ts +2 -19
  30. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
  31. package/dist/v2/components/chat/tool-call-group-parts.d.ts +7 -20
  32. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
  33. package/dist/v2/components/chat/turn-work.d.ts +0 -2
  34. package/dist/v2/components/chat/turn-work.d.ts.map +1 -1
  35. package/dist/v2/components/chat/types.d.ts +1 -2
  36. package/dist/v2/components/chat/types.d.ts.map +1 -1
  37. package/dist/v2/components/chat/use-chat-labels.d.ts +8 -0
  38. package/dist/v2/components/chat/use-chat-labels.d.ts.map +1 -0
  39. package/dist/v2/components/icons/index.d.ts +968 -1
  40. package/dist/v2/components/icons/index.d.ts.map +1 -1
  41. package/dist/v2/hooks/use-frontend-tool.d.ts +5 -0
  42. package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -1
  43. package/dist/v2/hooks/use-render-activity-message.d.ts +13 -9
  44. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  45. package/dist/v2/hooks/use-render-custom-messages.d.ts +5 -7
  46. package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
  47. package/dist/v2/index.cjs +1 -1
  48. package/dist/v2/index.mjs +196 -199
  49. package/dist/v2/index.mjs.map +1 -1
  50. package/dist/v2/lib/vue-core.d.ts +0 -1
  51. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  52. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  53. package/dist/v2/providers/types.d.ts +3 -46
  54. package/dist/v2/providers/types.d.ts.map +1 -1
  55. package/dist/v2/types/human-in-the-loop.d.ts +8 -29
  56. package/dist/v2/types/human-in-the-loop.d.ts.map +1 -1
  57. package/dist/v2/types/interrupt.d.ts +1 -22
  58. package/dist/v2/types/interrupt.d.ts.map +1 -1
  59. package/dist/v2/types/vue-activity-message-renderer.d.ts +16 -15
  60. package/dist/v2/types/vue-activity-message-renderer.d.ts.map +1 -1
  61. package/dist/v2/types/vue-custom-message-renderer.d.ts +7 -16
  62. package/dist/v2/types/vue-custom-message-renderer.d.ts.map +1 -1
  63. package/dist/v2/types/vue-tool-call-renderer.d.ts +16 -35
  64. package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
  65. package/package.json +7 -8
  66. package/src/__tests__/build-entries.test.ts +27 -0
  67. package/src/v2/components/LicenseWarningBanner.vue +31 -15
  68. package/src/v2/components/__tests__/LicenseWarningBanner.test.ts +20 -0
  69. package/src/v2/components/chat/CopilotChat.vue +19 -40
  70. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +14 -10
  71. package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +14 -6
  72. package/src/v2/components/chat/CopilotChatInput.vue +40 -30
  73. package/src/v2/components/chat/CopilotChatMessageView.vue +72 -140
  74. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +31 -13
  75. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +60 -37
  76. package/src/v2/components/chat/CopilotChatToolCallItem.vue +3 -3
  77. package/src/v2/components/chat/CopilotChatToolCallsView.vue +15 -13
  78. package/src/v2/components/chat/CopilotChatView.vue +5 -5
  79. package/src/v2/components/chat/CopilotChatWorkSummary.vue +44 -18
  80. package/src/v2/components/chat/__tests__/CopilotChat.labels.test.ts +19 -4
  81. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +33 -1
  82. package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.ts +247 -403
  83. package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +1 -99
  84. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +3 -19
  85. package/src/v2/components/chat/index.ts +0 -1
  86. package/src/v2/components/chat/tool-call-display-context.ts +2 -23
  87. package/src/v2/components/chat/tool-call-group-parts.ts +44 -85
  88. package/src/v2/components/chat/turn-work.ts +0 -9
  89. package/src/v2/components/chat/types.ts +4 -4
  90. package/src/v2/components/chat/use-chat-labels.ts +16 -0
  91. package/src/v2/components/icons/__tests__/icons.test.ts +72 -68
  92. package/src/v2/components/icons/index.ts +109 -27
  93. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +62 -1
  94. package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +105 -0
  95. package/src/v2/hooks/__tests__/use-render-tool.test.ts +3 -6
  96. package/src/v2/hooks/__tests__/zod-regression.test.ts +4 -3
  97. package/src/v2/hooks/use-frontend-tool.ts +10 -10
  98. package/src/v2/hooks/use-render-activity-message.ts +27 -48
  99. package/src/v2/hooks/use-render-custom-messages.ts +14 -35
  100. package/src/v2/lib/vue-core.ts +3 -5
  101. package/src/v2/providers/CopilotKitProvider.vue +29 -80
  102. package/src/v2/providers/__tests__/CopilotKitProvider.headersAtSendTime.test.ts +3 -3
  103. package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +3 -1
  104. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +17 -0
  105. package/src/v2/providers/types.ts +3 -49
  106. package/src/v2/types/__tests__/defineToolCallRenderer.test.ts +28 -33
  107. package/src/v2/types/defineToolCallRenderer.ts +5 -4
  108. package/src/v2/types/human-in-the-loop.ts +8 -32
  109. package/src/v2/types/interrupt.ts +7 -31
  110. package/src/v2/types/vue-activity-message-renderer.ts +24 -15
  111. package/src/v2/types/vue-custom-message-renderer.ts +12 -16
  112. package/src/v2/types/vue-tool-call-renderer.ts +23 -38
  113. package/vite.config.ts +1 -2
  114. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts +0 -9
  115. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +0 -1
  116. package/dist/v2/components/chat/tool-call-layout.d.ts +0 -115
  117. package/dist/v2/components/chat/tool-call-layout.d.ts.map +0 -1
  118. package/dist/v2/lib/transcription-client.d.ts +0 -19
  119. package/dist/v2/lib/transcription-client.d.ts.map +0 -1
  120. package/src/v2/components/chat/BasicMarkdown.vue +0 -160
  121. package/src/v2/components/chat/__tests__/BasicMarkdown.test.ts +0 -69
  122. package/src/v2/components/chat/tool-call-layout.ts +0 -304
  123. package/src/v2/lib/__tests__/transcription-client.test.ts +0 -67
  124. package/src/v2/lib/transcription-client.ts +0 -151
@@ -10,8 +10,15 @@
10
10
  * a custom wildcard renderer draws whatever it draws.
11
11
  */
12
12
  import { computed, provide, ref } from "vue";
13
+ import {
14
+ ɵformatLabel,
15
+ ɵformatToolNames,
16
+ ɵgetToolCallPreview,
17
+ ɵgetToolCallRowStatus,
18
+ ɵformatCountLabel,
19
+ } from "@copilotkit/core";
20
+ import type { ToolCallEntry, ɵToolCallRowStatus } from "@copilotkit/core";
13
21
  import { useCopilotId } from "../ui/use-id";
14
- import type { ToolCallEntry } from "./tool-call-layout";
15
22
  import { ToolCallGroupContextKey } from "./tool-call-display-context";
16
23
  import type { ToolCallGroupContextValue } from "./tool-call-display-context";
17
24
  import {
@@ -19,13 +26,9 @@ import {
19
26
  ToolCallChevron,
20
27
  ToolCallFailedCount,
21
28
  ToolCallStatusIcon,
22
- entryPreview,
23
- entryStatus,
24
- pluralize,
25
- statusLabels,
26
- toolNames,
29
+ toolCallStatusLabel,
27
30
  } from "./tool-call-group-parts";
28
- import type { ToolCallStatusName } from "./tool-call-group-parts";
31
+ import { useChatLabels } from "./use-chat-labels";
29
32
  import type { CopilotChatToolCallGroupAppearance } from "./types";
30
33
 
31
34
  const props = withDefaults(
@@ -49,6 +52,7 @@ defineSlots<{
49
52
  call(props: { call: ToolCallEntry }): unknown;
50
53
  }>();
51
54
 
55
+ const labels = useChatLabels();
52
56
  const open = ref(false);
53
57
  const bodyId = useCopilotId("cpk-tool-call-group");
54
58
  const toggle = () => {
@@ -64,13 +68,13 @@ const current = computed(() =>
64
68
  : undefined,
65
69
  );
66
70
  const preview = computed(() =>
67
- current.value ? entryPreview(current.value) : undefined,
71
+ current.value ? ɵgetToolCallPreview(current.value) : undefined,
68
72
  );
69
73
  const done = computed(
70
74
  () => props.calls.filter((call) => call.toolMessage).length,
71
75
  );
72
76
  const statuses = computed(() =>
73
- props.calls.map((call) => entryStatus(call, props.isActive)),
77
+ props.calls.map((call) => ɵgetToolCallRowStatus(call, props.isActive)),
74
78
  );
75
79
  const failedIds = computed(
76
80
  () =>
@@ -80,7 +84,7 @@ const failedIds = computed(
80
84
  .map((call) => call.toolCall.id),
81
85
  ),
82
86
  );
83
- const status = computed<ToolCallStatusName>(() =>
87
+ const status = computed<ɵToolCallRowStatus>(() =>
84
88
  current.value
85
89
  ? "running"
86
90
  : failedIds.value.size > 0
@@ -108,10 +112,27 @@ const behind = computed(() =>
108
112
  open.value ? 0 : Math.min(props.calls.length - 1, 2),
109
113
  );
110
114
  const latestStatus = computed(() =>
111
- latest.value ? entryStatus(latest.value, props.isActive) : "done",
115
+ latest.value ? ɵgetToolCallRowStatus(latest.value, props.isActive) : "done",
112
116
  );
113
117
  const latestPreview = computed(() =>
114
- latest.value ? entryPreview(latest.value) : undefined,
118
+ latest.value ? ɵgetToolCallPreview(latest.value) : undefined,
119
+ );
120
+ const runningLabel = computed(() =>
121
+ current.value
122
+ ? ɵformatLabel(labels.value.toolCallRunningLabel, {
123
+ name: current.value.toolCall.function.name,
124
+ })
125
+ : "",
126
+ );
127
+ const usedToolsLabel = computed(() =>
128
+ ɵformatCountLabel(props.calls.length, {
129
+ one: labels.value.toolCallUsedToolsOne,
130
+ other: labels.value.toolCallUsedToolsOther,
131
+ }),
132
+ );
133
+ const toolNames = computed(() => ɵformatToolNames(props.calls));
134
+ const latestStatusLabel = computed(() =>
135
+ toolCallStatusLabel(labels.value, latestStatus.value),
115
136
  );
116
137
  const layer = "cpk-tool-call-group-stack-layer";
117
138
  </script>
@@ -144,20 +165,18 @@ const layer = "cpk-tool-call-group-stack-layer";
144
165
  @click="toggle"
145
166
  >
146
167
  <template v-if="current">
147
- <span class="cpk-tool-call-group-running cpk-shimmer"
148
- >Running {{ current.toolCall.function.name }}</span
149
- >
168
+ <span class="cpk-tool-call-group-running cpk-shimmer">{{
169
+ runningLabel
170
+ }}</span>
150
171
  <span v-if="preview" class="cpk-tool-call-group-preview">{{
151
172
  preview
152
173
  }}</span>
153
174
  </template>
154
175
  <template v-else>
155
- <span class="cpk-tool-call-group-line-summary"
156
- >Used {{ pluralize(calls.length, "tool") }}</span
157
- >
158
- <span class="cpk-tool-call-group-line-names">{{
159
- toolNames(calls)
176
+ <span class="cpk-tool-call-group-line-summary">{{
177
+ usedToolsLabel
160
178
  }}</span>
179
+ <span class="cpk-tool-call-group-line-names">{{ toolNames }}</span>
161
180
  <ToolCallFailedCount :calls="calls" />
162
181
  </template>
163
182
  <ToolCallChevron :open="open" />
@@ -206,10 +225,15 @@ const layer = "cpk-tool-call-group-stack-layer";
206
225
  @click="toggle"
207
226
  >
208
227
  <template v-if="open">
209
- {{ pluralize(calls.length, "tool call") }}
228
+ {{
229
+ ɵformatCountLabel(calls.length, {
230
+ one: labels.toolCallToolCallCountOne,
231
+ other: labels.toolCallToolCallCountOther,
232
+ })
233
+ }}
210
234
  <ToolCallFailedCount :calls="calls" />
211
235
  <span class="cpk-tool-call-group-stack-collapse">
212
- Collapse
236
+ {{ labels.toolCallCollapseLabel }}
213
237
  <ToolCallChevron
214
238
  :open="true"
215
239
  class="cpk-tool-call-group-stack-collapse-icon"
@@ -236,13 +260,16 @@ const layer = "cpk-tool-call-group-stack-layer";
236
260
  >
237
261
  <!-- The status icon is decorative, so say the status. -->
238
262
  <span class="cpk-tool-call-group-stack-status">{{
239
- statusLabels[latestStatus]
263
+ latestStatusLabel
240
264
  }}</span>
241
265
  </span>
242
266
  <span class="cpk-tool-call-group-stack-meta">
243
267
  <ToolCallFailedCount :calls="calls" />
244
268
  <span class="cpk-tool-call-group-stack-count">{{
245
- pluralize(calls.length, "call")
269
+ ɵformatCountLabel(calls.length, {
270
+ one: labels.toolCallCallCountOne,
271
+ other: labels.toolCallCallCountOther,
272
+ })
246
273
  }}</span>
247
274
  <ToolCallChevron
248
275
  :open="false"
@@ -255,9 +282,7 @@ const layer = "cpk-tool-call-group-stack-layer";
255
282
  call change ("read_file: Running", then "read_file: Done"); the
256
283
  button only names it. -->
257
284
  <span aria-live="polite" class="cpk-tool-call-group-stack-live">{{
258
- open
259
- ? ""
260
- : `${latest.toolCall.function.name}: ${statusLabels[latestStatus]}`
285
+ open ? "" : `${latest.toolCall.function.name}: ${latestStatusLabel}`
261
286
  }}</span>
262
287
  </div>
263
288
  <!-- Each call gets the collapsed stack's card, easing out of the stack. -->
@@ -292,28 +317,26 @@ const layer = "cpk-tool-call-group-stack-layer";
292
317
  >
293
318
  <ToolCallStatusIcon :status="status" />
294
319
  <template v-if="current">
295
- <span class="cpk-tool-call-group-running cpk-shimmer"
296
- >Running {{ current.toolCall.function.name }}</span
297
- >
320
+ <span class="cpk-tool-call-group-running cpk-shimmer">{{
321
+ runningLabel
322
+ }}</span>
298
323
  <span v-if="preview" class="cpk-tool-call-group-preview">{{
299
324
  preview
300
325
  }}</span>
301
326
  </template>
302
327
  <template v-else>
303
- <span class="cpk-tool-call-group-card-summary"
304
- >Used {{ pluralize(calls.length, "tool") }}</span
305
- >
306
- <span class="cpk-tool-call-group-card-names">{{
307
- toolNames(calls)
328
+ <span class="cpk-tool-call-group-card-summary">{{
329
+ usedToolsLabel
308
330
  }}</span>
331
+ <span class="cpk-tool-call-group-card-names">{{ toolNames }}</span>
309
332
  </template>
310
333
  <span class="cpk-tool-call-group-card-meta">
311
334
  <ToolCallFailedCount v-if="status === 'failed'" :calls="calls" />
312
335
  <span v-else class="cpk-tool-call-group-card-status">{{
313
336
  status !== "running"
314
- ? statusLabels[status]
337
+ ? toolCallStatusLabel(labels, status)
315
338
  : done > 0
316
- ? `${done} done`
339
+ ? ɵformatLabel(labels.toolCallDoneCountLabel, { count: done })
317
340
  : ""
318
341
  }}</span>
319
342
  <ToolCallChevron :open="open" />
@@ -1,11 +1,11 @@
1
1
  <script setup lang="ts">
2
2
  import type { Message, ToolCall } from "@ag-ui/core";
3
3
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
4
+ import { ɵresolveToolCallRenderer } from "@copilotkit/core";
4
5
  import { useCopilotKit } from "../../providers/useCopilotKit";
5
6
  import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
6
7
  import type { CopilotChatToolCallRenderSlotProps } from "./types";
7
8
  import {
8
- getCoreRenderConfig,
9
9
  getCoreRenderProps,
10
10
  getToolCallMemoDeps,
11
11
  getToolCallRenderProps,
@@ -55,9 +55,9 @@ function getCoreProps() {
55
55
 
56
56
  function getRenderConfig() {
57
57
  const agentId = config.value?.agentId ?? DEFAULT_AGENT_ID;
58
- return getCoreRenderConfig(
59
- props.toolCall,
58
+ return ɵresolveToolCallRenderer(
60
59
  copilotkit.value.renderToolCalls,
60
+ props.toolCall.function.name,
61
61
  agentId,
62
62
  );
63
63
  }
@@ -3,6 +3,14 @@ import { computed, defineComponent, h, inject, provide, useSlots } from "vue";
3
3
  import type { PropType } from "vue";
4
4
  import type { Slots, VNode } from "vue";
5
5
  import type { AssistantMessage, Message } from "@ag-ui/core";
6
+ import {
7
+ ɵgetToolCallLayouts,
8
+ ɵgetToolCallRendererNames,
9
+ } from "@copilotkit/core";
10
+ import type {
11
+ CopilotChatToolCallDisplay,
12
+ ToolCallEntry,
13
+ } from "@copilotkit/core";
6
14
  import { useCopilotKit } from "../../providers/useCopilotKit";
7
15
  import type {
8
16
  CopilotChatToolCallGroupAppearance,
@@ -17,14 +25,6 @@ import {
17
25
  ToolCallGroupContextKey,
18
26
  } from "./tool-call-display-context";
19
27
  import type { ToolCallGroupContextValue } from "./tool-call-display-context";
20
- import {
21
- getToolCallLayouts,
22
- getToolCallRendererNames,
23
- } from "./tool-call-layout";
24
- import type {
25
- CopilotChatToolCallDisplay,
26
- ToolCallEntry,
27
- } from "./tool-call-layout";
28
28
 
29
29
  const props = withDefaults(
30
30
  defineProps<{
@@ -84,13 +84,15 @@ const layout = computed(() => {
84
84
  if (display.value === "separate") return undefined;
85
85
  const viewLayout = context?.value.layouts.get(props.message.id);
86
86
  if (viewLayout) return viewLayout;
87
- const rendererNames = getToolCallRendererNames(
87
+ const rendererNames = ɵgetToolCallRendererNames(
88
88
  copilotkit.value.renderToolCalls,
89
- getToolCallSlotNames(),
90
- );
91
- return getToolCallLayouts([props.message], props.messages, rendererNames).get(
92
- props.message.id,
89
+ { slotNames: getToolCallSlotNames() },
93
90
  );
91
+ return ɵgetToolCallLayouts(
92
+ [props.message],
93
+ props.messages,
94
+ rendererNames,
95
+ ).get(props.message.id);
94
96
  });
95
97
 
96
98
  // Whether the agent may still finish a trailing group's calls. Without a
@@ -11,8 +11,7 @@ import {
11
11
  import type { Message } from "@ag-ui/core";
12
12
  import type { Suggestion } from "@copilotkit/core";
13
13
  import type { Attachment } from "@copilotkit/shared";
14
- import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
15
- import { CopilotChatDefaultLabels } from "../../providers/types";
14
+ import { useChatLabels } from "./use-chat-labels";
16
15
  import { useKeyboardHeight } from "../../hooks/use-keyboard-height";
17
16
  import { usePinToSend } from "../../hooks/use-pin-to-send";
18
17
  import { IconChevronDown, IconUpload } from "../icons";
@@ -127,8 +126,7 @@ const emit = defineEmits<{
127
126
  "finish-transcribe": [];
128
127
  }>();
129
128
 
130
- const config = useCopilotChatConfiguration();
131
- const labels = computed(() => config.value?.labels ?? CopilotChatDefaultLabels);
129
+ const labels = useChatLabels();
132
130
  const componentSlots = useSlots();
133
131
 
134
132
  const scrollContainerRef = ref<HTMLElement | null>(null);
@@ -457,7 +455,9 @@ onBeforeUnmount(() => {
457
455
  >
458
456
  <div class="cpk-chat-view-drop-overlay-content">
459
457
  <IconUpload class="cpk-chat-view-drop-overlay-icon" />
460
- <span class="cpk-chat-view-drop-overlay-label">Drop files here</span>
458
+ <span class="cpk-chat-view-drop-overlay-label">{{
459
+ labels.chatDropFilesLabel
460
+ }}</span>
461
461
  </div>
462
462
  </div>
463
463
  <slot
@@ -5,10 +5,15 @@
5
5
  * for 9s · 7 tools" and toggles the work it folded away.
6
6
  */
7
7
  import { computed, ref, watch } from "vue";
8
+ import {
9
+ ɵcountFailedToolCalls,
10
+ ɵformatLabel,
11
+ ɵformatWorkDuration,
12
+ ɵformatCountLabel,
13
+ } from "@copilotkit/core";
8
14
  import { IconChevronRight } from "../icons";
9
15
  import type { CopilotChatWorkSummarySlotProps } from "./types";
10
- import { pluralize } from "./tool-call-group-parts";
11
- import { formatDuration } from "./turn-work";
16
+ import { useChatLabels } from "./use-chat-labels";
12
17
 
13
18
  // `onClick` falls through to the button.
14
19
  const props = defineProps<Omit<CopilotChatWorkSummarySlotProps, "onClick">>();
@@ -28,9 +33,8 @@ watch(
28
33
  { immediate: true },
29
34
  );
30
35
 
31
- const failed = computed(
32
- () => props.calls.filter((call) => call.toolMessage?.error).length,
33
- );
36
+ const labels = useChatLabels();
37
+ const failed = computed(() => ɵcountFailedToolCalls(props.calls));
34
38
  const liveMs = computed(() =>
35
39
  props.elapsedMs !== undefined && props.workingSince !== undefined
36
40
  ? props.elapsedMs + Math.max(0, now.value - props.workingSince)
@@ -39,9 +43,33 @@ const liveMs = computed(() =>
39
43
  // A finished reply worked for at least a second.
40
44
  const duration = computed(() =>
41
45
  props.elapsedMs !== undefined
42
- ? formatDuration(Math.max(1000, props.elapsedMs))
46
+ ? ɵformatWorkDuration(Math.max(1000, props.elapsedMs))
43
47
  : undefined,
44
48
  );
49
+ const toolCount = computed(() =>
50
+ ɵformatCountLabel(props.calls.length, {
51
+ one: labels.value.toolCallToolCountOne,
52
+ other: labels.value.toolCallToolCountOther,
53
+ }),
54
+ );
55
+ const doneLabel = computed(() =>
56
+ duration.value
57
+ ? ɵformatLabel(labels.value.workSummaryWorkedForLabel, {
58
+ duration: duration.value,
59
+ })
60
+ : ɵformatCountLabel(props.calls.length, {
61
+ one: labels.value.toolCallUsedToolsOne,
62
+ other: labels.value.toolCallUsedToolsOther,
63
+ }),
64
+ );
65
+ const liveDuration = computed(() =>
66
+ liveMs.value !== undefined ? ɵformatWorkDuration(liveMs.value) : undefined,
67
+ );
68
+ const failedLabel = computed(() =>
69
+ ɵformatLabel(labels.value.toolCallFailedCountLabel, {
70
+ count: failed.value,
71
+ }),
72
+ );
45
73
  </script>
46
74
 
47
75
  <template>
@@ -54,25 +82,23 @@ const duration = computed(() =>
54
82
  class="cpk-work-summary"
55
83
  >
56
84
  <template v-if="isWorking">
57
- <span class="cpk-work-summary-label cpk-shimmer">Working</span>
85
+ <span class="cpk-work-summary-label cpk-shimmer">{{
86
+ labels.workSummaryWorkingLabel
87
+ }}</span>
58
88
  <span
59
- v-if="liveMs !== undefined"
89
+ v-if="liveDuration !== undefined"
60
90
  class="cpk-work-summary-detail cpk-work-summary-timer"
61
- >{{ formatDuration(liveMs) }}</span
91
+ >{{ liveDuration }}</span
62
92
  >
63
93
  </template>
64
94
  <template v-else>
65
- <span class="cpk-work-summary-label">{{
66
- duration
67
- ? `Worked for ${duration}`
68
- : `Used ${pluralize(calls.length, "tool")}`
69
- }}</span>
95
+ <span class="cpk-work-summary-label">{{ doneLabel }}</span>
70
96
  <span v-if="duration" class="cpk-work-summary-detail">{{
71
- pluralize(calls.length, "tool")
97
+ toolCount
98
+ }}</span>
99
+ <span v-if="failed > 0" class="cpk-work-summary-failed">{{
100
+ failedLabel
72
101
  }}</span>
73
- <span v-if="failed > 0" class="cpk-work-summary-failed"
74
- >{{ failed }} failed</span
75
- >
76
102
  </template>
77
103
  <IconChevronRight
78
104
  aria-hidden="true"
@@ -1,7 +1,7 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { defineComponent, nextTick, onMounted } from "vue";
3
3
  import { fireEvent, render, screen } from "@testing-library/vue";
4
- import type { Message, ReasoningMessage } from "@ag-ui/core";
4
+ import type { ReasoningMessage } from "@ag-ui/core";
5
5
  import type { Attachment } from "@copilotkit/shared";
6
6
  import CopilotKitProvider from "../../../providers/CopilotKitProvider.vue";
7
7
  import CopilotChatConfigurationProvider from "../../../providers/CopilotChatConfigurationProvider.vue";
@@ -16,6 +16,8 @@ import { CopilotModalHeader, CopilotSidebarView } from "../index";
16
16
  // hard-coded English.
17
17
  const germanLabels: Partial<Record<keyof CopilotChatLabels, string>> = {
18
18
  reasoningMessageThinkingLabel: "Denkt nach…",
19
+ reasoningMessageThoughtForLabel: "Gedacht für {duration}",
20
+ reasoningMessageDurationFewSeconds: "ein paar Sekunden",
19
21
  chatInputSlashMenuLabel: "Befehle",
20
22
  chatInputSlashMenuEmptyText: "Keine Befehle gefunden",
21
23
  attachmentRemoveButtonLabel: "Anhang entfernen",
@@ -26,11 +28,11 @@ const germanLabels: Partial<Record<keyof CopilotChatLabels, string>> = {
26
28
  sidebarLabel: "Chat-Seitenleiste",
27
29
  };
28
30
 
29
- const reasoning = {
31
+ const reasoning: ReasoningMessage = {
30
32
  id: "reasoning-1",
31
33
  role: "reasoning",
32
34
  content: "",
33
- } as ReasoningMessage;
35
+ };
34
36
 
35
37
  const video: Attachment = {
36
38
  id: "clip",
@@ -66,7 +68,7 @@ function renderWithLabels(template: string) {
66
68
  return {
67
69
  labels: germanLabels,
68
70
  reasoning,
69
- reasoningMessages: [reasoning] as Message[],
71
+ reasoningMessages: [reasoning],
70
72
  attachments: [video],
71
73
  toolsMenu: [{ label: "Say hi", action: vi.fn() }],
72
74
  };
@@ -112,6 +114,19 @@ describe("chat strings from labels", () => {
112
114
  expect(screen.queryByText("Thinking…")).toBeNull();
113
115
  });
114
116
 
117
+ it("uses the configured duration labels once the model is done", () => {
118
+ renderWithLabels(`
119
+ <CopilotChatReasoningMessage
120
+ :message="reasoning"
121
+ :messages="reasoningMessages"
122
+ :is-running="false"
123
+ />
124
+ `);
125
+
126
+ expect(screen.getByText("Gedacht für ein paar Sekunden")).toBeDefined();
127
+ expect(screen.queryByText(/Thought for/)).toBeNull();
128
+ });
129
+
115
130
  it("passes the elapsed reasoning time to a custom header", async () => {
116
131
  vi.useFakeTimers();
117
132
  renderWithLabels(`
@@ -559,6 +559,38 @@ describe("CopilotChatInput", () => {
559
559
  expect(screen.getByRole("button", { name: "Stop" })).toBe(getSendButton());
560
560
  });
561
561
 
562
+ describe("Enter while the agent runs", () => {
563
+ async function pressEnterWhileRunning(modelValue: string) {
564
+ const onStop = vi.fn();
565
+ renderWithProvider({
566
+ props: { modelValue, isRunning: true },
567
+ listeners: { onSubmitMessage: mockOnSubmitMessage, onStop },
568
+ });
569
+ await fireEvent.keyDown(
570
+ screen.getByPlaceholderText("Type a message..."),
571
+ {
572
+ key: "Enter",
573
+ shiftKey: false,
574
+ },
575
+ );
576
+ return onStop;
577
+ }
578
+
579
+ it("sends the typed text and does not stop the run", async () => {
580
+ const onStop = await pressEnterWhileRunning(" next question ");
581
+
582
+ expect(mockOnSubmitMessage).toHaveBeenCalledWith("next question");
583
+ expect(onStop).not.toHaveBeenCalled();
584
+ });
585
+
586
+ it("stops the run when the input is empty", async () => {
587
+ const onStop = await pressEnterWhileRunning("");
588
+
589
+ expect(onStop).toHaveBeenCalledOnce();
590
+ expect(mockOnSubmitMessage).not.toHaveBeenCalled();
591
+ });
592
+ });
593
+
562
594
  it("names the send button with the configured labels", () => {
563
595
  renderWithProvider({
564
596
  template: `
@@ -799,7 +831,7 @@ describe("CopilotChatInput", () => {
799
831
 
800
832
  await fireEvent.click(addButton);
801
833
  const menuItem = await screen.findByRole("menuitem", {
802
- name: "Add photos or files",
834
+ name: "Add attachments",
803
835
  });
804
836
  await fireEvent.click(menuItem);
805
837
  expect(handleAddFile).toHaveBeenCalledTimes(1);