Skip to content

Commit a42eb45

Browse files
BenTaylorDevclaude
andcommitted
Merge origin/main into codex/ent-734-mcp-apps
Post-merge fix: add the missing examples/integrations/mcp-apps/.env.example (+ .gitignore negation, crewai-flows precedent) — the contract test 'mcp-apps documents the local Intelligence environment' was failing because the file was never added. 37/37 passing on the merged tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2 parents 1c4d2d4 + a5fbb5c commit a42eb45

17 files changed

Lines changed: 7588 additions & 10159 deletions

File tree

examples/integrations/a2a-middleware/.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ RESEARCH_PORT=9001
3939

4040
# Analysis Agent
4141
ANALYSIS_PORT=9002
42+
43+
44+
# ========================================
45+
# CopilotKit Intelligence Threads (Optional)
46+
# ========================================
47+
48+
# COPILOTKIT_LICENSE_TOKEN=
49+
# INTELLIGENCE_API_KEY=
50+
# INTELLIGENCE_API_URL=http://localhost:4201
51+
# INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4401
Lines changed: 150 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,161 @@
11
import {
22
CopilotRuntime,
3+
CopilotKitIntelligence,
34
createCopilotEndpoint,
45
InMemoryAgentRunner,
56
} from "@copilotkit/runtime/v2";
67
import { HttpAgent } from "@ag-ui/client";
8+
import type {
9+
AgentSubscriber,
10+
RunAgentInput,
11+
RunAgentParameters,
12+
RunAgentResult,
13+
} from "@ag-ui/client";
714
import { A2AMiddlewareAgent } from "@ag-ui/a2a-middleware";
15+
import type { A2AAgentConfig } from "@ag-ui/a2a-middleware";
816
import { handle } from "hono/vercel";
917

