Execution Engine
Each request runs the main loop in exec.Execute() for up to 128 iterations. Each iteration:
- Assemble messages:
SystemPrompts+OldHistories+UserInput+ToolHistories - Call
Agent.Send()on the chosen provider - Parse
tool_callsfrom the response - Dispatch the tool calls through
toolCall.go(three-pass concurrency, see below) - Append results to
ToolHistories - Stop when no
tool_callsremain or the iteration limit is hit
There is no inter-round delay — rate-limit protection comes from provider round-trip latency, the same-error circuit breaker, and per-tool internal limiters (e.g., search_web 2 s gap, api_* per-name 1 s gap).
Three-pass tool concurrency
toolCall.go splits each round's tool calls into three serial passes; only Pass 2 fans out:
| Pass | Mode | Work |
|---|---|---|
| 1 — pre-flight | Serial | Cache hit check (skipped for read_file), stub-tool short-circuit, confirm gate, JSON-schema validation |
| 2 — execute | Concurrent for IsConcurrent-tagged tools; serial otherwise |
tools.Execute |
| 3 — commit | Serial | Land sessionData.Tools and ToolHistories, update cache, emit EventToolResult, handle review tools |
Concurrent-tagged tools: read_file, list_files, glob_files, search_files, fetch_page, search_google_news, send_http_request, download_file, transcribe_media, calculate, invoke_subagent, search_chat_history, search_error_history, read_error, read_log, list_rag, search_rag, format_chatbot, list_chatbot, list_tools, list_schedule. search_web, write-class tools, api_*, and MCP tools always run serially.
Pending registry
internal/runtime/pending.go is the prefix-routed confirm/ask listener registry shared by the main agent and any in-process subagents. Producers (toolCall confirm, ask_user handler, store_secret handler) call Ask(ctx, req) and block on a per-entry buffered=1 reply channel; each runtime registers a listener via pending.RegisterListener(prefix) (TUI/CLI use "" to match all, the Telegram daemon listener uses "tg-") and only claims matching entries through PickNextFor(prefix). ctx cancellation removes the entry so a stale producer never wastes a human interaction.
The gate pending.HasListener(sessionID) checks whether a listener with a matching prefix is registered for that session. This replaces the old global pending.Active atomic.Bool so Telegram, Discord, and CLI confirm flows can run side by side without blocking each other.
Circuit breaker
When Agent.Send() returns the same error signature three times in a row (e.g., HTTP 429 with identical request payload), the loop aborts to prevent infinite retry storms. Distinct error signatures reset the counter.
Cross-turn workdir reset
Each new user message rebuilds the Executor and resets data.WorkDir to the process cwd via os.Getwd() — cd-mutated workdir does not persist across turns. Two guardrails prevent the LLM from inferring stale workdir from history:
- L1 (system prompt) —
Work directory: {{.WorkPath}}line plus an explicit reminder that priorcdtext is from older turns - L2 (per-message) — Every user message is wrapped with a metadata header containing the current timestamp and working directory; the workDir line is the strongest anchor and overrides any history recency bias
The TUI strips the wrapper visually via stripUserMetaHeader; the LLM still receives it verbatim.
[!NOTE] This document was auto-generated by Claude after reading the full source code.