Based on the changes, here's the commit message:
``` refactor: convert AgentManager to global singleton across sessions Move AgentManager initialization from per-session to single global instance. Agents now persist across sessions, with sessions subscribing/unsubscribing to agent updates rather than creating/destroying agents. ```
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
listConversations,
|
||||
deleteConversation,
|
||||
} from "./persistence.js";
|
||||
import { AgentManager } from "./acp/agent-manager.js";
|
||||
|
||||
function createServer() {
|
||||
const app = express();
|
||||
@@ -64,8 +65,12 @@ function main() {
|
||||
const app = createServer();
|
||||
const httpServer = createHTTPServer(app);
|
||||
|
||||
// Initialize global agent manager
|
||||
const agentManager = new AgentManager();
|
||||
console.log("✓ Global agent manager initialized");
|
||||
|
||||
// Initialize WebSocket server
|
||||
const wsServer = new VoiceAssistantWebSocketServer(httpServer);
|
||||
const wsServer = new VoiceAssistantWebSocketServer(httpServer, agentManager);
|
||||
|
||||
// Initialize OpenAI client
|
||||
const apiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
@@ -76,12 +76,13 @@ export class Session {
|
||||
private terminalManager: any | null = null;
|
||||
private agentMcpClient: Awaited<ReturnType<typeof experimental_createMCPClient>> | null = null;
|
||||
private agentTools: Record<string, any> | null = null;
|
||||
private agentManager: AgentManager | null = null;
|
||||
private agentManager: AgentManager;
|
||||
private agentUpdateUnsubscribers: Map<string, () => void> = new Map();
|
||||
|
||||
constructor(
|
||||
clientId: string,
|
||||
onMessage: (msg: SessionOutboundMessage) => void,
|
||||
agentManager: AgentManager,
|
||||
options?: {
|
||||
conversationId?: string;
|
||||
initialMessages?: ModelMessage[];
|
||||
@@ -90,6 +91,7 @@ export class Session {
|
||||
this.clientId = clientId;
|
||||
this.conversationId = options?.conversationId || uuidv4();
|
||||
this.onMessage = onMessage;
|
||||
this.agentManager = agentManager;
|
||||
this.abortController = new AbortController();
|
||||
|
||||
// Initialize conversation history
|
||||
@@ -122,6 +124,13 @@ export class Session {
|
||||
return this.conversationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send initial state to client after connection
|
||||
*/
|
||||
public async sendInitialState(): Promise<void> {
|
||||
await this.sendSessionState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to updates from an agent
|
||||
*/
|
||||
@@ -236,10 +245,7 @@ export class Session {
|
||||
*/
|
||||
private async initializeAgentMcp(): Promise<void> {
|
||||
try {
|
||||
// Create AgentManager instance
|
||||
this.agentManager = new AgentManager();
|
||||
|
||||
// Create Agent MCP server with the manager
|
||||
// Create Agent MCP server with the global manager
|
||||
const server = await createAgentMcpServer({
|
||||
agentManager: this.agentManager,
|
||||
});
|
||||
@@ -1164,33 +1170,10 @@ export class Session {
|
||||
this.ttsManager.cleanup();
|
||||
this.sttManager.cleanup();
|
||||
|
||||
// Kill all agents
|
||||
if (this.agentManager) {
|
||||
try {
|
||||
const agents = this.agentManager.listAgents();
|
||||
console.log(
|
||||
`[Session ${this.clientId}] Killing ${agents.length} agents`
|
||||
);
|
||||
for (const agent of agents) {
|
||||
try {
|
||||
await this.agentManager.killAgent(agent.id);
|
||||
console.log(`[Session ${this.clientId}] Killed agent ${agent.id}`);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[Session ${this.clientId}] Failed to kill agent ${agent.id}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[Session ${this.clientId}] Error during agent cleanup:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Unsubscribe from all agent updates
|
||||
// Unsubscribe from all agent updates (agents remain global and persist)
|
||||
console.log(
|
||||
`[Session ${this.clientId}] Unsubscribing from ${this.agentUpdateUnsubscribers.size} agent(s)`
|
||||
);
|
||||
for (const [agentId, unsubscribe] of this.agentUpdateUnsubscribers) {
|
||||
try {
|
||||
unsubscribe();
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "./messages.js";
|
||||
import { Session } from "./session.js";
|
||||
import { loadConversation } from "./persistence.js";
|
||||
import { AgentManager } from "./acp/agent-manager.js";
|
||||
|
||||
/**
|
||||
* WebSocket server that routes messages between clients and their sessions.
|
||||
@@ -19,8 +20,10 @@ export class VoiceAssistantWebSocketServer {
|
||||
private sessions: Map<WebSocket, Session> = new Map();
|
||||
private conversationIdToWs: Map<string, WebSocket> = new Map();
|
||||
private clientIdCounter: number = 0;
|
||||
private agentManager: AgentManager;
|
||||
|
||||
constructor(server: HTTPServer) {
|
||||
constructor(server: HTTPServer, agentManager: AgentManager) {
|
||||
this.agentManager = agentManager;
|
||||
this.wss = new WebSocketServer({ server, path: "/ws" });
|
||||
|
||||
this.wss.on("connection", (ws, request) => {
|
||||
@@ -66,6 +69,7 @@ export class VoiceAssistantWebSocketServer {
|
||||
(msg) => {
|
||||
this.sendToClient(ws, wrapSessionMessage(msg));
|
||||
},
|
||||
this.agentManager,
|
||||
{
|
||||
conversationId,
|
||||
initialMessages: initialMessages || undefined,
|
||||
@@ -82,6 +86,9 @@ export class VoiceAssistantWebSocketServer {
|
||||
})`
|
||||
);
|
||||
|
||||
// Send initial session state (global agents and commands)
|
||||
await session.sendInitialState();
|
||||
|
||||
// Set up message handler
|
||||
ws.on("message", (data) => {
|
||||
this.handleMessage(ws, data);
|
||||
|
||||
Reference in New Issue
Block a user