Skip to main content

Overview

Workspace Context Integration enables ambient intelligence by automatically detecting and syncing the user’s current work environment to agent systems. This allows AI agents to provide contextually relevant suggestions, smart defaults, and page-aware assistance without requiring explicit user input. Unlike user-initiated interactions, workspace context operates passively in the background, continuously monitoring the application state and making it available to both frontend and backend agent systems.

What is Ambient Intelligence?

Ambient intelligence means the system understands:
  • Where you are: Current page, active view, focused artifact
  • What you’re working on: Selected portfolio, date range, active workbook
  • What you can do: Available actions based on current context
  • Your preferences: User settings that affect agent behavior
This contextual awareness enables agents to offer proactive insights, pre-fill requests with relevant data, and filter tool availability based on the current page.

Key Benefits

Smart Defaults

Automatically pre-fill agent requests with portfolio ID, date, or workbook context

Page-Aware Tools

Show only relevant actions based on current view (spreadsheet vs dashboard)

Proactive Insights

Agent suggests contextually relevant actions without being asked

Reduced Friction

Users don’t need to repeatedly specify context in every request

Architecture

Context Flow Diagram

Automatic Context Detection

The WorkspaceProvider automatically detects context from:
  1. URL Parameters: Extracts IDs from route segments
  2. Page State: Monitors active components and focused elements
  3. Local Storage: Retrieves persisted user selections
  4. Props & Events: Listens to component state changes

State Management Flow

  • Frontend Detection
  • Backend Extraction
// Automatic detection in WorkspaceProvider
useEffect(() => {
  const detected = {
    currentView: detectViewFromRoute(pathname),
    workbookId: extractFromUrl('workbookId'),
    selectedPortfolioId: getUserSelection('portfolio'),
    selectedDate: getActiveDate(),
    availableActions: getActionsForView(currentView),
  };

  setContext(detected);
  syncToBackend(detected);
}, [pathname, pageState]);

Context Types

Core Context Fields

View Context

currentView - Current page/feature (workbook, dashboard, workflow)focusMode - User’s focus area within the view

Artifact Context

workbookId - Active spreadsheet identifierdashboardId - Active dashboard identifierworkflowId - Active workflow identifier

Selection Context

selectedPortfolioId - Currently selected portfolioselectedDate - Currently selected dateselectedDateRange - Date range selection

Action Context

availableActions - Actions available in current viewrecentActions - Recently performed actions

Context Schema

interface WorkspaceContext {
  // View Context
  currentView: 'workspace' | 'workbook' | 'dashboard' | 'workflow' | 'catalog' | 'validation' | 'visual_query_builder' | 'unknown';
  focusMode?: 'spreadsheet' | 'dashboard' | 'workflow' | 'validation' | 'pipeline' | 'catalog';

  // Artifact Context
  workbookId?: string;
  workbookName?: string;
  dashboardId?: string;
  dashboardName?: string;
  workflowId?: string;
  workflowName?: string;
  widgetId?: string;

  // Selection Context
  selectedPortfolioId?: string;
  selectedPortfolioName?: string;
  selectedDate?: string; // ISO 8601
  selectedDateRange?: {
    start: string;
    end: string;
  };

  // Action Context
  availableActions?: string[];

  // Session Context
  sessionId?: string;
  userId?: string;
  organizationId?: string;
  teamId?: string;
}

Backend Setup

Context Extraction Middleware

  • Next.js Route
  • Python Backend
// lib/middleware/workspace-context.ts
import { NextRequest } from 'next/server';
import { WorkspaceContextSchema } from '@/lib/validation/workspace-context-schema';

export async function extractWorkspaceContext(request: NextRequest) {
  try {
    // Try request body first
    const body = await request.json();
    if (body.workspace) {
      return WorkspaceContextSchema.parse(body.workspace);
    }

    // Fallback to headers
    const headerValue = request.headers.get('X-Workspace-Context');
    if (headerValue) {
      const parsed = JSON.parse(headerValue);
      return WorkspaceContextSchema.parse(parsed);
    }
  } catch (error) {
    console.warn('Failed to extract workspace context:', error);
  }

  return null;
}

// Usage in API route
export async function POST(request: NextRequest) {
  const workspace = await extractWorkspaceContext(request);

  const response = await fetch(AGENT_BACKEND_URL, {
    method: 'POST',
    headers: {
      'X-Workspace-Context': JSON.stringify(workspace),
    },
    body: JSON.stringify({ ...data, workspace }),
  });

  return response;
}

Forward Context to Backend

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

export async function syncWorkspaceContext(context: WorkspaceContext) {
  const authHeaders = await getAuthHeaders();
  const url = buildAgentUrl('/api/workspace/sync');

  await fetch(url, {
    method: 'POST',
    headers: {
      ...authHeaders,
      'Content-Type': 'application/json',
      'X-Workspace-Context': JSON.stringify(context),
    },
    body: JSON.stringify(context),
  });
}

Frontend Implementation

WorkspaceProvider Setup

