From Prompt to Action: The Agent Loop

In Gemini CLI, the decisive moment is not when the model suggests a tool; it is what happens between that suggestion and the next model response. This chapter follows one request through streamed output, tool-call creation, scheduling, execution, and feedback. The goal is to make the loop concrete enough that readers can tell whether an action was proposed, authorized, attempted, completed, blocked, or still waiting for more information.

A useful example is a request to investigate a failing test and fix it. Gemini CLI might need to read source files, search for related code, edit a test or implementation, and run the test suite. That work may take several exchanges with the model. Each exchange changes the state of the task: a file read supplies evidence, an edit changes the workspace, and a test result either supports or challenges the current hypothesis.

A streamed response is a set of events

The first step is a request from the session to the Gemini model. Gemini CLI sends the current user content and relevant conversation state to the model and receives a stream rather than waiting for one indivisible answer. A stream can contain visible text, thought information, citations, a function call, and a finish signal. Treating all of those pieces as “the answer” would hide the difference between explanation and requested work.

Visible text can be useful even when the model is still working. It may explain a hypothesis, state what it is inspecting, or describe a result. Citations can identify source material used in the response. A function call has a different meaning: it names a capability and supplies arguments for a possible operation. In the failing-test example, the model might request a file-reading tool with a path or a shell tool with a test command. The request is an instruction to the surrounding agent system, not a record that the file was read or the command ran.

Gemini CLI's turn-processing code collects these different signals as they arrive. When it sees a function call, it gives the call a stable identifier, finds the corresponding tool in the active registry, and constructs a tool-call request. The identifier matters because a single streamed turn can contain multiple calls, and later results must be matched to the right request. The registry can also fail to resolve the name, in which case the system returns a tool-not-found result instead of pretending that an unknown capability executed.

The distinction between a direct answer and a function call is also important for control. The model may conclude that it has enough information and return text without asking for a tool. In that case, the turn can finish. If it requests one or more tools, the agent session holds the request while another part of the system decides whether those calls may proceed. The model chooses a possible next move; it does not own the operating system, the policy decision, or the confirmation dialog.

A single streamed response can contain more than one proposal. For example, the model might request reads of two independent files before it has enough evidence to explain a failure. Gemini CLI collects those calls rather than treating the first one as the entire turn. Stable call identifiers let the system keep each argument, status, display description, and response associated with the right proposal. The separation also gives the surrounding system room to apply different outcomes to different calls: one read may succeed, another may fail validation, and a third operation may be denied. The model receives those outcomes as structured feedback instead of a misleading combined claim that “the tools ran.”

sequenceDiagram actor User participant Session as Agent session participant Model as Gemini model participant Turn as Turn handler participant Scheduler participant Tool participant History as Session history User->>Session: Submit task Session->>Model: Send request as a stream Model-->>Turn: Text, thoughts, citations, or function call Turn-->>Session: Visible event or tool-call request alt Tool request is allowed to proceed Session->>Scheduler: Schedule validated request Scheduler->>Tool: Execute after controls Tool-->>Scheduler: Result or tool error Scheduler->>History: Record outcome Scheduler-->>Session: Tool response parts Session->>Model: Continue with tool result Model-->>Session: Final answer or another tool request else Request is denied or cancelled Scheduler-->>Session: Denial or cancellation result Session->>History: Record stopped attempt end

This sequence shows why “the model called a tool” is an incomplete description. The turn handler translates the model's stream. The scheduler owns the transition from a request to a possible execution. The tool produces a result or error. The session then decides whether that result should become the next model input. At every boundary, the system can stop, retry, or return a failure instead of moving automatically to the next box.

The scheduler turns a request into work

Once Gemini CLI has one or more tool-call requests, the scheduler processes them through a staged lifecycle. The names of the internal states are less important than the responsibilities they represent: validating, awaiting approval, scheduled, executing, and then successful, errored, or cancelled. A request must pass through these stages before the result can be sent back to the model.

Validation comes first. The scheduler resolves the requested tool from the registry and builds an invocation from the supplied arguments. The builder checks the shape and meaning of those arguments and creates the object that knows how to describe and execute the operation. If the arguments are invalid, the scheduler creates an error response. This is an early boundary against malformed model output. It also keeps the later stages from operating on an unexamined command or path.

Policy and confirmation come next. The scheduler evaluates hooks and policy before execution. A policy can allow a call, deny it, or require user confirmation. If confirmation is required, Gemini CLI can show the user what a mutating operation intends to do, including a command or an edit description. The user can approve, cancel, or in some flows modify the proposed operation before it is scheduled. If policy denies the call, the tool never reaches the execution stage; an error-shaped result can still be returned to the model so it knows that its proposed route was unavailable.

This ordering makes the policy layer more than a switch attached to the terminal. It can inspect the particular tool and its arguments, and it can be combined with hook decisions. A hook may modify input or block the call; the scheduler can rebuild the invocation after a modification and then continue through policy and confirmation. The system therefore has a place to reject a dangerous or malformed request without relying on the model to notice its own mistake.

