Message Flow
User types a message in ChatScreen, which triggers a workflow-specific provider chain. The RouteEntry chain is iterated in order, with callProvider() sending the request. On failure, the system tries the next entry in the chain. Responses are streamed to the chat UI in real-time. For multi-step execution, the agent loop handles sequential processing.
Manual Routing Pipeline
Per-workflow RouteEntry chains are stored in localStorage for built-in workflows and JSON files for custom workflows.
resolveProvider() checks each entry in order:
- Provider has a key or is keyless
- Provider is not disabled
- Model capabilities include all required
workflowTypevalues
Returns the first matching entry or null if nothing is usable.
Smart Routing Pipeline (opt-in)
Replaces the manual chain when smartRouting is enabled for a workflow. scoreProvider() calculates a 0–1 score:
score = successRate(last 20 calls) × 0.7
+ speedScore(avgLatencyMs / 30000, capped) × 0.3
− healthPenalty(from healthCheck)
Three modes:
- best-first — picks highest-scored provider, falls back serially on failure
- serial — tries in scored order, stops on first success
- parallel — fires all capable providers simultaneously (optional
maxParallelcap)
Image Generation Path
callImageProvider.ts handles image workflows separately, routing to Pollinations (free, no key required) or DALL·E based on available keys. Responses are rendered inline in the chat UI and can be downloaded.
File Attachment
fileHandler.ts processes attached files:
- Reads file content (text, source code, etc.)
- Injects content silently into every subsequent message until detached
- Auto-saves AI-generated code responses to a
.tmpfile in the working directory
Agent Loop
lib/agentLoop.ts implements a tool-use agent using the OpenAI tool_calls API. The AI receives a merged set of tool definitions and can request tool executions in its response. The loop executes the requested tools and feeds results back until the model produces a plain-text response with no tool calls. Maximum 20 iterations per run; 10-minute timeout per API call.
Native Tools (9)
| Tool | Description |
|---|---|
read_file(path) | Reads a file from the filesystem via window.api |
write_file(path, content) | Writes or overwrites a file with full content |
rename_file(old_path, new_path) | Moves/renames a file; creates destination parent dirs automatically |
delete_file(path) | Permanently deletes a file |
list_directory(path) | Returns a recursive directory tree as formatted text |
create_directory(path) | Creates a directory and any missing parents (mkdir -p) |
search_web(query) | Searches the web via Jina Search (s.jina.ai/?q=) — no API key required |
fetch_url(url) | Fetches a URL as readable text via Jina Reader (r.jina.ai/<url>) |
read_image(path) | Loads a local image (jpeg/png/gif/webp) and injects it as a vision message |
Dynamic MCP Tools
At the start of every agent run, window.api.listTools() is called to discover all tools from connected MCP servers. These are merged with the 9 native tools and sent to the model. MCP tools are prefixed mcp__<server>__<tool>. See MCP Tools blueprint for details.
Vision Injection
read_image returns a sentinel string __IMG__:<dataUri>. The loop handles this specially:
- Pushes a text tool result (
"Image loaded.") to satisfy the API's tool result requirement - Pushes a separate user message with a
image_urlcontent block containing the base64 data URI
This works around the OpenAI API constraint that tool result messages cannot contain image content.
File-Based Logging (src/main/ipc/fileIpc.ts)
Conversation and tool activity are written to plain-text log files in the working directory. SQLite (userData/manyai.db) is reserved for configuration only — no logging tables.
Log Layout
{workingDir}/logs/{workflowType}/YYYY-MM-DD.log
When a file exceeds 512 KB, subsequent writes go to YYYY-MM-DD-2.log, then YYYY-MM-DD-3.log, etc. A new rotation file is only created when the current one also exceeds 512 KB — appending always targets the latest file with remaining space.
IPC Handler
| Channel | Description |
|---|---|
log-message | Appends one entry to the appropriate log file. Args: workingDir, type, role, content, meta? |
Entry Format
Each entry uses a friendly section header:
**************************************** User Posted Content [2026-05-03 14:22:11] **************************************** [user text] **************************************** Answer from Provider — gemini/gemini-2.5-flash-lite (1842ms) [2026-05-03 14:22:13] **************************************** [assistant response]
Logging Points
- ChatScreen: user message logged once (raw text) before the parallel provider call; each assistant response logged inside the provider loop with provider/model/latency metadata.
- Agent loop: each tool call result logged to
logs/programming/with tool name and truncated args.