@copilotkit/react-core 1.55.1 → 1.55.2-next.1
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/CHANGELOG.md +22 -0
- package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -1
- package/dist/{copilotkit-BY5S1-0P.mjs → copilotkit-Cd-NrDyp.mjs} +46 -16
- package/dist/copilotkit-Cd-NrDyp.mjs.map +1 -0
- package/dist/{copilotkit-Bz5-ImDl.cjs → copilotkit-Dgdpbqjt.cjs} +46 -16
- package/dist/copilotkit-Dgdpbqjt.cjs.map +1 -0
- package/dist/copilotkit-dwDWYpya.d.cts.map +1 -1
- package/dist/index.cjs +6 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +6 -3
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +28 -29
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/index.umd.js +52 -28
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +6 -6
- package/src/components/copilot-provider/copilotkit.tsx +2 -2
- package/src/hooks/use-agent-nodename.ts +3 -0
- package/src/hooks/use-coagent-state-render-bridge.helpers.ts +2 -1
- package/src/hooks/use-coagent-state-render-registry.ts +6 -6
- package/src/hooks/use-copilot-chat_internal.ts +1 -1
- package/src/lib/copilot-task.ts +1 -1
- package/src/utils/utils.ts +0 -2
- package/src/v2/a2ui/A2UIMessageRenderer.tsx +1 -1
- package/src/v2/components/MCPAppsActivityRenderer.tsx +32 -2
- package/src/v2/components/chat/CopilotChatMessageView.tsx +41 -5
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.tsx +192 -82
- package/src/v2/components/chat/__tests__/MCPAppsProxy.e2e.test.tsx +589 -0
- package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.tsx +458 -0
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +2 -2
- package/dist/copilotkit-BY5S1-0P.mjs.map +0 -1
- package/dist/copilotkit-Bz5-ImDl.cjs.map +0 -1
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for MCP Apps ui/message handler behavior.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the followUp logic that controls whether the agent is invoked
|
|
5
|
+
* after an MCP app sends a ui/message request via JSON-RPC:
|
|
6
|
+
*
|
|
7
|
+
* shouldFollowUp = params.followUp ?? role === "user"
|
|
8
|
+
*
|
|
9
|
+
* - User-role messages invoke runAgent (default followUp = true)
|
|
10
|
+
* - Assistant-role messages do NOT invoke runAgent (default followUp = false)
|
|
11
|
+
* - followUp: false skips runAgent regardless of role
|
|
12
|
+
* - followUp: true forces runAgent regardless of role
|
|
13
|
+
* - addMessage is always called when textContent is present
|
|
14
|
+
*/
|
|
15
|
+
import { fireEvent, screen, waitFor, act } from "@testing-library/react";
|
|
16
|
+
import { vi } from "vitest";
|
|
17
|
+
import {
|
|
18
|
+
activitySnapshotEvent,
|
|
19
|
+
renderWithCopilotKit,
|
|
20
|
+
runFinishedEvent,
|
|
21
|
+
runStartedEvent,
|
|
22
|
+
testId,
|
|
23
|
+
} from "../../../__tests__/utils/test-helpers";
|
|
24
|
+
import { MCPAppsActivityType } from "../../../components/MCPAppsActivityRenderer";
|
|
25
|
+
import {
|
|
26
|
+
AbstractAgent,
|
|
27
|
+
RunAgentInput,
|
|
28
|
+
RunAgentResult,
|
|
29
|
+
BaseEvent,
|
|
30
|
+
EventType,
|
|
31
|
+
} from "@ag-ui/client";
|
|
32
|
+
import { Observable, Subject } from "rxjs";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* MockMCPProxyAgent with spying support for ui/message tests.
|
|
36
|
+
*/
|
|
37
|
+
class MockMCPProxyAgent extends AbstractAgent {
|
|
38
|
+
private subject = new Subject<BaseEvent>();
|
|
39
|
+
public runAgentCalls: Array<{ input: Partial<RunAgentInput> }> = [];
|
|
40
|
+
public addMessageCalls: Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
role: string;
|
|
43
|
+
content: string;
|
|
44
|
+
}> = [];
|
|
45
|
+
|
|
46
|
+
private runAgentResponses: Map<string, unknown> = new Map();
|
|
47
|
+
|
|
48
|
+
setRunAgentResponse(method: string, response: unknown) {
|
|
49
|
+
this.runAgentResponses.set(method, response);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
emit(event: BaseEvent) {
|
|
53
|
+
if (event.type === EventType.RUN_STARTED) {
|
|
54
|
+
this.isRunning = true;
|
|
55
|
+
} else if (
|
|
56
|
+
event.type === EventType.RUN_FINISHED ||
|
|
57
|
+
event.type === EventType.RUN_ERROR
|
|
58
|
+
) {
|
|
59
|
+
this.isRunning = false;
|
|
60
|
+
}
|
|
61
|
+
act(() => {
|
|
62
|
+
this.subject.next(event);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
complete() {
|
|
67
|
+
this.isRunning = false;
|
|
68
|
+
act(() => {
|
|
69
|
+
this.subject.complete();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
clone(): MockMCPProxyAgent {
|
|
74
|
+
const cloned = new MockMCPProxyAgent();
|
|
75
|
+
cloned.agentId = this.agentId;
|
|
76
|
+
type Internal = {
|
|
77
|
+
subject: Subject<BaseEvent>;
|
|
78
|
+
runAgentCalls: Array<{ input: Partial<RunAgentInput> }>;
|
|
79
|
+
addMessageCalls: Array<{ id: string; role: string; content: string }>;
|
|
80
|
+
runAgentResponses: Map<string, unknown>;
|
|
81
|
+
};
|
|
82
|
+
(cloned as unknown as Internal).subject = (
|
|
83
|
+
this as unknown as Internal
|
|
84
|
+
).subject;
|
|
85
|
+
(cloned as unknown as Internal).runAgentCalls = (
|
|
86
|
+
this as unknown as Internal
|
|
87
|
+
).runAgentCalls;
|
|
88
|
+
(cloned as unknown as Internal).addMessageCalls = (
|
|
89
|
+
this as unknown as Internal
|
|
90
|
+
).addMessageCalls;
|
|
91
|
+
(cloned as unknown as Internal).runAgentResponses = (
|
|
92
|
+
this as unknown as Internal
|
|
93
|
+
).runAgentResponses;
|
|
94
|
+
|
|
95
|
+
const registry = this;
|
|
96
|
+
Object.defineProperty(cloned, "isRunning", {
|
|
97
|
+
get() {
|
|
98
|
+
return registry.isRunning;
|
|
99
|
+
},
|
|
100
|
+
set(v: boolean) {
|
|
101
|
+
registry.isRunning = v;
|
|
102
|
+
},
|
|
103
|
+
configurable: true,
|
|
104
|
+
enumerable: true,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const proto = MockMCPProxyAgent.prototype;
|
|
108
|
+
cloned.runAgent = async function (
|
|
109
|
+
input?: Partial<RunAgentInput>,
|
|
110
|
+
): Promise<RunAgentResult> {
|
|
111
|
+
const proxiedRequest = input?.forwardedProps?.__proxiedMCPRequest;
|
|
112
|
+
if (proxiedRequest) {
|
|
113
|
+
return registry.runAgent(input);
|
|
114
|
+
}
|
|
115
|
+
return proto.runAgent.call(cloned, input);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Track addMessage calls on the clone (the component uses the clone)
|
|
119
|
+
const origAddMessage = cloned.addMessage.bind(cloned);
|
|
120
|
+
cloned.addMessage = function (msg: Parameters<typeof origAddMessage>[0]) {
|
|
121
|
+
registry.addMessageCalls.push(msg as any);
|
|
122
|
+
return origAddMessage(msg);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Proxy run() calls so spies on the registry's run() see clone invocations
|
|
126
|
+
cloned.run = function (input: RunAgentInput): Observable<BaseEvent> {
|
|
127
|
+
return registry.run(input);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return cloned;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async detachActiveRun(): Promise<void> {}
|
|
134
|
+
|
|
135
|
+
run(_input: RunAgentInput): Observable<BaseEvent> {
|
|
136
|
+
return this.subject.asObservable();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async runAgent(input?: Partial<RunAgentInput>): Promise<RunAgentResult> {
|
|
140
|
+
const proxiedRequest = input?.forwardedProps?.__proxiedMCPRequest as
|
|
141
|
+
| {
|
|
142
|
+
serverHash?: string;
|
|
143
|
+
serverId?: string;
|
|
144
|
+
method: string;
|
|
145
|
+
params?: Record<string, unknown>;
|
|
146
|
+
}
|
|
147
|
+
| undefined;
|
|
148
|
+
|
|
149
|
+
if (proxiedRequest) {
|
|
150
|
+
if (input) {
|
|
151
|
+
this.runAgentCalls.push({ input });
|
|
152
|
+
}
|
|
153
|
+
const method = proxiedRequest.method;
|
|
154
|
+
const response = this.runAgentResponses.get(method);
|
|
155
|
+
if (response !== undefined) {
|
|
156
|
+
return { result: response, newMessages: [] };
|
|
157
|
+
}
|
|
158
|
+
if (method === "resources/read") {
|
|
159
|
+
return {
|
|
160
|
+
result: {
|
|
161
|
+
contents: [
|
|
162
|
+
{
|
|
163
|
+
uri: proxiedRequest.params?.uri,
|
|
164
|
+
mimeType: "text/html",
|
|
165
|
+
text: "<html><body>Test content</body></html>",
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
newMessages: [],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return { result: {}, newMessages: [] };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return super.runAgent(input);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function mcpAppsActivityContent(overrides: {
|
|
180
|
+
resourceUri?: string;
|
|
181
|
+
serverHash?: string;
|
|
182
|
+
}) {
|
|
183
|
+
return {
|
|
184
|
+
resourceUri: overrides.resourceUri ?? "ui://test-server/test-resource",
|
|
185
|
+
serverHash: overrides.serverHash ?? "abc123hash",
|
|
186
|
+
toolInput: {},
|
|
187
|
+
result: {
|
|
188
|
+
content: [{ type: "text", text: "Tool output" }],
|
|
189
|
+
isError: false,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Set up the agent, render, emit MCP activity, wait for iframe creation,
|
|
196
|
+
* then simulate sandbox-proxy-ready so the message handler gets installed.
|
|
197
|
+
*/
|
|
198
|
+
async function setupMCPActivity(
|
|
199
|
+
agent: MockMCPProxyAgent,
|
|
200
|
+
agentId: string,
|
|
201
|
+
userMessage: string,
|
|
202
|
+
): Promise<HTMLIFrameElement> {
|
|
203
|
+
agent.setRunAgentResponse("resources/read", {
|
|
204
|
+
contents: [
|
|
205
|
+
{
|
|
206
|
+
uri: "ui://test/app",
|
|
207
|
+
mimeType: "text/html",
|
|
208
|
+
text: "<html><body>App</body></html>",
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Use a unique threadId per test to avoid module-level mcpAppsRequestQueue
|
|
214
|
+
// state leaking between tests (the queue keys by threadId).
|
|
215
|
+
const threadId = testId("thread");
|
|
216
|
+
|
|
217
|
+
renderWithCopilotKit({
|
|
218
|
+
agents: { [agentId]: agent },
|
|
219
|
+
agentId,
|
|
220
|
+
threadId,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const input = await screen.findByRole("textbox");
|
|
224
|
+
fireEvent.change(input, { target: { value: userMessage } });
|
|
225
|
+
fireEvent.keyDown(input, { key: "Enter", code: "Enter" });
|
|
226
|
+
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(screen.getByText(userMessage)).toBeDefined();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
agent.emit(runStartedEvent());
|
|
232
|
+
agent.emit(
|
|
233
|
+
activitySnapshotEvent({
|
|
234
|
+
messageId: testId("mcp-activity"),
|
|
235
|
+
activityType: MCPAppsActivityType,
|
|
236
|
+
content: mcpAppsActivityContent({
|
|
237
|
+
resourceUri: "ui://test/app",
|
|
238
|
+
serverHash: "test-hash",
|
|
239
|
+
}),
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
242
|
+
agent.emit(runFinishedEvent());
|
|
243
|
+
|
|
244
|
+
// Wait for iframe to be created
|
|
245
|
+
let iframe: HTMLIFrameElement | null = null;
|
|
246
|
+
await waitFor(
|
|
247
|
+
() => {
|
|
248
|
+
iframe = document.querySelector("iframe[srcdoc]");
|
|
249
|
+
expect(iframe).not.toBeNull();
|
|
250
|
+
},
|
|
251
|
+
{ timeout: 3000 },
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Simulate sandbox-proxy-ready notification from the iframe.
|
|
255
|
+
// The message handler checks event.source === iframe.contentWindow.
|
|
256
|
+
// In jsdom, iframe.contentWindow exists for srcdoc iframes.
|
|
257
|
+
const readyEvent = new MessageEvent("message", {
|
|
258
|
+
data: {
|
|
259
|
+
jsonrpc: "2.0",
|
|
260
|
+
method: "ui/notifications/sandbox-proxy-ready",
|
|
261
|
+
},
|
|
262
|
+
source: iframe!.contentWindow,
|
|
263
|
+
origin: "",
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await act(async () => {
|
|
267
|
+
window.dispatchEvent(readyEvent);
|
|
268
|
+
// Give async setup() time to install the messageHandler
|
|
269
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
return iframe!;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Send a ui/message JSON-RPC request as if coming from the iframe.
|
|
277
|
+
*/
|
|
278
|
+
async function sendUiMessage(
|
|
279
|
+
iframe: HTMLIFrameElement,
|
|
280
|
+
params: {
|
|
281
|
+
role?: string;
|
|
282
|
+
content?: Array<{ type: string; text?: string }>;
|
|
283
|
+
followUp?: boolean;
|
|
284
|
+
},
|
|
285
|
+
) {
|
|
286
|
+
const msg = new MessageEvent("message", {
|
|
287
|
+
data: {
|
|
288
|
+
jsonrpc: "2.0",
|
|
289
|
+
id: testId("req"),
|
|
290
|
+
method: "ui/message",
|
|
291
|
+
params,
|
|
292
|
+
},
|
|
293
|
+
source: iframe.contentWindow,
|
|
294
|
+
origin: "",
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
await act(async () => {
|
|
298
|
+
window.dispatchEvent(msg);
|
|
299
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
describe("MCP Apps ui/message followUp behavior", () => {
|
|
304
|
+
beforeEach(() => {
|
|
305
|
+
vi.clearAllMocks();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("user-role message: addMessage IS called and runAgent IS invoked", async () => {
|
|
309
|
+
const agent = new MockMCPProxyAgent();
|
|
310
|
+
agent.agentId = "ui-msg-agent-user";
|
|
311
|
+
|
|
312
|
+
const iframe = await setupMCPActivity(
|
|
313
|
+
agent,
|
|
314
|
+
"ui-msg-agent-user",
|
|
315
|
+
"User role test",
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const runSpy = vi.spyOn(agent, "run");
|
|
319
|
+
|
|
320
|
+
await sendUiMessage(iframe, {
|
|
321
|
+
role: "user",
|
|
322
|
+
content: [{ type: "text", text: "Hello from MCP app" }],
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// addMessage should have been called
|
|
326
|
+
const userMsgCalls = agent.addMessageCalls.filter(
|
|
327
|
+
(c) => c.content === "Hello from MCP app" && c.role === "user",
|
|
328
|
+
);
|
|
329
|
+
expect(userMsgCalls.length).toBeGreaterThanOrEqual(1);
|
|
330
|
+
|
|
331
|
+
// runAgent should have been invoked (user role defaults to followUp: true)
|
|
332
|
+
expect(runSpy.mock.calls.length).toBeGreaterThan(0);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("assistant-role message: addMessage IS called but runAgent is NOT invoked", async () => {
|
|
336
|
+
const agent = new MockMCPProxyAgent();
|
|
337
|
+
agent.agentId = "ui-msg-agent-assist";
|
|
338
|
+
|
|
339
|
+
const iframe = await setupMCPActivity(
|
|
340
|
+
agent,
|
|
341
|
+
"ui-msg-agent-assist",
|
|
342
|
+
"Assist role test",
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
const runSpy = vi.spyOn(agent, "run");
|
|
346
|
+
|
|
347
|
+
await sendUiMessage(iframe, {
|
|
348
|
+
role: "assistant",
|
|
349
|
+
content: [{ type: "text", text: "Response from MCP" }],
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// addMessage should have been called
|
|
353
|
+
const assistCalls = agent.addMessageCalls.filter(
|
|
354
|
+
(c) => c.content === "Response from MCP" && c.role === "assistant",
|
|
355
|
+
);
|
|
356
|
+
expect(assistCalls.length).toBeGreaterThanOrEqual(1);
|
|
357
|
+
|
|
358
|
+
// run() should NOT have been called
|
|
359
|
+
expect(runSpy.mock.calls.length).toBe(0);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("followUp: false on user-role message: addMessage IS called but runAgent is NOT invoked", async () => {
|
|
363
|
+
const agent = new MockMCPProxyAgent();
|
|
364
|
+
agent.agentId = "ui-msg-agent-nofollowup";
|
|
365
|
+
|
|
366
|
+
const iframe = await setupMCPActivity(
|
|
367
|
+
agent,
|
|
368
|
+
"ui-msg-agent-nofollowup",
|
|
369
|
+
"No followUp test",
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
const runSpy = vi.spyOn(agent, "run");
|
|
373
|
+
|
|
374
|
+
await sendUiMessage(iframe, {
|
|
375
|
+
role: "user",
|
|
376
|
+
content: [{ type: "text", text: "Display only message" }],
|
|
377
|
+
followUp: false,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// addMessage should have been called
|
|
381
|
+
const calls = agent.addMessageCalls.filter(
|
|
382
|
+
(c) => c.content === "Display only message",
|
|
383
|
+
);
|
|
384
|
+
expect(calls.length).toBeGreaterThanOrEqual(1);
|
|
385
|
+
|
|
386
|
+
// run() should NOT have been called
|
|
387
|
+
expect(runSpy.mock.calls.length).toBe(0);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("followUp: true on assistant-role message: addMessage IS called AND runAgent IS invoked", async () => {
|
|
391
|
+
const agent = new MockMCPProxyAgent();
|
|
392
|
+
agent.agentId = "ui-msg-agent-force";
|
|
393
|
+
|
|
394
|
+
const iframe = await setupMCPActivity(
|
|
395
|
+
agent,
|
|
396
|
+
"ui-msg-agent-force",
|
|
397
|
+
"Force followUp test",
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
const runSpy = vi.spyOn(agent, "run");
|
|
401
|
+
|
|
402
|
+
await sendUiMessage(iframe, {
|
|
403
|
+
role: "assistant",
|
|
404
|
+
content: [{ type: "text", text: "Assistant with followUp" }],
|
|
405
|
+
followUp: true,
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// addMessage should have been called
|
|
409
|
+
const calls = agent.addMessageCalls.filter(
|
|
410
|
+
(c) => c.content === "Assistant with followUp",
|
|
411
|
+
);
|
|
412
|
+
expect(calls.length).toBeGreaterThanOrEqual(1);
|
|
413
|
+
|
|
414
|
+
// run() should have been called
|
|
415
|
+
expect(runSpy.mock.calls.length).toBeGreaterThan(0);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("message with text content always adds to agent messages regardless of followUp", async () => {
|
|
419
|
+
const agent = new MockMCPProxyAgent();
|
|
420
|
+
agent.agentId = "ui-msg-agent-all";
|
|
421
|
+
|
|
422
|
+
const iframe = await setupMCPActivity(
|
|
423
|
+
agent,
|
|
424
|
+
"ui-msg-agent-all",
|
|
425
|
+
"All messages test",
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
await sendUiMessage(iframe, {
|
|
429
|
+
role: "user",
|
|
430
|
+
content: [{ type: "text", text: "User msg" }],
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
await sendUiMessage(iframe, {
|
|
434
|
+
role: "assistant",
|
|
435
|
+
content: [{ type: "text", text: "Assistant msg" }],
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
await sendUiMessage(iframe, {
|
|
439
|
+
role: "user",
|
|
440
|
+
content: [{ type: "text", text: "No followUp msg" }],
|
|
441
|
+
followUp: false,
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const userCalls = agent.addMessageCalls.filter(
|
|
445
|
+
(c) => c.content === "User msg",
|
|
446
|
+
);
|
|
447
|
+
const assistCalls = agent.addMessageCalls.filter(
|
|
448
|
+
(c) => c.content === "Assistant msg",
|
|
449
|
+
);
|
|
450
|
+
const noFollowCalls = agent.addMessageCalls.filter(
|
|
451
|
+
(c) => c.content === "No followUp msg",
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
expect(userCalls.length).toBeGreaterThanOrEqual(1);
|
|
455
|
+
expect(assistCalls.length).toBeGreaterThanOrEqual(1);
|
|
456
|
+
expect(noFollowCalls.length).toBeGreaterThanOrEqual(1);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
@@ -74,8 +74,8 @@ export const CopilotChatConfigurationProvider: React.FC<
|
|
|
74
74
|
const mergedLabels: CopilotChatLabels = useMemo(
|
|
75
75
|
() => ({
|
|
76
76
|
...CopilotChatDefaultLabels,
|
|
77
|
-
...
|
|
78
|
-
...
|
|
77
|
+
...parentConfig?.labels,
|
|
78
|
+
...stableLabels,
|
|
79
79
|
}),
|
|
80
80
|
[stableLabels, parentConfig?.labels],
|
|
81
81
|
);
|