Portal Community

Integration Direction

EdgeStream pushes to WorkDesk — WorkDesk subscribes to topics and receives server-pushed events. WorkDesk never publishes to EdgeStream; it only receives. Publishing is done by backend services (HILTaskSyncService, notification services, workflow engine).

WorkDesk Topic Subscriptions

WorkDesk subscribes to exactly two EdgeStream topics per authenticated user session:

Task Events

tasks.{userId}

New HIL task assignments, task status changes (claimed by another, cancelled, deadline updated), task expiry warnings.

Notification Events

notifications.{userId}

New notifications of any type (workflow completed, HIL assigned, approval decision made, system alerts). Drives the notification bell badge count.

Subscription Setup in WorkDeskShell

Subscriptions are established in WorkDeskShell.tsx immediately after the user authenticates. The useSubscription hook manages the WebSocket connection lifecycle:

// WorkDeskShell.tsx — EdgeStream subscriptions
import { useSubscription } from '@bizfirstai/edgestream-react';
import { useInboxStore }   from './stores/inboxStore';
import { useNotifStore }   from './stores/notifStore';

export function WorkDeskShell() {
  const userId = useAuthStore(s => s.userId);
  const addTask = useInboxStore(s => s.addTask);
  const updateTask = useInboxStore(s => s.updateTask);
  const addNotif = useNotifStore(s => s.addNotification);
  const incrementBell = useNotifStore(s => s.incrementUnread);

  // Subscribe to task events
  useSubscription(`tasks.${userId}`, (event: TaskEvent) => {
    switch (event.type) {
      case 'task_assigned':
        addTask(event.task);
        showToast(`New task: ${event.task.title}`, 'info');
        break;
      case 'task_claimed_by_other':
        updateTask(event.taskId, { status: 'claimed', claimedBy: event.actorId });
        break;
      case 'task_cancelled':
        updateTask(event.taskId, { status: 'cancelled' });
        break;
      case 'task_deadline_warning':
        showToast(`Task due soon: ${event.taskTitle}`, 'warning');
        break;
    }
  });

  // Subscribe to notification events
  useSubscription(`notifications.${userId}`, (event: NotificationEvent) => {
    addNotif(event.notification);
    incrementBell();
    if (event.notification.priority === 'high') {
      showToast(event.notification.title, 'info');
    }
  });

  return ( <div className="shell">...</div> );
}

Event Payload Schemas

Each EdgeStream event published to WorkDesk's topics follows a typed schema:

// Task events — published to topics.{userId}
interface TaskAssignedEvent {
  type: 'task_assigned';
  task: {
    id:           string;
    type:         'approval' | 'form' | 'review';
    title:        string;
    workflowName: string;
    dueAt:        string | null;
    createdAt:    string;
    status:       'pending';
  };
}

interface TaskClaimedByOtherEvent {
  type:      'task_claimed_by_other';
  taskId:    string;
  actorId:   string;
  claimedAt: string;
}

interface TaskCancelledEvent {
  type:        'task_cancelled';
  taskId:      string;
  cancelledAt: string;
  reason:      string | null;
}

// Notification events — published to notifications.{userId}
interface NotificationEvent {
  type: 'notification_created';
  notification: {
    id:        string;
    type:      'hil_task_assigned' | 'workflow_completed' | 'approval_decision' | 'system_alert';
    title:     string;
    body:      string;
    priority:  'normal' | 'high';
    isRead:    false;
    createdAt: string;
    actionUrl: string | null;
  };
}

Connection Lifecycle

The EdgeStream WebSocket connection is managed by the useSubscription hook. WorkDesk handles connection failures gracefully with exponential backoff reconnection:

1

User Authenticates

WorkDeskShell mounts. useSubscription opens a WebSocket connection to EdgeStream using the user's Passport JWT.

2

Topics Subscribed

After connection established, WorkDesk sends subscription frames for tasks.{userId} and notifications.{userId}. EdgeStream acknowledges each subscription.

3

Events Received

Backend services publish events to the relevant topic. EdgeStream delivers them to all active subscribers matching that topic — in this case, the employee's WorkDesk browser session.

4

Connection Lost

If the WebSocket drops, useSubscription enters reconnection mode. WorkDesk shows a subtle "Reconnecting..." indicator in the status bar. Reconnect attempts use exponential backoff: 1s, 2s, 4s, 8s, max 30s.

5

Reconnected — Catch-Up Fetch

On reconnection, WorkDesk fetches any missed events via REST API (GET /api/workdesk/tasks?createdAfter={lastEventTime}) to cover the offline gap before EdgeStream resumes live delivery.

Multiple Browser Tabs

WorkDesk uses a shared WebSocket approach — if the employee has WorkDesk open in multiple tabs, only one WebSocket connection is maintained per browser using a BroadcastChannel leader election. The leader tab receives EdgeStream events and broadcasts them to other tabs:

// EdgeStream leader election for multi-tab scenarios
// (handled inside @bizfirstai/edgestream-react useSubscription hook)

// One tab wins the leader election and holds the WebSocket
// All other tabs receive events via BroadcastChannel
const bc = new BroadcastChannel('workdesk-edgestream');

// Leader tab — after receiving EdgeStream event:
bc.postMessage({ topic, event });  // fan out to follower tabs

// Follower tabs — receive via BroadcastChannel:
bc.onmessage = ({ data }) => dispatchEvent(data.topic, data.event);

Performance Characteristics

MetricValueNotes
Event delivery latency< 200ms p99From backend publish to browser event handler
Connection overhead1 WebSocket per browserLeader election reduces connections in multi-tab scenarios
Reconnection max backoff30 secondsAfter which WorkDesk shows a manual reconnect button
Catch-up fetch windowLast 15 minutesEvents older than 15 minutes are not replayed on reconnection
Max subscriptions per session10 topicsWorkDesk uses 2; remaining quota available for hosted App Studio apps
No Polling

WorkDesk never polls for task updates or notifications. All real-time updates are delivered exclusively via EdgeStream. This eliminates unnecessary API load and ensures instant delivery. The REST APIs are used only for initial page loads (hydrating state) and post-reconnection catch-up fetches.