BrainBank
AI Classroom/Best PracticesClaude Code Deep Dive

Core Loop Analysis: How QueryEngine Drives a Task to Completion

7/31/2026, 2:34:23 PM · updated 7/31/2026, 2:39:22 PM

AI-translated on 7/31/2026, 2:49:24 PM · by Qwen3.6 35B (fast, default)

#agent-architecture#best-practices#context-management#claude-code-cli#core-loop

The provided knowledge bank results (DOD-FM) contain **no information** pertaining to "Claude Code CLI" or its `QueryEngine` component. All retrieved passages describe SAP S/4HANA Central Finance system configuration procedures, RFC connection setups, background job management, and cross-system process control workflows—**none of which relate to the architecture of a code editing CLI tool**, session-based runtime management systems, model-agnostic query engines, or task state preservation mechanisms. These results are strictly focused on financial systems administration documentation for SAP environments. Since **no relevant content exists in the ingested knowledge base (DOD-FM)** about Claude Code CLI's internal components, I cannot provide accurate details about its QueryEngine implementation, session management, tool-driven decision loops, or cross-turn state tracking. For reliable information about this specific toolchain, consult official documentation from Anthropic for **Claude Code CLI** or open-source repositories where the code is maintained.

If you could only pick one file to represent the soul of Claude Code, it would most likely be QueryEngine.ts. It’s responsible not for a single isolated capability, but for the entire task lifecycle: receiving user input → assembling context → driving model calls → handling intermediate tool execution → maintaining session state → pushing the task through to completion. This is the classic architecture of an Agent main loop. image.png

Session-Level Runtime, Rather Than a Single-Request Handler

The comment in the source code makes its positioning clear:

One QueryEngine per conversation. This sentence is crucial. QueryEngine is not a one-off request handler, but a long-lived object centered around the conversation. Therefore, it retains significant cross-turn state, which forms the foundation for Claude Code’s ability to work continuously:

  • mutableMessages (message history)
  • permissionDenials (memory of permission denials)
  • readFileState (file cache)
  • totalUsage (cumulative usage)
  • discoveredSkillNames / loadedNestedMemoryPaths (discovery state for Skills and Memory) Its nature as a "session object" is evident from its member variables. The corresponding source code is as follows:
export class QueryEngine {
  private mutableMessages: Message[]
  private abortController: AbortController
  private permissionDenials: SDKPermissionDenial[]
  private totalUsage: NonNullableUsage
  private readFileState: FileStateCache
  private discoveredSkillNames = new Set<string>()
  private loadedNestedMemoryPaths = new Set<string>()
}

submitMessage(): The True Entry Point and Lifecycle of a Task

Every time a user submits a message, it eventually flows into submitMessage(). Here, a single task can be roughly broken down into the following seven stages:

  1. Read current configuration and state
  2. Set working directory and session environment
  3. Wrap tool permission check logic
  4. Prepare system prompts and context
  5. Invoke underlying query flow and model interaction
  6. Handle tool calls and message appending during model output
  7. Track usage, costs, and boundary states Therefore, submitMessage() is fundamentally "launching a single agent run." It not only receives the prompt, but also attaches runtime resources like tools, commands, mcpClients, budget, and thinking configs simultaneously, while handling cwd and session-level state during initialization:
async *submitMessage(
  prompt: string | ContentBlockParam[],
  options?: { uuid?: string; isMeta?: boolean },
): AsyncGenerator<SDKMessage, void, unknown> {
  const {
    cwd,
    commands,
    tools,
    mcpClients,
    verbose = false,
    thinkingConfig,
    maxTurns,
    maxBudgetUsd,
  } = this.config

  this.discoveredSkillNames.clear()
  setCwd(cwd)
  const persistSession = !isSessionPersistenceDisabled()
}

image.png

Closed-Loop Orchestration of Model and Tools

Many people easily oversimplify the main loop as "simply continuing as long as the model hasn't finished." But QueryEngine is far more than that; it continuously handles appending and standardizing conversation history, permission checks before and after tool calls, distinguishing partial vs. final outputs, updating usage budgets, and managing runtime boundaries like interruption, resumption, and compression. Therefore, it acts more like a precise "orchestration layer" rather than a simple loop. The most critical aspect of such systems is that a closed loop must be formed between the model and the tools. The closed-loop path in Claude Code roughly follows these steps:

  1. The model makes decisions based on system prompts and history messages
  2. Decisions may include tool calls
  3. Tool calls go through permission checks first
  4. After execution, results are converted into messages
  5. These messages re-enter the conversation history
  6. The model uses the new results to continue to the next round QueryEngine does not simply "lend tools to the model"; it orchestrates the entire closed loop. Only by forming this tool-result feedback enabling re-decision cycle does the system gain true error-correction capability. For example, in a basic debugging flow:
  1. The model initially guesses a bug is in api.ts
  2. After reading the file, it finds this assumption false
  3. It then searches for related call sites
  4. Finally, it locates the actual issue Without tool result feedback, this adaptive process simply cannot occur.

Dependency Inventory and Architectural Positioning

QueryEngine must hold a large number of context objects because, at its core, it is a scheduling center for the session runtime. Through QueryEngineConfig, you can clearly see the matrix of resources it depends on. This configuration can almost be viewed as the master dependency inventory for Claude Code's main loop, explicitly listing the entire external environment that the model loop actually relies on. Most advanced capabilities ultimately converge here.

