GitHub Copilot Agent Mode in VS Code
Agent mode is where Copilot stops proposing and starts doing. In VS Code it has the full supporting cast — checkpoints to rewind, custom agents to constrain it, MCP to extend it — which makes it meaningfully more capable here than in editors that have the mode but not the scaffolding.
This lesson covers the loop in depth, then builds a small REST API with it from an empty folder to a passing test suite.
Key takeaways
- The variable that most determines a good session is how precisely you scoped the task, not which model you picked.
- Terminal approval is the security boundary. Reading commands before approving is the whole control.
- Checkpoints make exploration cheap — you can let it try something and rewind cleanly.
- Ask agent mode to fix implementations, never to make tests pass, or it will edit the tests.
- Everything the agent produces is a draft. Running the suite yourself is not optional.
What agent mode does
Given a goal, agent mode searches the codebase, reads files you did not mention, edits across as many as it needs, runs terminal commands with your approval, reads the output, and iterates until it is done or stuck.
The mental model that works is a capable engineer who joined this morning: fast, literal, has read the repository, and has no idea which parts are load-bearing.
How it differs from the alternatives
Against chat
Chat produces text you apply. Agent mode edits your working tree directly and can run commands. The substantive difference is feedback: chat is guessing whether its code works, while agent mode can run your test suite, read the failure and fix it.
That makes agent mode much stronger on tasks with a verifiable finish line. If your project has a fast test suite, agent mode is far more useful than if it does not, because without tests “done” is only the agent’s opinion.
Against edit mode
Edit mode is VS Code’s middle gear, and choosing correctly between the two saves real time:
| Aspect | Edit mode | Agent mode |
|---|---|---|
| Who picks the files | You | Copilot |
| Runs commands | No | Yes, with approval |
| Iterates on results | No | Yes |
| Best when | You know the scope | You do not |
Renaming a concept across four files you can name is edit mode. Adding a feature that might touch anything is agent mode. Using agent mode for the first case works but is slower and gives you a larger diff to review.
Against the cloud agent
Agent mode runs on your machine while you watch. The cloud agent runs on GitHub’s infrastructure and returns a pull request. Use agent mode when you want to steer; use the cloud agent when you want to walk away. Cloud Agent vs Agent Mode covers the distinction properly.
The loop
- You state a goalScoped, with a definition of done.
- It exploresSearches and reads files, including ones you did not mention.
- It plansUsually states what it intends to change before changing it.
- It editsAcross whatever files it identified.
- It asks to run somethingTests, a build, a linter. This prompt is your control point.
- It reads the outputAnd returns to editing if something failed.
- You review the diffHuman judgementEvery file, including the ones you did not ask about.
- You run the tests yourselfHuman judgementNot the agent's claim about them — the actual command.
Steps marked Human judgement are the ones that do not get delegated — they are where you decide whether what Copilot produced is actually right.
Scoping the task
This is the highest-leverage thing you control, and it is worth more than model selection.
Improve the error handling in this service.
Replace the bare except: blocks in payments/ with specific exception types.
Preserve the existing log messages exactly. Do not change any function signature.
Run pytest tests/payments/ when you are done and fix anything that fails — but
do not modify the tests. If a test looks wrong, stop and tell me.
Four things the second does that the first does not: it names the target, states what must not change, defines done, and forbids the most common failure mode.
The rule of thumb: if you cannot describe what the finished diff should contain, the task is not ready for an agent. That is not a limitation of the tool; it is what delegation requires.
Tool use and terminal approval
Agent mode can run commands. That capability is what makes the feedback loop work, and it is also the part with real-world consequences.
Sensible defaults:
- Approve freely: tests, linters, type checkers, builds,
git status,git diff. - Read carefully: anything installing dependencies or writing outside the workspace.
- Decline or run yourself: anything destructive, anything touching production, anything involving credentials.
Checkpoints
VS Code supports checkpoints, which roll a session back to an earlier state.
This changes what is worth trying. With checkpoints, an exploratory instruction is cheap — let it attempt something, and if the direction is wrong, rewind rather than argue. Without them (Eclipse, for example) your safety net is Git, so you commit first.
Custom instructions in agent mode
Agent mode reads more instruction sources than chat does. In VS Code that
includes .github/copilot-instructions.md, path-specific files under
.github/instructions/, and agent files — AGENTS.md, and for agent mode also
CLAUDE.md and GEMINI.md.
The single most valuable line to put in them:
Do not modify existing tests to make a change pass. Raise the conflict instead.That converts the most common agent failure mode from something you must remember to forbid in every prompt into a standing rule.
Reading an agent diff
Reviewing what an agent produced is a different skill from reviewing a colleague’s pull request, because the failure modes are different. A colleague misunderstands the requirement; an agent satisfies the requirement in a way that technically works and quietly widens something.
Five things to look for, in order of how often they matter.
Check the files you did not ask about. Agents frequently make one helpful change outside the stated scope — reformatting a neighbouring function, adding a dependency, adjusting an unrelated import. Those are the least-reviewed changes in any diff, because they are not what you were looking for.
Look for silently widened behaviour. Broadened exception handling, a loosened type, removed validation, a default where a value was previously required. Each of these makes something pass without making it correct, and each reads as tidy.
Check what was deleted. Diffs draw the eye to additions. A removed guard clause or a removed comment explaining why something odd was necessary is where regressions hide.
Verify the tests fail without the fix. Revert the implementation change, re-run, and confirm red. If the suite passes both ways, it is not testing the change.
Check new dependencies. An agent will add a package to solve a problem the standard library already solves. That is a decision you should make consciously.
Multi-file work
Multi-file editing is agent mode’s clearest advantage and its clearest risk.
It is excellent at changes that are mechanically consistent across many places — renaming a concept, migrating a call pattern, adding a parameter everywhere it is needed, applying a linter’s fixes across a codebase. The work is tedious, the correct answer is obvious, and consistency is exactly what a pattern-matching model is good at.
It is poor at changes requiring different judgement in different places — where three of twelve call sites need special handling, and knowing which three depends on context that is not in the code. The agent will apply the same transformation twelve times, confidently, and eleven of them will be right.
When a diff spans many files, review it by category rather than file-by-file: check one instance of each kind of change carefully, then verify the rest are genuinely the same kind. That is what catches the twelfth case.
Custom agents and MCP
Custom agents are named configurations with their own instructions, tool permissions and MCP servers. The value is constraint rather than capability: a “migrations” agent that may read the schema and run the migration tool but may not touch application code is safer than general agent mode for that job.
MCP servers extend what the agent can reach — an issue tracker, a database, an internal service. Both are fully supported in VS Code.
Neither is a day-one feature. Use plain agent mode until you notice yourself writing the same preamble into every task; that repetition is the signal it belongs in a configuration instead.
A practical project: a small REST API
Step 1 — Create the project and set the rules
mkdir copilot-agent-api && cd copilot-agent-api
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
code .Before giving the agent anything, write the conventions. This is the step people skip and then spend the session correcting:
mkdir -p .github# Project conventions
Python 3.12, FastAPI, pytest. Run tests with `pytest -q` from the repository root.
## Style
- Type hints on every public function.
- Pydantic models for all request and response bodies.
## Errors
- Raise specific exceptions; never a bare `except:`.
- HTTP errors use FastAPI's HTTPException with a descriptive detail.
## Structure
- Routes in `app/api/`, business logic in `app/services/`.
- Route handlers must not contain business logic.
## Tests
- pytest under `tests/`, mirroring the module layout.
- Do not modify existing tests to make a change pass. Raise the conflict instead.Step 2 — Give agent mode the task
Create a FastAPI service in app/ with a single endpoint.
POST /metrics accepts a JSON body with a hostname field and returns CPU, memory
and disk utilisation as JSON.
Requirements:
- Pydantic models for request and response, in
app/models.py. - Return HTTP 400 with a descriptive message for an invalid hostname.
- Read utilisation from a function in
app/services/that I can stub in tests — do not call the real system inside the request handler. - pytest tests in
tests/covering a successful request, an invalid hostname, and a missing field. - Add
requirements.txt.
When finished, run pytest -q and fix any failures. Do not modify the tests.
The expected shape:
copilot-agent-api/ ├── .github/ │ └── copilot-instructions.md ├── app/ │ ├── init.py │ ├── main.py │ ├── models.py │ └── services/ │ └── metrics.py ├── tests/ │ └── test_metrics.py └── requirements.txt
Step 3 — Watch the approvals
You will be asked to approve pip install and pytest. Both are reasonable in a
throwaway virtual environment. Read them anyway — this is the habit, and a
throwaway project is the right place to build it.
Step 4 — Review the diff properly
Not the files — the diff. Specifically:
- Does the boundary match what you meant? What counts as an invalid hostname is a decision the agent made for you.
- Is the stub genuinely stubbable? If the service function is called directly inside the handler rather than injected, the tests cannot isolate it.
- Do the tests test the contract or the implementation? A test asserting the stub was called proves nothing about the endpoint.
- What did it add that you did not ask for? Extra dependencies, extra files, “helpful” extra endpoints.
Step 5 — Verify it yourself
pytest -qThen confirm the suite is actually testing something by breaking it deliberately:
# Temporarily change the 400 to a 422 in the handler, re-run, and confirm a test fails.A suite that passes both ways is not testing your change. This takes thirty seconds and is the difference between having tests and believing you do.
Step 6 — Run the service
pip install -r requirements.txt
uvicorn app.main:app --reloadcurl -sS -X POST http://127.0.0.1:8000/metrics \
-H 'Content-Type: application/json' \
-d '{"hostname": "localhost"}'Then send a deliberately invalid body and confirm you get a 400 with the message your instructions specified, not FastAPI’s default.
Step 7 — Iterate
When something is close but wrong, correct rather than restart:
- “The handler calls the metrics function directly — inject it so the tests can stub it.”
- “Move the hostname validation into the Pydantic model rather than the handler.”
- “That test asserts the stub was called. Assert on the response body instead.”
If a direction is badly wrong, roll back to a checkpoint instead of arguing.
Debugging with agent mode
Debugging is where the feedback loop pays for itself, because the agent can reproduce the failure rather than reason about it abstractly.
The pattern that works is to give it the real failure and let it verify its own fix:
test_invalid_hostname fails. Reproduce it by running pytest -q tests/test_metrics.py::test_invalid_hostname, identify the root cause rather
than the line that raised, then fix the implementation and demonstrate the fix by
re-running.
Do not modify the test. If you believe the test is wrong, stop and explain why.
Three things make that prompt work. It names the exact command, so the agent does not guess how your suite runs. It asks for the root cause rather than the throwing line, which is where a stack trace and an explanation diverge. And it forbids the shortcut.
For intermittent failures, the same approach with repetition:
This test passes individually but fails in the full suite. Run the full suite three times, identify whether the cause is shared state, ordering, or a timing assumption, and tell me which before changing anything.
Asking it to diagnose before fixing is worth doing whenever the cause is genuinely unclear — it separates the hypothesis from the change, so you can disagree with the hypothesis cheaply.
Failure modes and how they look
Agent sessions fail in recognisable ways. Naming them makes them easier to catch early, when the fix is cheap.
The confident loop. The agent runs a command, misreads the output, makes a change based on that misreading, runs it again, and repeats. Each iteration looks purposeful. The tell is that the same file is edited three or four times without the error changing. Stop it, read the actual error yourself, and restart with the diagnosis in the prompt.
Scope creep. You asked for a change to one layer and the session has touched a dozen files, added a dependency and reformatted something unrelated. This is what the approval prompts exist to catch, and it happens when they are approved without reading. Roll back to a checkpoint and re-prompt with explicit boundaries.
Working around the problem. The agent cannot make a test pass, so it changes
the test. Or it cannot resolve a type error, so it casts to any. Both are
locally rational and globally wrong. This one does not announce itself in the
approval prompts at all — it surfaces when you read the diff, which is why
reading the diff is not optional.
Plausible completion. The session ends with a confident summary of what was built. Some of it was not built, or was built differently from the description. The summary is generated from intent, not from verification. Run the thing.
Context exhaustion. A long session degrades: earlier decisions are forgotten, the same suggestion is offered twice, the agent contradicts itself. Long sessions are where this shows up, which is the practical argument for short ones with a commit between them.
When not to use agent mode
- Requirements still being decided. It will implement one interpretation with total confidence.
- Subtle correctness — concurrency, precision, authorisation logic. Plausible output is the failure mode.
- Code you cannot review. If you cannot evaluate the diff, the agent has not helped you.
- Irreversible consequences — migrations against real data, infrastructure applied live.
- Trivial changes. Writing the brief takes longer than making the edit.
Cost
Agent runs are the most expensive thing you can do with a Copilot subscription. A session that reads a dozen files, edits several, runs the suite twice and iterates consumes far more AI credits than a day of completions — and more again on a premium reasoning model.
Two consequences: scope tightly for cost as well as reviewability, and match the model to the task. A mechanical rename does not need a premium reasoning model.
If your credits are disappearing faster than expected, agent sessions are almost always the cause rather than completions, and the fix is usually scope rather than restraint — a vague task is expensive twice over, once in the credits spent exploring and again in the time spent reviewing the sprawl it produced.
Frequently asked questions
Is agent mode available on Copilot Free? Yes. Agent mode is included on every plan, including Copilot Free. What Free does not include is the cloud agent — the separate, asynchronous one that returns a pull request.
Why does agent mode sometimes not run my tests?
Usually because it cannot work out how. If your test command is unusual, state it
in the task or in your instructions file. pytest -q is inferable; a bespoke
script with environment setup is not.
Can I stop it partway through? Yes, at any point. In VS Code you can also roll back to a checkpoint, which is cleaner than interrupting and then manually undoing edits.
How is this different from the cloud agent? Agent mode runs locally while you watch and approve. The cloud agent runs on GitHub’s infrastructure, asynchronously, and returns a pull request. Different tools for different situations rather than better and worse versions of one.
Does agent mode work in other editors? Yes — Visual Studio, JetBrains, Eclipse and Xcode all support it. What differs is the supporting features: checkpoints are absent from Eclipse, edit mode exists only here and in JetBrains, and the customisation layer is preview in several.
Why did it change a file I did not mention? Because it decided that file was relevant, which is what agent mode does. This is why the review step focuses on unrequested changes — they are the ones you were not looking for.
Next steps
Continue to GitHub Copilot IDE Workflow: From Idea to Pull Request, which puts agent mode into the full sixteen-step workflow ending at a reviewed pull request.
For the conceptual grounding, Agent Mode Explained covers the model without the VS Code specifics, and GitHub Copilot for IDEs shows which editors have the supporting features described here.
Sources
Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.
Your progress
Saved in this browser only. No account, no server, and nothing leaves your device. Clearing site data resets it.
Was this lesson helpful?
Your answer is stored in this browser and is not sent anywhere.