@copilotkit/react-core 1.55.3-canary.1776243725 → 1.55.3-canary.1776979102

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 (58) hide show
  1. package/dist/{copilotkit-opur-20s.d.mts → copilotkit-3mXoM0Hd.d.mts} +9 -29
  2. package/dist/copilotkit-3mXoM0Hd.d.mts.map +1 -0
  3. package/dist/{copilotkit-EfopO2gn.d.cts → copilotkit-BDDjvB-p.d.cts} +9 -29
  4. package/dist/copilotkit-BDDjvB-p.d.cts.map +1 -0
  5. package/dist/{copilotkit-BoOnQHlE.cjs → copilotkit-BkcqmpWt.cjs} +162 -280
  6. package/dist/copilotkit-BkcqmpWt.cjs.map +1 -0
  7. package/dist/{copilotkit-Bm4ox8G0.mjs → copilotkit-C7n8Umv9.mjs} +164 -276
  8. package/dist/copilotkit-C7n8Umv9.mjs.map +1 -0
  9. package/dist/index.cjs +4 -9
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.mts +1 -1
  13. package/dist/index.mjs +4 -9
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/index.umd.js +143 -230
  16. package/dist/index.umd.js.map +1 -1
  17. package/dist/v2/index.cjs +1 -2
  18. package/dist/v2/index.d.cts +2 -2
  19. package/dist/v2/index.d.mts +2 -2
  20. package/dist/v2/index.mjs +2 -2
  21. package/dist/v2/index.umd.js +165 -279
  22. package/dist/v2/index.umd.js.map +1 -1
  23. package/package.json +6 -6
  24. package/src/components/copilot-provider/copilot-messages.tsx +24 -39
  25. package/src/components/copilot-provider/copilotkit-props.tsx +5 -9
  26. package/src/components/copilot-provider/copilotkit.tsx +1 -4
  27. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +16 -27
  28. package/src/hooks/use-copilot-chat_internal.ts +4 -15
  29. package/src/v2/__tests__/utils/test-helpers.tsx +7 -40
  30. package/src/v2/components/chat/CopilotChat.tsx +1 -1
  31. package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +15 -18
  32. package/src/v2/components/chat/CopilotChatMessageView.tsx +2 -7
  33. package/src/v2/components/chat/CopilotChatReasoningMessage.tsx +4 -17
  34. package/src/v2/components/chat/CopilotChatUserMessage.tsx +10 -13
  35. package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.tsx +5 -131
  36. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +0 -60
  37. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +1 -1
  38. package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +2 -5
  39. package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +2 -5
  40. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +1 -55
  41. package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +0 -8
  42. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +10 -10
  43. package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +2 -13
  44. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +4 -23
  45. package/src/v2/hooks/index.ts +0 -1
  46. package/src/v2/hooks/use-agent.tsx +10 -157
  47. package/src/v2/hooks/use-render-activity-message.tsx +3 -9
  48. package/src/v2/hooks/use-render-custom-messages.tsx +1 -6
  49. package/src/v2/providers/CopilotKitProvider.tsx +2 -6
  50. package/dist/copilotkit-Bm4ox8G0.mjs.map +0 -1
  51. package/dist/copilotkit-BoOnQHlE.cjs.map +0 -1
  52. package/dist/copilotkit-EfopO2gn.d.cts.map +0 -1
  53. package/dist/copilotkit-opur-20s.d.mts.map +0 -1
  54. package/src/components/copilot-provider/__tests__/error-visibility-prod.test.tsx +0 -70
  55. package/src/v2/components/chat/__tests__/CopilotChatCopyButton.clipboard.test.tsx +0 -241
  56. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +0 -327
  57. package/src/v2/hooks/__tests__/use-capabilities.test.tsx +0 -76
  58. package/src/v2/hooks/use-capabilities.tsx +0 -25
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect } from "react";
2
- import { screen, fireEvent, waitFor, act } from "@testing-library/react";
2
+ import { screen, fireEvent, waitFor } from "@testing-library/react";
3
3
  import { z } from "zod";
4
4
  import { defineToolCallRenderer, ReactToolCallRenderer } from "../../../types";
