diff --git a/README.md b/README.md index 6ad3ddc..c73e19b 100644 --- a/README.md +++ b/README.md @@ -21,27 +21,32 @@ npm install @openhands/agent-server-typescript-client ### Creating a Conversation ```typescript -import { RemoteConversation, AgentBase } from '@openhands/agent-server-typescript-client'; +import { Conversation, Agent, Workspace } from '@openhands/agent-server-typescript-client'; -const agent: AgentBase = { - name: 'CodeActAgent', +const agent = new Agent({ llm: { model: 'gpt-4', api_key: 'your-openai-api-key' } -}; - -const conversation = await RemoteConversation.create( - 'http://localhost:3000', // Agent server URL - agent, - { - apiKey: 'your-session-api-key', - initialMessage: 'Hello, can you help me write some code?', - callback: (event) => { - console.log('Received event:', event); - } +}); + +// Create a remote workspace +const workspace = new Workspace({ + host: 'http://localhost:3000', + workingDir: '/tmp', + apiKey: 'your-session-api-key' +}); + +const conversation = new Conversation(agent, workspace, { + callback: (event) => { + console.log('Received event:', event); } -); +}); + +// Start the conversation with an initial message +await conversation.start({ + initialMessage: 'Hello, can you help me write some code?' +}); // Start WebSocket for real-time events await conversation.startWebSocketClient(); @@ -54,13 +59,19 @@ await conversation.run(); ### Loading an Existing Conversation ```typescript -const conversation = await RemoteConversation.load( - 'http://localhost:3000', - 'conversation-id-here', - { - apiKey: 'your-session-api-key' - } -); +// Create a remote workspace for the existing conversation +const workspace = new Workspace({ + host: 'http://localhost:3000', + workingDir: '/tmp', + apiKey: 'your-session-api-key' +}); + +const conversation = new Conversation(agent, workspace, { + conversationId: 'conversation-id-here' +}); + +// Connect to the existing conversation +await conversation.start(); ``` ### Using the Workspace @@ -120,17 +131,18 @@ await conversation.updateSecrets({ ## API Reference -### RemoteConversation +### Conversation -The main class for managing conversations with OpenHands agents. +Factory function that creates conversations with OpenHands agents. -#### Static Methods +#### Constructor -- `RemoteConversation.create(host, agent, options)` - Create a new conversation -- `RemoteConversation.load(host, conversationId, options)` - Load an existing conversation +- `new Conversation(agent, workspace, options?)` - Create a new conversation instance #### Instance Methods +- `start(options?)` - Start the conversation (creates new or connects to existing) + - `sendMessage(message)` - Send a message to the agent - `run()` - Start agent execution - `pause()` - Pause agent execution diff --git a/example/src/components/ConversationManager.tsx b/example/src/components/ConversationManager.tsx index 9541bfd..cc3f9f1 100644 --- a/example/src/components/ConversationManager.tsx +++ b/example/src/components/ConversationManager.tsx @@ -2,8 +2,10 @@ import React, { useState, useEffect } from 'react'; import { ConversationManager as SDKConversationManager, ConversationInfo, + Conversation, RemoteConversation, - AgentBase, + Agent, + Workspace, Event } from '@openhands/agent-server-typescript-client'; import { useSettings } from '../contexts/SettingsContext'; @@ -118,7 +120,7 @@ export const ConversationManager: React.FC = () => { useEffect(() => { return () => { if (selectedConversation?.remoteConversation) { - selectedConversation.remoteConversation.stopWebSocketClient().catch(err => { + selectedConversation.remoteConversation.stopWebSocketClient().catch((err: any) => { console.warn('Failed to stop WebSocket client on unmount:', err); }); } @@ -190,23 +192,24 @@ export const ConversationManager: React.FC = () => { setError(null); try { // Create a simple agent configuration - const agent: AgentBase = { - kind: 'Agent', + const agent = new Agent({ llm: { model: settings.modelName, api_key: settings.apiKey || '' } - }; + }); + + // Create a remote workspace + const workspace = new Workspace({ + host: manager.host, + workingDir: '/tmp', + apiKey: manager.apiKey + }); - const conversation = await RemoteConversation.create( - manager.host, - agent, - { - apiKey: manager.apiKey, - initialMessage: 'Hello! I\'m ready to help you with your tasks.', - maxIterations: 50, - callback: (event: Event) => { - console.log('Received WebSocket event for new conversation:', event); + const conversation = new Conversation(agent, workspace, { + maxIterations: 50, + callback: (event: Event) => { + console.log('Received WebSocket event for new conversation:', event); // Update the conversation's events in real-time setConversations(prev => prev.map(conv => { @@ -221,6 +224,11 @@ export const ConversationManager: React.FC = () => { ); console.log('Created conversation:', conversation); + + // Start the conversation with initial message + await conversation.start({ + initialMessage: 'Hello! I\'m ready to help you with your tasks.' + }); // Start WebSocket client for real-time updates try { @@ -289,6 +297,12 @@ export const ConversationManager: React.FC = () => { // Load conversation details try { + // Get the conversation info to extract the agent + const conversationInfo = conversations.find(c => c.id === conversationId); + if (!conversationInfo) { + throw new Error('Conversation not found'); + } + // Create a callback to handle real-time events const eventCallback = (event: Event) => { console.log('Received WebSocket event:', event); @@ -320,15 +334,21 @@ export const ConversationManager: React.FC = () => { } }; + // Create a remote workspace for the existing conversation + const workspace = new Workspace({ + host: manager.host, + workingDir: '/tmp', + apiKey: manager.apiKey + }); + // Load conversation with callback - const remoteConversation = await RemoteConversation.load( - manager.host, - conversationId, - { - apiKey: manager.apiKey, - callback: eventCallback, - } - ); + const remoteConversation = new Conversation(conversationInfo.agent, workspace, { + conversationId: conversationId, + callback: eventCallback, + }); + + // Connect to the existing conversation + await remoteConversation.start(); console.log('Loaded remote conversation:', remoteConversation); // Start WebSocket client for real-time updates diff --git a/example/src/utils/serverStatus.ts b/example/src/utils/serverStatus.ts index 9332bc5..4c89ccb 100644 --- a/example/src/utils/serverStatus.ts +++ b/example/src/utils/serverStatus.ts @@ -1,5 +1,5 @@ import { Settings } from '../components/SettingsModal'; -import { HttpClient, RemoteConversation } from '@openhands/agent-server-typescript-client'; +import { HttpClient, RemoteConversation, RemoteWorkspace } from '@openhands/agent-server-typescript-client'; export interface ServerStatus { isConnected: boolean; @@ -51,9 +51,15 @@ export const testLLMConfiguration = async (settings: Settings): Promise<{ succes return { success: false, error: `Server not reachable: ${healthCheck.error}` }; } + // Create a workspace for the test conversation + const workspace = new RemoteWorkspace({ + host: settings.agentServerUrl, + workingDir: '/tmp/test-workspace', + apiKey: settings.agentServerApiKey, + }); + // Create a test conversation using the SDK - const conversation = await RemoteConversation.create( - settings.agentServerUrl, + const conversation = new RemoteConversation( { kind: 'Agent', llm: { @@ -61,16 +67,11 @@ export const testLLMConfiguration = async (settings: Settings): Promise<{ succes api_key: settings.apiKey, } }, - { - apiKey: settings.agentServerApiKey, - workspace: { - type: 'local', - path: '/tmp/test-workspace', - working_dir: '/tmp/test-workspace' - } - } + workspace ); + await conversation.start(); + try { // Send a simple test message to validate LLM configuration await conversation.sendMessage({ diff --git a/examples/basic-usage.ts b/examples/basic-usage.ts index b6f264f..78309d1 100644 --- a/examples/basic-usage.ts +++ b/examples/basic-usage.ts @@ -2,32 +2,37 @@ * Basic usage example for the OpenHands Agent Server TypeScript Client */ -import { RemoteConversation, AgentBase, AgentExecutionStatus } from '../src/index.js'; +import { Conversation, Agent, Workspace, AgentExecutionStatus } from '../src/index.js'; async function main() { // Define the agent configuration - const agent: AgentBase = { - name: 'CodeActAgent', + const agent = new Agent({ llm: { model: 'gpt-4', api_key: process.env.OPENAI_API_KEY || 'your-openai-api-key', }, - }; + }); try { + // Create a remote workspace + const workspace = new Workspace({ + host: 'http://localhost:3000', + workingDir: '/tmp', + apiKey: process.env.SESSION_API_KEY || 'your-session-api-key', + }); + // Create a new conversation console.log('Creating conversation...'); - const conversation = await RemoteConversation.create( - 'http://localhost:3000', // Replace with your agent server URL - agent, - { - apiKey: process.env.SESSION_API_KEY || 'your-session-api-key', - initialMessage: 'Hello! Can you help me write a simple Python script?', - callback: (event) => { - console.log(`Event received: ${event.kind} at ${event.timestamp}`); - }, - } - ); + const conversation = new Conversation(agent, workspace, { + callback: (event) => { + console.log(`Event received: ${event.kind} at ${event.timestamp}`); + }, + }); + + // Start the conversation with an initial message + await conversation.start({ + initialMessage: 'Hello! Can you help me write a simple Python script?', + }); console.log(`Conversation created with ID: ${conversation.id}`); @@ -79,14 +84,27 @@ async function main() { // Example of loading an existing conversation async function loadExistingConversation() { + const agent = new Agent({ + llm: { + model: 'gpt-4', + api_key: process.env.OPENAI_API_KEY || 'your-openai-api-key', + }, + }); + try { - const conversation = await RemoteConversation.load( - 'http://localhost:3000', - 'existing-conversation-id', - { - apiKey: process.env.SESSION_API_KEY || 'your-session-api-key', - } - ); + // Create a remote workspace for the existing conversation + const workspace = new Workspace({ + host: 'http://localhost:3000', + workingDir: '/tmp', + apiKey: process.env.SESSION_API_KEY || 'your-session-api-key', + }); + + const conversation = new Conversation(agent, workspace, { + conversationId: 'existing-conversation-id', + }); + + // Connect to the existing conversation + await conversation.start(); console.log(`Loaded conversation: ${conversation.id}`); diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index 91d0a86..10916dc 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -1,51 +1,90 @@ -import { RemoteConversation, RemoteWorkspace } from '../index'; +import { Conversation, Agent, Workspace, RemoteConversation, RemoteWorkspace } from '../index'; describe('OpenHands Agent Server TypeScript Client', () => { describe('Exports', () => { - it('should export RemoteConversation', () => { + it('should export Conversation', () => { + expect(Conversation).toBeDefined(); + expect(typeof Conversation).toBe('function'); + }); + + it('should export Agent', () => { + expect(Agent).toBeDefined(); + expect(typeof Agent).toBe('function'); + }); + + it('should export Workspace', () => { + expect(Workspace).toBeDefined(); + expect(typeof Workspace).toBe('function'); + }); + + it('should export RemoteConversation for backwards compatibility', () => { expect(RemoteConversation).toBeDefined(); expect(typeof RemoteConversation).toBe('function'); }); - it('should export RemoteWorkspace', () => { + it('should export RemoteWorkspace for backwards compatibility', () => { expect(RemoteWorkspace).toBeDefined(); expect(typeof RemoteWorkspace).toBe('function'); }); }); - describe('RemoteConversation', () => { - it('should create instance with config', () => { - const config = { + describe('New API - Conversation', () => { + it('should create instance with agent and workspace', () => { + const agent = new Agent({ + llm: { + model: 'gpt-4', + api_key: 'test-key', + }, + }); + + const workspace = new Workspace({ host: 'http://localhost:8000', + workingDir: '/tmp', apiKey: 'test-key', - }; + }); - const conversation = new RemoteConversation(config); + const conversation = new Conversation(agent, workspace); expect(conversation).toBeInstanceOf(RemoteConversation); + expect(conversation.workspace).toBe(workspace); }); + }); - it('should throw error when accessing workspace before initialization', () => { - const config = { - host: 'http://localhost:8000', - apiKey: 'test-key', - }; + describe('Agent', () => { + it('should create instance with LLM config', () => { + const agent = new Agent({ + llm: { + model: 'gpt-4', + api_key: 'test-key', + }, + }); + + expect(agent).toBeInstanceOf(Agent); + expect(agent.kind).toBe('Agent'); + expect(agent.llm.model).toBe('gpt-4'); + expect(agent.llm.api_key).toBe('test-key'); + }); + + it('should allow custom kind', () => { + const agent = new Agent({ + kind: 'CustomAgent', + llm: { + model: 'gpt-4', + api_key: 'test-key', + }, + }); - const conversation = new RemoteConversation(config); - expect(() => conversation.workspace).toThrow( - 'Workspace not initialized. Create or load a conversation first.' - ); + expect(agent.kind).toBe('CustomAgent'); }); }); - describe('RemoteWorkspace', () => { + describe('Workspace', () => { it('should create instance with options', () => { - const options = { + const workspace = new Workspace({ host: 'http://localhost:8000', workingDir: '/tmp', apiKey: 'test-key', - }; + }); - const workspace = new RemoteWorkspace(options); expect(workspace).toBeInstanceOf(RemoteWorkspace); expect(workspace.host).toBe('http://localhost:8000'); expect(workspace.workingDir).toBe('/tmp'); diff --git a/src/agent/agent.ts b/src/agent/agent.ts new file mode 100644 index 0000000..6e6d387 --- /dev/null +++ b/src/agent/agent.ts @@ -0,0 +1,45 @@ +/** + * Agent class that provides a constructor-based API for creating agents. + * Provides a cleaner API that matches the Python SDK naming. + */ + +import { AgentBase, LLM } from '../types/base'; + +export interface AgentOptions { + llm: LLM; + kind?: string; + name?: string; + [key: string]: any; +} + +/** + * Agent class that implements AgentBase interface. + * Provides a constructor-based API for creating agents. + * + * Usage: + * const agent = new Agent({ + * llm: { + * model: 'gpt-4', + * api_key: 'your-key' + * } + * }); + */ +export class Agent implements AgentBase { + kind: string; + llm: LLM; + name?: string; + [key: string]: any; + + constructor(options: AgentOptions) { + this.kind = options.kind || 'Agent'; + this.llm = options.llm; + this.name = options.name; + + // Copy any additional properties + Object.keys(options).forEach((key) => { + if (key !== 'kind' && key !== 'llm' && key !== 'name') { + this[key] = options[key]; + } + }); + } +} diff --git a/src/agent/index.ts b/src/agent/index.ts new file mode 100644 index 0000000..678d6d7 --- /dev/null +++ b/src/agent/index.ts @@ -0,0 +1 @@ +export { Agent, AgentOptions } from './agent'; diff --git a/src/conversation/conversation-manager.ts b/src/conversation/conversation-manager.ts index 3d73907..043a289 100644 --- a/src/conversation/conversation-manager.ts +++ b/src/conversation/conversation-manager.ts @@ -4,6 +4,7 @@ import { HttpClient } from '../client/http-client'; import { RemoteConversation } from './remote-conversation'; +import { RemoteWorkspace } from '../workspace/remote-workspace'; import { ConversationInfo, ConversationSearchRequest, @@ -86,22 +87,51 @@ export class ConversationManager { initialMessage?: string; maxIterations?: number; stuckDetection?: boolean; - workspace?: any; + workingDir?: string; } = {} ): Promise { - return RemoteConversation.create(this.host, agent, { + // Create a workspace for the conversation + const workspace = new RemoteWorkspace({ + host: this.host, + workingDir: options.workingDir || '/tmp', apiKey: this.apiKey, - ...options, }); + + const conversation = new RemoteConversation(agent, workspace, { + maxIterations: options.maxIterations, + stuckDetection: options.stuckDetection, + }); + + await conversation.start({ + initialMessage: options.initialMessage, + }); + + return conversation; } /** * Load an existing conversation */ - async loadConversation(conversationId: ConversationID): Promise { - return RemoteConversation.load(this.host, conversationId, { + async loadConversation( + conversationId: ConversationID, + workingDir: string = '/tmp' + ): Promise { + // Get conversation info to extract the agent + const conversationInfo = await this.getConversation(conversationId); + + // Create a workspace for the existing conversation + const workspace = new RemoteWorkspace({ + host: this.host, + workingDir, apiKey: this.apiKey, }); + + const conversation = new RemoteConversation(conversationInfo.agent, workspace, { + conversationId: conversationId, + }); + + await conversation.start(); + return conversation; } /** diff --git a/src/conversation/conversation.ts b/src/conversation/conversation.ts new file mode 100644 index 0000000..ab54dee --- /dev/null +++ b/src/conversation/conversation.ts @@ -0,0 +1,26 @@ +/** + * Conversation factory function that returns RemoteConversation + * Matches the Python SDK pattern: Conversation(agent, workspace) + */ + +import { AgentBase } from '../types/base'; +import { RemoteWorkspace } from '../workspace/remote-workspace'; +import { RemoteConversation, RemoteConversationOptions } from './remote-conversation'; + +/** + * Conversation class that extends RemoteConversation. + * Provides a cleaner API that matches the Python SDK naming. + * + * Usage: + * const conversation = new Conversation(agent, workspace); + * await conversation.start(); + * + * For existing conversations: + * const conversation = new Conversation(agent, workspace, { conversationId: 'existing-id' }); + * await conversation.start(); + */ +export class Conversation extends RemoteConversation { + constructor(agent: AgentBase, workspace: RemoteWorkspace, options?: RemoteConversationOptions) { + super(agent, workspace, options); + } +} diff --git a/src/conversation/remote-conversation.ts b/src/conversation/remote-conversation.ts index f398af9..784952e 100644 --- a/src/conversation/remote-conversation.ts +++ b/src/conversation/remote-conversation.ts @@ -27,38 +27,42 @@ import { } from '../models/conversation'; export interface RemoteConversationOptions { - host: string; conversationId?: string; - apiKey?: string; callback?: ConversationCallbackType; + initialMessage?: string; + maxIterations?: number; + stuckDetection?: boolean; } export class RemoteConversation { - public readonly host: string; - public readonly apiKey?: string; + public readonly agent: AgentBase; + public readonly workspace: RemoteWorkspace; private _conversationId?: string; private _state?: RemoteState; - private _workspace?: RemoteWorkspace; private client: HttpClient; private wsClient?: WebSocketCallbackClient; private callback?: ConversationCallbackType; - constructor(options: RemoteConversationOptions) { - this.host = options.host.replace(/\/$/, ''); - this.apiKey = options.apiKey; - this._conversationId = options.conversationId; + constructor( + agent: AgentBase, + workspace: RemoteWorkspace, + options: RemoteConversationOptions = {} + ) { + this.agent = agent; + this.workspace = workspace; this.callback = options.callback; + this._conversationId = options.conversationId; this.client = new HttpClient({ - baseUrl: this.host, - apiKey: this.apiKey, + baseUrl: workspace.host, + apiKey: workspace.apiKey, timeout: 60000, }); } get id(): ConversationID { if (!this._conversationId) { - throw new Error('Conversation ID not set. Create or load a conversation first.'); + throw new Error('Conversation ID not set. Call start() to initialize the conversation.'); } return this._conversationId; } @@ -66,18 +70,44 @@ export class RemoteConversation { get state(): RemoteState { if (!this._state) { if (!this._conversationId) { - throw new Error('Conversation not initialized. Create or load a conversation first.'); + throw new Error( + 'Conversation not initialized. Call start() to initialize the conversation.' + ); } this._state = new RemoteState(this.client, this._conversationId); } return this._state; } - get workspace(): RemoteWorkspace { - if (!this._workspace) { - throw new Error('Workspace not initialized. Create or load a conversation first.'); + async start( + options: { initialMessage?: string; maxIterations?: number; stuckDetection?: boolean } = {} + ): Promise { + if (this._conversationId) { + // Existing conversation - verify it exists + await this.client.get(`/api/conversations/${this._conversationId}`); + return; } - return this._workspace; + + // Create new conversation + let initialMessage: Message | undefined; + if (options.initialMessage) { + initialMessage = { + role: 'user', + content: [{ type: 'text', text: options.initialMessage }], + }; + } + + const request: CreateConversationRequest = { + agent: this.agent, + initial_message: initialMessage, + max_iterations: options.maxIterations || 50, + stuck_detection: options.stuckDetection ?? true, + workspace: { type: 'local', working_dir: this.workspace.workingDir }, + }; + + const response = await this.client.post('/api/conversations', request); + const conversationInfo = response.data; + this._conversationId = conversationInfo.id; } async conversationStats(): Promise { @@ -169,10 +199,10 @@ export class RemoteConversation { }; this.wsClient = new WebSocketCallbackClient({ - host: this.host, + host: this.workspace.host, conversationId: this.id, callback: combinedCallback, - apiKey: this.apiKey, + apiKey: this.workspace.apiKey, }); this.wsClient.start(); @@ -185,102 +215,9 @@ export class RemoteConversation { } } - // Static factory methods - static async create( - host: string, - agent: AgentBase, - options: { - apiKey?: string; - initialMessage?: string; - maxIterations?: number; - stuckDetection?: boolean; - workspace?: any; - callback?: ConversationCallbackType; - } = {} - ): Promise { - const client = new HttpClient({ - baseUrl: host.replace(/\/$/, ''), - apiKey: options.apiKey, - timeout: 60000, - }); - - // Convert string initialMessage to Message object if provided - let initialMessage: Message | undefined; - if (options.initialMessage) { - initialMessage = { - role: 'user', - content: [{ type: 'text', text: options.initialMessage }], - }; - } - - console.log('Agent object before request:', JSON.stringify(agent, null, 2)); - - const request: CreateConversationRequest = { - agent, - initial_message: initialMessage, - max_iterations: options.maxIterations || 50, - stuck_detection: options.stuckDetection ?? true, - workspace: options.workspace || { type: 'local', working_dir: '/tmp' }, - }; - - console.log('Full request object:', JSON.stringify(request, null, 2)); - - const response = await client.post('/api/conversations', request); - const conversationInfo = response.data; - - const conversation = new RemoteConversation({ - host, - conversationId: conversationInfo.id, - apiKey: options.apiKey, - callback: options.callback, - }); - - // Initialize workspace - conversation._workspace = new RemoteWorkspace({ - host, - workingDir: conversationInfo.workspace?.working_dir || '/tmp', - apiKey: options.apiKey, - }); - - return conversation; - } - - static async load( - host: string, - conversationId: string, - options: { - apiKey?: string; - callback?: ConversationCallbackType; - } = {} - ): Promise { - const conversation = new RemoteConversation({ - host, - conversationId, - apiKey: options.apiKey, - callback: options.callback, - }); - - // Verify conversation exists and get workspace info - const response = await conversation.client.get( - `/api/conversations/${conversationId}` - ); - const conversationInfo = response.data; - - // Initialize workspace - conversation._workspace = new RemoteWorkspace({ - host, - workingDir: conversationInfo.workspace?.working_dir || '/tmp', - apiKey: options.apiKey, - }); - - return conversation; - } - async close(): Promise { await this.stopWebSocketClient(); this.client.close(); - if (this._workspace) { - this._workspace.close(); - } + this.workspace.close(); } } diff --git a/src/conversation/remote-state.ts b/src/conversation/remote-state.ts index 95e338d..8d51632 100644 --- a/src/conversation/remote-state.ts +++ b/src/conversation/remote-state.ts @@ -44,12 +44,18 @@ export class RemoteState { } // Fallback to REST API if no cached state - const response = await this.client.get( - `/api/conversations/${this.conversationId}` - ); - const state = response.data; - this.cachedState = state; - return state; + const response = await this.client.get(`/api/conversations/${this.conversationId}`); + + // Handle the case where the API returns a full_state wrapper + let conversationInfo: ConversationInfo; + if (response.data.full_state) { + conversationInfo = response.data.full_state as ConversationInfo; + } else { + conversationInfo = response.data as ConversationInfo; + } + + this.cachedState = conversationInfo; + return conversationInfo; }); } diff --git a/src/index.ts b/src/index.ts index c0d6c0c..84d52a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,11 +7,16 @@ // Main conversation and workspace classes export { RemoteConversation } from './conversation/remote-conversation'; +export { Conversation } from './conversation/conversation'; export { ConversationManager } from './conversation/conversation-manager'; export { RemoteWorkspace } from './workspace/remote-workspace'; +export { Workspace } from './workspace/workspace'; export { RemoteState } from './conversation/remote-state'; export { RemoteEventsList } from './events/remote-events-list'; +// Agent classes +export { Agent } from './agent/agent'; + // WebSocket client for real-time events export { WebSocketCallbackClient } from './events/websocket-client'; @@ -45,6 +50,8 @@ export type { AlwaysConfirm, } from './types/base'; +export type { AgentOptions } from './agent/agent'; + export { EventSortOrder, AgentExecutionStatus } from './types/base'; // Workspace models @@ -76,19 +83,24 @@ export type { ConversationManagerOptions } from './conversation/conversation-man // Re-import for default export import { RemoteConversation } from './conversation/remote-conversation'; +import { Conversation } from './conversation/conversation'; import { ConversationManager } from './conversation/conversation-manager'; import { RemoteWorkspace } from './workspace/remote-workspace'; +import { Workspace } from './workspace/workspace'; import { RemoteState } from './conversation/remote-state'; import { RemoteEventsList } from './events/remote-events-list'; import { WebSocketCallbackClient } from './events/websocket-client'; import { HttpClient, HttpError } from './client/http-client'; import { EventSortOrder, AgentExecutionStatus } from './types/base'; +import { Agent } from './agent/agent'; // Default export for convenience export default { RemoteConversation, + Conversation, ConversationManager, RemoteWorkspace, + Workspace, RemoteState, RemoteEventsList, WebSocketCallbackClient, @@ -96,4 +108,5 @@ export default { HttpError, EventSortOrder, AgentExecutionStatus, + Agent, }; diff --git a/src/types/base.ts b/src/types/base.ts index 3b70cbb..bd4c9e1 100644 --- a/src/types/base.ts +++ b/src/types/base.ts @@ -79,6 +79,9 @@ export interface AgentBase { [key: string]: any; } +// Alias for user-facing API +export type Agent = AgentBase; + export interface LLM { model: string; api_key?: string; diff --git a/src/workspace/workspace.ts b/src/workspace/workspace.ts new file mode 100644 index 0000000..45e10ec --- /dev/null +++ b/src/workspace/workspace.ts @@ -0,0 +1,19 @@ +/** + * Workspace factory function that returns RemoteWorkspace + * Matches the Python SDK pattern: Workspace(host="http://...") + */ + +import { RemoteWorkspace, RemoteWorkspaceOptions } from './remote-workspace'; + +/** + * Workspace class that extends RemoteWorkspace. + * Provides a cleaner API that matches the Python SDK naming. + * + * Usage: + * const workspace = new Workspace({ host: 'http://localhost:8000', apiKey: 'key' }); + */ +export class Workspace extends RemoteWorkspace { + constructor(options: RemoteWorkspaceOptions) { + super(options); + } +}