10-
export async function POST(request: Request) {
11-
const researchAgentUrl =
12-
process.env.RESEARCH_AGENT_URL || "http://localhost:9001";
13-
const analysisAgentUrl =
14-
process.env.ANALYSIS_AGENT_URL || "http://localhost:9002";
15-
const orchestratorUrl =
16-
process.env.ORCHESTRATOR_URL || "http://localhost:9000";
17-
18-
const orchestrationAgent = new HttpAgent({
19-
url: orchestratorUrl,
20-
});
21-
22-
const a2aMiddlewareAgent = new A2AMiddlewareAgent({
23-
description:
24-
"Research assistant with 2 specialized agents: Research (LangGraph) and Analysis (ADK)",
25-
agentUrls: [researchAgentUrl, analysisAgentUrl],
26-
orchestrationAgent,
27-
instructions: `
28-
You are a research assistant that orchestrates between 2 specialized agents.
29-
30-
AVAILABLE AGENTS:
31-
32-
- Research Agent (LangGraph): Gathers and summarizes information about a topic
33-
- Analysis Agent (ADK): Analyzes research findings and provides insights
34-
35-
WORKFLOW STRATEGY (SEQUENTIAL - ONE AT A TIME):
36-
37-
When the user asks to research a topic:
38-
39-
1. Research Agent - First, gather information about the topic
40-
- Pass: The user's research query or topic
41-
- The agent will return structured JSON with research findings
42-
43-
2. Analysis Agent - Then, analyze the research results
44-
- Pass: The research results from step 1
45-
- The agent will return structured JSON with analysis and insights
46-
47-
3. Present the complete research and analysis to the user
48-
49-
CRITICAL RULES:
50-
- Call agents ONE AT A TIME, wait for results before making next call
51-
- Pass information from earlier agents to later agents
52-
- Synthesize all gathered information in final response
53-
`,
54-
});
55-
56-
const runtime = new CopilotRuntime({
57-
agents: {
58-
a2a_chat: a2aMiddlewareAgent,
59-
},
60-
runner: new InMemoryAgentRunner(),
61-
});
62-
63-
const app = createCopilotEndpoint({
64-
runtime,
65-
basePath: "/api/copilotkit",
66-
});
67-
68-
return handle(app)(request);
69-
}
18+
const researchAgentUrl =
19+
process.env.RESEARCH_AGENT_URL || "http://localhost:9001";
20+
const analysisAgentUrl =
21+
process.env.ANALYSIS_AGENT_URL || "http://localhost:9002";
22+
const orchestratorUrl = process.env.ORCHESTRATOR_URL || "http://localhost:9000";
23+
24+
type RuntimeRunAgentInput = RunAgentParameters &
25+
Partial<Pick<RunAgentInput, "messages" | "state" | "threadId">>;
26+
27+
type RuntimeA2AMiddlewareAgentConfig = Omit<
28+
A2AAgentConfig,
29+
"orchestrationAgent"
30+
> & {
31+
orchestrationAgentUrl: string;
32+
};
33+
34+
class RuntimeA2AMiddlewareAgent extends A2AMiddlewareAgent {
35+
private readonly config: RuntimeA2AMiddlewareAgentConfig;
36+
37+
constructor(config: RuntimeA2AMiddlewareAgentConfig) {
38+
super({
39+
...config,
40+
orchestrationAgent: new HttpAgent({
41+
url: config.orchestrationAgentUrl,
42+
}),
43+
});
44+
this.config = config;
45+
}
46+
47+
async runAgent(
48+
parameters: RuntimeRunAgentInput = {},
49+
subscriber?: AgentSubscriber,
50+
): Promise<RunAgentResult> {
51+
const isolatedAgent = new A2AMiddlewareAgent({
52+
...this.config,
53+
agentId: this.agentId,
54+
debug: this.debug,
55+
description: this.description,
56+
initialMessages: this.messages,
57+
initialState: this.state,
58+
threadId: parameters.threadId ?? this.threadId,
59+
orchestrationAgent: new HttpAgent({
60+
url: this.config.orchestrationAgentUrl,
61+
}),
62+
});
7063

71-
export async function GET(request: Request) {
72-
const runtime = new CopilotRuntime({
73-
agents: {},
74-
runner: new InMemoryAgentRunner(),
75-
});
76-
const app = createCopilotEndpoint({
77-
runtime,
78-
basePath: "/api/copilotkit",
79-
});
80-
return handle(app)(request);
64+
if (parameters.state) {
65+
isolatedAgent.setState(parameters.state);
66+
}
67+
68+
if (parameters.messages) {
69+
isolatedAgent.setMessages(parameters.messages);
70+
}
71+
72+
return isolatedAgent.runAgent(
73+
{
74+
context: parameters.context,
75+
forwardedProps: parameters.forwardedProps,
76+
runId: parameters.runId,
77+
tools: parameters.tools,
78+
},
79+
subscriber,
80+
);
81+
}
82+
83+
clone(): RuntimeA2AMiddlewareAgent {
84+
return new RuntimeA2AMiddlewareAgent({
85+
...this.config,
86+
agentId: this.agentId,
87+
debug: this.debug,
88+
description: this.description,
89+
initialMessages: this.messages,
90+
initialState: this.state,
91+
threadId: this.threadId,
92+
});
93+
}
8194
}
95+
96+
const a2aMiddlewareAgent = new RuntimeA2AMiddlewareAgent({
97+
orchestrationAgentUrl: orchestratorUrl,
98+
agentId: "a2a_chat",
99+
description:
100+
"Research assistant with 2 specialized agents: Research (LangGraph) and Analysis (ADK)",
101+
agentUrls: [researchAgentUrl, analysisAgentUrl],
102+
instructions: `
103+
You are a research assistant that orchestrates between 2 specialized agents.
104+
105+
AVAILABLE AGENTS:
106+
107+
- Research Agent (LangGraph): Gathers and summarizes information about a topic
108+
- Analysis Agent (ADK): Analyzes research findings and provides insights
109+
110+
WORKFLOW STRATEGY (SEQUENTIAL - ONE AT A TIME):
111+
112+
When the user asks to research a topic:
113+
114+
1. Research Agent - First, gather information about the topic
115+
- Pass: The user's research query or topic
116+
- The agent will return structured JSON with research findings
117+
118+
2. Analysis Agent - Then, analyze the research results
119+
- Pass: The research results from step 1
120+
- The agent will return structured JSON with analysis and insights
121+
122+
3. Present the complete research and analysis to the user
123+
124+
CRITICAL RULES:
125+
- Call agents ONE AT A TIME, wait for results before making next call
126+
- Pass information from earlier agents to later agents
127+
- Synthesize all gathered information in final response
128+
`,
129+
});
130+
131+
const runtime = new CopilotRuntime({
132+
agents: {
133+
a2a_chat: a2aMiddlewareAgent,
134+
},
135+
// --- copilotkit:intelligence (remove this block to opt out) ---
136+
...(process.env.COPILOTKIT_LICENSE_TOKEN
137+
? {
138+
intelligence: new CopilotKitIntelligence({
139+
apiKey: process.env.INTELLIGENCE_API_KEY ?? "",
140+
apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201",
141+
wsUrl:
142+
process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401",
143+
}),
144+
// Demo stub - replace with your own auth-derived user identity (e.g. OIDC)
145+
// before any multi-user deployment, or all users share one thread history.
146+
identifyUser: () => ({ id: "demo-user", name: "Demo User" }),
147+
licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN,
148+
}
149+
: { runner: new InMemoryAgentRunner() }),
150+
// --- /copilotkit:intelligence ---
151+
});
152+
153+
const app = createCopilotEndpoint({
154+
runtime,
155+
basePath: "/api/copilotkit",
156+
});
157+
158+
export const GET = handle(app);
159+
export const POST = handle(app);
160+
export const PATCH = handle(app);
161+
export const DELETE = handle(app);

examples/integrations/a2a-middleware/app/globals.css

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
@tailwind base;
2-
@tailwind components;
3-
@tailwind utilities;
1+
@import "tailwindcss";
2+
@config "../tailwind.config.ts";
43

54
@layer base {
65
:root {
@@ -60,3 +59,29 @@
6059
.a2a-message-enter {
6160
animation: slide-in 0.3s ease-out;
6261
}
62+
63+
.threadsLayout,
64+
body > [role="presentation"] {
65+
--foreground: oklch(0.145 0 0);
66+
--background: oklch(1 0 0);
67+
--card: oklch(1 0 0);
68+
--card-foreground: oklch(0.145 0 0);
69+
--primary: oklch(0.205 0 0);
70+
--primary-foreground: oklch(0.985 0 0);
71+
--secondary: oklch(0.97 0 0);
72+
--secondary-foreground: oklch(0.205 0 0);
73+
--muted: oklch(0.97 0 0);
74+
--muted-foreground: oklch(0.556 0 0);
75+
--accent: oklch(0.97 0 0);
76+
--accent-foreground: oklch(0.205 0 0);
77+
--destructive: oklch(0.577 0.245 27.325);
78+
--destructive-foreground: oklch(0.985 0 0);
79+
--border: oklch(0.922 0 0);
80+
--input: oklch(0.922 0 0);
81+
--ring: oklch(0.708 0 0);
82+
--radius: 0.625rem;
83+
--radius-sm: calc(var(--radius) - 4px);
84+
--radius-md: calc(var(--radius) - 2px);
85+
--radius-lg: var(--radius);
86+
--radius-xl: calc(var(--radius) + 4px);
87+
}

examples/integrations/a2a-middleware/app/page.tsx

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
import { useState } from "react";
44
import Chat from "@/components/chat";
5+
import {
6+
CopilotChatConfigurationProvider,
7+
CopilotKitProvider,
8+
} from "@copilotkit/react-core/v2";
9+
import { ThreadsDrawer } from "@/components/threads-drawer";
10+
import { ThreadsPanelGate } from "@/components/threads-drawer/locked-state";
11+
import styles from "@/components/threads-drawer/threads-drawer.module.css";
512

613
export type ResearchData = {
714
topic: string;
@@ -24,12 +31,15 @@ export type AnalysisData = {
2431
conclusion: string;
2532
};
2633

27-
export default function Home() {
34+
// Disable static optimization for this page
35+
export const dynamic = "force-dynamic";
36+
37+
function ResearchAssistant() {
2838
const [researchData, setResearchData] = useState<ResearchData | null>(null);
2939
const [analysisData, setAnalysisData] = useState<AnalysisData | null>(null);
3040

3141
return (
32-
<div className="relative flex h-screen overflow-hidden bg-[#DEDEE9] p-2">
42+
<div className="relative flex min-h-dvh overflow-hidden bg-[#DEDEE9] p-2">
3343
{/* Background blur circles - Creating the gradient effect */}
3444
<div
3545
className="absolute w-[445px] h-[445px] left-[1040px] top-[11px] rounded-full z-0"
@@ -51,8 +61,8 @@ export default function Home() {
5161
}}
5262
/>
5363

54-
<div className="flex flex-1 overflow-hidden z-10 gap-2">
55-
<div className="w-[450px] flex-shrink-0 border-2 border-white bg-white/50 backdrop-blur-md shadow-elevation-lg flex flex-col rounded-lg overflow-hidden">
64+
<div className="flex flex-1 flex-col gap-2 overflow-y-auto z-10 lg:flex-row lg:overflow-hidden">
65+
<div className="flex min-h-[calc(100dvh-1rem)] w-full flex-shrink-0 flex-col overflow-hidden rounded-lg border-2 border-white bg-white/50 shadow-elevation-lg backdrop-blur-md lg:w-[450px]">
5666
<div className="p-6 border-b border-[#DBDBE5]">
5767
<h1 className="text-2xl font-semibold text-[#010507] mb-1">
5868
Research Assistant
@@ -76,8 +86,8 @@ export default function Home() {
7686
</div>
7787
</div>
7888

79-
<div className="flex-1 overflow-y-auto rounded-lg bg-white/30 backdrop-blur-sm">
80-
<div className="mx-auto p-8">
89+
<div className="min-h-[520px] flex-1 overflow-y-auto rounded-lg bg-white/30 backdrop-blur-sm lg:min-h-0">
90+
<div className="mx-auto p-4 sm:p-8">
8191
<div className="mb-8">
8292
<h2 className="text-3xl font-semibold text-[#010507] mb-2">
8393
Research Results
@@ -104,7 +114,7 @@ export default function Home() {
104114
</div>
105115
)}
106116

107-
<div className="flex flex-row gap-2 items-stretch">
117+
<div className="flex flex-col gap-2 items-stretch xl:flex-row">
108118
{researchData && (
109119
<div className="flex-1 bg-white/60 backdrop-blur-md rounded-xl border-2 border-[#DBDBE5] shadow-elevation-md p-6">
110120
<div className="flex flex-col gap-0 mb-4">
@@ -189,3 +199,33 @@ export default function Home() {
189199
</div>
190200
);
191201
}
202+
203+
export default function Home() {
204+
const [threadId, setThreadId] = useState<string | undefined>(undefined);
205+
206+
return (
207+
<CopilotKitProvider
208+
runtimeUrl="/api/copilotkit"
209+
showDevConsole="auto"
210+
useSingleEndpoint={false}
211+
>
212+
<div className={`${styles.layout} threadsLayout`}>
213+
<ThreadsPanelGate>
214+
<ThreadsDrawer
215+
agentId="a2a_chat"
216+
threadId={threadId}
217+
onThreadChange={setThreadId}
218+
/>
219+
</ThreadsPanelGate>
220+
<div className={styles.mainPanel}>
221+
<CopilotChatConfigurationProvider
222+
agentId="a2a_chat"
223+
threadId={threadId}
224+
>
225+
<ResearchAssistant />
226+
</CopilotChatConfigurationProvider>
227+
</div>
228+
</div>
229+
</CopilotKitProvider>
230+
);
231+
}

0 commit comments

Comments
 (0)