/** * March Agent SDK - Agent State Client * Port of Python march_agent/agent_state_client.py */ import { APIException } from './exceptions.js' export interface AgentStateResponse { conversationId: string namespace: string state: Record createdAt?: string updatedAt?: string } /** * Async HTTP client for agent-state API. * Used for storing framework-specific state (e.g., Pydantic AI messages). */ export class AgentStateClient { private readonly baseUrl: string constructor(baseUrl: string) { this.baseUrl = baseUrl.replace(/\/$/, '') } /** * Store or update agent state. */ async put( conversationId: string, namespace: string, state: Record ): Promise<{ conversationId: string; namespace: string; created: boolean }> { const url = `${this.baseUrl}/agent-state/${conversationId}` const payload = { namespace, state, } try { const response = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }) if (!response.ok) { const errorText = await response.text() throw new APIException(`Failed to store agent state: ${response.status} - ${errorText}`, response.status) } return await response.json() as { conversationId: string; namespace: string; created: boolean } } catch (error) { if (error instanceof APIException) throw error throw new APIException(`Failed to store agent state: ${error}`) } } /** * Get agent state. */ async get( conversationId: string, namespace: string ): Promise { const url = new URL(`${this.baseUrl}/agent-state/${conversationId}`) url.searchParams.set('namespace', namespace) try { const response = await fetch(url.toString()) if (response.status === 404) { return null } if (!response.ok) { const errorText = await response.text() throw new APIException(`Failed to get agent state: ${response.status} - ${errorText}`, response.status) } return await response.json() as AgentStateResponse } catch (error) { if (error instanceof APIException) throw error throw new APIException(`Failed to get agent state: ${error}`) } } /** * Delete agent state. */ async delete( conversationId: string, namespace?: string ): Promise<{ conversationId: string; namespace?: string; deleted: number }> { const url = new URL(`${this.baseUrl}/agent-state/${conversationId}`) if (namespace) { url.searchParams.set('namespace', namespace) } try { const response = await fetch(url.toString(), { method: 'DELETE' }) if (!response.ok) { const errorText = await response.text() throw new APIException(`Failed to delete agent state: ${response.status} - ${errorText}`, response.status) } return await response.json() as { conversationId: string; namespace?: string; deleted: number } } catch (error) { if (error instanceof APIException) throw error throw new APIException(`Failed to delete agent state: ${error}`) } } }