diff --git a/packages/server/agents.json b/packages/server/agents.json index 6f5f407da..aea373ff6 100644 --- a/packages/server/agents.json +++ b/packages/server/agents.json @@ -24,7 +24,7 @@ { "id": "50128b81-017c-4ab5-8901-3d3f6b65898c", "title": "Agent 50128b81", - "sessionId": null, + "sessionId": "019a1c46-f0ea-76f4-8c1d-6c5d568f7b05", "options": { "type": "claude", "sessionId": null diff --git a/packages/server/src/server/acp/agent-manager.ts b/packages/server/src/server/acp/agent-manager.ts index f5d63cb12..3b841a2d0 100644 --- a/packages/server/src/server/acp/agent-manager.ts +++ b/packages/server/src/server/acp/agent-manager.ts @@ -81,11 +81,16 @@ class ACPClient implements Client { constructor( private agentId: string, private onUpdate: (agentId: string, update: SessionNotification) => void, - private onPermissionRequest: (agentId: string, params: RequestPermissionRequest) => Promise, + private onPermissionRequest: ( + agentId: string, + params: RequestPermissionRequest + ) => Promise, private persistence: AgentPersistence ) {} - async requestPermission(params: RequestPermissionRequest): Promise { + async requestPermission( + params: RequestPermissionRequest + ): Promise { console.log(`[Agent ${this.agentId}] Permission requested:`, params); // Forward to agent manager which will handle the permission flow @@ -97,9 +102,12 @@ class ACPClient implements Client { const claudeSessionId = params._meta?.claudeSessionId as string | undefined; if (claudeSessionId && this.persistence) { const persisted = await this.persistence.load(); - const persistedAgent = persisted.find(a => a.id === this.agentId); + const persistedAgent = persisted.find((a) => a.id === this.agentId); if (persistedAgent && persistedAgent.options.type === "claude") { - if (persistedAgent.options.sessionId === null || persistedAgent.options.sessionId !== claudeSessionId) { + if ( + persistedAgent.options.sessionId === null || + persistedAgent.options.sessionId !== claudeSessionId + ) { // Update the Claude session ID persistedAgent.options.sessionId = claudeSessionId; await this.persistence.upsert(persistedAgent); @@ -109,7 +117,9 @@ class ACPClient implements Client { this.onUpdate(this.agentId, params); } - async readTextFile(params: ReadTextFileRequest): Promise { + async readTextFile( + params: ReadTextFileRequest + ): Promise { console.log(`[Agent ${this.agentId}] Read text file:`, params.path); const fs = await import("fs/promises"); try { @@ -121,7 +131,9 @@ class ACPClient implements Client { } } - async writeTextFile(params: WriteTextFileRequest): Promise { + async writeTextFile( + params: WriteTextFileRequest + ): Promise { console.log(`[Agent ${this.agentId}] Write text file:`, params.path); const fs = await import("fs/promises"); try { @@ -151,7 +163,9 @@ export class AgentManager { for (const persistedAgent of persistedAgents) { try { - console.log(`[AgentManager] Loading agent ${persistedAgent.id} as uninitialized`); + console.log( + `[AgentManager] Loading agent ${persistedAgent.id} as uninitialized` + ); const agent: ManagedAgent = { id: persistedAgent.id, cwd: expandTilde(persistedAgent.cwd), @@ -172,11 +186,16 @@ export class AgentManager { }; this.agents.set(persistedAgent.id, agent); } catch (error) { - console.error(`[AgentManager] Failed to load agent ${persistedAgent.id}:`, error); + console.error( + `[AgentManager] Failed to load agent ${persistedAgent.id}:`, + error + ); } } - console.log(`[AgentManager] Loaded ${this.agents.size} agents as uninitialized`); + console.log( + `[AgentManager] Loaded ${this.agents.size} agents as uninitialized` + ); } /** @@ -214,7 +233,9 @@ export class AgentManager { currentAssistantMessageId: null, currentThoughtId: null, titleGenerationTriggered: false, - pendingSessionMode: options.initialPrompt ? null : options.initialMode ?? null, + pendingSessionMode: options.initialPrompt + ? null + : options.initialMode ?? null, state: { type: "uninitialized", persistedSessionId: null, @@ -271,12 +292,17 @@ export class AgentManager { // Auto-cancel if agent is currently processing if (status === "processing") { - console.log(`[Agent ${agentId}] Auto-cancelling current task before new prompt`); + console.log( + `[Agent ${agentId}] Auto-cancelling current task before new prompt` + ); try { await this.cancelAgent(agentId); - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); } catch (error) { - console.warn(`[Agent ${agentId}] Cancel failed, continuing with new prompt:`, error); + console.warn( + `[Agent ${agentId}] Cancel failed, continuing with new prompt:`, + error + ); } } @@ -286,7 +312,9 @@ export class AgentManager { agent.state.type !== "processing" && agent.state.type !== "completed" ) { - throw new Error(`Agent ${agentId} is not ready (state: ${agent.state.type})`); + throw new Error( + `Agent ${agentId} is not ready (state: ${agent.state.type})` + ); } const runtime = agent.state.runtime; @@ -305,13 +333,13 @@ export class AgentManager { agentId, timestamp: new Date(), notification: { - type: 'session', + type: "session", notification: { sessionId: runtime.sessionId, update: { - sessionUpdate: 'user_message_chunk', + sessionUpdate: "user_message_chunk", content: { - type: 'text', + type: "text", text: prompt, }, ...(options?.messageId ? { messageId: options.messageId } : {}), @@ -348,7 +376,9 @@ export class AgentManager { // Handle completion in background promptPromise .then((response) => { - console.log(`[Agent ${agentId}] Prompt completed with stopReason: ${response.stopReason}`); + console.log( + `[Agent ${agentId}] Prompt completed with stopReason: ${response.stopReason}` + ); const agent = this.agents.get(agentId); if (!agent || agent.state.type !== "processing") return; @@ -378,9 +408,12 @@ export class AgentManager { this.notifySubscribers(agentId); }) .catch((error) => { + console.error(`[Agent ${agentId}] Prompt failed:`, error); this.handleAgentError( agentId, - `Prompt failed: ${error instanceof Error ? error.message : String(error)}` + `Prompt failed: ${ + error instanceof Error ? error.message : String(error) + }` ); }); @@ -388,7 +421,9 @@ export class AgentManager { // If no maxWait specified, return immediately if (maxWait === undefined) { - console.log(`[Agent ${agentId}] Prompt sent (non-blocking, use get_agent_status to check completion)`); + console.log( + `[Agent ${agentId}] Prompt sent (non-blocking, use get_agent_status to check completion)` + ); return { didComplete: false }; } @@ -400,11 +435,15 @@ export class AgentManager { const response = await Promise.race([promptPromise, timeoutPromise]); - console.log(`[Agent ${agentId}] Prompt completed within ${maxWait}ms with stopReason: ${response.stopReason}`); + console.log( + `[Agent ${agentId}] Prompt completed within ${maxWait}ms with stopReason: ${response.stopReason}` + ); return { didComplete: true, stopReason: response.stopReason }; } catch (error) { if (error instanceof Error && error.message === "timeout") { - console.log(`[Agent ${agentId}] Prompt did not complete within ${maxWait}ms (still processing)`); + console.log( + `[Agent ${agentId}] Prompt did not complete within ${maxWait}ms (still processing)` + ); return { didComplete: false }; } // Real error - rethrow @@ -746,7 +785,8 @@ export class AgentManager { const initPromise = (async () => { try { const hasPersistedSession = - (agent.options.type === "claude" && agent.options.sessionId !== null) || + (agent.options.type === "claude" && + agent.options.sessionId !== null) || !!persistedSessionId; const mode = hasPersistedSession ? "resume" : "new"; @@ -852,7 +892,9 @@ export class AgentManager { }; if (agent.state.type !== "initializing") { - throw new Error(`Agent ${agentId} must be initializing before starting runtime`); + throw new Error( + `Agent ${agentId} must be initializing before starting runtime` + ); } agent.state = { @@ -906,7 +948,9 @@ export class AgentManager { null; if (!sessionIdToResume) { - throw new Error(`Cannot resume agent ${agentId}: No session ID available`); + throw new Error( + `Cannot resume agent ${agentId}: No session ID available` + ); } console.log(`[Agent ${agentId}] Loading session: ${sessionIdToResume}`); @@ -930,8 +974,9 @@ export class AgentManager { runtime.currentModeId = sessionResponse.modes?.currentModeId ?? null; runtime.availableModes = sessionResponse.modes?.availableModes ?? null; - const claudeSessionId = - sessionResponse._meta?.claudeSessionId as string | undefined; + const claudeSessionId = sessionResponse._meta?.claudeSessionId as + | string + | undefined; if ( claudeSessionId && agent.options.type === "claude" && @@ -944,7 +989,9 @@ export class AgentManager { } console.log( - `[Agent ${agentId}] Session ${mode === "new" ? "created" : "loaded"}: ACP=${effectiveSessionId}, Claude=${claudeSessionId || "N/A"}` + `[Agent ${agentId}] Session ${ + mode === "new" ? "created" : "loaded" + }: ACP=${effectiveSessionId}, Claude=${claudeSessionId || "N/A"}` ); await this.persistence.upsert({ @@ -980,7 +1027,9 @@ export class AgentManager { `[Agent ${agentId}] Runtime started successfully with mode: ${runtime.currentModeId}` ); } catch (error) { - const errorMessage = `Runtime startup failed: ${error instanceof Error ? error.message : String(error)}`; + const errorMessage = `Runtime startup failed: ${ + error instanceof Error ? error.message : String(error) + }`; console.error(`[Agent ${agentId}]`, errorMessage); agent.state = { type: "failed", @@ -1000,7 +1049,10 @@ export class AgentManager { * Handle session notifications from the ACP connection * Augments agent message and thought chunks with stable message IDs */ - private handleSessionNotification(agentId: string, update: SessionNotification): void { + private handleSessionNotification( + agentId: string, + update: SessionNotification + ): void { const agent = this.agents.get(agentId); if (!agent) return; @@ -1010,7 +1062,7 @@ export class AgentManager { const updateType = update.update.sessionUpdate; // Agent message chunks - add stable message ID - if (updateType === 'agent_message_chunk') { + if (updateType === "agent_message_chunk") { if (!agent.currentAssistantMessageId) { agent.currentAssistantMessageId = uuidv4(); } @@ -1023,7 +1075,7 @@ export class AgentManager { }; } // Agent thought chunks - add stable message ID - else if (updateType === 'agent_thought_chunk') { + else if (updateType === "agent_thought_chunk") { if (!agent.currentThoughtId) { agent.currentThoughtId = uuidv4(); } @@ -1040,7 +1092,7 @@ export class AgentManager { enrichedUpdate = update as EnrichedSessionNotification; // Reset message IDs on new turn (user message or tool call starts new turn) - if (updateType === 'tool_call' || updateType === 'user_message_chunk') { + if (updateType === "tool_call" || updateType === "user_message_chunk") { agent.currentAssistantMessageId = null; agent.currentThoughtId = null; } @@ -1051,7 +1103,7 @@ export class AgentManager { agentId, timestamp: new Date(), notification: { - type: 'session', + type: "session", notification: enrichedUpdate, }, }; @@ -1060,10 +1112,7 @@ export class AgentManager { agent.updates.push(agentUpdate); // Log update for debugging - console.log( - `[Agent ${agentId}] Session update:`, - updateType - ); + console.log(`[Agent ${agentId}] Session update:`, updateType); // Notify all subscribers for (const subscriber of agent.subscribers) { @@ -1086,7 +1135,9 @@ export class AgentManager { // Preserve runtime if it exists const runtime = - agent.state.type !== "uninitialized" && agent.state.type !== "killed" && agent.state.type !== "failed" + agent.state.type !== "uninitialized" && + agent.state.type !== "killed" && + agent.state.type !== "failed" ? agent.state.runtime : undefined; @@ -1112,7 +1163,7 @@ export class AgentManager { agentId, timestamp: new Date(), notification: { - type: 'status', + type: "status", status, error, }, @@ -1191,7 +1242,9 @@ export class AgentManager { // Generate unique request ID const requestId = uuidv4(); - console.log(`[Agent ${agentId}] Creating pending permission request: ${requestId}`); + console.log( + `[Agent ${agentId}] Creating pending permission request: ${requestId}` + ); // Create a promise that will be resolved when user responds return new Promise((resolve, reject) => { @@ -1212,8 +1265,8 @@ export class AgentManager { agentId, timestamp: new Date(), notification: { - type: 'permission', - requestId, // Pass the requestId that we stored in pendingPermissions + type: "permission", + requestId, // Pass the requestId that we stored in pendingPermissions request: params, }, }; @@ -1230,12 +1283,16 @@ export class AgentManager { } } - console.log(`[Agent ${agentId}] Permission request emitted: ${requestId}`); + console.log( + `[Agent ${agentId}] Permission request emitted: ${requestId}` + ); // Set a timeout to auto-reject after 5 minutes if no response setTimeout(() => { if (agent.pendingPermissions.has(requestId)) { - console.warn(`[Agent ${agentId}] Permission request ${requestId} timed out`); + console.warn( + `[Agent ${agentId}] Permission request ${requestId} timed out` + ); agent.pendingPermissions.delete(requestId); reject(new Error("Permission request timed out")); } @@ -1284,7 +1341,7 @@ export class AgentManager { agentId, timestamp: new Date(), notification: { - type: 'permission_resolved', + type: "permission_resolved", requestId, agentId, optionId, @@ -1303,7 +1360,9 @@ export class AgentManager { } } - console.log(`[Agent ${agentId}] Permission resolved notification emitted: ${requestId}`); + console.log( + `[Agent ${agentId}] Permission resolved notification emitted: ${requestId}` + ); } /**