Skip to main content

@enclave-vm/broker

Tool broker and session management for the EnclaveJS streaming runtime. Orchestrates code execution, tool handling, and session lifecycle.

Installation

npm install @enclave-vm/broker

Quick Start

import { createBroker } from '@enclave-vm/broker';
import { z } from 'zod';

const broker = createBroker({
  securityLevel: 'SECURE',
});

// Register a tool
broker.tool('fetchData', {
  description: 'Fetch data from a URL',
  argsSchema: z.object({
    url: z.string().url(),
    method: z.enum(['GET', 'POST']).default('GET'),
  }),
  handler: async ({ url, method }) => {
    const response = await fetch(url, { method });
    return response.json();
  },
});

// Execute code
const stream = broker.execute(`
  const data = await tools.fetchData({ url: 'https://api.example.com/data' });
  console.log('Fetched:', data.length, 'items');
  return data;
`);

for await (const event of stream) {
  if (event.type === 'stdout') {
    process.stdout.write(event.payload.chunk);
  }
}

Broker Configuration

import { createBroker, type BrokerConfig } from '@enclave-vm/broker';

const config: BrokerConfig = {
  // Security level for enclave-vm
  securityLevel: 'SECURE',  // 'STRICT' | 'SECURE' | 'STANDARD' | 'PERMISSIVE'

  // Default session limits
  defaultLimits: {
    timeout: 30000,
    memoryLimit: 128 * 1024 * 1024,
    maxToolCalls: 100,
    maxConsoleBytes: 1024 * 1024,
  },

  // Heartbeat interval (ms)
  heartbeatInterval: 30000,

  // Enable debug logging
  debug: false,
};

const broker = createBroker(config);

Tool Registry

Defining Tools

import { createToolRegistry, type ToolDefinition } from '@enclave-vm/broker';
import { z } from 'zod';

const registry = createToolRegistry();

// Define a tool
const tool: ToolDefinition = {
  name: 'sendEmail',
  description: 'Send an email to a recipient',
  argsSchema: z.object({
    to: z.string().email(),
    subject: z.string().max(100),
    body: z.string(),
  }),
  handler: async (args, context) => {
    // context provides session info
    console.log(`Session ${context.sessionId} sending email`);

    await emailService.send(args);
    return { sent: true, timestamp: Date.now() };
  },
  timeout: 10000,  // Tool-specific timeout
};

registry.register(tool);

// Or use the broker's shorthand
broker.tool('sendEmail', {
  argsSchema: z.object({ to: z.string().email() }),
  handler: async ({ to }) => { /* ... */ },
});

Tool Context

import type { ToolContext } from '@enclave-vm/broker';

// The handler receives context about the execution
const handler = async (args: Args, context: ToolContext) => {
  context.sessionId;   // Current session ID
  context.callId;      // Unique call ID
  context.timestamp;   // When the call was made
};

Tool Validation

const registry = createToolRegistry();

// Validate tool arguments
const result = registry.validate('sendEmail', {
  to: 'invalid-email',
});

if (!result.success) {
  console.log('Validation errors:', result.errors);
}

// Get tool info
const tool = registry.get('sendEmail');
console.log(tool?.description);

// List all tools
const tools = registry.list();

Session Management

Session Manager

import { createSessionManager, type SessionManagerConfig } from '@enclave-vm/broker';

const manager = createSessionManager({
  maxConcurrentSessions: 100,
  sessionTimeout: 300000,  // 5 minutes
  cleanupInterval: 60000,  // 1 minute
});

// Create a session
const { sessionId, stream } = await manager.create({
  code: 'return 1 + 1',
  limits: { timeout: 5000 },
});

// Get session info
const info = manager.get(sessionId);
console.log(info?.state);  // 'running' | 'waiting_for_tool' | 'completed' | ...

// List all sessions
const sessions = manager.list();

// Cancel a session
await manager.cancel(sessionId, 'User requested cancellation');

Session Info

import type { SessionInfo } from '@enclave-vm/broker';

const info: SessionInfo = {
  sessionId: 'ses_...',
  state: 'running',
  createdAt: Date.now(),
  limits: {
    timeout: 30000,
    memoryLimit: 128 * 1024 * 1024,
  },
  stats: {
    executionTime: 1234,
    toolCalls: 3,
    memoryUsage: 1024 * 1024,
  },
};

Broker Session

import { createBrokerSession, type BrokerSessionConfig } from '@enclave-vm/broker';

