diff --git a/apps/docs/reference/copilotkit-core.mdx b/apps/docs/reference/copilotkit-core.mdx index 2b8114a..3e3643a 100644 --- a/apps/docs/reference/copilotkit-core.mdx +++ b/apps/docs/reference/copilotkit-core.mdx @@ -314,8 +314,6 @@ runAgent(params: CopilotKitCoreRunAgentParams): Promise ```typescript interface CopilotKitCoreRunAgentParams { agent: AbstractAgent; // The agent to execute - withMessages?: Message[]; // Initial messages to send to the agent - agentId?: string; // Override the agent's default identifier } ``` diff --git a/packages/core/src/__tests__/core-basic-functionality.test.ts b/packages/core/src/__tests__/core-basic-functionality.test.ts index f2d40e2..90c8d3a 100644 --- a/packages/core/src/__tests__/core-basic-functionality.test.ts +++ b/packages/core/src/__tests__/core-basic-functionality.test.ts @@ -35,29 +35,6 @@ describe("CopilotKitCore.runAgent - Basic Functionality", () => { expect(agent.runAgentCalls[0].forwardedProps).toEqual({}); }); - it("should pass withMessages to agent.addMessages", async () => { - const initialMessages = [createMessage({ content: "Initial" })]; - const newMessages = [createAssistantMessage({ content: "Response" })]; - const agent = new MockAgent({ newMessages }); - copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); - - await copilotKitCore.runAgent({ agent: agent as any, withMessages: initialMessages }); - - expect(agent.addMessages).toHaveBeenCalledWith(initialMessages); - expect(agent.messages).toContain(initialMessages[0]); - }); - - it("should work without withMessages parameter", async () => { - const newMessages = [createAssistantMessage({ content: "Response" })]; - const agent = new MockAgent({ newMessages }); - copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); - - const result = await copilotKitCore.runAgent({ agent: agent as any }); - - expect(result.newMessages).toEqual(newMessages); - expect(agent.addMessages).not.toHaveBeenCalled(); - }); - it("should forward properties to agent.runAgent", async () => { const properties = { apiKey: "test-key", model: "gpt-4" }; copilotKitCore = new CopilotKitCore({ properties }); diff --git a/packages/core/src/__tests__/core-suggestions-e2e.test.ts b/packages/core/src/__tests__/core-suggestions-e2e.test.ts index 7ef4136..ee70c12 100644 --- a/packages/core/src/__tests__/core-suggestions-e2e.test.ts +++ b/packages/core/src/__tests__/core-suggestions-e2e.test.ts @@ -815,9 +815,9 @@ describe("CopilotKitCore - Suggestions E2E", () => { }; // Simulate user submitting a new message which triggers runAgent (and should clear/abort suggestions immediately) + consumerAgent.addMessages([createMessage({ content: "User follow-up" })]); await copilotKitCore.runAgent({ agent: consumerAgent as any, - withMessages: [createMessage({ content: "User follow-up" })], }); // The in-flight suggestion run should be aborted diff --git a/packages/core/src/core/run-handler.ts b/packages/core/src/core/run-handler.ts index 432f932..d8ddefc 100644 --- a/packages/core/src/core/run-handler.ts +++ b/packages/core/src/core/run-handler.ts @@ -7,7 +7,6 @@ import { FrontendTool } from "../types"; export interface CopilotKitCoreRunAgentParams { agent: AbstractAgent; - withMessages?: Message[]; } export interface CopilotKitCoreConnectAgentParams { @@ -140,7 +139,7 @@ export class RunHandler { /** * Run an agent */ - async runAgent({ agent, withMessages }: CopilotKitCoreRunAgentParams): Promise { + async runAgent({ agent }: CopilotKitCoreRunAgentParams): Promise { // Agent ID is guaranteed to be set by validateAndAssignAgentId if (agent.agentId) { void (this.core as unknown as CopilotKitCoreFriendsAccess).suggestionEngine.clearSuggestions(agent.agentId); @@ -150,9 +149,6 @@ export class RunHandler { agent.headers = { ...(this.core as unknown as CopilotKitCoreFriendsAccess).headers }; } - if (withMessages) { - agent.addMessages(withMessages); - } try { const runAgentResult = await agent.runAgent( { @@ -169,9 +165,6 @@ export class RunHandler { if (agent.agentId) { context.agentId = agent.agentId; } - if (withMessages) { - context.messageCount = withMessages.length; - } await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ error: runError, code: CopilotKitCoreErrorCode.AGENT_RUN_FAILED, diff --git a/packages/react/src/hooks/__tests__/use-agent.e2e.test.tsx b/packages/react/src/hooks/__tests__/use-agent.e2e.test.tsx new file mode 100644 index 0000000..fd5e22d --- /dev/null +++ b/packages/react/src/hooks/__tests__/use-agent.e2e.test.tsx @@ -0,0 +1,146 @@ +import React from "react"; +import { screen, fireEvent, waitFor } from "@testing-library/react"; +import { describe, it, expect } from "vitest"; +import { type BaseEvent, type RunAgentInput } from "@ag-ui/client"; +import { Observable } from "rxjs"; +import { + MockStepwiseAgent, + renderWithCopilotKit, + runStartedEvent, + runFinishedEvent, + textChunkEvent, + testId, +} from "@/__tests__/utils/test-helpers"; +import { useAgent } from "../use-agent"; +import { useCopilotKit } from "@/providers/CopilotKitProvider"; +import { CopilotChat } from "@/components/chat/CopilotChat"; + +/** + * Mock agent that captures RunAgentInput to verify state is passed correctly + */ +class StateCapturingMockAgent extends MockStepwiseAgent { + public lastRunInput?: RunAgentInput; + + run(input: RunAgentInput): Observable { + this.lastRunInput = input; + return super.run(input); + } +} + +describe("useAgent e2e", () => { + describe("setState passes state to agent run", () => { + it("agent receives state set via setState when runAgent is called", async () => { + const agent = new StateCapturingMockAgent(); + + /** + * Test component that: + * 1. Gets agent via useAgent() + * 2. Gets copilotkit via useCopilotKit() + * 3. Sets state on agent and calls runAgent + */ + function StateTestComponent() { + const { agent: hookAgent } = useAgent(); + const { copilotkit } = useCopilotKit(); + + const handleSetStateAndRun = async () => { + hookAgent.setState({ testKey: "testValue", counter: 42 }); + await copilotkit.runAgent({ agent: hookAgent }); + }; + + return ( + + ); + } + + renderWithCopilotKit({ + agent, + children: , + }); + + // Click the button to set state and trigger runAgent + const triggerBtn = await screen.findByTestId("trigger-btn"); + fireEvent.click(triggerBtn); + + // Wait for the agent's run method to be called + await waitFor(() => { + expect(agent.lastRunInput).toBeDefined(); + }); + + // Complete the agent run + agent.emit(runStartedEvent()); + agent.emit(runFinishedEvent()); + agent.complete(); + + // Verify the state was passed to the agent + expect(agent.lastRunInput?.state).toEqual({ + testKey: "testValue", + counter: 42, + }); + }); + }); + + describe("addMessage + runAgent displays in CopilotChat", () => { + it("messages added via useAgent show up in CopilotChat", async () => { + const agent = new MockStepwiseAgent(); + + /** + * Test component that: + * 1. Gets agent via useAgent() + * 2. Gets copilotkit via useCopilotKit() + * 3. Adds a user message and calls runAgent + */ + function MessageTestComponent() { + const { agent: hookAgent } = useAgent(); + const { copilotkit } = useCopilotKit(); + + const handleAddMessageAndRun = async () => { + hookAgent.addMessage({ + id: testId("user-msg"), + role: "user", + content: "Hello from useAgent!", + }); + await copilotkit.runAgent({ agent: hookAgent }); + }; + + return ( +
+ +
+ +
+
+ ); + } + + renderWithCopilotKit({ + agent, + children: , + }); + + // Click the button to add message and trigger runAgent + const sendBtn = await screen.findByTestId("send-btn"); + fireEvent.click(sendBtn); + + // User message should appear in the chat + await waitFor(() => { + expect(screen.getByText("Hello from useAgent!")).toBeDefined(); + }); + + // Simulate agent response + const responseId = testId("assistant-msg"); + agent.emit(runStartedEvent()); + agent.emit(textChunkEvent(responseId, "Hello! I received your message.")); + agent.emit(runFinishedEvent()); + agent.complete(); + + // Assistant response should appear in the chat + await waitFor(() => { + expect(screen.getByText("Hello! I received your message.")).toBeDefined(); + }); + }); + }); +});