How AionUi's WorkerManage Spawns and Manages AI Agent Worker Processes (Gemini, Codex, ACP)

AionUi's WorkerManage singleton spawns isolated child processes for each AI agent (Gemini, Codex, ACP) via Electron's utilityProcess.fork, caching active workers in a taskList array and orchestrating their lifecycle through type-specific AgentManager classes that communicate via a promise-based IPC protocol.

AionUi implements a robust multi-process architecture to isolate AI model execution from the main Electron thread. The WorkerManage class located in src/process/WorkerManage.ts serves as the central orchestrator for spawning and managing separate worker processes. This design ensures that native WASM binaries and long-running LLM operations do not block the UI while maintaining clean inter-process communication channels.

The WorkerManage Singleton Architecture

The WorkerManage singleton acts as the primary registry for all active AI agent processes. It maintains an in-memory cache of running tasks and exposes methods for creating, retrieving, and terminating worker instances.

Creating Typed Agent Managers via buildConversation

The entry point for spawning workers is the buildConversation() method. This function receives a TChatConversation record and instantiates the appropriate manager based on conversation.type.

When conversation.type equals 'gemini', 'codex', or 'acp', WorkerManage constructs GeminiAgentManager, CodexAgentManager, or AcpAgentManager respectively. Each manager extends BaseAgentManager and embeds a ForkTask instance that launches the actual child process.

Source: src/process/WorkerManage.ts (lines 47-99)

Caching and Task Lookup

All alive managers are stored in the private taskList array. The cache can be bypassed using the skipCache option when rebuilding conversations.

To retrieve a running manager, use getTaskById() for synchronous lookup or getTaskByIdRollbackBuild() for asynchronous fallback. The latter rebuilds the manager from the SQLite database or local chat-history file when the cache is empty, ensuring conversation persistence across app restarts.

Source: src/process/WorkerManage.ts (lines 28-60)

Termination and Cleanup

The kill(id) method locates the manager by conversation ID, invokes its task.kill() method (which delegates to the underlying ForkTask), and removes the entry from taskList. For bulk cleanup during application shutdown, clear() iterates over all cached tasks, invokes kill() on each, and empties the array.

Source: src/process/WorkerManage.ts (lines 62-77)

ForkTask - The Process Spawning Engine

The ForkTask class in src/worker/fork/ForkTask.ts handles the low-level mechanics of spawning Node.js child processes using Electron's utilityProcess API.

Working Directory Resolution for Packaged Apps

In packaged Electron applications, workers must execute from the app.asar.unpacked directory to access native WASM binaries required by aioncli-core. The getWorkerCwd() method determines the correct working directory dynamically, ensuring binary portability across development and production environments.

Electron utilityProcess and Message Protocol

The core spawning logic uses utilityProcess.fork(this.path, [], { cwd }) to launch worker scripts such as src/worker/gemini.ts. Parent and child processes communicate through a lightweight protocol featuring type, data, and optional pipeId fields.

The postMessagePromise() method implements RPC-style communication: it sends a request to the child process and returns a Promise that resolves or rejects upon receiving the corresponding response. This enables clean asynchronous operations for commands like start, confirm, and custom tool executions.

Graceful Shutdown

The kill() method forwards the termination signal to fcp.kill(), deregisters the process.on('exit') listener, and ensures no orphaned processes remain when closing conversations or quitting the application.

Agent-Specific Manager Implementations

Each AI provider implements a specialized manager extending BaseAgentManager, which internally creates a ForkTask pointing to its dedicated worker script.

GeminiAgentManager

Located in src/process/task/GeminiAgentManager.ts, this manager handles the Google Gemini SDK integration. It builds system prompts, manages tool-confirmation UI flows, supports auto-approval modes (yolo, autoEdit), and streams responses back to the renderer process.

CodexAgentManager

Found in src/process/task/CodexAgentManager.ts, this manager wraps the OpenAI SDK and mirrors Gemini's lifecycle architecture. It handles message processing, cron-command detection, and auto-approval workflows for the Codex model.

AcpAgentManager

The AcpAgentManager in src/process/task/AcpAgentManager.ts implements the Model-Context-Protocol (MCP) bridge. It manages set_mode commands, tool call executions, and session persistence for the ACP agent type.

Complete Lifecycle Example

The following TypeScript example demonstrates the full lifecycle of creating, using, and terminating a Gemini worker:

import WorkerManage from '@/process/WorkerManage';

// 1️⃣ Build (or reuse) a Gemini conversation
const geminiTask = WorkerManage.buildConversation({
  id: 'conv-123',
  type: 'gemini',
  extra: {
    workspace: '/Users/me/projects/foo',
    presetRules: 'You are a helpful assistant.',
    enabledSkills: ['cron'],
    sessionMode: 'default',
  },
  model: { provider: 'gemini', model: 'gemini-1.5-pro' }
});

// 2️⃣ Send a user message to the worker
await geminiTask.sendMessage({
  input: 'Create a React component that shows a countdown timer.',
  msg_id: 'msg-001'
});

// 3️⃣ List all active workers (useful for debugging UI)
console.log(WorkerManage.listTasks());
// → [{ id: 'conv-123', type: 'gemini' }, …]

// 4️⃣ Terminate a specific worker
WorkerManage.kill('conv-123');

// 5️⃣ Flush everything (e.g., on app shutdown)
WorkerManage.clear();

Summary

  • WorkerManage acts as a singleton registry in src/process/WorkerManage.ts, caching active agent managers in the taskList array and providing methods like buildConversation() and kill().
  • Agent managers (GeminiAgentManager, CodexAgentManager, AcpAgentManager) extend BaseAgentManager and embed a ForkTask to isolate each AI provider in its own process.
  • ForkTask in src/worker/fork/ForkTask.ts spawns child processes using utilityProcess.fork() with proper working directory handling via getWorkerCwd() for packaged apps.
  • RPC communication occurs via postMessagePromise(), implementing a promise-based message protocol with type, data, and pipeId fields for bi-directional IPC.
  • Lifecycle management includes getTaskById() for retrieval, getTaskByIdRollbackBuild() for persistent state recovery, kill() for individual termination, and clear() for bulk cleanup during shutdown.

Frequently Asked Questions

How does AionUi ensure worker processes can access native binaries in production?

AionUi uses the getWorkerCwd() method within ForkTask to detect packaged environments and set the working directory to app.asar.unpacked. This ensures that native WASM binaries used by aioncli-core remain accessible to forked worker processes, as these binaries cannot be executed from within the asar archive.

What happens if a conversation is reopened after the app restarts?

The getTaskByIdRollbackBuild() method in WorkerManage handles cache misses by asynchronously rebuilding the agent manager from the SQLite database or local chat-history file. This ensures that conversation state persists even when the taskList cache is cleared during application restarts.

Can multiple AI agents run simultaneously in AionUi?

Yes. WorkerManage maintains all active agents in the taskList array, allowing concurrent execution of Gemini, Codex, and ACP workers. Each operates in its own isolated process via separate ForkTask instances, preventing interference between different AI model executions.

How does the IPC protocol handle asynchronous responses from workers?

The postMessagePromise() method in ForkTask implements a request-response correlation using unique message identifiers. When the parent sends a command like start or confirm, it returns a Promise that resolves upon receiving the corresponding response from the child process, enabling clean async/await patterns across process boundaries.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →