Skip to main content

Agent Integration Patterns

OpsHub NAV offers 4 specialized integration patterns for connecting to the Agent System. Each pattern is optimized for specific use cases, from simple chat interfaces to complex multi-agent workflows.
Quick Start: Most developers should start with CopilotKit for simple chat or AG-UI for complex workflows. Choose SSE for real-time streaming or Workspace Context for ambient intelligence features.

Why 4 Different Patterns?

Different AI-powered features require different communication patterns:
  • CopilotKit: Best for simple, familiar chat interfaces with minimal setup
  • AG-UI Protocol: Designed for complex multi-agent orchestration with shared state
  • SSE Streaming: Optimized for real-time text generation with low latency
  • Workspace Context: Enables context-aware features without explicit user requests
Each pattern has trade-offs in complexity, latency, state management, and bidirectional communication capabilities.

Pattern Comparison

FeatureCopilotKitAG-UISSE StreamingWorkspace Context
Connection TypeWebSocketHTTPServer-Sent EventsHTTP
Bidirectional✅ Yes✅ Yes (polling)❌ No✅ Yes
State ManagementClient-sideShared (Zustand)NoneServer-side
Best ForChat UIMulti-agent workflowsStreaming textContext awareness
ComplexityLowHighLowMedium
LatencyLowMediumLowMedium

Quick Decision Guide

Use this flowchart to choose the right pattern:

Decision Matrix

If You Need…Use This PatternWhy
Quick chat interface for help/Q&ACopilotKitLow complexity, familiar chat UX, React hooks included
Multiple agents collaborating on a taskAG-UIShared state management, multi-agent coordination
Real-time LLM completion streamingSSE StreamingLowest latency for token-by-token display
Context-aware suggestions without user askingWorkspace ContextAutomatic page detection, ambient intelligence
Human-in-loop approval workflowsAG-UIBuilt-in tool execution gates and state persistence
Simple request/response with streamingSSE StreamingMinimal overhead, no state management needed

Pattern 1: CopilotKit (WebSocket)

What It Is

CopilotKit provides a drop-in chat interface with React hooks and UI components, connected via WebSocket for real-time bidirectional communication.

When to Use

✅ Use For

  • Simple chat interfaces
  • Contextual help and assistance
  • Quick Q&A features
  • Real-time collaboration

❌ Avoid For

  • Complex multi-agent orchestration
  • State persistence across sessions
  • Bulk data processing

Key Features

  • React hooks for easy integration (useCopilotChat, useCopilotAction)
  • Pre-built UI components (CopilotPopup, CopilotSidebar)
  • WebSocket connection for real-time updates
  • Low latency bidirectional communication

Quick Example

// app/(app)/help/page.tsx
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotPopup } from "@copilotkit/react-ui";

export default function HelpPage() {
  return (
    <CopilotKit runtimeUrl="/api/copilotkit">
      <div className="content">
        <h1>Need Help?</h1>
        <p>Ask our AI assistant anything!</p>
      </div>
      <CopilotPopup
        instructions="You are a helpful assistant for OpsHub NAV users."
        defaultOpen={true}
      />
    </CopilotKit>
  );
}
API Proxy Required: Create /api/copilotkit/route.ts to proxy requests to the backend with proper authentication.

Learn More

CopilotKit Pattern Details

Complete implementation guide with backend setup, authentication, and advanced features

Pattern 2: AG-UI Protocol

What It Is

AG-UI is a protocol for multi-agent orchestration with shared state management, designed for complex workflows where multiple agents collaborate.

When to Use

✅ Use For

  • Complex agent workflows
  • Multi-agent coordination
  • Shared state across components
  • Tool execution with human-in-the-loop

❌ Avoid For

  • Simple single-message requests
  • Stateless operations
  • Quick prototypes

Key Features

  • Multi-agent state management via Zustand stores
  • Tool execution framework with approval gates
  • Message history persisted across sessions
  • Human-in-the-loop workflows with signals

Quick Example

// components/agent/DataAnalysisWorkflow.tsx
import { useMultiAgentClient } from '@/lib/agent/multi-agent-client';

export function DataAnalysisWorkflow() {
  const client = useMultiAgentClient({
    userId: user.id,
    sessionId: 'analysis-session',
  });

  const runAnalysis = async () => {
    // Step 1: Query agent fetches data
    await client.sendMessage('Fetch portfolio holdings', {
      agent: 'query-agent',
      portfolioId: 'port-123',
    });

    // Step 2: Analysis agent processes
    await client.sendMessage('Calculate risk metrics', {
      agent: 'analysis-agent',
    });

    // Step 3: Validation agent checks
    await client.sendMessage('Validate against ASIC RG94', {
      agent: 'validation-agent',
    });
  };

  return (
    <div>
      <MessageList messages={client.messages} />
      <button onClick={runAnalysis}>Run Multi-Agent Analysis</button>
    </div>
  );
}
Complexity Trade-off: AG-UI requires more setup than CopilotKit but provides fine-grained control over multi-agent workflows.

