import { PersonaProtocolBase } from './base'; import { PersonaResponse, Session, ProtocolStatus, PersonaProtocolBaseConfig, PersonaMessage, PersonaPacket, PersonaCommand, } from '../types'; import { ToolInstance } from '../tools'; type PersonaRESTProtocolConfig = PersonaProtocolBaseConfig & { apiUrl: string; }; class PersonaRESTProtocol extends PersonaProtocolBase { status: ProtocolStatus; autostart: boolean; session: Session; config: PersonaRESTProtocolConfig; notify: boolean = true; context: Record = {}; tools: ToolInstance[] = []; constructor(config: PersonaRESTProtocolConfig) { super(); this.config = config; this.status = 'disconnected'; this.autostart = true; } public getName(): string { return 'rest'; } public getPriority(): number { return 100; } public async connect(session: Session): Promise { this.setStatus('connected'); return session; } public async disconnect(): Promise { this.setStatus('disconnected'); this.session = null; } public async syncSession(session: Session): Promise { this.session = session; } private buildHeaders(): Record { return { 'Content-Type': 'application/json', 'x-persona-apikey': this.config.apiKey, }; } public async sendPacket(packet: PersonaPacket): Promise { const { apiUrl, agentId } = this.config; const sessionId = this.session ?? 'new'; // ─── Comandi ────────────────────────────────────────────────── if (packet.type === 'command') { const cmd = packet.payload as PersonaCommand; if (cmd.command === 'set_initial_context') { this.context = cmd.arguments as Record; return; } if (cmd.command === 'set_local_tools') { const tools = cmd.arguments?.tools as ToolInstance[] | undefined; if (tools?.length) { this.notifyPacket({ type: 'message', payload: { type: 'text', role: 'assistant', text: 'Local tools with rest protocol are not supported.', }, }); } return; } if (cmd.command === 'cancel') { try { await fetch(`${apiUrl}/sessions/${encodeURIComponent(String(sessionId))}/cancel`, { method: 'POST', headers: this.buildHeaders(), }); this.notifyPacket({ type: 'agent_end', payload: { sessionId: String(sessionId), stopReason: 'cancelled', totalTurns: 0, }, }); } catch (error) { this.config.logger?.error('Error cancelling via REST:', error); } return; } if (cmd.command === 'steer') { const text = (cmd.arguments?.text as string) || ''; if (!text) return; try { const res = await fetch(`${apiUrl}/sessions/${encodeURIComponent(String(sessionId))}/steer`, { method: 'POST', headers: this.buildHeaders(), body: JSON.stringify({ text }), }); const result = await res.json(); this.notifyPacket({ type: 'steer_accepted', payload: result }); } catch (error) { this.config.logger?.error('Error steering via REST:', error); } return; } if (cmd.command === 'get_agent_state') { try { const res = await fetch(`${apiUrl}/sessions/${encodeURIComponent(String(sessionId))}/agent-state`, { method: 'GET', headers: this.buildHeaders(), }); const state = await res.json(); this.notifyPacket({ type: 'command', payload: { command: 'get_agent_state', arguments: state } }); } catch (error) { this.config.logger?.error('Error getting agent state via REST:', error); } return; } return; } // ─── Request (messaggio utente) ─────────────────────────────── if (packet.type !== 'request') return; const input = packet.payload as PersonaMessage; // Emetti agent_start sintetico (REST è sincrono, quindi emettiamo subito) this.notifyPacket({ type: 'agent_start', payload: { sessionId: String(sessionId), agentId, timestamp: new Date().toISOString(), }, }); try { const response = await fetch(`${apiUrl}/sessions/${encodeURIComponent(String(sessionId))}/messages`, { body: JSON.stringify({ agentId, userMessage: input, initialContext: this.context, tools: this.tools }), method: 'POST', headers: this.buildHeaders(), }); const personaResponse = (await response.json()) as PersonaResponse; // Forward messages this.notifyPackets( personaResponse.response.messages.map((payload) => ({ type: 'message', payload, })), ); // Emetti agent_end sintetico this.notifyPacket({ type: 'agent_end', payload: { sessionId: String(sessionId), stopReason: 'completed', totalTurns: 1, }, }); // Update session from response if (personaResponse.sessionId && personaResponse.sessionId !== sessionId) { this.session = personaResponse.sessionId; this.notifyPacket({ type: 'command', payload: { command: 'set_initial_context', arguments: { sessionId: personaResponse.sessionId }, }, }); } } catch (error) { this.notifyPacket({ type: 'message', payload: { role: 'assistant', type: 'text', text: 'An error occurred while processing your request. Please try again later.', }, }); this.notifyPacket({ type: 'agent_end', payload: { sessionId: String(sessionId), stopReason: 'error', totalTurns: 0, errorMessage: String(error), }, }); this.config.logger?.error('Error sending REST packet:', error); } } } export { PersonaRESTProtocol }; export type { PersonaRESTProtocolConfig };