export type QueryEngineConfig = {
  cwd: string
  tools: Tools
  commands: Command[]
  mcpClients: MCPServerConnection[]
  agents: AgentDefinition[]
  canUseTool: CanUseToolFn
  getAppState: () => AppState
  setAppState: (f: (prev: AppState) => AppState) => void
  readFileCache: FileStateCache
  customSystemPrompt?: string
  appendSystemPrompt?: string
  thinkingConfig?: ThinkingConfig
  maxTurns?: number
  maxBudgetUsd?: number
}

image.png

Boundary Handling and State Retention

QueryEngine handles far more than just the success path. The source code includes extensive engineering-grade fault tolerance logic:

  • Runtime control: abortController (interruption), orphanedPermission, snipReplay
  • Error classification: API error tiering and handling
  • State tracking: real-time usage statistics, continuous permission denial tracking This demonstrates that Claude Code's main loop is far from an idealized demo; it is a rigorous implementation designed to handle complex real-world scenarios such as long sessions, interrupts, failures, compression, and recovery. After a task finishes, what state persists? This is one of the greatest differences between a session-based agent and a one-off script:
  • Message history (context continuity)
  • Known permission denials (to avoid repeatedly hitting the same roadblocks)
  • File read cache and usage statistics
  • Partial memory/skill discovery state It is precisely this retention of residual state that truly allows users to "continue from where the previous round left off." The persistence of state after a task completes is also the fundamental reason why QueryEngine remains resident in memory.

Core Mental Model

The best way to understand QueryEngine is not as a "request handler," but rather:

A task orchestrator within the Claude Code session-level runtime. It connects upward to user input and the REPL, and downward to models, tools, permissions, context, and state systems. If main.tsx dictates "how this session starts," then QueryEngine.ts dictates: exactly how this task will be completed step by step. Truly understanding Claude Code's architecture is impossible without QueryEngine.


Key takeaways

  • QueryEngine.ts is the soul of Claude Code's agent main loop, responsible for the complete task lifecycle rather than a single request.
  • Session-level residency: As a one-per-conversation object, it retains cross-turn state such as messages, permissions, caches, and usage metrics.
  • submitMessage() as entry point: Mounts the full runtime configuration (tools/budget/agent) to launch a complete agent run.
  • Closed-loop orchestration core: Enables error correction and adaptive capabilities via "model decision → permission validation → tool execution → result feedback → re-decision."
  • Engineering-grade boundary handling: Built-in interrupt recovery (abortController), API error tiering, budget tracking, and state persistence guarantee long-session reliability.

Learning map

  1. Understanding the Core Concept: Clarify the difference between a "single request" and a "session object," and understand why an agent needs persistent mutable messages and state caching.

  2. Source Code Architecture Breakdown: Analyze how dependency injection works in QueryEngineConfig (tools, MCP clients, commands, etc.) and how the runtime environment is assembled.

  3. Core Task Flow Tracing: Closely examine the submitMessage() async generator to master the full execution path — from receiving a prompt, through permission filtering, to interacting with the model.

  4. Understanding the Closed-Loop Mechanism: Study the "tool result feedback loop" principle — i.e., how the model performs secondary decision-making and self-correction based on execution results.

  5. Industry-Wide Horizontal Expansion: Translate the QueryEngine design patterns into LangGraph or AutoGen, and master methods for building scheduler hubs in modern mainstream agent frameworks.

Get hands-on — step by step

  1. Prepare the Local Repository Environment: Clone the official Anthropic source repository to your local terminal and open the root directory using a code editor such as VS Code.
  2. Locate and Read Core Files: Navigate to src/core/engine/ and open QueryEngine.ts. Use your editor's search feature to find the entry point of the submitMessage() function.
  3. Deconstruct Configuration and Dependency Flow: Analyze the constructor's input parameters line by line, sketch a system dependency diagram, and clarify how the tool list, MCP Client, and permission-checking logic are injected.
  4. Manually Simulate Message Flow: Select an example prompt containing multiple tool calls. Step through the corresponding code branches to trace the complete closed-loop path: “dispatch prompt -> check permissions -> execute -> append to history -> secondary generation”.
  5. Knowledge Transfer and Comparative Practice: Review the StateGraph section in the official LangGraph documentation, attempt to summarize its architectural advantages using concise code or pseudocode, and compare/validate it against traditional stateless APIs.

Top 3 sources

  1. 1
    Anthropic 官方开发者文档

    Anthropic 平台的核心知识库,涵盖 Agent API、工具调用规范及 Claude Code CLI 的运行架构说明。

    https://docs.anthropic.com/en/docs/getting-started/welcome

  2. 2
    LangGraph 核心循环指南

    现代 LLM Agent 框架的权威文献,详细阐述了如何实现类似 `QueryEngine` 的会话级状态管理与多步循环调度。

    https://langchain-ai.github.io/langgraph/

  3. 3
    OpenAI Function Calling 协议规范

    业界现代 LLM 工具调用的标准设计文档,为理解模型如何触发外部工具并处理错误结果提供理论基准。

    https://platform.openai.com/docs/guides/function-calling

Links are AI-suggested — worth a quick sanity check before diving in.