Chapter 2: An agent is a for loop
Every conversational AI, coding assistant, and autonomous agent you have ever used shares the exact same core architecture.
Underneath the user interface, ChatGPT, Claude Code, GitHub Copilot, and custom enterprise agents are all driven by a standard while loop.
In its purest form, an agent looks like this:
let chatHistory = [];
while (true) {
const userInput = await getUserInput();
chatHistory.push(userInput);
const chatbotResponse = await sendChatRequestToLlm(chatHistory);
chatHistory.push(chatbotResponse);
displayToUser(chatbotResponse);
}

The four-step heartbeat
Every turn of the loop follows the same four-step cycle:
- Capture Input: Wait for input from the outside world.
- Append to History: Record the new message at the end of the
chatHistoryarray. - Dispatch to the LLM: Send the accumulated transcript over the wire to the model.
- Record the Output: Append the model's response to
chatHistoryand take action.
Let us explore each of these four steps in depth.
Step 1: Capture Input (The Intake Gate)
The first step of every iteration is acquiring fresh information from outside the loop.
const userInput = await getUserInput();

In a simple web chatbot, this intake gate pauses execution until a human finishes typing a prompt and hits Enter.
In an autonomous coding agent, however, the input does not always come from a human keyboard. The intake gate receives data from diverse external sources:
- Human prompts: Instructions such as "Refactor auth.ts to use argon2."
- Command outputs: The exit code and standard output of a test runner (
npm test). - File system reads: The text contents of a source file read from disk.
- Compiler errors: Stack traces and diagnostic warnings emitted by a build tool.
- External events: Webhook payloads, database query results, or API responses.
Regardless of where the raw input originates, the intake stage normalizes the data into a standard message object:
const userInput = {
role: "user",
content: "Please fix the failing unit test in auth.spec.ts"
};
Step 2: Append to History (The Scribe's Scroll)
Once input is captured, the host program immediately appends it to an in-memory array known as chatHistory.
chatHistory.push(userInput);

This step sounds trivial, but it represents the single most important architectural responsibility of your application: state accumulation.
The chatHistory array is an append-only chronological ledger of everything that has occurred in the conversation so far:
[
{ role: "system", content: "You are an expert TypeScript engineer." },
{ role: "user", content: "Fix the failing test." },
{ role: "assistant", content: "I will read the test file first." },
{ role: "user", content: "FAIL: Expected 200 OK, received 401 Unauthorized" }
]
Every message retains its exact position in the sequence. Order matters: the LLM reads messages sequentially from beginning to end to reconstruct the context of the session.
Step 3: Dispatch to the LLM (The Outbound Flight)
With the new message recorded, the host program serializes the entire chatHistory array into a JSON payload and transmits it over HTTP POST to the remote model provider.
const chatbotResponse = await sendChatRequestToLlm(chatHistory);

This step highlights a fundamental reality of modern AI: large language models are completely stateless.
When you send a request to OpenAI, Anthropic, or a local Ollama instance:
- The server does not maintain an open session for you.
- The model has zero memory of any previous API calls you made.
- The model does not know what was said five seconds ago unless it is explicitly included in the payload.
If you only sent the single latest user input, the model would receive the prompt with zero context and give an incoherent response.
Because the model retains nothing, your host application acts as the external memory bank. You send the entire accumulated history on every turn. The model processes the full transcript from start to finish, generates the next token sequence, and immediately forgets everything the moment the HTTP response stream completes.
Step 4: Record Output and Decide Next Action (The Receipt & Turnstile)
When the remote model finishes generating its answer, the host program receives the completion payload.
chatHistory.push(chatbotResponse);
displayToUser(chatbotResponse);

This final step closes the loop through two distinct actions:
- Record the Assistant Turn: The model's output is wrapped into a message object with role
"assistant"and pushed ontochatHistory. This guarantees that on subsequent turns, the model will see its own prior statements. - Evaluate the Turnstile: The host checks what kind of response was returned.
In a basic chatbot, the text is rendered to the user's screen, and the loop returns to Step 1 to await the next human message.
In an agentic system, however, the response might be a tool call rather than a human-facing message. If the model returns a request to execute a bash command or read a file, the host skips waiting for the human: it executes the requested tool, wraps the tool's output into a new message, appends it to chatHistory, and immediately loops back to Step 3.
From simple chatbot to autonomous coding agent
You might wonder how a simple four-step loop turns into an autonomous harness like Claude Code or Devin.
The answer is tool execution chaining inside the loop:
- User Turn: The user asks the agent to fix a bug.
- Append & Dispatch: Prompt is appended to
chatHistoryand sent to the LLM. - Tool Call: Instead of plain prose, the model returns a structured tool call requesting to read
auth.ts. - Tool Execution: The host loop intercepts the tool call, reads the file from disk, and appends the file contents to
chatHistory. - Immediate Re-dispatch: The loop cycles back without human intervention, sending the updated history with the file contents back to the LLM.
- Action Output: The LLM analyzes the code, decides on a patch, and returns a tool call to write the fix to disk.
- Resolution: The host writes the file, runs the test suite, records the passing result in
chatHistory, and asks the LLM for a final summary. - Final Display: The model emits plain text confirming the fix, which is displayed to the user.
The model is still just taking text in and producing text out. The while loop is the engine that provides statefulness, momentum, and agency.
In the next chapter, we will examine the exact structure of chat messages and construct our first live API request.