JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 20
  • Score
    100M100P100Q58209F
  • License UNLICENSED

Client-side SDK for interacting with Arthur Intelligence and Booths system.

Package Exports

  • arthur-sdk
  • arthur-sdk/package.json

Readme

Arthur SDK

Build Format Check Tests

A client-side TypeScript SDK for interacting with Arthur Intelligence and the Booths system. Includes both full-featured ArthurSDK for interactive conversations and ArthurSyncSDK for simple synchronous booth interactions.

Table of Contents

Installation

npm install arthur-sdk

Quick Start Guide

Follow this step-by-step process to set up and use the Arthur SDK:

import {
  ArthurSDK,
  ArthurSyncSDK,
  BoothRegistry,
  ToolRegistry,
} from 'arthur-sdk';

// STEP 1: Create registries
const toolRegistry = new ToolRegistry();
const boothRegistry = new BoothRegistry();

// STEP 2: Register tools first (they are referenced by booths)
toolRegistry.registerTool({
  name: 'example-tool',
  description: 'An example tool',
  parameters: { input: 'string' },
  global: true, // Available to all booths
  execute: async (args) => {
    return { result: 'Tool executed successfully', input: args.input };
  },
});

// STEP 3: Register booths (they can reference the tools)
boothRegistry.registerBooth({
  id: 'example-booth',
  name: 'Example Booth',
  description: 'An example booth',
  context: 'This is an example booth for demonstration purposes.',
  // No tools array = only has access to global tools
});

// STEP 4: Initialize the SDK
const sdk = new ArthurSDK({
  userId: 'your-user-id',
  clientId: 'your-client-id', // Required for client identification
  interactURL: 'https://your-api.com/api/message', // Must be absolute URL
  interactionEventsURL: 'https://your-api.com/api/interactionloop', // Must be absolute URL
  agentConfig: {
    agent: 'armor',
    boothRegistry,
    toolRegistry,
  },
  sessionId: 'existing-session-id', // Optional: resume existing session
});

// STEP 5: Set up callbacks
sdk.onMessagesReceived = (messages) => {
  console.log('New messages:', messages);
};

sdk.onInteractionLoopComplete = () => {
  console.log('Interaction loop completed');
};

sdk.onSessionIdChanged = (sessionId) => {
  console.log('Session ID changed:', sessionId);
  localStorage.setItem('arthur-session-id', sessionId);
};

// STEP 6: Configure headers (optional)
sdk.setAdditionalHeaders({
  Authorization: 'Bearer your-token',
  'X-Custom-Header': 'custom-value',
});

// STEP 7: Send messages and interact
await sdk.sendMessageAndListen('Hello, Arthur!');

API Reference

ArthurSDK

The main SDK class for interacting with Arthur Intelligence.

Constructor Options

Required Parameters

  • userId (string): Unique identifier for the user
  • clientId (string): Unique identifier for the client instance
  • interactURL (string): Absolute URL for sending messages (e.g., 'https://api.example.com/api/message')
  • interactionEventsURL (string): Absolute URL for the interaction loop (e.g., 'https://api.example.com/api/interactionloop')
  • agentConfig (AgentConfig): Agent configuration containing:
    • agent (AgentType): Type of agent ('armor' | 'custom')
    • boothRegistry (BoothRegistry, optional): Registry for managing booth configurations
    • toolRegistry (ToolRegistry, optional): Registry for managing tool configurations

Optional Parameters

  • sessionId (string): Existing session ID to resume a previous conversation
    • Default: undefined

Constructor Examples

Basic initialization for a new conversation
The simplest setup using only required parameters. Default empty registries will be created automatically.

const basicSdk = new ArthurSDK({
  userId: 'user-12345',
  clientId: 'web-client-001',
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    // boothRegistry and toolRegistry are optional - defaults to empty registries
  },
});

Using custom tool and booth registries
Most common pattern where you provide pre-configured registries with your tools and booths.

const customToolRegistry = new ToolRegistry();
const customBoothRegistry = new BoothRegistry();
// ... register your tools and booths first

const customSdk = new ArthurSDK({
  userId: 'user-12345',
  clientId: 'web-client-001',
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    toolRegistry: customToolRegistry,
    boothRegistry: customBoothRegistry,
  },
});

Resuming an existing conversation session
Use this when you want to continue a previous conversation by providing the session ID.

const resumedSdk = new ArthurSDK({
  userId: 'user-12345',
  clientId: 'web-client-001',
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    toolRegistry: customToolRegistry,
    boothRegistry: customBoothRegistry,
  },
  sessionId: 'session-abc-456', // Resume previous conversation
});

Development and testing setup
Simplified configuration for local development with localhost URLs.

const devSdk = new ArthurSDK({
  userId: 'dev-user',
  clientId: 'dev-client',
  interactURL: 'http://localhost:8080/api/message',
  interactionEventsURL: 'http://localhost:8080/api/interactionloop',
  agentConfig: {
    agent: 'armor',
  },
});

Production setup with environment variables
Enterprise-ready configuration using environment variables and session persistence.

const prodSdk = new ArthurSDK({
  userId: process.env.USER_ID!,
  clientId: process.env.CLIENT_ID!,
  interactURL: process.env.ARTHUR_API_URL + '/api/message',
  interactionEventsURL: process.env.ARTHUR_API_URL + '/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    toolRegistry: productionToolRegistry,
    boothRegistry: productionBoothRegistry,
  },
  sessionId: getStoredSessionId(), // Optional: restore session
});

Core Methods

ArthurSDK.sendMessage()

Send a message to the Arthur Intelligence API without automatically starting the interaction loop listener.

Example

await sdk.sendMessage('Hello, Arthur!');

Syntax

// Send a simple text message
await sdk.sendMessage(message);

// Send complex message array
await sdk.sendMessage(messages);

Parameters

  • message (string | ResponseInputItem[]): The message to send. Can be a simple string or an array of ResponseInputItem objects for complex message structures.

Return Type

Promise<void> - Returns a promise that resolves when the message has been sent successfully. The promise will reject if there's an error during sending (network issues, invalid message format, etc.).

More Examples

Simple user query
Most common usage - sending a basic user message.

await sdk.sendMessage('What is the weather today?');

System logging and notifications
Use developer role for internal system messages, logging, or debugging information.

await sdk.sendMessage([{ role: 'developer', content: 'User logged in' }]);

User query with developer context
Add hidden context that helps the LLM understand the user's situation without the user seeing it.

const userQueryWithContext: ResponseInputItem[] = [
  {
    role: 'user',
    content: 'What is the weather forecast for tomorrow?',
  },
  {
    role: 'developer',
    content:
      'User is located in New York, NY. Use metric units for temperature.',
  },
];
await sdk.sendMessage(userQueryWithContext);

Assistant gaslighting (fake conversation history)
Inject an assistant message that the assistant will believe it actually said, making it part of its conversation history.

const gaslightingExample: ResponseInputItem[] = [
  {
    role: 'user',
    content: 'Help me with email drafting',
  },
  {
    role: 'assistant',
    content:
      'I specialize in professional email composition and always use formal tone.',
  },
];
await sdk.sendMessage(gaslightingExample);

Combining developer context with assistant gaslighting
Use both techniques together for maximum control over the LLM's understanding and behavior.

const fullControlExample: ResponseInputItem[] = [
  {
    role: 'user',
    content: 'Write a status update email',
  },
  {
    role: 'developer',
    content:
      'User is a project manager sending weekly update to stakeholders. Include metrics and next steps.',
  },
  {
    role: 'assistant',
    content:
      'I create structured, professional status emails with clear sections and actionable items.',
  },
];
await sdk.sendMessage(fullControlExample);

Under The Hood

You can send messages without starting the interaction loop listener. These messages will be queued in the session until you're ready to start the listener. This is useful for sending background messages without blocking the UI. The LLM will not respond immediately to these messages, but they will be stored.

Important: When you start the interaction loop listener, the SDK will process all queued messages from the session. The LLM sees messages in chronological order, so make sure to send all your messages BEFORE starting the listener, otherwise the LLM will see the last message as the most recent in the conversation.

ArthurSDK.startInteractionLoopListener()

