Обзор Agent Engine
Система Agent в Elftia основана на многодвижковой архитектуре диспетчеризации. EngineDispatcher выступает единым диспетчером запросов, который направляет запросы к соответствующей реализации движка на основе EngineType. Каждый движок реализует интерфейс IEngine и инкапсулирует разные backend services.
Диаграмма архитектуры
graph TB
Router["AgentRouter / MagiService"] --> Dispatcher["EngineDispatcher"]
Dispatcher --> TinyElf["TinyElfEngine<br/>tinyelf"]
Dispatcher --> ClaudeSdk["ClaudeSdkEngine<br/>claude-sdk"]
Dispatcher --> CliRunner["CliRunnerEngine<br/>cli"]
Dispatcher --> Chat["ChatEngine<br/>chat"]
Dispatcher --> STChat["STChatEngine<br/>st-chat"]
Dispatcher --> Api["ApiEngine<br/>api"]
TinyElf --> TinyLoop["TinyElfAgentLoop<br/>Tool call loop"]
TinyElf --> ToolReg["ToolRegistry<br/>Tool registry"]
TinyElf --> LLMAdapter["TinyElfLLMAdapter<br/>LLM adapter"]
ClaudeSdk --> AgentSvc["AgentService<br/>Claude Agent SDK"]
ClaudeSdk --> Proxy["AgentProxyServer<br/>HTTP proxy"]
CliRunner --> Supervisor["ProcessSupervisor<br/>Process management"]
Chat --> Pipeline["ChatCompletionPipeline<br/>Chat pipeline"]
Интерфейс IEngine
Все движки должны реализовать интерфейс IEngine:
interface IEngine {
readonly engineType: EngineType;
startSession(ctx: EngineSessionContext): Promise<void>;
resumeSession(ctx: EngineSessionContext): Promise<void>;
interrupt(dbSessionId: string): Promise<boolean>;
isActive(dbSessionId: string): boolean;
reloadMcpForAllActive?(): Promise<void>;
}
| Метод | Описание |
|---|---|
startSession | Запустить новую сессию (сохранить пользовательское сообщение, выполнить цикл движка) |
resumeSession | Продолжить разговор в существующей сессии |
interrupt | Прервать активную сессию, вернуть успешность операции |
isActive | Проверить, выполняется ли сессия |
reloadMcpForAllActive | Горячая перезагрузка MCP tools для всех активных сессий (необязательно) |
EngineSessionContext
Все движки используют одну и ту же структуру контекста сессии. Каждый движок использует поля по необходимости:
interface EngineSessionContext {
dbSessionId: string; // Database session ID
session: ChatSession; // Full session record
sender: WebContents; // Electron IPC sender
prompt: string; // User message
attachments?: Array<...>; // Multimodal attachments
providerId?: string; // LLM provider ID
model?: string; // Model ID (v89+ is bare SDK id)
/**
* v89+: CLI backend binding (from chat_sessions.cliBackend column).
* 'claude-code' goes through ClaudeSdkEngine + AgentProxyServer.passThrough;
* 'codex' / 'gemini-cli' goes through CliRunnerEngine.
*/
cliBackend?: 'claude-code' | 'codex' | 'gemini-cli' | null;
/**
* v89+: 1M-context preference (from chat_sessions.useExtendedContext column).
* Engines / AgentProxyServer / TransformerChainExecutor decide whether to
* inject 'context-1m-2025-08-07' into anthropic_beta based on this.
*/
useExtendedContext?: boolean;
engineConfig?: EngineConfig; // Engine-specific configuration
projectPath?: string; // Project path
agentId?: string; // Agent ID
// Chat engine specific
systemPrompt?: string;
parentId?: string | null;
thinkingLevel?: string;
searchOrchestration?: PipelineSearchConfig;
mcpConfig?: PipelineMcpConfig;
// Magi/Agent engine specific
providerEnv?: Record<string, string>;
onSessionEnd?: () => void;
isOfficialProvider?: boolean;
customAgentOptions?: { ... };
permissionMode?: string;
channelSource?: string;
channelId?: string;
channelSenderId?: string;
channelUserPermissions?: { canUseTool: boolean; requireConfirmation: boolean };
}
EngineDispatcher
EngineDispatcher — ядро регистрации и диспетчеризации движков:
class EngineDispatcher {
private engines = new Map<EngineType, IEngine>();
registerEngine(engine: IEngine): void;
getEngine(type: EngineType): IEngine;
hasEngine(type: EngineType): boolean;
dispatch(type: EngineType, ctx: EngineSessionContext): Promise<void>;
resume(type: EngineType, ctx: EngineSessionContext): Promise<void>;
interrupt(dbSessionId: string): Promise<boolean>;
reloadMcpForAllActive(): Promise<void>;
getEngineInfos(): EngineInfo[];
}
Регистрация движков
Движки регистрируются в packages/desktop/app/main/index.ts:
const dispatcher = new EngineDispatcher();
dispatcher.registerEngine(new TinyElfEngine(...));
dispatcher.registerEngine(new ClaudeSdkEngine(...));
dispatcher.registerEngine(new CliRunnerEngine(getProcessSupervisor(), db, logger));
dispatcher.registerEngine(new ChatEngine(...));
dispatcher.registerEngine(new STChatEngine(...));
dispatcher.registerEngine(new ApiEngine(...));
Механизм прерывания
Метод interrupt() проходит по всем движкам, находит движок, которому принадлежит целевая сессия, и прерывает её:
async interrupt(dbSessionId: string): Promise<boolean> {
for (const engine of this.engines.values()) {
if (engine.isActive(dbSessionId)) {
return engine.interrupt(dbSessionId);
}
}
return false;
}
Реестр типов движков
| EngineType | Класс | Backend Service | Отслеживание активных сессий |
|---|---|---|---|
tinyelf | TinyElfEngine | Встроенный цикл Agent | Map<string, ActiveTinyElfSession> |
claude-sdk | ClaudeSdkEngine | AgentService (SDK) | Внутреннее управление AgentService |
cli | CliRunnerEngine | ProcessSupervisor | Map<string, ActiveCliSession> |
chat | ChatEngine | ChatCompletionPipeline | Внутренний Map |
st-chat | STChatEngine | CompletionService | Внутренний Map |
api | ApiEngine | CompletionService | Без состояния |
Отслеживание активных сессий
Движок TinyElf использует ActiveTinyElfSession для отслеживания состояния каждой активной сессии:
interface ActiveTinyElfSession {
dbSessionId: string;
projectPath: string;
abortController: AbortController;
parentMessageId?: string;
lastAssistantMessageId?: string;
directMcpConnections?: Array<...>;
hookExecutor?: HookExecutor;
sdkDualWrite?: {
sdkSessionId: string;
lastUuid: string;
seqNum: number;
};
subagentManager?: SubagentManager;
}
IPC Events
Движки отправляют события в renderer process через sender.send('agent:event', ...):
| Тип события | Payload | Описание |
|---|---|---|
init | { sessionId, systemInfo } | Инициализация сессии |
sessionCreated | { sessionId } | Создание сессии завершено |
userMessage | { sessionId, message } | Пользовательское сообщение сохранено |
streamDelta | { sessionId, delta } | Потоковая дельта текста/мышления |
toolCallStart | { sessionId, block } | Tool call запущен |
toolCallEnd | { sessionId, block, status } | Tool call завершён |
assistantMessage | { sessionId, message } | Сообщение Assistant сохранено |
permissionRequest | { sessionId, requestId, toolName, input } | Запрос подтверждения разрешения |
result | { sessionId, stats } | Результат статистики выполнения |
complete | { sessionId } | Сессия завершена |
error | { sessionId, error } | Событие ошибки |
retry | { sessionId, retry } | Уведомление о повторе API |
processing | { sessionId, message } | Состояние обработки |
Ключевые файлы
| Файл | Путь | Описание |
|---|---|---|
| Интерфейс IEngine | services/agent-core/engine/types.ts | Интерфейс движка и определения контекста |
| EngineDispatcher | services/agent-core/engine/EngineDispatcher.ts | Регистрация и диспетчеризация движков |
| Точка входа модуля | services/agent-core/engine/index.ts | Экспорт всех движков |
| TinyElfEngine | services/agent-core/engine/tinyelf/TinyElfEngine.ts | Реализация движка TinyElf |
| ClaudeSdkEngine | services/agent-core/engine/ClaudeSdkEngine.ts | Движок Claude SDK |
| CliRunnerEngine | services/agent-core/engine/cli/CliRunnerEngine.ts | Движок CLI |
| ChatEngine | services/agent-core/engine/ChatEngine.ts | Движок Chat |
| STChatEngine | services/agent-core/engine/STChatEngine.ts | Движок Single-turn chat |
| ApiEngine | services/agent-core/engine/ApiEngine.ts | Движок API |
| Определение EngineType | @shared/contracts/elftia-agent-types.ts | Определения типов |
Все пути указаны относительно packages/desktop/app/main/.
Точки расширения
- Добавить новый движок: реализовать интерфейс
IEngine+ зарегистрировать вEngineDispatcher+ добавитьEngineType - Добавить новые IPC events: отправлять через
sender.send()в callbacks движка - Горячая перезагрузка MCP: реализовать метод
reloadMcpForAllActive()
Связанные модули
| Модуль | Путь | Связь |
|---|---|---|
| AgentRouter | services/routers/AgentRouter.ts | Получает IPC calls и диспетчеризует их |
| MagiService | services/agent-core/magi/MagiService.ts | Высокоуровневая оркестрация, выбирает движок |
| CompletionService | services/capabilities/llm/completion/CompletionService.ts | Вызовы LLM API |
| AgentService | services/agent-core/agent/AgentService.ts | Управление сессиями Claude SDK |