diff --git a/packages/server/src/server/agent-loading-service.ts b/packages/server/src/server/agent-loading-service.ts index e15985149..f075850e2 100644 --- a/packages/server/src/server/agent-loading-service.ts +++ b/packages/server/src/server/agent-loading-service.ts @@ -16,7 +16,11 @@ const pendingAgentBootstrapLoads = new Map>(); export type AgentLoadingServiceOptions = { agentManager: Pick< AgentManager, - "createAgent" | "getAgent" | "reloadAgentSession" | "resumeAgentFromPersistence" + | "createAgent" + | "getAgent" + | "hydrateTimelineFromProvider" + | "reloadAgentSession" + | "resumeAgentFromPersistence" >; agentStorage: Pick; logger: pino.Logger; @@ -51,10 +55,7 @@ export class AgentLoadingService { try { return await initPromise; } finally { - const current = pendingAgentBootstrapLoads.get(options.agentId); - if (current === initPromise) { - pendingAgentBootstrapLoads.delete(options.agentId); - } + pendingAgentBootstrapLoads.delete(options.agentId); } } @@ -62,15 +63,24 @@ export class AgentLoadingService { handle: AgentPersistenceHandle; overrides?: Partial; }): Promise { - return this.agentManager.resumeAgentFromPersistence(options.handle, options.overrides); + const snapshot = await this.agentManager.resumeAgentFromPersistence( + options.handle, + options.overrides, + ); + await this.agentManager.hydrateTimelineFromProvider(snapshot.id); + return snapshot; } async refreshAgent(options: { agentId: string }): Promise { const existing = this.agentManager.getAgent(options.agentId); if (existing) { - return existing.persistence - ? await this.agentManager.reloadAgentSession(options.agentId) - : existing; + if (!existing.persistence) { + return existing; + } + + const snapshot = await this.agentManager.reloadAgentSession(options.agentId); + await this.agentManager.hydrateTimelineFromProvider(snapshot.id); + return snapshot; } const record = await this.agentStorage.get(options.agentId); @@ -83,12 +93,14 @@ export class AgentLoadingService { throw new Error(`Agent ${options.agentId} cannot be refreshed because it lacks persistence`); } - return this.agentManager.resumeAgentFromPersistence( + const snapshot = await this.agentManager.resumeAgentFromPersistence( handle, buildConfigOverrides(record), options.agentId, extractTimestamps(record), ); + await this.agentManager.hydrateTimelineFromProvider(snapshot.id); + return snapshot; } private async loadStoredAgent(options: { agentId: string }): Promise { @@ -126,6 +138,7 @@ export class AgentLoadingService { ); } - return this.agentManager.getAgent(options.agentId) ?? snapshot; + await this.agentManager.hydrateTimelineFromProvider(snapshot.id); + return snapshot; } } diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index cd6a09fda..493c8b467 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -195,7 +195,6 @@ import { const execAsync = promisify(exec); const MAX_INITIAL_AGENT_TITLE_CHARS = Math.min(60, MAX_EXPLICIT_AGENT_TITLE_CHARS); -const pendingAgentInitializations = new Map>(); const DEFAULT_AGENT_PROVIDER = AGENT_PROVIDER_IDS[0]; const WORKSPACE_GIT_WATCH_REMOVED_FINGERPRINT = "__removed__"; @@ -1191,10 +1190,7 @@ export class Session { if (storedUpdatedAt) { const liveUpdatedAt = Date.parse(payload.updatedAt); const persistedUpdatedAt = Date.parse(storedUpdatedAt); - if ( - !Number.isNaN(persistedUpdatedAt) && - (Number.isNaN(liveUpdatedAt) || persistedUpdatedAt > liveUpdatedAt) - ) { + if (Number.isNaN(liveUpdatedAt) || persistedUpdatedAt > liveUpdatedAt) { payload.updatedAt = storedUpdatedAt; } } @@ -1231,15 +1227,9 @@ export class Session { record.id, ), sessionId: record.runtimeInfo.sessionId, - ...(Object.prototype.hasOwnProperty.call(record.runtimeInfo, "model") - ? { model: record.runtimeInfo.model ?? null } - : {}), - ...(Object.prototype.hasOwnProperty.call(record.runtimeInfo, "thinkingOptionId") - ? { thinkingOptionId: record.runtimeInfo.thinkingOptionId ?? null } - : {}), - ...(Object.prototype.hasOwnProperty.call(record.runtimeInfo, "modeId") - ? { modeId: record.runtimeInfo.modeId ?? null } - : {}), + model: record.runtimeInfo.model ?? null, + thinkingOptionId: record.runtimeInfo.thinkingOptionId ?? null, + modeId: record.runtimeInfo.modeId ?? null, ...(record.runtimeInfo.extra ? { extra: record.runtimeInfo.extra } : {}), } : undefined; @@ -1287,48 +1277,12 @@ export class Session { })) .filter((value) => !Number.isNaN(value.parsed)); - if (timestamps.length === 0) { - return record.updatedAt; - } - timestamps.sort((a, b) => b.parsed - a.parsed); return timestamps[0].raw; } private async ensureAgentLoaded(agentId: string): Promise { - const existing = this.agentManager.getAgent(agentId); - if (existing) { - return existing; - } - - const inflight = pendingAgentInitializations.get(agentId); - if (inflight) { - return inflight; - } - - const initPromise = (async () => { - await this.requireStoredAgentRecord(agentId); - return this.agentLoadingService.ensureAgentLoaded({ agentId }); - })(); - - pendingAgentInitializations.set(agentId, initPromise); - - try { - return await initPromise; - } finally { - const current = pendingAgentInitializations.get(agentId); - if (current === initPromise) { - pendingAgentInitializations.delete(agentId); - } - } - } - - private async requireStoredAgentRecord(agentId: string): Promise { - const record = await this.agentStorage.get(agentId); - if (!record) { - throw new Error(`Agent not found: ${agentId}`); - } - return record; + return this.agentLoadingService.ensureAgentLoaded({ agentId }); } private isProviderVisibleToClient(provider: string): boolean { @@ -3006,7 +2960,7 @@ export class Session { const started = await this.handleSendAgentMessage( snapshot.id, - trimmedPrompt ?? "", + trimmedPrompt || "", resolveClientMessageId(clientMessageId), images, attachments, @@ -3018,10 +2972,7 @@ export class Session { } if (requestId) { - const agentPayload = await this.getAgentPayloadById(snapshot.id); - if (!agentPayload) { - throw new Error(`Agent ${snapshot.id} not found after creation`); - } + const agentPayload = await this.buildAgentPayload(snapshot); this.emit({ type: "status", payload: { @@ -3033,41 +2984,6 @@ export class Session { }); } - if (trimmedPrompt || (images?.length ?? 0) > 0 || (attachments?.length ?? 0) > 0) { - scheduleAgentMetadataGeneration({ - agentManager: this.agentManager, - agentId: snapshot.id, - cwd: snapshot.cwd, - initialPrompt: trimmedPrompt, - explicitTitle, - paseoHome: this.paseoHome, - logger: this.sessionLogger, - }); - - void this.handleSendAgentMessage( - snapshot.id, - trimmedPrompt ?? "", - resolveClientMessageId(clientMessageId), - images, - attachments, - outputSchema ? { outputSchema } : undefined, - ).catch((promptError) => { - this.sessionLogger.error( - { err: promptError, agentId: snapshot.id }, - `Failed to run initial prompt for agent ${snapshot.id}`, - ); - this.emit({ - type: "activity_log", - payload: { - id: uuidv4(), - timestamp: new Date(), - type: "error", - content: `Initial prompt failed: ${(promptError as Error)?.message ?? promptError}`, - }, - }); - }); - } - if (worktreeBootstrap) { void runAsyncWorktreeBootstrap({ agentId: snapshot.id, @@ -3149,10 +3065,7 @@ export class Session { await this.forwardAgentUpdate(snapshot); const timelineSize = this.agentManager.getTimeline(snapshot.id).length; if (requestId) { - const agentPayload = await this.getAgentPayloadById(snapshot.id); - if (!agentPayload) { - throw new Error(`Agent ${snapshot.id} not found after resume`); - } + const agentPayload = await this.buildAgentPayload(snapshot); this.emit({ type: "status", payload: {