Manually start listening for server-sent events from the interaction loop. Requires that a session ID already exists (from a previous sendMessage call).

Example

// First send a message to establish session
await sdk.sendMessage('Initial message');
// Then start listening manually
sdk.startInteractionLoopListener();

Syntax

// Start listening (no parameters)
sdk.startInteractionLoopListener();

Parameters

None. This method requires that a session ID has already been established through a previous sendMessage or sendMessageAndListen call.

Return Type

void - This method returns immediately after starting the EventSource connection. It does not return a promise. Responses will be handled through the registered callback functions (onMessagesReceived, onInteractionLoopComplete). Will throw an error if no session ID exists.

More Examples

// CORRECT: Send all messages first, then start listening
await sdk.sendMessage('Setup conversation');
await sdk.sendMessage([{ role: 'developer', content: 'User context info' }]);
await sdk.sendMessage('Main user query');
// Now start listening - LLM will see messages in correct order
sdk.startInteractionLoopListener();

// INCORRECT: Don't interleave messages and listener starts
await sdk.sendMessage('First message');
sdk.startInteractionLoopListener(); // LLM thinks this is the last message
await sdk.sendMessage('This will be seen as NEWER than the listener start');

// Restart listening after connection issues
try {
  sdk.startInteractionLoopListener();
} catch (error) {
  if (error.message.includes('No session ID found')) {
    // Need to send a message first to establish session
    await sdk.sendMessage('Reconnecting...');
    sdk.startInteractionLoopListener();
  }
}

// Error handling
sdk.onInteractionLoopComplete = () => {
  console.log('Conversation ended, can restart if needed');
};

ArthurSDK.sendMessageAndListen()

Send a message and automatically start listening for responses via the interaction loop. This is the most commonly used method for interactive conversations.

Example

await sdk.sendMessageAndListen('Hello, I need help with my tasks');

Syntax

// Send text message and start listening
await sdk.sendMessageAndListen(message);

// Send complex message array and start listening
await sdk.sendMessageAndListen(messages);

Parameters

  • message (string | ResponseInputItem[]): The message to send. Can be a simple string or an array of ResponseInputItem objects for complex message structures.

Return Type

Promise<void> - Returns a promise that resolves when the message has been sent successfully and the interaction loop listener has been started. The promise will reject if there's an error during sending or if starting the listener fails.

More Examples

// Start a new conversation
await sdk.sendMessageAndListen('I need help with weather and email');

// Continue an existing conversation
await sdk.sendMessageAndListen('Can you send that email now?');

// Send with complex message structure
await sdk.sendMessageAndListen([
  { role: 'user', content: 'Please help me with these tasks:' },
  { role: 'user', content: '1. Check weather in New York' },
  { role: 'user', content: '2. Send email to john@example.com' },
]);

Multi-task request with full context control
Combine user query, developer context, and assistant gaslighting for complex interactive tasks.

const advancedMessage: ResponseInputItem[] = [
  {
    role: 'user',
    content: 'I need help with weather and sending an important email',
  },
  {
    role: 'developer',
    content:
      'Priority request - user is traveling tomorrow and needs weather info for NYC. Email recipient is their boss.',
  },
  {
    role: 'assistant',
    content:
      'I always provide detailed weather forecasts and help craft professional emails with appropriate tone.',
  },
];
await sdk.sendMessageAndListen(advancedMessage);
// Interactive session with immediate response handling
sdk.onMessagesReceived = (messages) => {
  // Handle real-time responses
  messages.forEach(msg => console.log(msg.content));
};
await sdk.sendMessageAndListen('Start interactive session');

ArthurSDK.onMessagesReceived

Set a callback function to handle incoming messages and message updates in real-time.

Example

sdk.onMessagesReceived = (messages) => {
  console.log('New messages:', messages);
};

Syntax

// Set the callback function
sdk.onMessagesReceived = callback;

// Get the current callback function
const currentCallback = sdk.onMessagesReceived;

Parameters

  • callback ((messages: ConversationMessage[]) => void): Function that receives the complete array of conversation messages whenever messages are received or updated.

Return Type

void - This is a setter property. The callback function itself should not return anything.

More Examples

Basic message logging
Simple logging of all incoming messages.

sdk.onMessagesReceived = (messages) => {
  messages.forEach((msg) => {
    console.log(`${msg.role}: ${msg.content}`);
  });
};

UI update with message filtering
Update your UI and handle different message types appropriately.

sdk.onMessagesReceived = (messages) => {
  const userMessages = messages.filter((msg) => msg.type === 'message');
  const toolCalls = messages.filter((msg) => msg.type === 'tool_call');

  // Update UI with user/assistant messages
  updateChatUI(userMessages);

  // Show loading indicators for active tool calls
  toolCalls.forEach((toolCall) => {
    if (toolCall.loading) {
      showToolLoadingIndicator(toolCall.content.name);
    }
  });
};

Message state management
Integration with state management systems.

sdk.onMessagesReceived = (messages) => {
  // Update your application state
  dispatch(updateMessages(messages));

  // Save to local storage for persistence
  localStorage.setItem('chat-messages', JSON.stringify(messages));

  // Trigger other side effects
  if (messages.length > 0) {
    markConversationAsActive();
  }
};

Message Types and Shapes

The messages array passed to your callback contains ConversationMessage objects. Important: This array includes ALL messages in the conversation, including the messages you sent via sendMessage() and sendMessageAndListen(). You don't need to manually add your sent messages to your UI - they're automatically included in this array.

Here are the different types you'll encounter:

User/Assistant Text Messages
Standard conversation messages from users or AI assistants.

// ConversationMessageUserText
{
  type: 'message',
  role: 'user' | 'assistant' | 'developer',
  content: 'Hello, I need help with weather',
  id: 'msg-123',
  time: '2024-01-15T10:30:00Z',
  loading: false
}

Tool Call Messages
Messages representing tool executions in progress or completed.

// ConversationMessageToolCall
{
  type: 'tool_call',
  role: 'assistant',
  loading: true,  // true while executing, false when complete
  id: 'call-456',
  time: '2024-01-15T10:30:05Z',
  content: {
    call_id: 'call-456',
    name: 'weather-api',
    results: { temperature: 72, conditions: 'sunny' } // present when loading: false
  }
}

Handling Different Message Types
Use type guards to handle each message type appropriately.

import {
  isConversationMessageUserText,
  isConversationMessageToolCall,
} from 'arthur-sdk';

sdk.onMessagesReceived = (messages) => {
  messages.forEach((message) => {
    if (isConversationMessageUserText(message)) {
      // Handle text message
      console.log(`${message.role}: ${message.content}`);
      displayTextMessage(message);
    } else if (isConversationMessageToolCall(message)) {
      // Handle tool call message
      if (message.loading) {
        showToolExecutionIndicator(message.content.name);
      } else {
        hideToolExecutionIndicator(message.content.name);
        displayToolResults(message.content.results);
      }
    }
  });
};

Complete Message Flow Example
How messages evolve during a typical conversation with tool usage.

// Initial messages array might look like:
[
  {
    type: 'message',
    role: 'user',
    content: 'What is the weather in New York?',
    id: 'msg-1',
  },
  {
    type: 'message',
    role: 'assistant',
    content: "I'll check the weather in New York for you.",
    id: 'msg-2',
  },
  {
    type: 'tool_call',
    role: 'assistant',
    loading: true, // Tool is executing
    id: 'call-1',
    content: {
      call_id: 'call-1',
      name: 'weather-api',
    },
  },
][
  // After tool execution completes, the same array updates to:
  // ... previous messages unchanged ...
  ({
    type: 'tool_call',
    role: 'assistant',
    loading: false, // Tool completed
    id: 'call-1',
    content: {
      call_id: 'call-1',
      name: 'weather-api',
      results: { temperature: 68, conditions: 'partly cloudy' },
    },
  },
  {
    type: 'message',
    role: 'assistant',
    content: 'The weather in New York is 68°F and partly cloudy.',
    id: 'msg-3',
  })
];

Important Note About Message Inclusion
The messages array automatically includes your sent messages - no manual UI updates needed for sent messages.

