strategies_SSESessionStrategy.js
const EventSource = require('eventsource');
const axios = require('axios');
/**
* @typedef {Object} AssisfySDK
* @property {string} apiKey - The API key
* @property {Object} config - SDK configuration
* @property {'production' | 'staging' | 'development'} environment - Current environment
*/
/**
* @typedef {Object} Session
* @property {string} sessionId - The session ID
* @property {Function} emit - Event emitter function
*/
/** @type {string[]} */
const EVENT_TYPES = ['started', 'browser_started', 'action_run', 'thoughts_and_memories',
'external_resource_requested', 'complete', 'error', 'hearbeat', 'connected'];
/**
* Strategy for Server-Sent Events (SSE) connections
*/
class SSESessionStrategy {
/** @type {string} */
sessionId;
/** @type {EventSource} */
eventSource;
/** @type {AssisfySDK} */
sdk;
/** @type {Session} */
session;
/**
* Create a new SSE session strategy
* @param {Session} session - The session instance
* @param {AssisfySDK} sdk - The SDK instance
*/
constructor(session, sdk) {
this.eventSource = null;
this.sessionId = session.sessionId;
this.sdk = sdk;
this.session = session;
this.connectEventSource();
}
/**
* Connect to the event source
* @private
*/
connectEventSource() {
const { sseUrl } = this.sdk.config;
const sessionId = this.sessionId;
const apiKey = this.sdk.apiKey;
const url = `${sseUrl}?sessionId=${sessionId}&apiKey=${apiKey}`;
this.eventSource = new EventSource(url);
// Handle connection events
this.eventSource.onopen = () => {
this.session.emit('session_connected', { sessionId });
};
this.eventSource.onerror = (error) => {
this.session.emit('session_error', { sessionId, error: error.message });
};
// Register event listeners for all supported event types
EVENT_TYPES.forEach(eventType => {
this.eventSource.addEventListener(eventType, (event) => {
try {
const eventData = JSON.parse(event.data);
this.session.emit("message", {
event: eventType,
data: eventData
});
} catch (error) {
this.session.emit('session_error', {
sessionId,
error: `Error parsing event data: ${error.message}`
});
}
});
});
}
/**
* Handle a permission request
* @param {boolean} [granted=false] - Whether to grant the permission
* @returns {Promise<void>}
*/
async handlePermissionRequest(granted = false) {
const { sseEgressUrl } = this.sdk.config;
try {
await axios.post(
`${sseEgressUrl}?sessionId=${this.sessionId}&apiKey=${this.sdk.apiKey}`,
{
event: 'external_resource_granted',
data: {
granted,
resource_type: 'admin_permission_request'
}
}
);
} catch (error) {
this.session.emit('session_error', {
sessionId: this.sessionId,
error: `Error granting permission: ${error.message}`
});
}
}
/**
* Handle user input
* @param {string} [input=''] - The user input
* @returns {Promise<void>}
*/
async handleInput(input = '') {
const { sseEgressUrl } = this.sdk.config;
try {
await axios.post(
`${sseEgressUrl}?sessionId=${this.sessionId}&apiKey=${this.sdk.apiKey}`,
{
event: 'external_resource_granted',
data: {
input,
resource_type: 'request_user_input',
granted: true
}
}
);
} catch (error) {
this.session.emit('session_error', {
sessionId: this.sessionId,
error: `Error handling input: ${error.message}`
});
}
}
/**
* Close the event source connection
*/
close() {
if (this.eventSource) {
this.eventSource.close();
this.session.emit('session_disconnected', {
sessionId: this.sessionId,
code: 1000,
reason: 'Client closed connection'
});
}
}
}
module.exports = SSESessionStrategy;