Return to the failing-test example. Suppose the model proposes an edit and a test command together. The scheduler can validate both proposals, but it does not have to treat them as equally ready. An edit may require a confirmation, while a read-only inspection may be allowed. If the edit is approved before a following test, that test should observe the resulting workspace rather than run against an earlier state. If a hook changes the command or rejects the edit, the model should receive that fact before it decides what to do next. The scheduler is therefore the owner of transition rules: it converts an uncertain model proposal into a sequence of checked states, and it preserves enough information about the transition for the next turn to reason from what actually happened.

Scheduling determines order. Gemini CLI may receive several tool calls in one model response. Calls that are marked or inferred to be parallelizable can be grouped and executed together when their order does not affect correctness. This is useful when the model asks to read several independent files: waiting for each read in sequence would add latency without adding control. But not every operation can be treated that way. The scheduler keeps edits and topic-changing operations ordered, and a request can explicitly require waiting for a previous call.

The distinction is a practical concurrency rule. Parallelism is appropriate for independent evidence gathering; it is dangerous when one operation changes the conditions assumed by another. An edit followed immediately by a test has a causal order even if both appear in the same high-level plan. The scheduler's job is not to maximize concurrency at any price. It is to use concurrency where the request declares or supports independence and preserve sequence where state changes matter.

Execution produces evidence, not just success flags. When a call is scheduled, the tool executor marks it as executing, runs the invocation, and converts the result into a response that has both model-facing content and user-facing display information. A successful file read, a test's output, and a command error are different kinds of evidence, even though all travel back through the same result path. The executor also records timing and tool metadata in telemetry when that feature is enabled.

Large results require another decision. Gemini CLI can distill tool output when context management is active, or truncate oversized shell and MCP output while saving the larger content to a temporary file. This prevents one command from consuming the entire next request. It also means that the model may receive a bounded representation rather than every byte produced by the tool. For a high-stakes diagnosis, the operator should know whether the conclusion came from a complete result or from a managed excerpt.

After execution, the scheduler finalizes each call and the session records the completed tool interaction. The resulting function-response parts become the next input to the model. In the failing-test example, the test output is not merely printed and forgotten. It is placed into the conversation so the model can revise its hypothesis, propose another operation, or explain that the task is complete. This is the feedback loop that turns a sequence of isolated commands into an agentic process.

The response has two audiences. The model needs content it can use in the next decision, while the person at the terminal needs a readable description of the operation and its outcome. Gemini CLI keeps those concerns together without making them identical: the executor converts tool output into a model-facing function response and a display result, and it can retain metadata such as timing or live output. This matters when a command produces a long log or a structured external response. A useful display may be abbreviated, yet the model still needs a clear indication of success, error, or cancellation. Conversely, a model-facing result can be bounded so that the next request remains usable.

The loop has explicit stopping points

The normal path is not the only path. A user can cancel while the model is streaming, while a confirmation is waiting, or while a tool is executing. The scheduler cancels queued and active calls, and the session emits an aborted result rather than starting another turn. A tool can request that execution stop after returning a result. A fatal tool error or invalid model stream can end the agent loop. A session turn limit can also terminate work that continues too long.

Some failures are useful feedback. An invalid parameter, missing tool, denied operation, or ordinary command error can be represented as a tool response and sent back to the model. The model may then choose a different route. Other failures are boundaries around the whole interaction: a user cancellation, a fatal error, a blocked stream, or an explicit stop should not be disguised as an invitation to keep executing.

This distinction protects the meaning of the conversation. If a test command fails, the model can analyze the failure because the failure is evidence about the project. If the user cancels a destructive edit, the model should not receive a signal that encourages it to retry the edit automatically. If the system cannot parse a response safely, continuing would make the next action depend on an uncertain state. Termination is therefore part of the loop's design, not an exception to be added after the happy path.

Retries have a similarly careful meaning. A transient request failure can cause Gemini CLI to retry the model call, and the implementation can add a small corrective nudge after certain invalid or empty responses. That is different from silently repeating a tool with side effects. Model-request recovery belongs to the communication path; tool execution is separately tracked and can be cancelled or ended with an error. Keeping those cases distinct prevents a network retry from being confused with permission to repeat an edit, deployment, or other external action.

The result is a controlled feedback cycle:

  1. The session sends a request and the model streams a response.
  2. The turn handler separates explanation from function-call proposals.
  3. The scheduler validates, governs, and possibly confirms each proposal.
  4. Eligible tools execute, while denied, invalid, or cancelled calls become explicit results.
  5. Results are recorded and returned to the model as new evidence.
  6. The next turn continues only if the loop has not finished, been stopped, or failed.

The next question follows naturally from this sequence. The model's next decision depends on the tool result, but it also depends on everything else included in the request: project instructions, accessed-file context, previous turns, and any output that was compressed or truncated. To understand why the same loop can behave differently in different repositories or at different points in a long session, we must examine what Gemini CLI puts into the model's working context.

References