5
5
  import {
@@ -1060,128 +1060,6 @@ describe("CopilotChat E2E - Chat Basics and Streaming Patterns", () => {
1060
1060
  agent.complete();
1061
1061
  });
1062
1062
 
1063
- it("should not auto-collapse when user manually toggled during streaming", async () => {
1064
- const agent = new MockStepwiseAgent();
1065
- renderWithCopilotKit({ agent });
1066
-
1067
- const input = await screen.findByRole("textbox");
1068
- fireEvent.change(input, { target: { value: "User toggle test" } });
1069
- fireEvent.keyDown(input, { key: "Enter", code: "Enter" });
1070
-
1071
- await waitFor(() => {
1072
- expect(screen.getByText("User toggle test")).toBeDefined();
1073
- });
1074
-
1075
- const reasoningId = testId("reasoning");
1076
- const textId = testId("text");
1077
-
1078
- // Start streaming reasoning — panel should auto-open
1079
- agent.emit(runStartedEvent());
1080
- agent.emit(reasoningStartEvent(reasoningId));
1081
- agent.emit(reasoningMessageStartEvent(reasoningId));
1082
- agent.emit(
1083
- reasoningMessageContentEvent(reasoningId, "Deep analysis in progress"),
1084
- );
1085
-
1086
- await waitFor(() => {
1087
- expect(screen.getByText("Thinking…")).toBeDefined();
1088
- });
1089
-
1090
- // Panel should be open (aria-expanded="true") while streaming
1091
- await waitFor(() => {
1092
- const header = screen.getByText("Thinking…");
1093
- const button = header.closest("button");
1094
- expect(button?.getAttribute("aria-expanded")).toBe("true");
1095
- });
1096
-
1097
- // User manually collapses during streaming — this sets userToggledRef
1098
- const header = screen.getByText("Thinking…");
1099
- const button = header.closest("button");
1100
- act(() => {
1101
- if (button) {
1102
- fireEvent.click(button);
1103
- }
1104
- });
1105
-
1106
- // Should now be collapsed by user action
1107
- await waitFor(() => {
1108
- const btn = screen.getByText("Thinking…").closest("button");
1109
- expect(btn?.getAttribute("aria-expanded")).toBe("false");
1110
- });
1111
-
1112
- // Now streaming ends — because userToggledRef is true, the panel
1113
- // should stay in whatever state the user set (collapsed).
1114
- agent.emit(reasoningMessageEndEvent(reasoningId));
1115
- agent.emit(reasoningEndEvent(reasoningId));
1116
- agent.emit(textChunkEvent(textId, "Done."));
1117
- agent.emit(runFinishedEvent());
1118
- agent.complete();
1119
-
1120
- // Panel should remain collapsed (not flash open then closed)
1121
- await waitFor(() => {
1122
- const btn = screen.getByText(/Thought for/).closest("button");
1123
- expect(btn?.getAttribute("aria-expanded")).toBe("false");
1124
- });
1125
- });
1126
-
1127
- it("should keep panel open when user re-expands during streaming", async () => {
1128
- const agent = new MockStepwiseAgent();
1129
- renderWithCopilotKit({ agent });
1130
-
1131
- const input = await screen.findByRole("textbox");
1132
- fireEvent.change(input, {
1133
- target: { value: "Re-expand toggle test" },
1134
- });
1135
- fireEvent.keyDown(input, { key: "Enter", code: "Enter" });
1136
-
1137
- await waitFor(() => {
1138
- expect(screen.getByText("Re-expand toggle test")).toBeDefined();
1139
- });
1140
-
1141
- const reasoningId = testId("reasoning");
1142
- const textId = testId("text");
1143
-
1144
- // Start streaming reasoning — panel auto-opens
1145
- agent.emit(runStartedEvent());
1146
- agent.emit(reasoningStartEvent(reasoningId));
1147
- agent.emit(reasoningMessageStartEvent(reasoningId));
1148
- agent.emit(reasoningMessageContentEvent(reasoningId, "Thinking hard"));
1149
-
1150
- await waitFor(() => {
1151
- const btn = screen.getByText("Thinking…").closest("button");
1152
- expect(btn?.getAttribute("aria-expanded")).toBe("true");
1153
- });
1154
-
1155
- // User collapses, then re-expands (both set userToggledRef = true)
1156
- const headerEl = screen.getByText("Thinking…");
1157
- const btn = headerEl.closest("button");
1158
- act(() => {
1159
- if (btn) {
1160
- fireEvent.click(btn); // collapse
1161
- fireEvent.click(btn); // re-expand
1162
- }
1163
- });
1164
-
1165
- await waitFor(() => {
1166
- const b = screen.getByText("Thinking…").closest("button");
1167
- expect(b?.getAttribute("aria-expanded")).toBe("true");
1168
- });
1169
-
1170
- // Streaming ends — because userToggledRef is true, panel should
1171
- // stay in the user's chosen state (open).
1172
- agent.emit(reasoningMessageEndEvent(reasoningId));
1173
- agent.emit(reasoningEndEvent(reasoningId));
1174
- agent.emit(textChunkEvent(textId, "All done."));
1175
- agent.emit(runFinishedEvent());
1176
- agent.complete();
1177
-
1178
- // Panel should remain open (not auto-collapse)
1179
- await waitFor(() => {
1180
- const b = screen.getByText(/Thought for/).closest("button");
1181
- expect(b?.getAttribute("aria-expanded")).toBe("true");
1182
- });
1183
- });
1184
-
1185
1063
  it("should expand and collapse reasoning content on click", async () => {
1186
1064
  const agent = new MockStepwiseAgent();
1187
1065
  renderWithCopilotKit({ agent });
@@ -1216,16 +1094,12 @@ describe("CopilotChat E2E - Chat Basics and Streaming Patterns", () => {
1216
1094
  expect(button?.getAttribute("aria-expanded")).toBe("false");
1217
1095
  });
1218
1096
 
1219
- // Click to expand — wrap in act() so React 18 flushes the state
1220
- // update synchronously instead of deferring it through the scheduler,
1221
- // which can race with waitFor polling on slow CI runners.
1097
+ // Click to expand
1222
1098
  const header = screen.getByText(/Thought for/);
1223
1099
  const button = header.closest("button");
1224
- act(() => {
1225
- if (button) {
1226
- fireEvent.click(button);
1227
- }
1228
- });
1100
+ if (button) {
1101
+ fireEvent.click(button);
1102
+ }
1229
1103
 
1230
1104
  // Should now be expanded
1231
1105
  await waitFor(() => {
@@ -11,8 +11,6 @@ import {
11
11
  } from "../../../__tests__/utils/test-helpers";
12
12
  import { ReactActivityMessageRenderer } from "../../../types";
13
13
  import { useCopilotKit } from "../../../providers";
14
- import { AbstractAgent } from "@ag-ui/client";
15
- import { getThreadClone } from "../../../hooks/use-agent";
16
14
 
17
15
  describe("CopilotChat activity message rendering", () => {
18
16
  it("renders custom components for activity snapshots", async () => {
@@ -150,62 +148,4 @@ describe("CopilotChat activity message rendering", () => {
150
148
  expect(capturedCopilotkit).toBeDefined();
151
149
  });
152
150
 
153
- it("passes the per-thread clone (not the registry agent) to activity message renderers", async () => {
154
- // Regression test for: A2UI button clicks firing runAgent on the registry
155
- // agent instead of the per-thread clone that CopilotChat renders from.
156
- // Caused by useRenderActivityMessage calling copilotkit.getAgent() directly
157
- // instead of getThreadClone(registryAgent, threadId) ?? registryAgent.
158
- const agent = new MockStepwiseAgent();
159
- const agentId = "action-agent";
160
- agent.agentId = agentId;
161
- const threadId = "thread-for-action-test";
162
-
163
- let capturedAgent: AbstractAgent | undefined;
164
-
165
- const activityRenderer: ReactActivityMessageRenderer<{ label: string }> = {
166
- activityType: "button-action",
167
- content: z.object({ label: z.string() }),
168
- render: ({ content, agent: renderedAgent }) => {
169
- capturedAgent = renderedAgent;
170
- return <button data-testid="action-button">{content.label}</button>;
171
- },
172
- };
173
-
174
- renderWithCopilotKit({
175
- agents: { [agentId]: agent },
176
- agentId,
177
- threadId,
178
- renderActivityMessages: [activityRenderer],
179
- });
180
-
181
- const input = await screen.findByRole("textbox");
182
- fireEvent.change(input, { target: { value: "show me buttons" } });
183
- fireEvent.keyDown(input, { key: "Enter", code: "Enter" });
184
-
185
- await waitFor(() => {
186
- expect(screen.getByText("show me buttons")).toBeDefined();
187
- });
188
-
189
- agent.emit(runStartedEvent());
190
- agent.emit(
191
- activitySnapshotEvent({
192
- messageId: testId("activity-action"),
193
- activityType: "button-action",
194
- content: { label: "Click Me" },
195
- }),
196
- );
197
- agent.emit(runFinishedEvent());
198
-
199
- await waitFor(() => {
200
- expect(screen.getByTestId("action-button")).toBeDefined();
201
- });
202
-
203
- // CopilotChat creates a per-thread clone via useAgent. The activity renderer
204
- // must receive that clone so that handleAction → runAgent targets the same
205
- // instance chat is rendering from.
206
- const clone = getThreadClone(agent, threadId);
207
- expect(clone).toBeDefined();
208
- expect(capturedAgent).toBe(clone);
209
- expect(capturedAgent).not.toBe(agent); // must NOT be the registry agent
210
- });
211
151
  });
@@ -669,7 +669,7 @@ describe("CopilotChatAssistantMessage", () => {
669
669
 
670
670
  await waitFor(() => {
671
671
  expect(consoleSpy).toHaveBeenCalledWith(
672
- "Failed to copy to clipboard:",
672
+ "Failed to copy message:",
673
673
  expect.any(Error),
674
674
  );
675
675
  });
@@ -250,11 +250,8 @@ class MockStepwiseAgent extends AbstractAgent {
250
250
  }
251
251
 
252
252
  clone(): MockStepwiseAgent {
253
- const cloned = new MockStepwiseAgent();
254
- cloned.agentId = this.agentId;
255
- (cloned as unknown as { subject: Subject<BaseEvent> }).subject =
256
- this.subject;
257
- return cloned;
253
+ // For tests, return same instance so we can keep controlling it.
254
+ return this;
258
255
  }
259
256
 
260
257
  async detachActiveRun(): Promise<void> {}
@@ -52,11 +52,8 @@ class MockStepwiseAgent extends AbstractAgent {
52
52
  }
53
53
 
54
54
  clone(): MockStepwiseAgent {
55
- const cloned = new MockStepwiseAgent();
56
- cloned.agentId = this.agentId;
57
- (cloned as unknown as { subject: Subject<BaseEvent> }).subject =
58
- this.subject;
59
- return cloned;
55
+ // For tests, return same instance so we can keep controlling it.
56
+ return this;
60
57
  }
61
58
 
62
59
  async detachActiveRun(): Promise<void> {}
@@ -79,61 +79,7 @@ class MockMCPProxyAgent extends AbstractAgent {
79
79
  }
80
80
 
81
81
  clone(): MockMCPProxyAgent {
82
- const cloned = new MockMCPProxyAgent();
83
- cloned.agentId = this.agentId;
84
- type Internal = {
85
- subject: Subject<BaseEvent>;
86
- runAgentCalls: Array<{ input: Partial<RunAgentInput> }>;
87
- runAgentResponses: Map<string, unknown>;
88
- };
89
- (cloned as unknown as Internal).subject = (
90
- this as unknown as Internal
91
- ).subject;
92
- (cloned as unknown as Internal).runAgentCalls = (
93
- this as unknown as Internal
94
- ).runAgentCalls;
95
- (cloned as unknown as Internal).runAgentResponses = (
96
- this as unknown as Internal
97
- ).runAgentResponses;
98
- // Share isRunning with the original so that emit(runFinishedEvent()) on the
99
- // registry is visible to waitForAgentIdle() which now receives the clone.
100
- //
101
- // Also proxy runAgent dynamically so tests that monkey-patch agent.runAgent
102
- // after renderWithCopilotKit (which creates the clone) still take effect.
103
- // The clone is created and cached before tests can override runAgent, so a
104
- // static copy would always see the pre-patch prototype method.
105
- const registry = this;
106
- Object.defineProperty(cloned, "isRunning", {
107
- get() {
108
- return registry.isRunning;
109
- },
110
- set(v: boolean) {
111
- registry.isRunning = v;
112
- },
113
- configurable: true,
114
- enumerable: true,
115
- });
116
- // Override runAgent so that:
117
- // - Proxied MCP requests delegate to registry.runAgent (picking up any
118
- // monkey-patches the test installed after renderWithCopilotKit).
119
- // - User-message runs call the prototype method with `this = clone` so
120
- // that clone.messages is updated (CopilotKit renders clone.messages).
121
- const proto = MockMCPProxyAgent.prototype;
122
- cloned.runAgent = async function (
123
- input?: Partial<RunAgentInput>,
124
- ): Promise<RunAgentResult> {
125
- const proxiedRequest = input?.forwardedProps?.__proxiedMCPRequest;
126
- if (proxiedRequest) {
127
- // Delegate to the registry so that monkey-patches applied by tests
128
- // (e.g. "throws an error", "uses a controlled promise") take effect.
129
- return registry.runAgent(input);
130
- }
131
- // For user-message runs: call the prototype method bound to the clone
132
- // so AbstractAgent.runAgent processes events on clone and updates
133
- // clone.messages (which is what CopilotKit reads to render messages).
134
- return proto.runAgent.call(cloned, input);
135
- };
136
- return cloned;
82
+ return this;
137
83
  }
138
84
 
139
85
  async detachActiveRun(): Promise<void> {}
@@ -42,16 +42,8 @@ describe("useAgentContext timing - follow-up run sees updated context", () => {
42
42
  * with no new messages — which is fine; we only need to capture context.
43
43
  */
44
44
  class ContextCapturingAgent extends MockStepwiseAgent {
45
- // Shared so the clone and original both see the captured contexts
46
45
  public contextPerRun: Context[][] = [];
47
46
 
48
- clone(): this {
49
- const cloned = super.clone();
50
- (cloned as unknown as ContextCapturingAgent).contextPerRun =
51
- this.contextPerRun;
52
- return cloned;
53
- }
54
-
55
47
  async runAgent(
56
48
  parameters?: RunAgentParameters,
57
49
  subscriber?: AgentSubscriber,
@@ -313,7 +313,7 @@ describe("useAgent throttleMs", () => {
313
313
  expect(screen.getByTestId("count").textContent).toBe("3");
314
314
  });
315
315
 
316
- it("with throttleMs, onStateChanged still fires immediately", async () => {
316
+ it("with throttleMs, onStateChanged still fires immediately", () => {
317
317
  const TestComponent = createTestComponent({
318
318
  updates: [
319
319
  UseAgentUpdate.OnMessagesChanged,
@@ -330,8 +330,8 @@ describe("useAgent throttleMs", () => {
330
330
  notifyMessagesChanged(mockAgent);
331
331
  });
332
332
 
333
- // Fire onStateChanged 10ms later — fires via microtask batch (not synchronously)
334
- await act(async () => {
333
+ // Fire onStateChanged 10ms later — should render immediately, not throttled
334
+ act(() => {
335
335
  vi.advanceTimersByTime(10);
336
336
  mockAgent.state = { count: 42 };
337
337
  notifyStateChanged(mockAgent);
@@ -372,7 +372,7 @@ describe("useAgent throttleMs", () => {
372
372
  expect(renderCount.current).toBe(countBeforeUnmount);
373
373
  });
374
374
 
375
- it("with throttleMs and updates excluding OnMessagesChanged, throttle is a no-op", async () => {
375
+ it("with throttleMs and updates excluding OnMessagesChanged, throttle is a no-op", () => {
376
376
  const TestComponent = createTestComponent({
377
377
  updates: [UseAgentUpdate.OnStateChanged],
378
378
  throttleMs: 100,
@@ -380,8 +380,8 @@ describe("useAgent throttleMs", () => {
380
380
 
381
381
  render(<TestComponent />);
382
382
 
383
- // Only onStateChanged is subscribed — fires via microtask batch
384
- await act(async () => {
383
+ // Only onStateChanged is subscribed — should fire immediately
384
+ act(() => {
385
385
  mockAgent.state = { value: "test" };
386
386
  notifyStateChanged(mockAgent);
387
387
  });
@@ -649,7 +649,7 @@ describe("useAgent throttleMs", () => {
649
649
  expect(renderCount.current).toBe(rendersAfterMount + 1);
650
650
  });
651
651
 
652
- it("with throttleMs, onRunInitialized still fires immediately during throttle window", async () => {
652
+ it("with throttleMs, onRunInitialized still fires immediately during throttle window", () => {
653
653
  const renderCount = { current: 0 };
654
654
  const TestComponent = createTestComponent({
655
655
  updates: [
@@ -670,13 +670,13 @@ describe("useAgent throttleMs", () => {
670
670
  });
671
671
  expect(renderCount.current).toBe(rendersAfterMount + 1);
672
672
 
673
- // Fire onRunInitialized 10ms later — fires via microtask batch
674
- await act(async () => {
673
+ // Fire onRunInitialized 10ms later — should render immediately
674
+ act(() => {
675
675
  vi.advanceTimersByTime(10);
676
676
  notifyRunInitialized(mockAgent);
677
677
  });
678
678
 
679
- // Run status notification fires via microtask batch
679
+ // Run status notification is NOT throttled — renders immediately
680
680
  expect(renderCount.current).toBe(rendersAfterMount + 2);
681
681
  });
682
682
 
@@ -19,21 +19,10 @@ import { CopilotChat } from "../../components/chat/CopilotChat";
19
19
  * Mock agent that captures RunAgentInput to verify state is passed correctly
20
20
  */
21
21
  class StateCapturingMockAgent extends MockStepwiseAgent {
22
- // Shared via a container so the clone and original both see the same value
23
- private _capture: { lastRunInput?: RunAgentInput } = {};
24
-
25
- get lastRunInput(): RunAgentInput | undefined {
26
- return this._capture.lastRunInput;
27
- }
28
-
29
- clone(): this {
30
- const cloned = super.clone();
31
- (cloned as unknown as StateCapturingMockAgent)._capture = this._capture;
32
- return cloned;
33
- }
22
+ public lastRunInput?: RunAgentInput;
34
23
 
35
24
  run(input: RunAgentInput): Observable<BaseEvent> {
36
- this._capture.lastRunInput = input;
25
+ this.lastRunInput = input;
37
26
  return super.run(input);
38
27
  }
39
28
  }
@@ -502,24 +502,13 @@ describe("useFrontendTool E2E - Dynamic Registration", () => {
502
502
  describe("Agent input plumbing", () => {
503
503
  it("forwards registered frontend tools to runAgent input", async () => {
504
504
  class InstrumentedMockAgent extends MockStepwiseAgent {
505
- // Shared so the clone and original both see the captured parameters
506
- private _capture: { lastRunParameters?: RunAgentParameters } = {};
507
-
508
- get lastRunParameters(): RunAgentParameters | undefined {
509
- return this._capture.lastRunParameters;
510
- }
511
-
512
- clone(): this {
513
- const cloned = super.clone();
514
- (cloned as unknown as InstrumentedMockAgent)._capture = this._capture;
515
- return cloned;
516
- }
505
+ public lastRunParameters?: RunAgentParameters;
517
506
 
518
507
  async runAgent(
519
508
  parameters?: RunAgentParameters,
520
509
  subscriber?: AgentSubscriber,
521
510
  ) {
522
- this._capture.lastRunParameters = parameters;
511
+ this.lastRunParameters = parameters;
523
512
  return super.runAgent(parameters, subscriber);
524
513
  }
525
514
  }
@@ -579,16 +568,8 @@ describe("useFrontendTool E2E - Dynamic Registration", () => {
579
568
  class OneShotToolCallAgent extends AbstractAgent {
580
569
  private runCount = 0;
581
570
  clone(): OneShotToolCallAgent {
582
- const cloned = new OneShotToolCallAgent();
583
- cloned.agentId = this.agentId;
584
- // Share runCount via reference so the second run emits different args
585
- Object.defineProperty(cloned, "runCount", {
586
- get: () => this.runCount,
587
- set: (v: number) => {
588
- this.runCount = v;
589
- },
590
- });
591
- return cloned;
571
+ // Keep state across runs so the second run emits different args
572
+ return this;
592
573
  }
593
574
  async detachActiveRun(): Promise<void> {}
594
575
  run(_input: RunAgentInput): Observable<BaseEvent> {
@@ -8,7 +8,6 @@ export { useRenderTool } from "./use-render-tool";
8
8
  export { useDefaultRenderTool } from "./use-default-render-tool";
9
9
  export { useHumanInTheLoop } from "./use-human-in-the-loop";
10
10
  export { useAgent, UseAgentUpdate } from "./use-agent";
11
- export { useCapabilities } from "./use-capabilities";
12
11
  export { useAgentContext } from "./use-agent-context";
13
12
  export type { AgentContextInput, JsonSerializable } from "./use-agent-context";
14
13
  export { useSuggestions } from "./use-suggestions";