起動フェーズ
Elftia のメインプロセス起動フローは 13 のフェーズに分かれており、それぞれの責務と依存関係が明確に定義されています。すべてのフェーズのコードは、packages/desktop/app/main/index.ts 内の createServices() 関数にあります。
起動フェーズの概要
flowchart TB
P0[Phase 0: Protocol Registration + Logger]
P1[Phase 1: Core Services]
P2[Phase 2: Feature Services]
P25[Phase 2.5: Package Loading]
P26[Phase 2.6: Package Sync]
P27[Phase 2.7: Package Management Service]
PS1[Phase S1: Magi Dispatch]
PS2[Phase S2: MagiService]
PS25[Phase S2.5: CronService]
PC1[Phase C1: Channel Plugins]
PS3[Phase S3: ChannelMagiBridge]
P3[Phase 3: IPC Route Registration]
PT[Phase T1-T3: Todo/Task/Notes]
PW1[Phase W1: Window Creation]
PW2[Phase W2: WebSocket/Protocol]
P0 --> P1 --> P2 --> P25 --> P26 --> P27
P27 --> PS1 --> PS2 --> PS25
PS25 --> PC1 --> PS3
PS3 --> P3 --> PT --> PW1 --> PW2
Phase 0: プロトコル登録 + Logger
app.whenReady() の前に実行されます。カスタムプロトコルとコアロギングサービスを登録します。
flowchart LR
A[Register elftia:// protocol] --> B[Register privileged schemes]
B --> C[wallpaper://]
B --> D[media://]
B --> E[resource://]
| ステップ | アクション | 説明 |
|---|---|---|
| 1 | app.setAsDefaultProtocolClient('elftia') | Deep Link プロトコルを登録 |
| 2 | protocol.registerSchemesAsPrivileged() | wallpaper/media/resource の privileged protocol を登録 |
| 3 | new LoggerService() | ロギングサービスを作成(Winston + daily-rotate) |
| 4 | setIpcLogger(logger) | IPC レイヤーの logger を設定 |
| 5 | new ErrorHandler(logger) | グローバルエラーハンドラーを作成 |
| 6 | setIpcErrorHandler(errorHandler) | IPC レイヤーのエラーハンドラーを設定 |
| 7 | new AutoUpdateService(logger) | 自動更新サービスを作成 |
| 8 | new DownloadService(logger) | ダウンロードサービスを作成 |
| 9 | new WebUpdateService(logger, downloadManager) | Web 更新サービスを作成 |
主要ファイル: packages/desktop/app/main/services/infra/logger/LoggerService.ts, packages/desktop/app/main/services/infra/error/ErrorHandler.ts
Phase 1: コアサービス
アプリケーションの実行に必要な基盤サービスを作成します。これらのサービスには外部依存関係がありません。
flowchart LR
RT[RuntimeService] --> AP[AppPaths]
AP --> DB[DbClient + ready]
DB --> SS[SettingsService]
SS --> CS[ConfigStore]
CS --> Migrate[Config Migration]
Migrate --> Auth[Auth Service Chain]
Auth --> WC[WindowControlsService]
| ステップ | サービス | 依存関係 | 説明 |
|---|---|---|---|
| 1 | RuntimeService | なし | ランタイム情報(バージョン、プラットフォーム、チャネル) |
| 2 | AppPaths | なし | パス解決 |
| 3 | DbClient | AppPaths | Database Worker の作成 |
| 4 | await db.ready() | DbClient | Worker の初期化完了まで ブロッキング待機 |
| 5 | initDatabase() | db.ready | Drizzle ORM + WAL + migrate |
| 6 | SettingsService | AppPaths | アプリケーション設定 |
| 7 | ConfigStore | AppPaths | electron-store の config ファイル |
| 8 | migrateSettingsToConfigStore() | Settings, ConfigStore | SQLite -> config.json の移行 |
| 9 | settings.setConfigStore(configStore) | - | Delegate パターンの接続 |
| 10 | configStore.startWatching() | - | fs.watch による config のホットリロード |
| 11 | AccountService | AppPaths | ユーザー認証情報 |
| 12 | AuthTokenStore | AccountService | Token ストレージ |
| 13 | AuthSessionService | Runtime, AuthStore, AuthApi | Auth セッション |
| 14 | AppPreferencesService | ConfigStore | アプリケーション設定値 |
| 15 | WindowControlsService | Settings | ウィンドウ制御 |
重要ポイント: await db.ready() は、処理を進める前に SQLite Worker がスキーマ作成を完了したことを保証する ブロッキングポイント です。これにより、初回起動時に 2 つの接続が同時に書き込むことで発生する SQLITE_BUSY エラーを防ぎます。
Phase 2: 機能サービス
軽量な機能サービスを作成します。重い初期化は使用時まで遅延されます。
| サービス | 依存関係 | 説明 |
|---|---|---|
ConfigManager | Logger, AppPaths | Config ファイル管理 |
SecurityService | Logger | AES-256-GCM 暗号化 |
PerformanceMonitor | Logger | パフォーマンス監視 |
CacheService | Logger | 汎用キャッシュ |
DatabaseOptimizer | Logger, DbClient | SQLite 最適化 |
NotificationService | Logger | デスクトップ通知 |
PluginManager | Logger | Plugin ライフサイクル |
ProjectService | DbClient, AccountService | プロジェクト管理 |
DiagnosticsService | AppPaths, ProjectService | 診断サービス |
ChatService | Logger | チャットサービス |
McpService | Logger | MCP server 管理 |
LLMConfigService | AppPaths, DbClient | LLM 設定 |
CompletionService | AppPaths, LLMConfigService | LLM completion |
ApiKeyPoolService | DB, CompletionService | 複数キーの round-robin |
ElfiService | ConfigStore, LLMConfig, Completion | Elfi assistant |
MediaConfigService | AppPaths, DbClient | メディア設定 |
ImageGenerationService | MediaConfig | 画像生成 |
ThemeService | Settings, AuthSession | テーマ管理 |
AgentService | DbClient, Settings, Logger | Agent サービス |
TransformerService | - | リクエスト変換 |
WebSearchService | - | Web 検索 |
EngineDispatcher + engine registration | 各 engine の依存関係 | Engine dispatch |
Engine 登録順序
{/* Engine registration */}
engineDispatcher.registerEngine(new ApiEngine(...));
engineDispatcher.registerEngine(new ChatEngine(...));
engineDispatcher.registerEngine(new ClaudeSdkEngine(...));
engineDispatcher.registerEngine(new CliRunnerEngine(...));
engineDispatcher.registerEngine(new TinyElfEngine(...));
engineDispatcher.registerEngine(new STChatEngine(...));
Phase 2.5: パッケージ読み込み
Agent パッケージ、Persona パッケージ、Script plugin をディスクからメモリに読み込みます。
flowchart TB
subgraph "Phase 2.5a: Agent Packages"
AL[AgentPackageLoader] --> BP[Load builtin packages]
BP --> UP[Load user packages]
UP --> AR[AgentPackageRegistry]
end
subgraph "Phase 2.5b: Persona Packages"
PL[PersonaPackageLoader] --> BPP[Load builtin packages]
BPP --> UPP[Load user packages]
UPP --> PR[PersonaPackageRegistry]
end
subgraph "Phase 2.5c: Script Plugins"
SL[ScriptPluginLoader] --> BSP[Load builtin plugins]
BSP --> USP[Load user plugins]
USP --> SR[ScriptPluginRegistry]
end
読み込みパス(二重ディレクトリパターン):
| パッケージ種別 | Builtin Path(packaged) | Builtin Path(dev) | User Path |
|---|---|---|---|
| Agent | resources/agent-packages/ | ../../agent-packages/ | {userData}/agent-packages/ |
| Persona | resources/persona-packages/ | ../../persona-packages/ | {userData}/persona-packages/ |
| Script | resources/script-plugins/ | ../../script-plugins/ | {userData}/script-plugins/ |
Phase 2.6-2.7: パッケージ同期と管理
| フェーズ | アクション | 説明 |
|---|---|---|
| 2.6 | PackageSyncService.syncAll() | メモリ内のパッケージ定義をデータベースへ同期(FK anchor の upsert + persona データ) |
| 2.7 | new AgentPackageService(...) | install/uninstall をサポートするパッケージ管理サービスを作成 |
Phase S1: Magi Dispatch
Agent のオーケストレーションと dispatch に関連するサービスを作成します。
flowchart TB
AGM[AgentManagementService] --> EB[SessionEventBus]
EB --> AD[AgentDiscovery]
AD --> AO[AgentOrchestrator]
AO --> MR[MessageRouter]
MR --> SDI[SessionDispatcherImpl]
AO -.->|setEngineDispatcher| ED[EngineDispatcher]
AO -.->|setDbProvider| DB[DbClient]
SDI -.->|injected into| TE[TinyElfEngine]
| ステップ | サービス | 説明 |
|---|---|---|
| 1 | AgentManagementService | Agent CRUD + package registry クエリ |
| 2 | InternalSessionEventBus | Session event の publish/subscribe |
| 3 | AgentDiscovery | スケジュール可能な Agent roster(DB + package registry から検出) |
| 4 | AgentOrchestrator | Agent dispatch とオーケストレーション、API dispatch handler |
| 5 | MessageRouter | デュアルモードの message routing(direct / Orchestrator 経由) |
| 6 | SessionDispatcherImpl | セッション横断 dispatch の実装 |
| 7 | TinyElfEngine へ注入 | tinyElfEngine.setSessionDispatcher(sessionDispatcher) |
| 8 | SubagentRegistry.setDb(db) | Subagent registry の write-through persistence |
履歴メモ:
DispatchServerHTTP dispatch server はcleanup-legacy-mcp(2026-05-19)で削除されました。dispatchtool は現在、統合 registry を通じてMagiDispatchProvider+MagiDispatchHandlersによりプロセス内で構成されます。handlers は HTTP を経由せず、AgentOrchestrator/AgentDiscoveryを直接呼び出します。CLI subprocess は、中央のservices/capabilities/tools/mcp-builtin/BuiltinMcpHttpServer.tsを通じてこれにアクセスします。
Phase S2: MagiService
コアのメッセージ処理サービスを作成し、すべての依存関係を注入します。
{/* MagiService dependency injection chain */}
const magiService = new MagiService(
magiWorkspace, magiSession, magiOptionsBuilder,
magiOrchestrator, magiMessageRouter, magiEventBus,
llmConfig, logger, configStore,
);
// Deferred injection (avoids circular dependencies)
magiService.setAgentService(agent);
magiService.setCompletionService(completion);
magiService.setAsrService(new AsrService(asrConfig));
magiService.setEngineDispatcher(engineDispatcher);
magiService.setDbProvider(db);
magiService.setMcpListProvider(() => mcp.list());
magiService.setDeveloperModeProvider(() => ...);
MCP ホットリロード: MCP server list が変更されると、Magi workspace に自動で同期され、active session がホットリロードされます。
mcp.on('servers:changed', () => {
magiService.syncMcpServersToWorkspace();
magiService.notifyMcpChanged();
});
Phase S2.5: CronService
Cron scheduled task サービスを作成します:
| 注入 | 目的 |
|---|---|
setNotificationService(notification) | デスクトップ通知 |
setNotificationChecker(...) | 通知設定を確認 |
setAgentExecutor(...) | MagiService 経由で scheduled task を実行 |
setStatusBroadcaster(...) | タスク状態をフロントエンドへブロードキャスト |
Phase C1: Channel Plugins
Channel plugin を検出、読み込み、登録します。
flowchart TB
CL[ChannelPluginLoader] --> Discover[discover plugins]
Discover --> Load[load each plugin]
Load --> CR[ChannelPluginRegistry.registerPlugin]
CR --> CM[ChannelMarketplaceService<br/>async fetch remote manifest]
subgraph "Security Service Wiring"
UPS[UserPermissionService] --> CMR[ChannelMessageRouter]
CPG[ChannelPermissionGate] --> CMR
AL[AuditLogger] --> TE[TinyElfEngine]
RL[RateLimiter] --> CMR
IS[InputSanitizer] --> CMR
PG[PromptGuardian] --> CMR
end
CR --> CMR
読み込みパス(二重ディレクトリパターン):
| ソース | Path(packaged) | Path(dev) |
|---|---|---|
| Bundled(read-only) | resources/bundled-channel-plugins/ | elftia-channels/dist-plugins/ |
| Marketplace(writable) | {userData}/channel-plugins/ | same |
Security service wiring order:
UserPermissionService-> ChannelMessageRouter(ユーザー権限チェック)ChannelPermissionGate-> ChannelMessageRouter + TinyElfEngine(権限確認)AuditLogger-> TinyElfEngine(監査ログ)RateLimiter-> ChannelMessageRouter(レート制限)InputSanitizer-> ChannelMessageRouter(入力サニタイズ)PromptGuardian-> ChannelMessageRouter(prompt injection 検出)
Phase S3: ChannelMagiBridge
Channel event を MagiService に橋渡しします:
const channelMagiBridge = new ChannelMagiBridge(
channelRegistry,
channelMessageRouter,
magiService,
logger,
);
履歴メモ:
ChannelServerHTTP server はcleanup-legacy-mcp(2026-05-19)で削除されました。channel_send/channel_listなどの tool は現在、統合 registry を通じてMagiChannelProvider+MagiChannelHandlersによりプロセス内で構成されます。handlers はChannelPluginRegistry/ChannelMessageRouterを直接呼び出します。
Phase 3: IPC ルート登録
すべての IPC handler は、ウィンドウ作成の 前に 登録されます。
const router = new Router(/* 60+ service dependencies */);
const { skillHubDownloadService } = router.register();
registerAllRouters() は内部で 68 個以上の Router instance を作成して登録します:
| Router Class | Channel Prefix |
|---|---|
AuthRouter | auth:* |
ProjectRouter | projects:* |
SessionRouter | sessions:* |
CompletionRouter | completion:* |
LLMRouter | llmProviders:*, llmModels:* |
MediaRouter | media:* |
MagiRouter | magi:* |
ChannelPluginRouter | channels:* |
SecurityRouter | security:* |
TodoRouter | todo:* |
TaskRouter | tasks:* |
NoteRouter | notes:* |
SubagentRouter | subagents:* |
ElfiRouter | elfi:* |
EnvironmentRouter | env:* |
| ... and 50+ other routers | ... |
Phase T1-T3: Todo/Task/Notes
flowchart LR
T1[Phase T1: TodoService] --> T1a[initialize — load TODO.md]
T2[Phase T2: TaskService] --> T2a[initialize — scan tasks]
T3[Phase T3: NoteFileService + NoteWatcher] --> T3a[ensureVaultDir]
T3a --> T3b[scanAllNotes + reindex]
T3b --> T3c[DB notes migration check]
| フェーズ | サービス | アクション |
|---|---|---|
| T1 | TodoService | Markdown to-do system を初期化 |
| T2 | TaskService | filesystem + SQLite task management を初期化 |
| T3 | NoteFileService | vault directory の存在を保証 |
| T3 | NoteWatcher | Filesystem watcher |
| T3 | Notes migration | DB から .md files への一回限りの移行 |
| T3 | Startup reindex | すべての notes をスキャンして DB index を更新 |
Phase W1: ウィンドウ作成
すべての service と IPC route が登録された後に BrowserWindow を作成します:
mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
preload: preloadPath,
},
});
Phase W2: WebSocket/Protocol 処理
ウィンドウ作成後の初期化:
| ステップ | アクション |
|---|---|
| 1 | wallpaper:// protocol handler を登録 |
| 2 | media:// protocol handler を登録 |
| 3 | resource:// protocol handler を登録 |
| 4 | renderer URL(dev server または file://)を読み込み |
| 5 | 保留中の Deep Link を処理 |
| 6 | Channel instance を復元(autoConnect) |
| 7 | Content Security Policy 設定 |
| 8 | Navigation interception(isAllowedNavigation) |
起動タイミング
createServices() は主要フェーズのタイミングを記録します:
[INFO] Core services initialized { duration: ~50ms }
[INFO] Feature services created { duration: ~200ms }
[INFO] Agent packages loaded { builtin: N, user: M }
[INFO] Persona packages loaded { builtin: N, user: M }
[INFO] Script plugins loaded { builtin: N, user: M }
[INFO] Magi dispatch services created (Phase S1)
[INFO] MagiService created (Phase S2)
[INFO] ChannelMagiBridge created (Phase S3)
[INFO] IPC handlers registered { duration: ~500ms }
関連ファイル
| ファイル | 説明 |
|---|---|
packages/desktop/app/main/index.ts | メインプロセス entry、createServices() 関数 |
packages/desktop/app/main/services/routers/index.ts | registerAllRouters() route 登録 |
packages/desktop/app/main/db/index.ts | initDatabase() database 初期化 |
packages/desktop/app/main/workers/DbClient.ts | Database Worker client |
packages/desktop/app/main/services/infra/logger/LoggerService.ts | Logging service |
packages/desktop/app/main/services/infra/paths/paths.ts | AppPaths path 管理 |
packages/desktop/app/main/services/platform/config/store/ConfigStore.ts | Config store + fs.watch |
packages/desktop/app/main/services/agent-core/agent/AgentPackageLoader.ts | Agent package loader |
packages/desktop/app/main/services/agent-core/agent/PersonaPackageLoader.ts | Persona package loader |
packages/desktop/app/main/services/capabilities/tools/script-plugin/ScriptPluginLoader.ts | Script plugin loader |
packages/desktop/app/main/services/capabilities/integrations/channel/ChannelPluginLoader.ts | Channel plugin loader |
packages/desktop/app/main/services/agent-core/magi/MagiService.ts | Magi core service |
packages/desktop/app/main/services/agent-core/magi/AgentOrchestrator.ts | Agent dispatch orchestrator |
packages/desktop/app/main/services/platform/cron/CronService.ts | Scheduled task service |
packages/desktop/app/main/services/content/workspace/todo/TodoService.ts | To-do service |
packages/desktop/app/main/services/content/workspace/tasks/TaskService.ts | Task management service |
packages/desktop/app/main/services/content/workspace/notes/NoteFileService.ts | Note file service |