← Engineering with AI STAGE 08 · 28 MIN

Agents: instructions, permissions, and dependencies

Give a coding agent standing instructions, the least access that does the job, and a dependency check it cannot talk its way past.

Open in the laboratory ↗

BY THE END, YOU CAN

  • Write and maintain a repository instruction file that agents read
  • Scope an agent’s tools, permissions, and isolation to the task and its consequences
  • Check a suggested dependency before installing it, including names a model invented
The instruction file asks; the sandbox, the permissions, and the approval step enforce. A suggested package is checked on the index before anything is installed.

Diagram description: AGENTS.md → Agent in a sandbox (reads); Agent in a sandbox → Repository + tests (runs); Agent in a sandbox → Human approval; Human approval → Production; Agent in a sandbox → Package index (suggests)

Standing instructions in the repository

Coding agents read instruction files kept in the repository.

AGENTS.md is an open format for them, and some tools use their own file names for the same idea. It holds what an agent needs and a README would bury: how to build and run the checks, the conventions to follow, what not to touch, and security notes. In a repository with several such files, the one closest to the edited file wins, and an explicit request in the conversation overrides them all.

Keep the file short, true, and current: change it in the same commit as the code it describes, and delete rules that no longer hold, because a stale instruction is followed as faithfully as a correct one. Never put secrets in it. Above all, remember what it is: context the agent usually follows, not a lock.

A prompt injection or a mistaken plan can ignore it, so every limit that matters must also be enforced somewhere the agent cannot edit.

Give the agent the least access that does the job

An agent with a shell and network access can do whatever the account it runs under can do.

OWASP calls the risk excessive agency and names three causes: too much functionality, too many permissions, and too much autonomy. Answer each one. Offer narrow tools instead of open-ended ones: a command that runs the tests rather than a general shell, fetching from a short list of sites rather than any URL.

Give it the smallest permissions: a read-only token, a test database, no production credentials. Require a person to approve actions that are hard to undo or that reach other people, such as pushing, deploying, deleting, or sending messages. Enforce authorization in the systems the agent calls, never by trusting the model to behave.

Then isolate the run in a container, a virtual machine, or a separate working copy, with the network restricted, so a mistake or an injected instruction stays inside. Logs and rate limits do not prevent damage, but they limit it and show what happened.

Check every dependency an agent suggests

Models sometimes recommend packages that do not exist.

A study presented at USENIX Security 2025 generated 576,000 code samples with 16 models: on average, at least 5.2% of the packages that commercial models suggested and 21.7% of those from open-source models did not exist, 205,474 different invented names in all. Many come back: 43% of the invented names reappeared in all ten repetitions of the same prompt. That repetition is what an attacker needs, because a name that models keep inventing can be registered with malicious code and simply waited on.

This is sometimes called slopsquatting, a relative of typosquatting, where the name is one typo away from a popular package. Before installing, confirm that the package exists on the official index and is the project you meant, preferably by following a link from that project’s own documentation. Look at its age, its release history, and its repository.

Distrust a name one letter away from a well-known one. Pin versions in a lock file with hashes, so a later change cannot slip in.

Python
import json, urllib.error, urllib.request

def index_facts(name):
    request = urllib.request.Request(
        f"https://pypi.org/simple/{name}/",
        headers={"Accept": "application/vnd.pypi.simple.v1+json"})
    try:
        with urllib.request.urlopen(request, timeout=10) as response:
            data = json.load(response)
    except urllib.error.HTTPError as error:
        if error.code == 404:
            return None  # no such package: do not install a guess
        raise
    uploads = [f["upload-time"] for f in data["files"] if f.get("upload-time")]
    return {"versions": len(data["versions"]), "first_upload": min(uploads, default=None)}

COMMON FAILURE MODE

Treating the instruction file as a security boundary. It is advice the agent usually follows; the limits that matter belong in permissions, isolation, and checks the agent cannot change.

TRY IT YOURSELF

Write an AGENTS.md for Relay in at most 30 lines. Then list what an agent fixing a failing test needs (files, commands, network) and what it must not have, and say how each limit is enforced outside the instruction file.

RETRIEVAL CHECK / APPLY THE IDEA

Make the call in the laboratory.

The two-question checkpoint, your notes, and your saved progress are in the laboratory. Free, with no account.

Open in the laboratory ↗

CONNECT IT TO RELAY

Add an AGENTS.md to Relay with its build and test commands and its security notes. Run your agent in a separate working copy with a test database and no production credentials, require approval for pushes, and check every new dependency on the index before adding it to the lock file.