// lib/contexts/WorkspaceContext.tsx
import { createContext, useContext, useEffect, useState } from 'react';
import { usePathname } from 'next/navigation';
import { syncWorkspaceContext } from '@/lib/agent/workspace-sync';
import type { WorkspaceContext } from '@/lib/types/workspace';

const WorkspaceContext = createContext<WorkspaceContext | null>(null);

export function WorkspaceProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();
  const [context, setContext] = useState<WorkspaceContext>({
    currentView: 'unknown',
    availableActions: [],
  });

  useEffect(() => {
    // Auto-detect context from URL and page state
    const detected = detectContext(pathname);
    setContext(detected);

    // Sync to backend
    syncWorkspaceContext(detected);
  }, [pathname]);

  return (
    <WorkspaceContext.Provider value={context}>
      {children}
    </WorkspaceContext.Provider>
  );
}

export function useWorkspaceContext() {
  const context = useContext(WorkspaceContext);
  if (!context) {
    throw new Error('useWorkspaceContext must be used within WorkspaceProvider');
  }
  return context;
}

Context Detection Logic

// lib/utils/context-detection.ts
import type { WorkspaceContext } from '@/lib/types/workspace';

export function detectContext(pathname: string): WorkspaceContext {
  const segments = pathname.split('/').filter(Boolean);

  // Detect view from route
  const currentView = detectViewFromRoute(segments);

  // Extract IDs from URL
  const workbookId = extractIdFromSegments(segments, 'workbooks');
  const dashboardId = extractIdFromSegments(segments, 'dashboards');
  const workflowId = extractIdFromSegments(segments, 'workflows');

  // Determine available actions
  const availableActions = getActionsForView(currentView);

  return {
    currentView,
    workbookId,
    dashboardId,
    workflowId,
    availableActions,
  };
}

function detectViewFromRoute(segments: string[]): WorkspaceContext['currentView'] {
  if (segments.includes('workbooks')) return 'workbook';
  if (segments.includes('dashboards')) return 'dashboard';
  if (segments.includes('workflows')) return 'workflow';
  if (segments.includes('catalog')) return 'catalog';
  if (segments.includes('validation')) return 'validation';
  if (segments.includes('data-modeling')) return 'visual_query_builder';
  return 'workspace';
}

function getActionsForView(view: string): string[] {
  const actionMap: Record<string, string[]> = {
    workbook: ['update_spreadsheet', 'create_formula', 'insert_data'],
    dashboard: ['create_widget', 'update_chart', 'refresh_data'],
    workflow: ['create_workflow', 'update_workflow', 'test_workflow'],
    validation: ['create_rule', 'run_validation', 'view_breaches'],
  };

  return actionMap[view] || [];
}

Custom Hook for Components

// lib/hooks/use-agent-context.ts
import { useWorkspaceContext } from '@/lib/contexts/WorkspaceContext';

export function useAgentContext() {
  const workspace = useWorkspaceContext();

  const sendAgentRequest = async (message: string) => {
    const response = await fetch('/api/agent', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        messages: [{ role: 'user', content: message }],
        workspace, // ← Automatically include context
      }),
    });

    return response.json();
  };

  return {
    ...workspace,
    sendAgentRequest,
  };
}

Complete Example

Page-Specific Agent Suggestions

  • Workbook Page
  • Backend Tool
// app/workbooks/[id]/page.tsx
import { useAgentContext } from '@/lib/hooks/use-agent-context';
import { useEffect, useState } from 'react';

export default function WorkbookPage({ params }: { params: { id: string } }) {
  const { currentView, workbookId, sendAgentRequest } = useAgentContext();
  const [suggestions, setSuggestions] = useState([]);

  useEffect(() => {
    // Context automatically includes workbookId
    // Agent knows we're in a spreadsheet context
    async function fetchSuggestions() {
      const response = await sendAgentRequest('Give me suggestions');
      setSuggestions(response.suggestions);
    }

    fetchSuggestions();
  }, [workbookId]);

  return (
    <div>
      <h1>Workbook {workbookId}</h1>
      <AgentSuggestions items={suggestions} />
      <SpreadsheetGrid />
    </div>
  );
}

Context Detection

Automatic Detection

The WorkspaceProvider automatically detects context changes through:
Monitors Next.js usePathname() hook to detect navigation
useEffect(() => {
  const newContext = detectContext(pathname);
  setContext(newContext);
}, [pathname]);
Listens to component state changes via props
useEffect(() => {
  updateContext({
    selectedPortfolioId: portfolio.id,
    selectedPortfolioName: portfolio.name,
  });
}, [portfolio]);
Detects user selections and updates context
const handlePortfolioSelect = (portfolio) => {
  updateContext({
    selectedPortfolioId: portfolio.id,
  });
};

Manual Context Override

In some cases, you may want to manually set context:
import { useWorkspaceContext } from '@/lib/contexts/WorkspaceContext';

function MyComponent() {
  const { updateContext } = useWorkspaceContext();

  const handleCustomAction = () => {
    // Manually update context
    updateContext({
      focusMode: 'validation',
      availableActions: ['run_validation', 'view_results'],
    });
  };
}

Tool Selection

Context-Based Tool Filtering

Tools can declare their required context to control availability:
  • Tool Definition
  • Agent Tool Selection