Learn More

AG-UI Protocol Details

Complete implementation guide with state management, tool execution, and multi-agent coordination

Pattern 3: SSE Streaming (Vercel AI SDK)

What It Is

Server-Sent Events (SSE) provide one-way real-time streaming from server to client, ideal for LLM token-by-token completions.

When to Use

✅ Use For

  • Real-time text streaming
  • Progress updates during long operations
  • Token-by-token completions
  • Simple request/response with streaming

❌ Avoid For

  • Bidirectional communication needs
  • Complex state management
  • Multi-turn conversations

Key Features

  • Lowest latency for streaming text
  • Token-by-token display for LLM responses
  • Progress updates during long-running operations
  • Simple implementation with minimal overhead

Quick Example

// components/StreamingCompletion.tsx
import { useAgentStream } from '@/lib/agent/use-agent-stream';

export function StreamingCompletion() {
  const { messages, isLoading, sendMessage } = useAgentStream({
    sessionId: generateId(),
  });

  return (
    <div>
      <div className="messages">
        {messages.map(msg => (
          <div key={msg.id}>
            <strong>{msg.role}:</strong> {msg.content}
          </div>
        ))}
      </div>

      <form onSubmit={(e) => {
        e.preventDefault();
        sendMessage(e.currentTarget.message.value);
      }}>
        <input name="message" placeholder="Type your message..." />
        <button disabled={isLoading}>Send</button>
      </form>
    </div>
  );
}
One-Way Communication: SSE is server-to-client only. For bidirectional needs, use WebSocket (CopilotKit) or HTTP polling (AG-UI).

Learn More

SSE Streaming Details

Complete implementation guide with event handling, error recovery, and optimization

Pattern 4: Workspace Context Sync

What It Is

Workspace Context Sync provides automatic context detection from the current page, enabling ambient intelligence and proactive suggestions.

When to Use

✅ Use For

  • Automatic context detection
  • Ambient intelligence features
  • Page-aware AI suggestions
  • Proactive insights

❌ Avoid For

  • User-initiated interactions (use other patterns)
  • Scenarios where context is not relevant
  • Simple CRUD operations

Key Features

  • Automatic page detection from URL and state
  • Context-aware suggestions without user prompting
  • Server-side context storage for persistence
  • Proactive insights based on user activity

Quick Example

// app/(app)/data-modeling/page.tsx
import { useWorkspace } from '@/lib/contexts/WorkspaceContext';

export function DataModelingPage() {
  const workspace = useWorkspace();
  const [suggestions, setSuggestions] = useState([]);

  useEffect(() => {
    // Context automatically synced to backend
    workspace.updateContext({
      page: '/data-modeling',
      selectedTables: selectedTables.map(t => t.name),
      currentQuery: queryBuilder.toSQL(),
    });

    // Backend provides context-aware suggestions
    fetchSuggestions().then(setSuggestions);
  }, [selectedTables, queryBuilder]);

  return (
    <div>
      <QueryBuilder />
      <SmartSuggestions suggestions={suggestions} />
    </div>
  );
}
Privacy Consideration: Context sync happens in the background. Ensure users are aware of context collection and provide opt-out options if needed.

Learn More

Workspace Context Details

Complete implementation guide with context detection, privacy controls, and optimization

Authentication & Security

All 4 integration patterns use the same standardized authentication approach for consistency and security.

Standard Authentication Pattern

// lib/api/backend-auth.ts
import { createClient } from '@/lib/supabase/server';

