@copilotkit/vue 1.79.1 → 1.80.0
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/PARITY.md +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{use-render-activity-message-B2gajRTK.js → use-render-activity-message-BhgLK8XD.js} +3066 -3040
- package/dist/use-render-activity-message-BhgLK8XD.js.map +1 -0
- package/dist/use-render-activity-message-DfvDG9VF.cjs +70 -0
- package/dist/use-render-activity-message-DfvDG9VF.cjs.map +1 -0
- package/dist/v2/components/MCPAppsActivityRenderer.d.ts +21 -0
- package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +30 -0
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts +12 -0
- package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts +5 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/types.d.ts +5 -0
- package/dist/v2/providers/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/v2/components/MCPAppsActivityRenderer.ts +14 -0
- package/src/v2/components/__tests__/MCPAppsActivityRenderer.e2e.test.ts +74 -2
- package/src/v2/components/chat/CopilotChatInput.vue +5 -1
- package/src/v2/components/chat/CopilotChatUserMessage.vue +4 -4
- package/src/v2/components/chat/CopilotChatView.vue +1 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +39 -0
- package/src/v2/components/chat/__tests__/CopilotChatUserMessage.test.ts +42 -1
- package/src/v2/components/chat/__tests__/CopilotChatView.test.ts +56 -0
- package/src/v2/providers/CopilotKitProvider.types.ts +12 -0
- package/src/v2/providers/CopilotKitProvider.vue +14 -2
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxReplay.test.ts +157 -0
- package/src/v2/providers/types.ts +5 -0
- package/dist/use-render-activity-message-B2gajRTK.js.map +0 -1
- package/dist/use-render-activity-message-CxZhxbqx.cjs +0 -70
- package/dist/use-render-activity-message-CxZhxbqx.cjs.map +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { h, nextTick } from "vue";
|
|
2
|
+
import { defineComponent, h, nextTick } from "vue";
|
|
3
|
+
import { render, screen } from "@testing-library/vue";
|
|
3
4
|
import { mount } from "@vue/test-utils";
|
|
4
5
|
import type { UserMessage } from "@ag-ui/core";
|
|
5
6
|
import { CopilotChatDefaultLabels } from "../../../providers/types";
|
|
6
7
|
import CopilotChatUserMessage from "../CopilotChatUserMessage.vue";
|
|
8
|
+
import CopilotChatConfigurationProvider from "../../../providers/CopilotChatConfigurationProvider.vue";
|
|
7
9
|
|
|
8
10
|
describe("CopilotChatUserMessage", () => {
|
|
9
11
|
let originalClipboard: Clipboard | undefined;
|
|
@@ -101,6 +103,45 @@ describe("CopilotChatUserMessage", () => {
|
|
|
101
103
|
).toBe(false);
|
|
102
104
|
});
|
|
103
105
|
|
|
106
|
+
it("names the branch buttons with the configured labels", () => {
|
|
107
|
+
const Host = defineComponent({
|
|
108
|
+
components: { CopilotChatConfigurationProvider, CopilotChatUserMessage },
|
|
109
|
+
setup() {
|
|
110
|
+
return {
|
|
111
|
+
message: {
|
|
112
|
+
id: "user-5",
|
|
113
|
+
role: "user",
|
|
114
|
+
timestamp: new Date(),
|
|
115
|
+
content: "Branch message",
|
|
116
|
+
} as UserMessage,
|
|
117
|
+
labels: {
|
|
118
|
+
userMessageToolbarPreviousBranchLabel: "Vorherige Antwort",
|
|
119
|
+
userMessageToolbarNextBranchLabel: "Nächste Antwort",
|
|
120
|
+
},
|
|
121
|
+
onSwitchToBranch: vi.fn(),
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
template: `
|
|
125
|
+
<CopilotChatConfigurationProvider thread-id="thread-1" :labels="labels">
|
|
126
|
+
<CopilotChatUserMessage
|
|
127
|
+
:message="message"
|
|
128
|
+
:branch-index="1"
|
|
129
|
+
:number-of-branches="3"
|
|
130
|
+
:on-switch-to-branch="onSwitchToBranch"
|
|
131
|
+
/>
|
|
132
|
+
</CopilotChatConfigurationProvider>
|
|
133
|
+
`,
|
|
134
|
+
});
|
|
135
|
+
render(Host);
|
|
136
|
+
|
|
137
|
+
expect(
|
|
138
|
+
screen.getByRole("button", { name: "Vorherige Antwort" }),
|
|
139
|
+
).toBeDefined();
|
|
140
|
+
expect(
|
|
141
|
+
screen.getByRole("button", { name: "Nächste Antwort" }),
|
|
142
|
+
).toBeDefined();
|
|
143
|
+
});
|
|
144
|
+
|
|
104
145
|
it("renders branch navigation and emits switch payload exactly once", async () => {
|
|
105
146
|
const onSwitchToBranch = vi.fn();
|
|
106
147
|
const message: UserMessage = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { defineComponent, h, nextTick, ref } from "vue";
|
|
3
3
|
import { mount } from "@vue/test-utils";
|
|
4
|
+
import { render, screen } from "@testing-library/vue";
|
|
4
5
|
import type { AssistantMessage, Message, UserMessage } from "@ag-ui/core";
|
|
5
6
|
import type { Suggestion } from "@copilotkit/core";
|
|
6
7
|
import CopilotKitProvider from "../../../providers/CopilotKitProvider.vue";
|
|
@@ -404,6 +405,61 @@ describe("CopilotChatView", () => {
|
|
|
404
405
|
.trigger("click");
|
|
405
406
|
});
|
|
406
407
|
|
|
408
|
+
it("names the scroll-to-bottom button with the configured label", async () => {
|
|
409
|
+
const Host = defineComponent({
|
|
410
|
+
components: {
|
|
411
|
+
CopilotKitProvider,
|
|
412
|
+
CopilotChatConfigurationProvider,
|
|
413
|
+
CopilotChatView,
|
|
414
|
+
},
|
|
415
|
+
setup() {
|
|
416
|
+
return {
|
|
417
|
+
messages: createChatMessages(12),
|
|
418
|
+
labels: { chatScrollToBottomLabel: "Nach unten scrollen" },
|
|
419
|
+
};
|
|
420
|
+
},
|
|
421
|
+
template: `
|
|
422
|
+
<CopilotKitProvider runtime-url="/api/copilotkit">
|
|
423
|
+
<CopilotChatConfigurationProvider
|
|
424
|
+
thread-id="thread-1"
|
|
425
|
+
agent-id="default"
|
|
426
|
+
:labels="labels"
|
|
427
|
+
>
|
|
428
|
+
<CopilotChatView :messages="messages" />
|
|
429
|
+
</CopilotChatConfigurationProvider>
|
|
430
|
+
</CopilotKitProvider>
|
|
431
|
+
`,
|
|
432
|
+
});
|
|
433
|
+
render(Host);
|
|
434
|
+
|
|
435
|
+
// Let the initial auto-scroll settle, then scroll away from the bottom so
|
|
436
|
+
// the button appears.
|
|
437
|
+
const scrollElement = await screen.findByTestId("copilot-chat-view-scroll");
|
|
438
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
439
|
+
let scrollTop = 0;
|
|
440
|
+
Object.defineProperty(scrollElement, "scrollHeight", {
|
|
441
|
+
configurable: true,
|
|
442
|
+
get: () => 1000,
|
|
443
|
+
});
|
|
444
|
+
Object.defineProperty(scrollElement, "clientHeight", {
|
|
445
|
+
configurable: true,
|
|
446
|
+
get: () => 300,
|
|
447
|
+
});
|
|
448
|
+
Object.defineProperty(scrollElement, "scrollTop", {
|
|
449
|
+
configurable: true,
|
|
450
|
+
get: () => scrollTop,
|
|
451
|
+
set: (value: number) => {
|
|
452
|
+
scrollTop = value;
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
scrollTop = 120;
|
|
456
|
+
scrollElement.dispatchEvent(new Event("scroll"));
|
|
457
|
+
|
|
458
|
+
expect(
|
|
459
|
+
await screen.findByRole("button", { name: "Nach unten scrollen" }),
|
|
460
|
+
).toBeDefined();
|
|
461
|
+
});
|
|
462
|
+
|
|
407
463
|
it("preserves scroll position when new messages arrive and the user is scrolled up", async () => {
|
|
408
464
|
const messages = ref<Message[]>(createChatMessages(12));
|
|
409
465
|
|
|
@@ -92,6 +92,18 @@ export interface CopilotKitProviderProps {
|
|
|
92
92
|
loadingComponent?: Component;
|
|
93
93
|
includeSchema?: boolean;
|
|
94
94
|
};
|
|
95
|
+
/** Configuration for the built-in MCP Apps renderer. */
|
|
96
|
+
mcpApps?: {
|
|
97
|
+
/**
|
|
98
|
+
* Restore `allow-same-origin` on the MCP App sandbox. Default `false`.
|
|
99
|
+
*
|
|
100
|
+
* **Unsafe.** The widget then runs with your page's origin: it can read
|
|
101
|
+
* your cookies, storage and DOM, and call your APIs as the signed-in user.
|
|
102
|
+
* Only set it for widgets you wrote and trust that need `localStorage`,
|
|
103
|
+
* `IndexedDB` or cookies. By default widgets get an opaque origin.
|
|
104
|
+
*/
|
|
105
|
+
allowSameOriginFallback?: boolean;
|
|
106
|
+
};
|
|
95
107
|
/**
|
|
96
108
|
* Enable debug logging for the client-side event pipeline.
|
|
97
109
|
*
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
triggerRef,
|
|
11
11
|
watch,
|
|
12
12
|
} from "vue";
|
|
13
|
+
import type { Component, FunctionalComponent } from "vue";
|
|
13
14
|
import { z } from "zod";
|
|
14
15
|
import type { AbstractAgent } from "@ag-ui/client";
|
|
15
16
|
import { ToolCallStatus } from "@copilotkit/core";
|
|
@@ -91,6 +92,13 @@ const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follo
|
|
|
91
92
|
- Minimal transitions (150ms) for hover/focus states only. No decorative animations.
|
|
92
93
|
- Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
|
|
93
94
|
|
|
95
|
+
/** The MCP Apps renderer with `mcpApps.allowSameOriginFallback` applied. */
|
|
96
|
+
const SameOriginMCPAppsActivityRenderer: FunctionalComponent = (_, { attrs }) =>
|
|
97
|
+
h(MCPAppsActivityRenderer as Component, {
|
|
98
|
+
...attrs,
|
|
99
|
+
allowSameOriginFallback: true,
|
|
100
|
+
});
|
|
101
|
+
|
|
94
102
|
const props = withDefaults(defineProps<CopilotKitProviderProps>(), {
|
|
95
103
|
headers: () => ({}),
|
|
96
104
|
properties: () => ({}),
|
|
@@ -103,6 +111,7 @@ const props = withDefaults(defineProps<CopilotKitProviderProps>(), {
|
|
|
103
111
|
openGenerativeUI: undefined,
|
|
104
112
|
useSingleEndpoint: undefined,
|
|
105
113
|
a2ui: undefined,
|
|
114
|
+
mcpApps: undefined,
|
|
106
115
|
enableInspector: undefined,
|
|
107
116
|
});
|
|
108
117
|
|
|
@@ -424,6 +433,8 @@ const builtInFrontendTools = computed<VueFrontendTool[]>(() => {
|
|
|
424
433
|
description: GENERATE_SANDBOXED_UI_DESCRIPTION,
|
|
425
434
|
parameters: GenerateSandboxedUiArgsSchema,
|
|
426
435
|
handler: async () => "UI generated",
|
|
436
|
+
// This acknowledgement has no side effects and is safe to reconstruct.
|
|
437
|
+
reconnectBehavior: "resume-pending",
|
|
427
438
|
followUp: true,
|
|
428
439
|
render:
|
|
429
440
|
OpenGenerativeUIToolRenderer as unknown as VueFrontendTool["render"],
|
|
@@ -438,8 +449,9 @@ const builtInActivityRenderers = computed<
|
|
|
438
449
|
{
|
|
439
450
|
activityType: MCPAppsActivityType,
|
|
440
451
|
content: MCPAppsActivityContentSchema as unknown as z.ZodSchema<unknown>,
|
|
441
|
-
render:
|
|
442
|
-
|
|
452
|
+
render: (props.mcpApps?.allowSameOriginFallback
|
|
453
|
+
? SameOriginMCPAppsActivityRenderer
|
|
454
|
+
: MCPAppsActivityRenderer) as unknown as VueActivityMessageRenderer<unknown>["render"],
|
|
443
455
|
},
|
|
444
456
|
];
|
|
445
457
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { cleanup, render, waitFor } from "@testing-library/vue";
|
|
2
|
+
import { defineComponent } from "vue";
|
|
3
|
+
import CopilotKitProvider from "../CopilotKitProvider.vue";
|
|
4
|
+
import { useCopilotKit } from "../useCopilotKit";
|
|
5
|
+
import { HttpAgent } from "@ag-ui/client";
|
|
6
|
+
import type { Message } from "@ag-ui/client";
|
|
7
|
+
import type { CopilotKitCore } from "@copilotkit/core";
|
|
8
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
9
|
+
|
|
10
|
+
/** Capture the real provider core after its built-in sandbox tool is registered. */
|
|
11
|
+
async function mountProvider() {
|
|
12
|
+
let core: CopilotKitCore | undefined;
|
|
13
|
+
const Capture = defineComponent({
|
|
14
|
+
/** Expose the injected core without retaining Vue's reactive wrapper. */
|
|
15
|
+
setup() {
|
|
16
|
+
core = useCopilotKit().copilotkit.value;
|
|
17
|
+
return () => null;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
render(
|
|
21
|
+
defineComponent({
|
|
22
|
+
components: { CopilotKitProvider, Capture },
|
|
23
|
+
template:
|
|
24
|
+
'<CopilotKitProvider :open-generative-u-i="{}"><Capture /></CopilotKitProvider>',
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
await waitFor(() =>
|
|
28
|
+
expect(core?.getTool({ toolName: "generateSandboxedUi" })).toBeDefined(),
|
|
29
|
+
);
|
|
30
|
+
if (!core) throw new Error("Missing core");
|
|
31
|
+
return core;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Hydrate imported tool calls with controlled approval and side-effect handlers. */
|
|
35
|
+
async function setupReplay(completed = false) {
|
|
36
|
+
const core = await mountProvider();
|
|
37
|
+
const agent = new HttpAgent({
|
|
38
|
+
url: "https://runtime.invalid",
|
|
39
|
+
agentId: "default",
|
|
40
|
+
threadId: "imported-thread",
|
|
41
|
+
});
|
|
42
|
+
const saved: Message[] = [
|
|
43
|
+
{
|
|
44
|
+
id: "imported-message",
|
|
45
|
+
role: "assistant",
|
|
46
|
+
toolCalls: [
|
|
47
|
+
{
|
|
48
|
+
id: "sandbox-id",
|
|
49
|
+
type: "function",
|
|
50
|
+
function: {
|
|
51
|
+
name: "generateSandboxedUi",
|
|
52
|
+
arguments: JSON.stringify({ html: "<button>Calculator</button>" }),
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "approval-id",
|
|
57
|
+
type: "function",
|
|
58
|
+
function: { name: "meeting", arguments: "{}" },
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "side-effect-id",
|
|
62
|
+
type: "function",
|
|
63
|
+
function: { name: "sendEmail", arguments: "{}" },
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
if (completed)
|
|
69
|
+
saved.push(
|
|
70
|
+
{
|
|
71
|
+
id: "saved-sandbox",
|
|
72
|
+
role: "tool",
|
|
73
|
+
toolCallId: "sandbox-id",
|
|
74
|
+
content: "UI generated",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "saved-approval",
|
|
78
|
+
role: "tool",
|
|
79
|
+
toolCallId: "approval-id",
|
|
80
|
+
content: "approved",
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
vi.spyOn(agent, "connectAgent").mockImplementation(async () => {
|
|
84
|
+
agent.setMessages(structuredClone(saved));
|
|
85
|
+
return { result: undefined, newMessages: structuredClone(saved) };
|
|
86
|
+
});
|
|
87
|
+
const submissions: Message[][] = [];
|
|
88
|
+
const run = vi.spyOn(agent, "runAgent").mockImplementation(async () => {
|
|
89
|
+
submissions.push(structuredClone(agent.messages));
|
|
90
|
+
return { result: undefined, newMessages: [] };
|
|
91
|
+
});
|
|
92
|
+
let answer!: (value: string) => void;
|
|
93
|
+
const pending = new Promise<string>((resolve) => {
|
|
94
|
+
answer = resolve;
|
|
95
|
+
});
|
|
96
|
+
const approval = vi.fn(() => pending);
|
|
97
|
+
const sideEffect = vi.fn(async () => "sent");
|
|
98
|
+
core.addTool({
|
|
99
|
+
name: "meeting",
|
|
100
|
+
type: "human-in-the-loop",
|
|
101
|
+
handler: approval,
|
|
102
|
+
});
|
|
103
|
+
core.addTool({ name: "sendEmail", handler: sideEffect });
|
|
104
|
+
return { core, agent, saved, run, submissions, answer, approval, sideEffect };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
describe("CopilotKitProvider — sandboxed UI replay", () => {
|
|
108
|
+
afterEach(() => {
|
|
109
|
+
cleanup();
|
|
110
|
+
vi.restoreAllMocks();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("restores the original sandbox result before the pending approval continues", async () => {
|
|
114
|
+
const { core, agent, run, submissions, answer, approval, sideEffect } =
|
|
115
|
+
await setupReplay();
|
|
116
|
+
await core.connectAgent({ agent });
|
|
117
|
+
await waitFor(() => expect(approval).toHaveBeenCalledTimes(1));
|
|
118
|
+
expect(agent.messages).toContainEqual(
|
|
119
|
+
expect.objectContaining({
|
|
120
|
+
role: "tool",
|
|
121
|
+
toolCallId: "sandbox-id",
|
|
122
|
+
content: "UI generated",
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
expect(run).not.toHaveBeenCalled();
|
|
126
|
+
answer("approved");
|
|
127
|
+
await waitFor(() => expect(run).toHaveBeenCalledTimes(1));
|
|
128
|
+
expect(
|
|
129
|
+
submissions[0]?.filter((message) => message.role === "tool"),
|
|
130
|
+
).toEqual([
|
|
131
|
+
expect.objectContaining({
|
|
132
|
+
toolCallId: "sandbox-id",
|
|
133
|
+
content: "UI generated",
|
|
134
|
+
}),
|
|
135
|
+
expect.objectContaining({
|
|
136
|
+
toolCallId: "approval-id",
|
|
137
|
+
content: "approved",
|
|
138
|
+
}),
|
|
139
|
+
]);
|
|
140
|
+
expect(sideEffect).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("leaves saved sandbox and approval results completed across repeated reconnects", async () => {
|
|
144
|
+
const { core, agent, saved, run, approval, sideEffect } =
|
|
145
|
+
await setupReplay(true);
|
|
146
|
+
const tool = core.getTool({ toolName: "generateSandboxedUi" });
|
|
147
|
+
if (!tool?.handler) throw new Error("Missing sandbox handler");
|
|
148
|
+
const handler = vi.spyOn(tool, "handler");
|
|
149
|
+
await core.connectAgent({ agent });
|
|
150
|
+
await core.connectAgent({ agent });
|
|
151
|
+
expect(agent.messages).toEqual(saved);
|
|
152
|
+
expect(handler).not.toHaveBeenCalled();
|
|
153
|
+
expect(approval).not.toHaveBeenCalled();
|
|
154
|
+
expect(sideEffect).not.toHaveBeenCalled();
|
|
155
|
+
expect(run).not.toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -5,6 +5,8 @@ export const CopilotChatDefaultLabels = {
|
|
|
5
5
|
chatInputToolbarFinishTranscribeButtonLabel: "Finish",
|
|
6
6
|
chatInputToolbarAddButtonLabel: "Add photos or files",
|
|
7
7
|
chatInputToolbarToolsButtonLabel: "Tools",
|
|
8
|
+
chatInputToolbarSendButtonLabel: "Send message",
|
|
9
|
+
chatInputToolbarStopButtonLabel: "Stop",
|
|
8
10
|
assistantMessageToolbarCopyCodeLabel: "Copy",
|
|
9
11
|
assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
|
|
10
12
|
assistantMessageToolbarCopyMessageLabel: "Copy",
|
|
@@ -14,10 +16,13 @@ export const CopilotChatDefaultLabels = {
|
|
|
14
16
|
assistantMessageToolbarRegenerateLabel: "Regenerate",
|
|
15
17
|
userMessageToolbarCopyMessageLabel: "Copy",
|
|
16
18
|
userMessageToolbarEditMessageLabel: "Edit",
|
|
19
|
+
userMessageToolbarPreviousBranchLabel: "Previous branch",
|
|
20
|
+
userMessageToolbarNextBranchLabel: "Next branch",
|
|
17
21
|
chatDisclaimerText:
|
|
18
22
|
"AI can make mistakes. Please verify important information.",
|
|
19
23
|
chatToggleOpenLabel: "Open chat",
|
|
20
24
|
chatToggleCloseLabel: "Close chat",
|
|
25
|
+
chatScrollToBottomLabel: "Scroll to bottom",
|
|
21
26
|
modalHeaderTitle: "CopilotKit Chat",
|
|
22
27
|
welcomeMessageText: "How can I help you today?",
|
|
23
28
|
} as const;
|