@tool("update_spreadsheet")
def update_spreadsheet(
    cell: str,
    value: str,
    config: Optional[RunnableConfig] = None
) -> Dict[str, Any]:
    """
    Update a cell in the active spreadsheet.

    REQUIRED CONTEXT:
    - current_view: 'workbook'
    - workbook_id: Must be present

    This tool is only available when user is viewing a workbook.
    """
    workspace_context = extract_workspace_context(config)

    if not workspace_context or workspace_context.current_view != 'workbook':
        return {
            "error": "This action requires an active workbook",
            "suggestion": "Please open a workbook first"
        }

    if not workspace_context.workbook_id:
        return {
            "error": "No workbook ID detected",
            "suggestion": "Ensure you have a workbook open"
        }

    # Perform update...
    return {"success": True}

Smart Defaults

Pre-filling Agent Requests

Context enables automatic pre-filling of common parameters:
@tool("query_portfolio_holdings")
def query_portfolio_holdings(
    portfolio_id: Optional[str] = None,
    date: Optional[str] = None,
    config: Optional[RunnableConfig] = None
) -> List[Dict]:
    """
    Query portfolio holdings.

    SMART DEFAULTS:
    - portfolio_id: Uses selected portfolio if not specified
    - date: Uses selected date if not specified
    """
    workspace_context = extract_workspace_context(config)

    # Smart default: Use selected portfolio
    if not portfolio_id and workspace_context:
        portfolio_id = workspace_context.selected_portfolio_id
        if portfolio_id:
            logger.info(f"Auto-detected portfolio: {workspace_context.selected_portfolio_name}")

    # Smart default: Use selected date
    if not date and workspace_context:
        date = workspace_context.selected_date
        if date:
            logger.info(f"Auto-detected date: {date}")

    # Validate required params
    if not portfolio_id:
        return {
            "error": "Portfolio ID required",
            "suggestion": "Select a portfolio or specify portfolio_id parameter"
        }

    # Execute query with smart defaults...

Best Practices

Context Hygiene

DO: Clear context when navigating away from artifacts
useEffect(() => {
  return () => {
    // Cleanup on unmount
    updateContext({
      workbookId: undefined,
      dashboardId: undefined,
    });
  };
}, []);
DON’T: Let stale context persist across unrelated pages
// BAD: Context from previous page leaks into new page
// Workbook ID still set after navigating to dashboard

Performance Optimization

DO: Debounce context updates for rapid changes
import { useDebouncedCallback } from 'use-debounce';

const debouncedSync = useDebouncedCallback(
  (context) => syncWorkspaceContext(context),
  500 // Wait 500ms after last change
);

useEffect(() => {
  debouncedSync(context);
}, [context]);
DO: Only sync context when it actually changes
const prevContextRef = useRef<WorkspaceContext>();

useEffect(() => {
  // Deep comparison to avoid unnecessary syncs
  if (!isEqual(prevContextRef.current, context)) {
    syncWorkspaceContext(context);
    prevContextRef.current = context;
  }
}, [context]);

Security Considerations

DO: Validate context permissions on backend
def validate_context_permissions(
    workspace_context: WorkspaceContext,
    user_id: str
) -> bool:
    """Ensure user has access to context artifacts"""
    if workspace_context.workbook_id:
        if not user_has_workbook_access(user_id, workspace_context.workbook_id):
            raise PermissionError("User cannot access this workbook")

    if workspace_context.selected_portfolio_id:
        if not user_has_portfolio_access(user_id, workspace_context.selected_portfolio_id):
            raise PermissionError("User cannot access this portfolio")

    return True
DON’T: Trust frontend context without validation

Troubleshooting

Context Not Detected

Symptoms: Agent doesn’t have access to current page context Solutions:
1

Check WorkspaceProvider

Ensure your component is wrapped in <WorkspaceProvider>
// In app/layout.tsx
<WorkspaceProvider>
  {children}
</WorkspaceProvider>
2

Verify Detection Logic

Add debug logging to context detection
useEffect(() => {
  console.log('Current pathname:', pathname);
  console.log('Detected context:', context);
}, [pathname, context]);
3

Check Sync Status

Verify context is being sent to backend
// In workspace-sync.ts
console.log('Syncing context:', context);

Context Not Reaching Backend

Symptoms: Backend tools don’t receive workspace context Solutions:
1

Verify Headers

Check that X-Workspace-Context header is set
// In API route
console.log('Request headers:', request.headers.get('X-Workspace-Context'));
2

Check Request Body

Ensure context is in request body if not using headers
# In Python backend
print(f"Request body workspace: {data.workspace}")
3

Validate Schema

Ensure context matches expected schema
try:
    workspace_context = WorkspaceContext(**workspace_data)
    print(f"Valid context: {workspace_context}")
except Exception as e:
    print(f"Invalid context: {e}")

Stale Context

Symptoms: Context from previous page persists on new page Solutions:
  • Add cleanup in useEffect return function
  • Implement deep comparison to detect actual changes
  • Clear artifact-specific context on navigation


Implementation Status: Phase 1 & 2 Complete Last Updated: 2025-10-29