export async function getAuthHeaders(): Promise<HeadersInit> {
  const supabase = await createClient();
  const { data: { session }, error } = await supabase.auth.getSession();

  if (error || !session) {
    throw new Error('Unauthorized: User must be authenticated');
  }

  // CRITICAL: Use session.access_token (JWT), NOT user.id
  return {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${session.access_token}`,
  };
}

URL Configuration Helper

// lib/config/agent-backend-url.ts
export function buildAgentUrl(path: string): string {
  const base = process.env.AGENT_BACKEND_URL;
  if (!base) {
    throw new Error('AGENT_BACKEND_URL not configured');
  }
  return `${base}${path}`;
}

Usage in API Routes

// app/api/agent/*/route.ts
import { buildAgentUrl } from '@/lib/config/agent-backend-url';
import { getAuthHeaders } from '@/lib/api/backend-auth';

export async function POST(request: Request) {
  const authHeaders = await getAuthHeaders();
  const backendUrl = buildAgentUrl('/api/endpoint');

  const response = await fetch(backendUrl, {
    method: 'POST',
    headers: authHeaders,
    body: await request.text(),
  });

  return response;
}

Security Best Practices

  • ✅ DO
  • ❌ DON'T
  • Use getAuthHeaders() for all backend requests
  • Use buildAgentUrl() for URL construction
  • Keep AGENT_BACKEND_URL server-side only (no NEXT_PUBLIC_)
  • Validate JWT tokens on backend
  • Use Next.js API routes as proxy (never direct browser → backend)
Common Security Mistake: Never use user.id as a Bearer token. Always use session.access_token which is a proper JWT that the backend can validate.

Environment Configuration

# .env.local (server-side only)
AGENT_BACKEND_URL=https://opshub-agent-backend.fly.dev
AGENT_API_KEY=your_api_key

# DEPRECATED (backward compatibility only)
# NEXT_PUBLIC_AGENT_API_URL=...
# NEXT_PUBLIC_AGENT_BACKEND_URL=...
EnvironmentAGENT_BACKEND_URL
Developmenthttp://localhost:8000
Staginghttps://staging-agent.fly.dev
Productionhttps://opshub-agent-backend.fly.dev

Getting Started

Step 1: Choose Your Pattern

Use the Quick Decision Guide to select the right pattern for your use case.

Step 2: Set Up Authentication

Configure environment variables and create helper functions:
# Add to .env.local
AGENT_BACKEND_URL=http://localhost:8000

Step 3: Create API Proxy

All patterns require a Next.js API route to proxy requests:
// app/api/agent/[pattern]/route.ts
import { buildAgentUrl } from '@/lib/config/agent-backend-url';
import { getAuthHeaders } from '@/lib/api/backend-auth';

export async function POST(request: Request) {
  const authHeaders = await getAuthHeaders();
  const backendUrl = buildAgentUrl('/api/pattern-endpoint');

  const response = await fetch(backendUrl, {
    method: 'POST',
    headers: authHeaders,
    body: await request.text(),
  });

  return response;
}

Step 4: Follow Pattern-Specific Guide


Pattern Selection Examples

Example 1: Help Chat

Requirement: Add a help chatbot to answer user questions Pattern: CopilotKit Why: Simple chat interface, no complex state, familiar UX

Example 2: Data Analysis Workflow

Requirement: Multi-step analysis involving data fetch → calculation → validation → report Pattern: AG-UI Protocol Why: Multiple agents need to coordinate, shared state required, human approval before final report

Example 3: Report Generation

Requirement: Generate a long report with real-time progress updates Pattern: SSE Streaming Why: One-way streaming of progress text, no bidirectional communication needed

Example 4: Smart Query Suggestions

Requirement: Show relevant query suggestions based on current page and data Pattern: Workspace Context Sync Why: Automatic context detection, proactive suggestions without user prompting

Troubleshooting Common Issues

”Unauthorized” Errors

Cause: Using user.id instead of JWT token Fix:
// WRONG ❌
headers: { 'Authorization': `Bearer ${user.id}` }

// CORRECT ✅
const authHeaders = await getAuthHeaders();

“AGENT_BACKEND_URL not configured”

Cause: Missing environment variable Fix:
# Add to .env.local
AGENT_BACKEND_URL=http://localhost:8000

CORS Errors in Browser

Cause: Direct browser → backend requests (bypassing Next.js proxy) Fix: Always use Next.js API routes as proxy
// WRONG ❌
fetch('https://backend.fly.dev/api/chat')

// CORRECT ✅
fetch('/api/agent/chat')  // Proxied through Next.js

WebSocket Connection Fails (CopilotKit)

Cause: WebSocket endpoint not properly configured Fix: Ensure backend WebSocket endpoint matches frontend config
# Backend
@app.websocket("/copilotkit")
async def copilotkit_ws(websocket: WebSocket)
// Frontend
<CopilotKit runtimeUrl="/api/copilotkit">

Summary

Simple Chat

Use CopilotKit
  • Low complexity
  • React hooks included
  • WebSocket real-time

Complex Workflows

Use AG-UI
  • Multi-agent coordination
  • Shared state management
  • Human-in-the-loop

Streaming Text

Use SSE
  • Lowest latency
  • Token-by-token display
  • Simple implementation

Context Awareness

Use Workspace Context
  • Automatic detection
  • Ambient intelligence
  • Proactive insights

Additional Resources


Last Updated: October 29, 2025 Status: ✅ Production Ready - All patterns fixed and standardized