Engineering AI Coding Agents: Loops, Context, and Permissions

    This article explains how AI coding agents work as loop-plus-tools systems, covering loop designs, context management, tool permissions, evaluation metrics, and practical guidance for building controllable workflows.

    Engineering AI Coding Agents: Loops, Context, and Permissions

    Introduction

    AI coding assistants have evolved from chatbots that answer code questions into autonomous systems that can read a project, edit files, run commands, and verify results. These systems are commonly called agents. What sets them apart from ordinary conversational models is less the model weights than the surrounding runtime: who decides the next action, how history is retained, and how tool use is constrained.

    Evaluations often focus on model quality or UI polish and overlook runtime structure. This article is not a product comparison. It examines three engineering problems: how the loop is organized, how context is managed, and how tools are used safely.

    1. What an Agent Is: a Loop Plus Tools

    An agent has two core pieces. The loop keeps the task moving. Tools interact with the outside world. The model only generates text. It can edit files or run tests because the runtime parses model output as tool calls, executes them, and feeds results back.

    A typical skeleton:

    1. Sample: the model generates a reply from the current context. The reply may be text, tool-call requests, or both.
    2. Execute tools: the runtime parses calls and runs them within allowed permissions.
    3. Inject results: execution results are appended to the context in a standard message format.
    4. Sample again: the model continues from the updated context until it stops requesting tools.

    The loop looks simple, but each step affects stability, speed, and safety.

    2. Agent Loop: Three Common Patterns

    2.1 Streaming Parallel

    In streaming mode, model output arrives in chunks. As soon as a complete tool call is recognized, the runtime can start that tool while the model is still generating later tokens. For example, a file read may finish while the model is still describing a plan. Latency drops, but the implementation is harder: incomplete arguments, ordering, and dependencies must be handled.

    2.2 Event-Driven

    User input, model output, tool results, and state changes are all events, decoupled through a queue. Mid-task intervention is easier: new instructions can be placed in the next round of context without aborting the current run. The cost is a heavier protocol and stricter state management.

    2.3 Serial Dispatch

    Each round runs a single tool, in order. Speed is sacrificed for predictability, which helps debugging and reproduction. When every step must be audited, this conservative design is often a better fit.

    Comparison:

    Dimension Streaming parallel Event-driven Serial dispatch
    Responsiveness High Medium Low
    Implementation complexity High High Low
    Mid-task intervention Harder Easy Harder
    Reproducibility Medium Medium High

    2.4 Guardrails Against Runaway Loops

    In any pattern, an agent may repeat itself: rereading the same file or retrying a failed command with the same arguments. Common defenses include:

    • Hash recent tool names and arguments; interrupt if the same call repeats consecutively.
    • Cap maximum steps and wall-clock time per task.
    • Back off on consecutive failures or require human intervention.
    1. def agent_loop(model, tools, messages, max_steps=20):
    2. recent = []
    3. for _ in range(max_steps):
    4. reply = model.sample(messages, tools=tools.schemas())
    5. messages.append(reply)
    6. if not reply.tool_calls:
    7. return reply.text
    8. for call in reply.tool_calls:
    9. signature = (call.name, str(call.arguments))
    10. recent.append(signature)
    11. if recent[-3:].count(signature) == 3:
    12. raise RuntimeError("Repeated tool call detected; task aborted")
    13. if tools.requires_confirmation(call) and not ask_user(call):
    14. result = "User declined this operation"
    15. else:
    16. result = tools.run(call)
    17. messages.append(tool_result(call.id, result))
    18. raise RuntimeError("Exceeded maximum steps; task stopped")

    The sample omits exception handling and logging. It shows how the loop, duplicate detection, and permission confirmation fit together.

    3. Context Engineering: Managing a Finite Window

    Context windows are finite. Real projects, docs, chat history, and tool output far exceed that capacity. Context engineering answers two questions: which history to keep, and how to retrieve the rest on demand.

    3.1 Layered Injection

    Context is usually stacked by priority:

    1. Safety and compliance constraints: highest priority; nothing may override them.
    2. System role and behavior rules: duties and output style.
    3. Project rules: coding standards, directory conventions, test requirements.
    4. Session memory: user preferences and confirmed facts across turns.
    5. Current task and latest feedback: what drives this round’s actions.

    Conflict handling matters. Project-specific rules can override generic ones; higher-level safety rules cannot be rewritten by lower layers. The pattern resembles configuration overlay and reduces behavioral uncertainty.

    3.2 Retrieve on Demand Instead of Loading Everything

    Stuffing an entire codebase into context is expensive and distracting. Locate relevant files via directory structure, symbol indexes, or search, then place only needed snippets in the window. Retrieval results should include path and line numbers so the model can cite them and users can verify.

    3.3 Multi-Level Compression

    When context approaches the limit, history must be compressed. Typical strategies, from cheaper to more expensive:

    • Drop old tool output: long logs and large files usually need only conclusions and key lines.
    • Sliding window: keep the last few complete turns; discard older detail.
    • Structured summaries: goals, completed work, remaining work, and key decisions.
    • Tiered processing: a smaller model extracts raw facts; a stronger model turns them into long-term memory, balancing cost and quality.
    1. def maybe_compress(messages, limit_tokens, count_tokens, summarize, keep_recent=6):
    2. if count_tokens(messages) < 0.8 * limit_tokens:
    3. return messages
    4. head = messages[:1]
    5. middle = messages[1:-keep_recent]
    6. tail = messages[-keep_recent:]
    7. summary = summarize(middle)
    8. return head + [summary_message(summary)] + tail

    The main risk is losing constraints. Explicit user requirements, safety rules, and unfinished task state must be preserved in the summary.

    3.4 Isolate Subtasks

    Complex work can go to sub-agents with their own context. They return summaries and conclusions, not the full search trail. The main thread stays cleaner, and failures stay isolated.

    4. Tooling and Permissions

    Tools give agents power and introduce risk. Deletes, network requests, and shell commands can be irreversible, and model output is not always reliable.

    4.1 Tool Tiers

    By side effect:

    Level Example Default policy
    Read-only Read files, search code Auto-run
    Write Edit files, create directories Record changes; allow rollback
    High risk Delete data, run arbitrary commands, access the network User confirmation or sandbox

    Most operations are read-only. Confirming every one trains users to click through everything.

    4.2 Least Privilege and Sandboxes

    Give the agent only the access the task needs: restrict directories, limit outbound network, and run commands in an isolated environment such as a container or temporary workspace, cleaned up afterward.

    4.3 Tool Output Is Data, Not Instructions

    Web pages, comments, and logs may contain text that tries to change agent behavior (prompt injection). Treat tool output as data, not as a new instruction source. Keep system instructions, user requests, and external content distinct in the message schema, and flag suspicious instructions in external content.

    4.4 Auditability

    Log every tool call: time, arguments, result summary, and confirmation status. Logs support debugging and compliance.

    5. How to Evaluate an Agent System

    Demo quality is a biased signal. Design tests along:

    1. Task completion rate: share of real-repo problems that pass verification.
    2. Step efficiency: average model and tool calls for similar tasks.
    3. Recovery: whether the agent adapts after tool errors or test failures.
    4. Safety boundaries: interception of prompt-injection and over-privilege cases.
    5. Context stability: whether key constraints survive mid-task compression.

    Measure on a shared task set and environment. Teams can use these as a baseline for build-or-buy decisions.

    Agents are moving toward longer-running work, which needs stronger persistence and resume. Observability is becoming standard: loop traces, tool chains, and token use should be visible. Multi-agent collaboration is spreading, so role split, handoff protocols, and conflict resolution matter more.

    In practice: define permission boundaries before expanding capabilities; make every step traceable; judge results with metrics, not impressions; when safety and speed conflict, default to safety.

    Conclusion

    An agent is not mysterious: it is a loop with tools. Usability depends on loop control, context organization, and tool permissions. Understanding those three pieces is what lets you build AI coding workflows that are both efficient and controllable.