// Lower-level session wrapper
const session = createBrokerSession({
  enclave,          // Enclave instance
  toolRegistry,     // Tool registry
  sessionId,        // Session ID
  limits,           // Session limits
});

// Execute code
const stream = session.execute(code);

// Submit tool result
await session.submitToolResult(callId, result);

// Cancel execution
await session.cancel('Timeout exceeded');

HTTP API

Express Integration

import express from 'express';
import { createBroker, createExpressRouter } from '@enclave-vm/broker';

const app = express();
const broker = createBroker();

// Mount the broker routes
const router = createExpressRouter(broker);
app.use('/api/enclave', router);

// Routes created:
// POST /api/enclave/sessions - Create session
// GET  /api/enclave/sessions - List sessions
// GET  /api/enclave/sessions/:id - Get session info
// POST /api/enclave/sessions/:id/cancel - Cancel session

app.listen(3000);

Manual Route Registration

import { registerExpressRoutes } from '@enclave-vm/broker';

const app = express();
const broker = createBroker();

registerExpressRoutes(app, broker, {
  prefix: '/api/v1',
  middleware: [authMiddleware],
});

Session Handler

import { createSessionHandler, type SessionHandlerConfig } from '@enclave-vm/broker';

const handler = createSessionHandler({
  broker,
  onError: (error, req) => {
    console.error('Handler error:', error);
  },
});

// Use with any HTTP framework
app.post('/sessions', async (req, res) => {
  const result = await handler.createSession(req.body);
  res.json(result);
});

API Types

import type {
  CreateSessionRequest,
  SessionInfoResponse,
  ListSessionsResponse,
  ErrorResponse,
  StreamOptions,
} from '@enclave-vm/broker';

// Create session request
const request: CreateSessionRequest = {
  code: 'console.log("Hello")',
  limits: {
    timeout: 30000,
  },
  streamOptions: {
    includeHeartbeats: false,
  },
};

// Session info response
const response: SessionInfoResponse = {
  sessionId: 'ses_...',
  state: 'completed',
  createdAt: 1234567890,
  stats: {
    executionTime: 100,
    toolCalls: 0,
  },
};

Event Filtering

Filter which events are streamed to clients.

Type-Based Filtering

import { createEventFilter, createTypeFilter } from '@enclave-vm/broker';
import { FilterMode } from '@enclave-vm/types';

// Exclude heartbeats and debug logs
const filter = createEventFilter({
  typeFilter: createTypeFilter({
    mode: FilterMode.EXCLUDE,
    types: ['heartbeat'],
  }),
});

// Filter events
const shouldInclude = filter.matches(event);

// Filter a stream
const filteredStream = filter.filterStream(eventStream);

Content-Based Filtering

import { createContentFilter } from '@enclave-vm/broker';
import { FilterMode, PatternType } from '@enclave-vm/types';

// Filter out events containing sensitive data
const filter = createEventFilter({
  contentFilter: createContentFilter({
    mode: FilterMode.EXCLUDE,
    patterns: [
      { type: PatternType.REGEX, content: 'password|secret|token' },
      { type: PatternType.PREFIX, content: 'DEBUG:' },
    ],
  }),
});

Combined Filtering

const filter = createEventFilter({
  typeFilter: {
    mode: FilterMode.INCLUDE,
    types: ['stdout', 'log', 'final', 'error'],
  },
  contentFilter: {
    mode: FilterMode.EXCLUDE,
    patterns: [
      { type: PatternType.GLOB, content: '*internal*' },
    ],
  },
});

// Use with streaming
const stream = broker.execute(code);
for await (const event of stream) {
  if (filter.matches(event)) {
    sendToClient(event);
  }
}

Always-Allowed Events

Some events are always included regardless of filters to ensure protocol correctness:
import { DEFAULT_ALWAYS_ALLOW } from '@enclave-vm/types';

// These events bypass filters:
// - session_init
// - final
// - error

Execution Options

import type { ExecuteOptions } from '@enclave-vm/broker';

const options: ExecuteOptions = {
  // Override default limits
  limits: {
    timeout: 60000,
    maxToolCalls: 50,
  },

  // Pre-defined globals
  globals: {
    config: { apiKey: 'xxx' },
  },

  // Event filter
  filter: createEventFilter({ /* ... */ }),

  // Reference sidecar for large data
  references: new Map([
    ['ref_data', largeDataset],
  ]),
};

const stream = broker.execute(code, options);