// ❌ WRONG: Don't manually add sent messages to your UI
await sdk.sendMessage('Hello!');
addMessageToUI({ role: 'user', content: 'Hello!' }); // Don't do this!

// ✅ CORRECT: Just send the message and let the callback handle UI updates
sdk.onMessagesReceived = (messages) => {
  updateCompleteUI(messages); // This already includes your sent message
};
await sdk.sendMessage('Hello!');

// The callback will receive:
// [
//   { type: 'message', role: 'user', content: 'Hello!', id: 'msg-1' },
//   { type: 'message', role: 'assistant', content: 'Hi there!', id: 'msg-2' }
// ]

ArthurSDK.pushMessage()

Add a new message to the conversation array. This immediately triggers the onMessagesReceived callback with the updated messages array.

Example

const newMessage = {
  type: 'message',
  role: 'user',
  content: 'This is a manually added message',
  id: 'manual-msg-1',
};
sdk.pushMessage(newMessage);

Syntax

sdk.pushMessage(message);

Parameters

  • message (ConversationMessage): The message object to add to the conversation. Must be either ConversationMessageUserText or ConversationMessageToolCall.

Return Type

void - This method does not return anything. The message is added to the internal messages array and immediately triggers the onMessagesReceived callback.

More Examples

Adding a user message programmatically
Useful for injecting messages from external sources or cached conversations.

const userMessage: ConversationMessage = {
  type: 'message',
  role: 'user',
  content: 'Restored from cache',
  id: 'cached-msg-1',
  time: '2024-01-15T09:00:00Z',
};
sdk.pushMessage(userMessage);

Adding a tool call message
For reconstructing conversations that included tool executions.

const toolMessage: ConversationMessage = {
  type: 'tool_call',
  role: 'assistant',
  loading: false,
  id: 'tool-call-1',
  content: {
    call_id: 'tool-call-1',
    name: 'weather-api',
    results: { temperature: 75, conditions: 'sunny' },
  },
};
sdk.pushMessage(toolMessage);

ArthurSDK.updateMessage()

Update an existing message in the conversation by its ID. Useful for updating loading states or modifying message content.

Example

sdk.updateMessage('msg-123', (message) => ({
  ...message,
  loading: false,
}));

Syntax

sdk.updateMessage(id, updater);

Parameters

  • id (string): The unique identifier of the message to update
  • updater ((message: ConversationMessage) => ConversationMessage): Function that receives the current message and returns the updated message

Return Type

void - This method does not return anything. The message is updated in the internal messages array and immediately triggers the onMessagesReceived callback.

More Examples

Update tool call completion status
Mark a tool call as completed and add results.

sdk.updateMessage('tool-call-456', (msg) => ({
  ...msg,
  loading: false,
  content: {
    ...msg.content,
    results: { success: true, data: 'operation completed' },
  },
}));

Update message content
Modify the text content of an existing message.

sdk.updateMessage('msg-789', (msg) => ({
  ...msg,
  content: msg.content + ' (edited)',
  time: new Date().toISOString(),
}));

Conditional updates
Only update if certain conditions are met.

sdk.updateMessage('msg-101', (msg) => {
  if (msg.type === 'tool_call' && msg.loading) {
    return { ...msg, loading: false };
  }
  return msg; // No change if conditions not met
});

ArthurSDK.setMessages()

Replace the entire conversation messages array. This immediately triggers the onMessagesReceived callback with the new messages.

Example

const newMessages = [
  { type: 'message', role: 'user', content: 'Hello', id: 'msg-1' },
  { type: 'message', role: 'assistant', content: 'Hi there!', id: 'msg-2' },
];
sdk.setMessages(newMessages);

Syntax

sdk.setMessages(messages);

Parameters

  • messages (ConversationMessage[]): Array of conversation messages to replace the current messages with

Return Type

void - This method does not return anything. The messages array is replaced and immediately triggers the onMessagesReceived callback.

More Examples

Load conversation from storage
Restore a previously saved conversation.

const savedMessages = JSON.parse(
  localStorage.getItem('conversation-history') || '[]',
);
sdk.setMessages(savedMessages);

Reset conversation
Clear all messages and start fresh.

sdk.setMessages([]);

Initialize with system message
Start a conversation with a predefined system context.

const initialMessages = [
  {
    type: 'message',
    role: 'assistant',
    content: "Welcome! I'm ready to help you with weather and email tasks.",
    id: 'welcome-msg',
  },
];
sdk.setMessages(initialMessages);

ArthurSDK.clearSession()

Clear the current session ID and optionally clear the conversation messages. This will cause the next sendMessage() call to create a new session on the server.

Example

// Clear session and messages (start completely fresh)
sdk.clearSession();

// Clear session but keep messages for display purposes
sdk.clearSession(false);

Syntax

sdk.clearSession(clearMessages);

Parameters

  • clearMessages (boolean, optional): Whether to clear the messages array. Default: true

Return Type

void - This method does not return anything. The session is cleared, optionally messages are cleared, any active EventSource connection is closed, and the onSessionIdChanged callback is triggered with undefined.

More Examples

Complete session reset (default behavior)
Clear both the session and all conversation messages for a completely fresh start.

// This is the default - clears session AND messages
sdk.clearSession();
// Equivalent to: sdk.clearSession(true);

// Next message will create a brand new session
await sdk.sendMessage('Starting a new conversation');

Keep messages for UI display
Clear the session but preserve messages in the UI for user reference.

// Keep messages visible but start new session
sdk.clearSession(false);

// Messages still visible to user, but next message starts new server session
await sdk.sendMessage('Continue with new session');

Programmatic session management
Integrate session clearing with your application logic.

// Handle user logout - clear everything
function handleLogout() {
  sdk.clearSession(); // Clear session and messages
  localStorage.removeItem('arthur-session-id'); // Clear stored session
  redirectToLogin();
}

// Handle "new conversation" button
function startNewConversation() {
  sdk.clearSession(); // Fresh session and clear messages
  showWelcomeMessage();
}

// Handle session timeout - keep messages for reference
function handleSessionTimeout() {
  sdk.clearSession(false); // Clear session but keep messages
  showSessionExpiredNotification();
}

With session change callback handling
Handle the session clearing in your callback.

sdk.onSessionIdChanged = (sessionId) => {
  if (sessionId === undefined) {
    // Session was cleared
    console.log('Session cleared - next message will create new session');
    localStorage.removeItem('arthur-session-id');
    updateUIForNoSession();
  } else {
    // New session created
    console.log('New session:', sessionId);
    localStorage.setItem('arthur-session-id', sessionId);
    updateUIForActiveSession(sessionId);
  }
};

// This will trigger the callback with undefined
sdk.clearSession();

Error recovery and reconnection
Use session clearing for error recovery scenarios.

// Handle connection errors by starting fresh
sdk.onInteractionLoopComplete = () => {
  if (sdk.eventSource?.readyState === EventSource.CLOSED) {
    console.log('Connection lost - clearing session for fresh start');
    sdk.clearSession(false); // Keep messages but clear session

    // Attempt to reconnect with fresh session
    setTimeout(async () => {
      try {
        await sdk.sendMessage('Reconnecting...');
      } catch (error) {
        console.error('Failed to reconnect:', error);
      }
    }, 2000);
  }
};

ArthurSDK.setAdditionalHeaders()

Set multiple custom headers that will be included in all HTTP requests to the server.

Example

sdk.setAdditionalHeaders({
  Authorization: 'Bearer your-token-here',
  'X-API-Version': '2.1',
  'X-Client-Source': 'web-app',
});

Syntax

sdk.setAdditionalHeaders(headers);

Parameters

  • headers (Record<string, string>): Object containing header key-value pairs to set. This will replace all existing additional headers.

Return Type

void - This method does not return anything.

More Examples

Authentication headers
Set authorization and API credentials for secured endpoints.

sdk.setAdditionalHeaders({
  Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
  'X-API-Key': 'your-api-key-here',
});

Custom request context
Add application-specific metadata to requests.

sdk.setAdditionalHeaders({
  'X-User-Role': 'admin',
  'X-Organization-ID': 'org-12345',
  'X-Request-Source': 'dashboard',
  'X-Client-Version': '1.2.3',
});

Replacing existing headers
Since this method replaces all headers, use it for complete header resets.

