refactor(providers): move session message delegation into sessions service

Move provider-backed session history and message normalization calls out of the
generic providers service so the service name reflects the behavior it owns.

Add a dedicated sessions service for listing session-capable providers,
normalizing live provider events, and fetching persisted session history through
the provider registry. Update realtime handlers and the unified messages route to
depend on `sessionsService` instead of `providersService`.

This separates session message operations from other provider concerns such as
auth and MCP, keeping the provider services easier to navigate as the module
grows.
This commit is contained in:
Haileyesus
2026-04-17 15:29:35 +03:00
parent b74b5fb967
commit 0f1e515b39
6 changed files with 23 additions and 14 deletions

View File

@@ -24,7 +24,7 @@ import {
notifyRunStopped,
notifyUserIfEnabled
} from './services/notification-orchestrator.js';
import { providersService } from './modules/providers/services/providers.service.js';
import { sessionsService } from './modules/providers/services/sessions.service.js';
import { createNormalizedMessage } from './shared/utils.js';
const activeSessions = new Map();
@@ -649,7 +649,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
const sid = capturedSessionId || sessionId || null;
// Use adapter to normalize SDK events into NormalizedMessage[]
const normalized = providersService.normalizeMessage('claude', transformedMessage, sid);
const normalized = sessionsService.normalizeMessage('claude', transformedMessage, sid);
for (const msg of normalized) {
// Preserve parentToolUseId from SDK wrapper for subagent tool grouping
if (transformedMessage.parentToolUseId && !msg.parentToolUseId) {

View File

@@ -1,7 +1,7 @@
import { spawn } from 'child_process';
import crossSpawn from 'cross-spawn';
import { notifyRunFailed, notifyRunStopped } from './services/notification-orchestrator.js';
import { providersService } from './modules/providers/services/providers.service.js';
import { sessionsService } from './modules/providers/services/sessions.service.js';
import { createNormalizedMessage } from './shared/utils.js';
// Use cross-spawn on Windows for better command execution
@@ -189,7 +189,7 @@ async function spawnCursor(command, options = {}, ws) {
case 'assistant':
// Accumulate assistant message chunks
if (response.message && response.message.content && response.message.content.length > 0) {
const normalized = providersService.normalizeMessage('cursor', response, capturedSessionId || sessionId || null);
const normalized = sessionsService.normalizeMessage('cursor', response, capturedSessionId || sessionId || null);
for (const msg of normalized) ws.send(msg);
}
break;
@@ -219,7 +219,7 @@ async function spawnCursor(command, options = {}, ws) {
}
// If not JSON, send as stream delta via adapter
const normalized = providersService.normalizeMessage('cursor', line, capturedSessionId || sessionId || null);
const normalized = sessionsService.normalizeMessage('cursor', line, capturedSessionId || sessionId || null);
for (const msg of normalized) ws.send(msg);
}
};

View File

@@ -1,5 +1,5 @@
// Gemini Response Handler - JSON Stream processing
import { providersService } from './modules/providers/services/providers.service.js';
import { sessionsService } from './modules/providers/services/sessions.service.js';
class GeminiResponseHandler {
constructor(ws, options = {}) {
@@ -56,7 +56,7 @@ class GeminiResponseHandler {
}
// Normalize via adapter and send all resulting messages
const normalized = providersService.normalizeMessage('gemini', event, sid);
const normalized = sessionsService.normalizeMessage('gemini', event, sid);
for (const msg of normalized) {
this.ws.send(msg);
}

View File

@@ -7,17 +7,23 @@ import type {
} from '@/shared/types.js';
/**
* Application service for provider message operations.
* Application service for provider-backed session message operations.
*
* Callers pass a provider id and this service resolves the concrete provider
* class, keeping normalization/history call sites decoupled from implementation
* file layout.
*/
export const providersService = {
export const sessionsService = {
/**
* Lists provider ids that can load session history and normalize live messages.
*/
listProviderIds(): LLMProvider[] {
return providerRegistry.listProviders().map((provider) => provider.id);
},
/**
* Normalizes one provider-native event into frontend session message events.
*/
normalizeMessage(
providerName: string,
raw: unknown,
@@ -26,6 +32,9 @@ export const providersService = {
return providerRegistry.resolveProvider(providerName).normalizeMessage(raw, sessionId);
},
/**
* Fetches normalized persisted session history for one provider/session pair.
*/
fetchHistory(
providerName: string,
sessionId: string,

View File

@@ -15,7 +15,7 @@
import { Codex } from '@openai/codex-sdk';
import { notifyRunFailed, notifyRunStopped } from './services/notification-orchestrator.js';
import { providersService } from './modules/providers/services/providers.service.js';
import { sessionsService } from './modules/providers/services/sessions.service.js';
import { createNormalizedMessage } from './shared/utils.js';
// Track active sessions
@@ -264,7 +264,7 @@ export async function queryCodex(command, options = {}, ws) {
const transformed = transformCodexEvent(event);
// Normalize the transformed event into NormalizedMessage(s) via adapter
const normalizedMsgs = providersService.normalizeMessage('codex', transformed, currentSessionId);
const normalizedMsgs = sessionsService.normalizeMessage('codex', transformed, currentSessionId);
for (const msg of normalizedMsgs) {
sendMessage(ws, msg);
}

View File

@@ -10,7 +10,7 @@
*/
import express from 'express';
import { providersService } from '../modules/providers/services/providers.service.js';
import { sessionsService } from '../modules/providers/services/sessions.service.js';
const router = express.Router();
@@ -38,13 +38,13 @@ router.get('/:sessionId/messages', async (req, res) => {
: null;
const offset = parseInt(req.query.offset || '0', 10);
const availableProviders = providersService.listProviderIds();
const availableProviders = sessionsService.listProviderIds();
if (!availableProviders.includes(provider)) {
const available = availableProviders.join(', ');
return res.status(400).json({ error: `Unknown provider: ${provider}. Available: ${available}` });
}
const result = await providersService.fetchHistory(provider, sessionId, {
const result = await sessionsService.fetchHistory(provider, sessionId, {
projectName,
projectPath,
limit,