// Initial headers
sdk.setAdditionalHeaders({
  Authorization: 'Bearer old-token',
  'X-Version': '1.0',
});

// Replace with new set (old headers are removed)
sdk.setAdditionalHeaders({
  Authorization: 'Bearer new-token',
  'X-Version': '2.0',
  'X-Feature-Flag': 'new-ui',
});

ArthurSDK.setHeader()

Set a single custom header that will be included in all HTTP requests to the server. If the header already exists, it will be updated.

Example

sdk.setHeader('Authorization', 'Bearer new-token-here');

Syntax

sdk.setHeader(key, value);

Parameters

  • key (string): The header name/key to set
  • value (string): The header value to set

Return Type

void - This method does not return anything.

More Examples

Update authentication token
Refresh authorization headers without affecting other headers.

// Set initial auth
sdk.setHeader('Authorization', 'Bearer initial-token');

// Later update just the auth token
sdk.setHeader('Authorization', 'Bearer refreshed-token');

Add API versioning
Specify API version for backward compatibility.

sdk.setHeader('X-API-Version', '2.1');
sdk.setHeader('Accept', 'application/vnd.api+json;version=2.1');

Request tracking and debugging
Add correlation IDs and debug information.

sdk.setHeader('X-Request-ID', generateUUID());
sdk.setHeader('X-Debug-Mode', 'true');
sdk.setHeader('X-Source-Component', 'chat-widget');

ArthurSDK.removeHeader()

Remove a specific header from the additional headers collection. The header will no longer be included in HTTP requests.

Example

sdk.removeHeader('X-Debug-Mode');

Syntax

sdk.removeHeader(key);

Parameters

  • key (string): The header name/key to remove

Return Type

void - This method does not return anything. If the header doesn't exist, no error is thrown.

More Examples

Remove authentication
Clear authorization headers when logging out.

sdk.removeHeader('Authorization');
sdk.removeHeader('X-API-Key');

Clean up temporary headers
Remove debug or temporary headers after use.

// Set temporary debug headers
sdk.setHeader('X-Debug-Session', 'debug-123');
sdk.setHeader('X-Trace-Enabled', 'true');

// Remove them when debugging is complete
sdk.removeHeader('X-Debug-Session');
sdk.removeHeader('X-Trace-Enabled');

Conditional header removal
Remove headers based on application state.

// Remove development headers in production
if (process.env.NODE_ENV === 'production') {
  sdk.removeHeader('X-Debug-Mode');
  sdk.removeHeader('X-Test-Environment');
}

ArthurSDK.getAdditionalHeaders()

Get a copy of all additional headers currently set. Returns an object containing all custom headers that will be included in HTTP requests.

Example

const headers = sdk.getAdditionalHeaders();
console.log(headers); // { 'Authorization': 'Bearer token', 'X-API-Version': '2.1' }

Syntax

const headers = sdk.getAdditionalHeaders();

Parameters

None.

Return Type

Record<string, string> - Object containing all additional headers as key-value pairs. This is a copy, so modifying the returned object won't affect the SDK's headers.

More Examples

Header inspection and debugging
Check what headers are currently set for debugging purposes.

const currentHeaders = sdk.getAdditionalHeaders();
console.log('Current headers:', currentHeaders);

// Check if specific header exists
if ('Authorization' in currentHeaders) {
  console.log('Authorization header is set');
}

Backup and restore headers
Save current headers before making temporary changes.

// Backup current headers
const originalHeaders = sdk.getAdditionalHeaders();

// Make temporary changes
sdk.setAdditionalHeaders({
  'X-Test-Mode': 'true',
  'X-Mock-Data': 'enabled',
});

// Later restore original headers
sdk.setAdditionalHeaders(originalHeaders);

Header validation and sanitization
Validate headers before sending requests.

const headers = sdk.getAdditionalHeaders();

// Validate required headers
const requiredHeaders = ['Authorization', 'X-API-Version'];
const missingHeaders = requiredHeaders.filter((header) => !(header in headers));

if (missingHeaders.length > 0) {
  console.warn('Missing required headers:', missingHeaders);
}

// Sanitize sensitive information in logs
const sanitizedHeaders = { ...headers };
if (sanitizedHeaders.Authorization) {
  sanitizedHeaders.Authorization = 'Bearer [REDACTED]';
}
console.log('Headers for logging:', sanitizedHeaders);

ArthurSDK.messages

Direct access to the current conversation messages array. This property provides read and write access to the internal messages collection.

Example

// Read current messages
console.log('Current messages:', sdk.messages);

// Add a message directly (not recommended - use pushMessage instead)
sdk.messages.push(newMessage);

Type

ConversationMessage[] - Array containing all conversation messages in chronological order.

More Examples

Reading conversation state
Access current conversation messages for display or analysis.

const messageCount = sdk.messages.length;
const lastMessage = sdk.messages[sdk.messages.length - 1];

console.log(`Conversation has ${messageCount} messages`);
if (lastMessage) {
  console.log('Last message:', lastMessage.content);
}

Filtering messages by type
Extract specific types of messages from the conversation.

const userMessages = sdk.messages.filter(
  (msg) => msg.type === 'message' && msg.role === 'user',
);

const toolCalls = sdk.messages.filter((msg) => msg.type === 'tool_call');

const activeToolCalls = toolCalls.filter((call) => call.loading);

Direct manipulation (use with caution)
While direct access is available, using the SDK methods is preferred.

// ❌ Direct manipulation - can bypass callbacks
sdk.messages.push(newMessage);

// ✅ Preferred approach - triggers callbacks
sdk.pushMessage(newMessage);

ArthurSDK.messagesList

Getter property that returns the messages array. This is an alias for the messages property, providing the same functionality with a more descriptive name.

Example

const allMessages = sdk.messagesList;
console.log('Total messages:', allMessages.length);

Type

ConversationMessage[] - Array containing all conversation messages, identical to the messages property.

More Examples

Equivalent access patterns
Both properties provide the same data.

// These are functionally identical
const messages1 = sdk.messages;
const messages2 = sdk.messagesList;

console.log(messages1 === messages2); // true

Choosing between messages and messagesList
Use whichever naming convention fits your codebase better.

// Shorter, more direct
const count = sdk.messages.length;

// More explicit, self-documenting
const messageHistory = sdk.messagesList;

ArthurSDK.toolRegistry

Access to the ToolRegistry instance used by the SDK. This provides access to all registered tools and their configurations.

Example

const weatherTool = sdk.toolRegistry.getTool('weather-api');
console.log('Weather tool:', weatherTool);

Type

ToolRegistry - The ToolRegistry instance containing all registered tools and their execution logic.

More Examples

Tool inspection and debugging
Examine available tools and their configurations.

const allTools = sdk.toolRegistry.getAllTools();
const toolNames = sdk.toolRegistry.getToolNames();

console.log('Available tools:', toolNames);
console.log('Tool configurations:', allTools);

Runtime tool registration
Add new tools after SDK initialization.

// Register additional tools at runtime
sdk.toolRegistry.registerTool({
  name: 'custom-calculator',
  description: 'Performs custom calculations',
  parameters: { expression: 'string' },
  execute: async (args) => {
    return { result: eval(args.expression) }; // Use carefully in production!
  },
});

Tool execution verification
Check if required tools are available before operations.

const requiredTools = ['weather-api', 'email-sender'];
const missingTools = requiredTools.filter(
  (toolName) => !sdk.toolRegistry.getTool(toolName),
);

if (missingTools.length > 0) {
  console.error('Missing required tools:', missingTools);
}

ArthurSDK.eventSource

Access to the current EventSource instance used for server-sent events. This property is null when not connected to the interaction loop.

Example

if (sdk.eventSource) {
  console.log('Connection state:', sdk.eventSource.readyState);
} else {
  console.log('Not connected to server events');
}

Type

EventSource | null - The EventSource instance for receiving real-time updates from the server, or null when disconnected.

More Examples

Connection state monitoring
Monitor the real-time connection status.

if (sdk.eventSource) {
  const states = {
    [EventSource.CONNECTING]: 'Connecting',
    [EventSource.OPEN]: 'Connected',
    [EventSource.CLOSED]: 'Disconnected',
  };

  console.log('EventSource state:', states[sdk.eventSource.readyState]);
} else {
  console.log('EventSource not initialized');
}

Connection management
Manually manage connection lifecycle if needed.

// Check if connected before attempting operations
const isConnected = sdk.eventSource?.readyState === EventSource.OPEN;

if (isConnected) {
  console.log('Ready to receive real-time updates');
} else {
  console.log('Connection not available for real-time updates');
}

// Force close connection (usually handled by SDK)
if (sdk.eventSource) {
  sdk.eventSource.close();
}

Debug connection issues
Add custom event listeners for debugging connection problems.

if (sdk.eventSource) {
  sdk.eventSource.addEventListener('error', (event) => {
    console.error('EventSource error:', event);
  });

  sdk.eventSource.addEventListener('open', () => {
    console.log('EventSource connection opened');
  });
}

### ArthurSDK.onInteractionLoopComplete

Set a callback function that executes when the interaction loop ends (conversation completes or connection closes).

#### Example
```typescript
sdk.onInteractionLoopComplete = () => {
  console.log('Conversation completed');
};

Syntax

// Set the callback function
sdk.onInteractionLoopComplete = callback;

// Get the current callback function
const currentCallback = sdk.onInteractionLoopComplete;

Parameters

  • callback (() => void): Function that takes no parameters and is called when the interaction loop completes.

Return Type

void - This is a setter property. The callback function itself should not return anything.

More Examples

UI cleanup and state reset
Clean up loading states and reset UI when conversation ends.

sdk.onInteractionLoopComplete = () => {
  // Hide loading indicators
  hideLoadingSpinner();

  // Reset UI state
  setConversationStatus('completed');

  // Enable new conversation button
  enableNewConversationButton();
};

Logging and analytics
Track conversation completion for analytics and debugging.

sdk.onInteractionLoopComplete = () => {
  // Log conversation metrics
  console.log('Conversation ended at:', new Date().toISOString());

  // Send analytics event
  analytics.track('conversation_completed', {
    duration: Date.now() - conversationStartTime,
    messageCount: sdk.messages.length,
  });

  // Clean up resources
  cleanupConversationResources();
};

Error handling and reconnection
Handle unexpected disconnections and implement retry logic.

sdk.onInteractionLoopComplete = () => {
  if (sdk.eventSource?.readyState === EventSource.CLOSED) {
    console.log('Connection closed unexpectedly');

    // Attempt to reconnect after delay
    setTimeout(() => {
      if (confirmReconnection()) {
        sdk.startInteractionLoopListener();
      }
    }, 5000);
  }
};

ArthurSDK.onSessionIdChanged

Set a callback function that executes whenever the session ID changes, typically when starting a new conversation.

Example

sdk.onSessionIdChanged = (sessionId) => {
  console.log('Session ID changed:', sessionId);
};

Syntax

// Set the callback function
sdk.onSessionIdChanged = callback;

// Get the current callback function
const currentCallback = sdk.onSessionIdChanged;

Parameters

  • callback ((sessionId: string | undefined) => void): Function that receives the new session ID as a parameter, or undefined when the session is cleared.

Return Type

void - This is a setter property. The callback function itself should not return anything.

More Examples

Session persistence
Save session IDs for conversation continuity across app restarts.

sdk.onSessionIdChanged = (sessionId) => {
  // Save to localStorage for persistence
  localStorage.setItem('arthur-session-id', sessionId);

  // Update application state
  setCurrentSessionId(sessionId);

  // Log session change
  console.log('New session started:', sessionId);
};

Multi-user session management
Handle session changes in multi-user applications.

sdk.onSessionIdChanged = (sessionId) => {
  // Associate session with current user
  const userId = getCurrentUserId();
  saveUserSession(userId, sessionId);

  // Update session metadata
  updateSessionMetadata({
    sessionId,
    userId,
    startedAt: new Date().toISOString(),
    status: 'active',
  });
};

Session analytics and monitoring
Track session creation for monitoring and analytics.

sdk.onSessionIdChanged = (sessionId) => {
  // Send analytics event
  analytics.track('session_started', {
    sessionId,
    timestamp: Date.now(),
    userAgent: navigator.userAgent,
  });

  // Update monitoring dashboards
  sessionMonitor.recordNewSession(sessionId);

  // Set up session timeout monitoring
  setupSessionTimeout(sessionId);
};

SDK Initialization and Usage Examples

// Step 4: Initialize SDK with all options
const sdk = new ArthurSDK({
  userId: 'user-123',
  clientId: 'client-456',
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    toolRegistry, // Previously created with tools
    boothRegistry, // Previously created with booths
  },
  sessionId: 'existing-session-id', // Optional: resume session
});

// Step 5: Set up event handlers
sdk.onMessagesReceived = (messages) => {
  // Update your UI with new messages
  messages.forEach((message) => {
    if (message.type === 'message') {
      console.log(`${message.role}: ${message.content}`);
    } else if (message.type === 'tool_call') {
      console.log(`Tool call: ${message.content.name}`);
    }
  });
};

sdk.onInteractionLoopComplete = () => {
  // Conversation ended, update UI
  console.log('Conversation completed');
  hideLoadingSpinner();
};

sdk.onSessionIdChanged = (sessionId) => {
  // Save session for later use
  localStorage.setItem('arthur-session', sessionId);
};

// Step 6: Configure authentication/headers
sdk.setAdditionalHeaders({
  Authorization: 'Bearer ' + getAuthToken(),
  'X-User-Context': 'web-app',
});

// Step 7: Send messages and listen for responses
await sdk.sendMessageAndListen('Hello, I need help with weather and email');

// Alternative: Send without listening (for fire-and-forget)
await sdk.sendMessage('Just logging this message');

// Manual control of interaction loop
await sdk.sendMessage('Setup message');
sdk.startInteractionLoopListener(); // Start listening manually

ArthurSyncSDK

The ArthurSyncSDK provides a simplified interface for synchronous interactions with individual booths without the full agent interaction loop. This SDK is designed for single, direct booth calls that return immediate responses.

Key Differences from ArthurSDK

  • Synchronous: Single request-response pattern, no event streaming
  • Stateless: No session management or conversation history
  • Direct booth interaction: Works with both predefined booth IDs and custom booth configurations
  • Simpler API: Just one main method (interact()) for all operations
  • Arbitrary URLs: Full control over API endpoints

Constructor Options

Required Parameters

Optional Parameters

  • headers (Record<string, string>): Initial headers to include in all requests

Constructor Examples

Basic initialization

import { ArthurSyncSDK } from 'arthur-sdk';

const syncSDK = new ArthurSyncSDK({
  syncApiUrl: 'https://api.example.com/interact-sync',
});

With authentication headers

const syncSDK = new ArthurSyncSDK({
  syncApiUrl: 'https://api.example.com/interact-sync',
  headers: {
    Authorization: 'Bearer your-token-here',
    'X-Client-Version': '1.0.0',
  },
});

ArthurSyncSDK.interact()

Send a message to a booth and receive an immediate response.

Example

const response = await syncSDK.interact({
  message: 'What are the current flagged numbers?',
  booth: 'call-reputation-insights-booth',
  clientId: 'arthur_cli',
});

if (response.success) {
  console.log('Response:', response.output);
} else {
  console.error('Error:', response.error);
}

Syntax

const result = await syncSDK.interact({
  message,
  booth,
  clientId,
  toolmodules? // Optional
});

Parameters

  • message (string | ResponseInputItem[]): The message to send to the booth
  • booth (BoothId | BoothConfig): Either a predefined booth ID or custom booth configuration
  • clientId (ClientId): Client identifier ('arkon' | 'arthur_cli' | 'armor_dial')
  • toolmodules (ToolModule[], optional): Additional tools (only with custom BoothConfig)

Available Booth IDs

  • 'base-booth': General assistance for ARMOR platform
  • 'call-reputation-insights-booth': Call reputation analysis and insights
  • 'concurrent-session-booth': Session management and retrieval
  • 'data-analytics-booth': Data analytics and reporting
  • 'onboarding-booth': User onboarding assistance

Return Type

interface SyncResponse {
  success: boolean;
  output?: ResponseInputItem[];
  error?: string;
}

More Examples

Using predefined booth IDs

// Simple string message
const response1 = await syncSDK.interact({
  message: 'Help me with onboarding',
  booth: 'onboarding-booth',
  clientId: 'arthur_cli',
});

// Complex message array
const response2 = await syncSDK.interact({
  message: [
    { type: 'text', text: 'Analyze this data: ' },
    { type: 'text', text: 'Sales data for Q1 2024' },
  ],
  booth: 'data-analytics-booth',
  clientId: 'arthur_cli',
});

Using custom booth configuration

const customBooth = {
  id: 'custom-support',
  name: 'Custom Support Booth',
  description: 'Provides specialized customer support',
  context:
    'You are a helpful customer support agent specializing in technical issues.',
  tools: ['knowledge-search', 'ticket-creator'],
};

const response = await syncSDK.interact({
  message: 'I need help with my account',
  booth: customBooth,
  clientId: 'arthur_cli',
});

Using custom booth with toolmodules

const customBooth = {
  id: 'analytics-booth',
  name: 'Analytics Booth',
  description: 'Data analysis booth',
  context: 'You are a data analyst with access to specialized tools.',
  tools: ['data-processor'],
};

const toolmodules = [
  {
    name: 'data-processor',
    description: 'Processes raw data',
    parameters: {
      type: 'object',
      properties: {
        data: { type: 'string' },
        format: { type: 'string', enum: ['json', 'csv'] },
      },
    },
    global: false,
    execute: async (args) => ({
      processed: true,
      records: parseData(args.data, args.format),
    }),
  },
];

const response = await syncSDK.interact({
  message: 'Process this CSV data: name,age,city\\nJohn,25,NYC',
  booth: customBooth,
  clientId: 'arthur_cli',
  toolmodules,
});

Header Management

ArthurSyncSDK.setHeaders()

Set multiple custom headers for all requests.

syncSDK.setHeaders({
  Authorization: 'Bearer new-token',
  'X-Request-Source': 'mobile-app',
  'Content-Language': 'en-US',
});

ArthurSyncSDK.setHeader()

Set a single header.

syncSDK.setHeader('Authorization', 'Bearer updated-token');

ArthurSyncSDK.removeHeader()

Remove a specific header.

syncSDK.removeHeader('X-Debug-Mode');

ArthurSyncSDK.getHeaders()

Get all current headers.

const currentHeaders = syncSDK.getHeaders();
console.log('Current headers:', currentHeaders);

Error Handling

The sync SDK handles errors gracefully and returns them in the response:

const response = await syncSDK.interact({
  message: 'Test message',
  booth: 'invalid-booth-id',
  clientId: 'arthur_cli',
});

if (!response.success) {
  switch (response.error) {
    case 'message is required':
      console.error('Message cannot be empty');
      break;
    case 'Client arthur_cli does not have access to booth invalid-booth-id':
      console.error('Access denied to booth');
      break;
    default:
      console.error('Unknown error:', response.error);
  }
}

Common Error Messages

  • "message is required" - Missing or empty message
  • "clientId is required" - Missing client ID
  • "Client {clientId} does not have access to booth {boothId}" - Access denied
  • "booth must be either a valid BOOTH_ID (...) or a BoothConfig object" - Invalid booth
  • "toolmodules can only be provided when booth is a BoothConfig object" - Invalid tool usage
  • "toolmodules[{index}] ({toolName}) is not compatible with this booth configuration" - Incompatible tool

Complete Usage Example

import { ArthurSyncSDK, BoothConfig, ToolModule } from 'arthur-sdk';

// Initialize SDK
const syncSDK = new ArthurSyncSDK({
  syncApiUrl: 'https://api.example.com/interact-sync',
  headers: {
    Authorization: 'Bearer your-token',
    'X-Client-App': 'my-app',
  },
});

// Example 1: Simple booth interaction
const quickResponse = await syncSDK.interact({
  message: 'What are the current flagged phone numbers?',
  booth: 'call-reputation-insights-booth',
  clientId: 'arthur_cli',
});

if (quickResponse.success) {
  console.log('Flagged numbers:', quickResponse.output);
}

// Example 2: Custom booth with tools
const customBooth: BoothConfig = {
  id: 'sales-analyzer',
  name: 'Sales Data Analyzer',
  description: 'Analyzes sales performance data',
  context:
    'You are a sales analyst. Use the data-processor tool to analyze sales data and provide insights.',
  tools: ['data-processor', 'chart-generator'],
};

const customTools: ToolModule[] = [
  {
    name: 'data-processor',
    description: 'Processes sales data',
    parameters: {
      type: 'object',
      properties: {
        period: { type: 'string' },
        metrics: { type: 'array', items: { type: 'string' } },
      },
    },
    global: false,
    execute: async ({ period, metrics }) => ({
      summary: `Processed ${metrics.length} metrics for ${period}`,
      data: generateAnalysis(period, metrics),
    }),
  },
];

const analysisResponse = await syncSDK.interact({
  message:
    'Analyze our Q1 sales performance focusing on revenue and conversion rates',
  booth: customBooth,
  clientId: 'arthur_cli',
  toolmodules: customTools,
});

if (analysisResponse.success) {
  console.log('Sales analysis:', analysisResponse.output);
} else {
  console.error('Analysis failed:', analysisResponse.error);
}

ToolRegistry

Manages tool configurations and execution. Tools should be registered first as they are referenced by booths.

ToolRegistry.registerTool()

Register a single tool in the registry. If a tool with the same name already exists, it will be overwritten.

Example

toolRegistry.registerTool({
  name: 'weather-api',
  description: 'Get weather information',
  parameters: { location: 'string' },
  execute: async ({ location }) => {
    return { temperature: 75, conditions: 'sunny' };
  },
});

Syntax

toolRegistry.registerTool(tool);

Parameters

  • tool (ToolModule): Tool configuration object containing name, description, parameters, and optional execute function

Return Type

void - This method does not return anything.

ToolRegistry.registerTools()

Register multiple tools at once.

Example

const tools = [
  {
    name: 'calculator',
    description: 'Mathematical calculations',
    parameters: { operation: 'string', a: 'number', b: 'number' },
    global: true,
  },
  {
    name: 'email-sender',
    description: 'Send emails',
    parameters: { to: 'string', subject: 'string', body: 'string' },
  },
];
toolRegistry.registerTools(tools);

Syntax

toolRegistry.registerTools(tools);

Parameters

  • tools (ToolModule[]): Array of tool configuration objects to register

Return Type

void - This method does not return anything.

ToolRegistry.getTool()

Retrieve a tool by its name.

Example

const weatherTool = toolRegistry.getTool('weather-api');
if (weatherTool) {
  console.log('Tool found:', weatherTool.description);
}

Syntax

const tool = toolRegistry.getTool(toolName);

Parameters

  • toolName (string): The name of the tool to retrieve

Return Type

ToolModule | undefined - The tool configuration if found, undefined otherwise.

ToolRegistry.getAllTools()

Returns all registered tools as a record object indexed by tool names.

Example

const allTools = toolRegistry.getAllTools();
Object.keys(allTools).forEach((toolName) => {
  console.log(`Tool: ${toolName}`, allTools[toolName]);
});

Syntax

const tools = toolRegistry.getAllTools();

Parameters

None.

Return Type

Record<string, ToolModule> - Object containing all tools indexed by their names.

ToolRegistry.getAllToolsWithoutExecute()

Returns all tools without their execute functions, useful for serialization when sending to the server.

Example

const toolsForServer = toolRegistry.getAllToolsWithoutExecute();
// Safe to JSON.stringify - no functions included

Syntax

const tools = toolRegistry.getAllToolsWithoutExecute();

Parameters

None.

Return Type

Omit<ToolModule, 'execute'>[] - Array of tools without execute functions.

ToolRegistry.unregisterTool()

Remove a tool from the registry by its name.

Example

try {
  toolRegistry.unregisterTool('deprecated-tool');
  console.log('Tool removed successfully');
} catch (error) {
  console.error('Tool not found:', error.message);
}

Syntax

toolRegistry.unregisterTool(toolName);

Parameters

  • toolName (string): The name of the tool to remove

Return Type

void - This method does not return anything. Throws an error if the tool doesn't exist.

Tool Registration Examples

const toolRegistry = new ToolRegistry();

// Step 1: Register global tools (available to all booths)
toolRegistry.registerTool({
  name: 'calculator',
  description: 'Performs mathematical calculations',
  parameters: {
    operation: 'string',
    a: 'number',
    b: 'number',
  },
  global: true, // Available to ALL booths
  execute: async ({ operation, a, b }) => {
    switch (operation) {
      case 'add':
        return { result: a + b };
      case 'subtract':
        return { result: a - b };
      case 'multiply':
        return { result: a * b };
      case 'divide':
        return { result: a / b };
      default:
        throw new Error('Unknown operation');
    }
  },
});

// Step 2: Register booth-specific tools
toolRegistry.registerTool({
  name: 'weather-api',
  description: 'Get weather information for a location',
  parameters: {
    location: 'string',
    units: 'string',
  },
  // No global flag = booth-specific tool
  execute: async ({ location, units = 'metric' }) => {
    // Weather API implementation
    const response = await fetch(
      `/api/weather?location=${location}&units=${units}`,
    );
    return await response.json();
  },
});

toolRegistry.registerTool({
  name: 'send-email',
  description: 'Send an email message',
  parameters: {
    to: 'string',
    subject: 'string',
    body: 'string',
  },
  global: false, // Explicitly booth-specific
  execute: async ({ to, subject, body }) => {
    // Email sending implementation
    return { sent: true, messageId: 'msg-' + Date.now() };
  },
});

BoothRegistry

Manages booth configurations within the system. Booths should be registered after tools so they can reference existing tools.

BoothRegistry.registerBooth()

Register a single booth configuration in the registry.

Example

boothRegistry.registerBooth({
  id: 'weather-assistant',
  name: 'Weather Assistant',
  description: 'Provides weather information and forecasts',
  context: 'You are a helpful weather assistant.',
  tools: ['weather-api'],
});

Syntax

boothRegistry.registerBooth(boothConfig);

Parameters

  • boothConfig (BoothConfig): Booth configuration object containing id, role, description, and optional tools array

Return Type

void - This method does not return anything.

BoothRegistry.registerBooths()

Register multiple booth configurations at once.

Example

const booths = [
  {
    id: 'weather-assistant',
    name: 'Weather Assistant',
    description: 'Weather information',
    context: 'You help with weather queries.',
  },
  {
    id: 'email-assistant',
    name: 'Email Assistant',
    description: 'Email management',
    context: 'You help with email tasks.',
    tools: ['send-email'],
  },
];
boothRegistry.registerBooths(booths);

Syntax

boothRegistry.registerBooths(boothConfigs);

Parameters

  • boothConfigs (BoothConfig[]): Array of booth configuration objects to register

Return Type

void - This method does not return anything.

BoothRegistry.getBoothById()

Retrieve a booth configuration by its unique ID.

Example

const weatherBooth = boothRegistry.getBoothById('weather-assistant');
if (weatherBooth) {
  console.log('Booth found:', weatherBooth.name);
}

Syntax

const booth = boothRegistry.getBoothById(boothId);

Parameters

  • boothId (string): The unique identifier of the booth to retrieve

Return Type

BoothConfig | undefined - The booth configuration if found, undefined otherwise.

BoothRegistry.getAllBooths()

Returns all registered booth configurations as an array.

Example

const allBooths = boothRegistry.getAllBooths();
allBooths.forEach((booth) => {
  console.log(`Booth: ${booth.name} - ${booth.description}`);
});

Syntax

const booths = boothRegistry.getAllBooths();

Parameters

None.

Return Type

BoothConfig[] - Array containing all registered booth configurations.

BoothRegistry.unregisterBooth()

Remove a booth from the registry by its ID.

Example

const wasRemoved = boothRegistry.unregisterBooth('deprecated-booth');
if (wasRemoved) {
  console.log('Booth removed successfully');
} else {
  console.log('Booth not found');
}

Syntax

const success = boothRegistry.unregisterBooth(boothId);

Parameters

  • boothId (string): The unique identifier of the booth to remove

Return Type

boolean - True if the booth was found and removed, false otherwise.

BoothRegistry.toArray()

Get all booth configurations as an array, useful for serialization.

Example

const boothArray = boothRegistry.toArray();
// Same as getAllBooths(), provided for consistency

Syntax

const booths = boothRegistry.toArray();

Parameters

None.

Return Type

BoothConfig[] - Array containing all registered booth configurations.

Booth Registration Examples

const boothRegistry = new BoothRegistry();

// Step 3: Register booths with tool access control
boothRegistry.registerBooth({
  id: 'weather-assistant',
  name: 'Weather Assistant',
  description: 'Provides weather information and forecasts',
  context:
    'You are a helpful weather assistant. Use the weather-api tool to get current conditions and forecasts.',
  tools: ['weather-api'], // Has access to weather-api + all global tools (calculator)
});

boothRegistry.registerBooth({
  id: 'email-assistant',
  name: 'Email Assistant',
  description: 'Helps with email composition and sending',
  context:
    'You are an email assistant. Help users compose and send emails using the send-email tool.',
  tools: ['send-email'], // Has access to send-email + all global tools (calculator)
});

boothRegistry.registerBooth({
  id: 'office-assistant',
  name: 'General Office Assistant',
  description: 'Multi-purpose assistant for office tasks',
  context:
    'You are a general office assistant with access to multiple tools. Help with calculations, weather, and emails.',
  tools: ['weather-api', 'send-email'], // Has access to both tools + all global tools
});

boothRegistry.registerBooth({
  id: 'math-tutor',
  name: 'Math Tutor',
  description: 'Helps with mathematical problems',
  context:
    'You are a math tutor. Help students solve problems using the calculator tool.',
  // No tools array = only has access to global tools (calculator)
});

Understanding Tool Access

The booth examples above demonstrate the flexible tool access system:

weather-assistant

  • Tools Array: ['weather-api']
  • Available Tools: weather-api + calculator (global)

email-assistant

  • Tools Array: ['send-email']
  • Available Tools: send-email + calculator (global)

office-assistant

  • Tools Array: ['weather-api', 'send-email']
  • Available Tools: weather-api + send-email + calculator (global)

math-tutor

  • Tools Array: (none)
  • Available Tools: calculator (global only)

Booth and Tool Relationships

The Arthur SDK provides a flexible system for controlling which tools are available to which booths. There are two ways to configure tool access:

Global Tools

Tools marked with global: true are available to all booths in the system. These are typically utility functions or common operations that any booth might need.

// Global tool - available to all booths
toolRegistry.registerTool({
  name: 'calculator',
  description: 'Performs mathematical calculations',
  parameters: { operation: 'string', a: 'number', b: 'number' },
  global: true, // This tool is available to all booths
  execute: async ({ operation, a, b }) => {
    switch (operation) {
      case 'add':
        return { result: a + b };
      case 'subtract':
        return { result: a - b };
      case 'multiply':
        return { result: a * b };
      case 'divide':
        return { result: a / b };
      default:
        throw new Error('Unknown operation');
    }
  },
});

Booth-Specific Tools

Tools without the global flag (or with global: false) are only available to booths that explicitly include them in their tools array. This provides fine-grained control over tool access.

// Booth-specific tools
toolRegistry.registerTool({
  name: 'weather-api',
  description: 'Get weather information',
  parameters: { location: 'string' },
  // No global flag = booth-specific tool
  execute: async ({ location }) => {
    // Weather API implementation
    return { temperature: 72, conditions: 'sunny' };
  },
});

toolRegistry.registerTool({
  name: 'send-email',
  description: 'Send an email',
  parameters: { to: 'string', subject: 'string', body: 'string' },
  global: false, // Explicitly not global
  execute: async ({ to, subject, body }) => {
    // Email sending implementation
    return { sent: true, messageId: 'msg-123' };
  },
});

// Register booths with specific tool access
boothRegistry.registerBooth({
  id: 'weather-assistant',
  name: 'Weather Assistant',
  description: 'Provides weather information',
  context:
    'You are a weather assistant. Use the weather-api tool to get current conditions.',
  tools: ['weather-api'], // Only has access to weather-api tool (plus global tools)
});

boothRegistry.registerBooth({
  id: 'email-assistant',
  name: 'Email Assistant',
  description: 'Helps with email management',
  context:
    'You are an email assistant. You can send emails using the send-email tool.',
  tools: ['send-email'], // Only has access to send-email tool (plus global tools)
});

boothRegistry.registerBooth({
  id: 'office-assistant',
  name: 'Office Assistant',
  description: 'General office assistant',
  context: 'You are a general office assistant with access to multiple tools.',
  tools: ['weather-api', 'send-email'], // Has access to both tools (plus global tools)
});

Tool Access Summary

For any given booth, the available tools are:

  1. All global tools (global: true)
  2. Tools listed in the booth's tools array
// Example: What tools are available to each booth?

// weather-assistant booth can use:
// - calculator (global: true)
// - weather-api (in booth's tools array)

// email-assistant booth can use:
// - calculator (global: true)
// - send-email (in booth's tools array)

// office-assistant booth can use:
// - calculator (global: true)
// - weather-api (in booth's tools array)
// - send-email (in booth's tools array)

Best Practices

  1. Use global tools for utilities: Math, text processing, common operations
  2. Use booth-specific tools for specialized functions: API calls, domain-specific operations
  3. Group related booths with similar tool access: Customer service booths might share CRM tools
  4. Consider security implications: Don't give sensitive tools global access

Advanced Usage Patterns

Message Handling with Types

import { ConversationMessage, isConversationMessageUserText } from 'arthur-sdk';

sdk.onMessagesReceived = (messages: ConversationMessage[]) => {
  messages.forEach((message) => {
    if (isConversationMessageUserText(message)) {
      console.log('User/Assistant message:', message.content);
    } else if (message.type === 'tool_call') {
      console.log('Tool call:', message.content.name);
      // Show loading indicator for tool execution
    }
  });
};

Dynamic Header Management

// Update headers based on authentication state
function updateAuthHeaders(token: string) {
  sdk.setHeader('Authorization', `Bearer ${token}`);
}

// Remove authentication when logging out
function clearAuth() {
  sdk.removeHeader('Authorization');
}

// Add request tracking
sdk.setHeader('X-Request-ID', generateRequestId());

Session Persistence

// Load existing session on app startup
const savedSessionId = localStorage.getItem('arthur-session-id');

const sdk = new ArthurSDK({
  userId: getCurrentUserId(),
  clientId: getClientId(),
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    toolRegistry,
    boothRegistry,
  },
  sessionId: savedSessionId, // Resume if available
});

// Save new session IDs automatically
sdk.onSessionIdChanged = (sessionId) => {
  localStorage.setItem('arthur-session-id', sessionId);
};

Types

The SDK exports comprehensive TypeScript types for all interfaces:

Core Types

ConversationMessage
Union type for all conversation messages

ConversationMessageUserText
Text messages from user/assistant

ConversationMessageToolCall
Tool call messages

MessageResponse
Response from message API

IncomingMessage
Union type for all incoming server messages

BoothConfig Structure

Required Properties

  • id (string): Unique booth identifier
  • name (string): Display name for the booth
  • description (string): Brief description of the booth's purpose
  • context (string): System context/prompt for the booth

Optional Properties

  • tools (string[]): Array of tool names available to this booth
  • plugins (string[]): Array of plugin names (future use)

ToolModule Structure

Required Properties

  • name (string): Unique tool identifier
  • description (string): Description of what the tool does
  • parameters (Record<string, any>): Object describing the tool's parameters

Optional Properties

  • execute (Function): Function that executes the tool logic
  • global (boolean): If true, tool is available to all booths

Message Types

IncomingMessageBoothsCommand
Booth command messages

IncomingMessageExecuteTools
Tool execution requests

IncomingMessageToolCall
Tool call notifications

IncomingMessageToolCallResult
Tool call results

IncomingMessageFunctionCallOutput
Function call outputs

Type Guards

isConversationMessageToolCall(message: any): boolean
Check if message is a tool call

isConversationMessageUserText(message: any): boolean
Check if message is user/assistant text

isResponseInputItem(message: any): boolean
Check if message is a response input item

Session Management Details

Understanding how the Arthur server handles sessions is crucial for proper conversation management:

Server Session Behavior

When sending a message without a session ID:

  • If no existing session exists for this client: A new session is automatically created
  • If an existing session exists for this client: The existing session is deleted and a brand new session is created

This means that every message sent without a session ID effectively starts a fresh conversation, even if there was a previous session.

SDK Session Responsibilities

The SDK handles the runtime session management automatically, but developers are responsible for persistence:

What the SDK does:

  • Automatically stores the session ID received from the server
  • Includes the current session ID in subsequent requests
  • Triggers onSessionIdChanged callback when sessions change
  • Provides access to the current session via internal state

What developers must do:

  • Store sessions locally if persistence across app restarts is needed
  • Remove sessions from the SDK to intentionally start fresh conversations

Session Persistence Examples

Save session for persistence:

sdk.onSessionIdChanged = (sessionId) => {
  // Store session for later restoration
  localStorage.setItem('arthur-session-id', sessionId);
  console.log('Session saved:', sessionId);
};

Restore previous session:

const savedSessionId = localStorage.getItem('arthur-session-id');
const sdk = new ArthurSDK({
  userId: 'user-123',
  clientId: 'client-456',
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    // Include your registries here
  },
  sessionId: savedSessionId, // Resume previous conversation
});

Purge conversation (start fresh):

// Method 1: Use clearSession() - RECOMMENDED
sdk.clearSession(); // Clears session and messages, triggers callback
// Next sendMessage() call will create a new session

// Method 2: Clear session but keep messages visible
sdk.clearSession(false); // Clear session but keep messages in UI
// Next sendMessage() call will create a new session

// Method 3: Manual approach (not recommended)
localStorage.removeItem('arthur-session-id');
const newSdk = new ArthurSDK({
  userId: 'user-123',
  clientId: 'client-456',
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    // Include your registries here
  },
  // No sessionId = fresh session will be created
});

Important Session Implications

Starting Fresh Conversations:

  • Use sdk.clearSession() to clear the current session (recommended)
  • Or simply don't provide a sessionId when initializing the SDK
  • Or remove the stored session ID from your persistence layer
  • The server will automatically create a new session on the next message

Maintaining Conversation History:

  • Always save the session ID when onSessionIdChanged is triggered
  • Provide the saved session ID when reinitializing the SDK
  • The server will continue the existing conversation context

Multi-User Applications:

// Store sessions per user
sdk.onSessionIdChanged = (sessionId) => {
  const userId = getCurrentUserId();
  if (sessionId === undefined) {
    // Session cleared - remove from storage
    localStorage.removeItem(`arthur-session-${userId}`);
  } else {
    // New session - store it
    localStorage.setItem(`arthur-session-${userId}`, sessionId);
  }
};

// Restore user-specific session
const userId = getCurrentUserId();
const userSession = localStorage.getItem(`arthur-session-${userId}`);
const sdk = new ArthurSDK({
  userId,
  clientId: 'multi-user-client',
  interactURL: 'https://api.example.com/api/message',
  interactionEventsURL: 'https://api.example.com/api/interactionloop',
  agentConfig: {
    agent: 'armor',
    // Include your registries here
  },
  sessionId: userSession,
});

Error Handling

The SDK includes built-in error handling for common scenarios:

try {
  await sdk.sendMessageAndListen('Hello');
} catch (error) {
  if (error.message.includes('No session ID found')) {
    // Handle session initialization error
    console.log('Please establish a session first');
  } else if (
    error.message.includes('Tool') &&
    error.message.includes('could not be found')
  ) {
    // Handle missing tool error
    console.log('Required tool is not registered');
  }
}

Browser Compatibility

The SDK uses modern browser APIs:

  • EventSource: For server-sent events (widely supported)
  • fetch: For HTTP requests (polyfill available for older browsers)
  • URL constructor: For URL manipulation (supported in all modern browsers)

Ensure your target browsers support these APIs or include appropriate polyfills.

License

ISC