- 2.1 Select and configure agent tools — identify required tools, configure them, set tool permissions
- 2.2 Configure MCP servers — add an MCP server to an agent, the GitHub remote MCP server, registries, allowlists
- 2.3 Integrate agents within development environments — execution context, repository and branch scope, CI invocation, autonomous branches and PRs, environment constraints
- 2.4 Operate agents with safe execution paths — error handling, retries, rollbacks, escalation, traceability
2.1 Select and configure agent tools
Identify required tools
Agents act through tools: every file read, edit, shell command, API call or subagent invocation is a tool call. Choosing tools is choosing capability, so start from the task and add only what it needs.
| Tool family | Examples | Needed for |
|---|---|---|
| Read and search | read / view, search, grep, glob | Planning, review, research — safe by default |
| Edit | edit, write, create | Implementation agents only |
| Execute | execute / shell / bash / powershell | Build, test, lint — the most powerful and most dangerous |
| Agent | agent / task / custom-agent | Invoking another custom agent as a subagent |
| Web | web (web_fetch, web_search) | External docs — not applicable to cloud agent today |
| GitHub MCP server | github/*, github/list_workflow_runs | Issues, PRs, Actions logs, code scanning alerts |
| Playwright MCP server | playwright/* | Browsing and screenshotting the app running on localhost |
| Custom MCP servers | sentry/get_issue_details | External systems: observability, tickets, cloud resources |
Configure agent tools
In a custom agent profile, the tools property filters every tool available to the agent, built-in or
from MCP servers. Its semantics are a classic exam target:
tools value | Effect |
|---|---|
Omitted, or ["*"] | All available tools, including every configured MCP server tool |
[] | No tools at all |
["read", "search", "edit"] | Only those tools (aliases are case-insensitive) |
["github/*"] | All tools from the github MCP server |
["sentry/get_issue_details"] | One named tool from one MCP server |
| An unknown name | Silently ignored — so a profile can list product-specific tools safely |
| Alias | Also accepted | Cloud agent mapping |
|---|---|---|
execute | shell, Bash, powershell | Shell tools |
read | Read, NotebookRead | view |
edit | Edit, MultiEdit, Write, NotebookEdit | Edit tools such as str_replace |
search | Grep, Glob | search |
agent | custom-agent, Task | Custom agent tools |
web, todo | WebSearch, WebFetch, TodoWrite | Not applicable to cloud agent |
---
name: security-reviewer
description: Reviews changes for vulnerabilities and reports findings. Read-only; never edits code.
tools: ["read", "search", "github/*"]
---
Review the changes on this branch for injection, secrets, unsafe deserialization and
missing authorization checks. Report each finding with file, line and a suggested fix.
Do not modify files. Configure agent tool permissions
"Which tools exist" and "which tools may run without asking" are separate questions, and each surface answers them differently.
| Surface | How permissions work |
|---|---|
| Copilot cloud agent |
Non-interactive: configured MCP tools are used autonomously, without asking for approval.
The only gate is what you allowlist — the tools field of each MCP server and of the agent profile.
Allowlist specific read-only tools wherever possible.
|
| Copilot CLI |
Read-only operations run automatically; tools that modify the system prompt for approval.
--available-tools / --excluded-tools decide which tools the model even knows about;
--allow-tool / --deny-tool pre-approve or forbid them. Deny always wins,
even over --allow-all and saved approvals.
|
| Automations | You select exactly which tools each automation may use (or let Copilot Suggest tools from the prompt) |
| GitHub Agentic Workflows | tools: in frontmatter enables toolsets; writes happen only through declared safe-outputs |
| Copilot SDK | Per-agent tools lists, session availableTools / excludedTools, and an onPermissionRequest handler |
| Agent skills | The allowed-tools frontmatter field pre-approves tools a skill's scripts need — pre-approve shell only for skills you fully trust |
| Copilot CLI option | Effect |
|---|---|
--allow-tool='shell(git:*)' --deny-tool='shell(git push)' | Every git command except git push |
--allow-tool='read, write(.github/copilot-instructions.md)' | All reads; writes to one file only |
--allow-tool='MyMCP(create_issue)' | One tool from one MCP server |
--deny-tool=write | No file writes, whatever else is allowed |
--available-tools='bash,edit,view,grep,glob' | The model cannot see any other tool, so it cannot even try |
--allow-all / --yolo | All tools, paths and URLs — isolated environments only; can be blocked by enterprise settings |
/reset-allowed-tools | Revokes approvals granted during the session and clears saved ones for the location |
Two layers, two different questions
--available-tools and --excluded-tools change what the model can choose from;
--allow-tool and --deny-tool change what runs without a prompt. A tool missing
from the available set cannot be used even if it is in --allow-tool. Command-line options last for
the session only; approvals such as "don't ask again in this repo" persist in
~/.copilot/permissions-config.json.
2.2 Configure MCP servers
The Model Context Protocol is an open standard for connecting AI clients to tools. Instead of a one-off integration per system, a client talks to an MCP server that exposes tools in a structured format and performs the real action against the underlying service. Three concepts work together:
- An MCP server exposes tools — locally (a process on your machine,
stdio) or remotely (hosted,http/sse). - An MCP registry makes approved servers discoverable.
- An allowlist policy decides which servers may actually run.
flowchart LR
subgraph GOV["Organization and enterprise policy"]
direction LR
REG["MCP registry<br/>catalogue of approved servers"] --> CLIENT["MCP client<br/>IDE · Copilot CLI"]
ALLOW["Allowlist policy<br/>MCP servers in Copilot · managed settings"] --> CLIENT
end
CLIENT --> SERVER["MCP server<br/>local stdio or remote HTTP"]
SERVER --> SYS["Underlying system<br/>GitHub · Sentry · Azure · Jira"] What cloud agent supports
Copilot cloud agent and Copilot code review use MCP tools only — not MCP resources or prompts —
and do not support remote MCP servers that authenticate with OAuth. Code review additionally uses
only tools annotated readOnlyHint: true. Once configured, cloud agent calls MCP tools
without asking for approval.
Add an MCP server as a tool to an agent
| Where | How | Scope |
|---|---|---|
| Repository settings on GitHub | Settings → Copilot → MCP servers: paste JSON with an mcpServers object | Cloud agent and code review in that repository |
| Custom agent profile | The mcp-servers YAML property | Only that custom agent (ignored by IDE custom agents) |
| VS Code | Agent mode → Tools → Add MCP server, or .vscode/mcp.json, or the MCP gallery (@mcp in Extensions) | Workspace or user |
| Copilot CLI | /mcp add or copilot mcp add → ~/.copilot/mcp-config.json; workspace .mcp.json; --additional-mcp-config for one session | User, workspace or session |
| Copilot SDK | mcpServers on the session or on a custom agent | Your application |
In the repository JSON, type and tools are required for every server.
Local servers need command and args; remote servers need a url and optional
headers. Secrets are never pasted in: create an Agents secret whose name starts with
COPILOT_MCP_ and reference it as $COPILOT_MCP_NAME, ${COPILOT_MCP_NAME}
or ${COPILOT_MCP_NAME:-default}.
{
"mcpServers": {
"sentry": {
"type": "local",
"command": "npx",
"args": ["@sentry/mcp-server@latest", "--host=$SENTRY_HOST"],
"tools": ["get_issue_details", "search_issues"],
"env": {
"SENTRY_HOST": "https://contoso.sentry.io",
"SENTRY_ACCESS_TOKEN": "$COPILOT_MCP_SENTRY_ACCESS_TOKEN"
}
},
"docs": {
"type": "http",
"url": "https://docs.contoso.com/mcp",
"tools": ["search_docs"],
"headers": { "Authorization": "Bearer $COPILOT_MCP_DOCS_TOKEN" }
}
}
} ---
name: incident-triage
description: Correlates production errors with recent commits
tools: ["read", "search", "sentry/get_issue_details"]
mcp-servers:
sentry:
type: local
command: npx
args: ["@sentry/mcp-server@latest"]
tools: ["*"]
env:
SENTRY_ACCESS_TOKEN: ${{ secrets.COPILOT_MCP_SENTRY_TOKEN }}
---
Find the Sentry issue referenced in the task and identify the commit most likely to have caused it. Configurations combine in a fixed order, each level able to override the one before. An agent can use MCP tools configured in its own profile and in repository settings.
flowchart LR OOB["1 · Out-of-the-box servers<br/>GitHub MCP · Playwright MCP"] --> AGENT["2 · Custom agent profile<br/>mcp-servers property"] AGENT --> REPO["3 · Repository settings<br/>Settings → Copilot → MCP servers"] REPO --> EFFECTIVE["Effective configuration<br/>later levels override earlier ones"]
To confirm servers started, open the cloud agent session log, expand the Start MCP Servers step and
look for their tools. Servers that need extra runtimes (for example uv or pipx) need those
installed in copilot-setup-steps.yml.
Configure the GitHub remote MCP server
The GitHub MCP server gives agents GitHub data — repositories, issues, pull requests, Actions runs, code scanning
alerts. It comes in two forms: the remote server hosted by GitHub at
https://api.githubcopilot.com/mcp/ (recommended for most users) and a local server you
run yourself (the ghcr.io/github/github-mcp-server image). GitHub Enterprise Server supports only the local
server.
| Knob | Remote server | Local server |
|---|---|---|
| Enable toolsets | X-MCP-Toolsets: repos,issues header, or a toolset URL such as /mcp/x/actions | GITHUB_TOOLSETS env var or --toolsets |
| Individual tools | X-MCP-Tools header | GITHUB_TOOLS / --tools |
| Read-only | Append /readonly to the URL, or X-MCP-Readonly: true | GITHUB_READ_ONLY=true |
| Lockdown | X-MCP-Lockdown — hides public issue content from users without push access (a content filter, not a security boundary) | GITHUB_LOCKDOWN_MODE |
| Authentication | One-click OAuth in the IDE, or a PAT | PAT or GitHub App credentials |
| Remote-only toolsets | copilot (for example, create a PR with Copilot cloud agent) and support docs search | — |
The default toolsets are repos, issues and pull_requests; others such as
actions, code_security and secret_protection are opt-in.
// .vscode/mcp.json — the remote GitHub MCP server in VS Code
{
"servers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
}
}
} Inside Copilot cloud agent
Cloud agent gets the GitHub MCP server automatically, connected with a specially scoped token that has
read-only access to the current repository only. To give it wider access — for example, to read issues in
another repository — configure the server yourself in repository MCP settings with a PAT or a GitHub App, preferably
still on the /readonly URL, and store the credential as a COPILOT_MCP_ Agents secret.
{
"mcpServers": {
"github-mcp-server": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/readonly",
"tools": ["*"],
"headers": {
"X-MCP-Toolsets": "repos,issues,pull_requests,actions,code_security"
}
}
}
} Configure MCP registries
A registry is a catalogue of MCP servers. IDEs use the GitHub MCP Registry by default; an organization or enterprise can point Copilot at a custom registry so developers discover a curated set of approved servers. A custom registry can be self-hosted (fork the open-source MCP Registry, run it in Docker, or build your own) or provided by Azure API Center with anonymous read access. It must follow the v0.1 registry specification:
GET /v0.1/servers
GET /v0.1/servers/{serverName}/versions/latest
GET /v0.1/servers/{serverName}/versions/{version}
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Enter the registry's base URL in the Copilot policies (enterprise: AI controls → MCP; organization:
Settings → Copilot → Policies). For Azure API Center, give the base URL only — not
/v0.1/servers.
Configure MCP allow lists
Organizations have three levers, from coarse to fine:
- The "MCP servers in Copilot" policy decides whether MCP servers can run at all in Copilot clients for Business and Enterprise users.
- Registry-based restriction — "Restrict MCP access to registry servers": Allow all (any server) or Registry only (only servers in the configured registry; even local servers must be listed, with exactly matching IDs). The policy applies immediately.
managed-settings.jsonallowlists —allowedMcpServersanddeniedMcpServers, matching servers securely by name, URL or exactstdiocommand. This is the method GitHub now recommends.
{
"allowedMcpServers": [
{ "serverUrl": "https://api.githubcopilot.com/*" },
{ "serverCommand": ["npx", "@playwright/mcp@latest"] }
],
"deniedMcpServers": [
{ "serverCommand": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/"] }
]
} | Custom registry restriction | managed-settings.json allowlist | |
|---|---|---|
| Release phase | Public preview, not prioritized | Generally available, recommended |
| Matching | By name or ID only — users can bypass it by editing config files | By name, URL or exact command — secure matching |
| Setup | Host an HTTPS registry that meets the spec | A settings file, usually in the enterprise's .github-private repository, or deployed by MDM |
Managed-settings evaluation is strict and predictable:
- Built-in default servers (such as the GitHub MCP server) are always allowed.
- A server matching any
deniedMcpServersentry is blocked — deny beats allow. - If
allowedMcpServersexists, anything not matching it is blocked. - A URL or command containing an unresolved variable is blocked, because it cannot be verified.
- A malformed list is treated as an empty allowlist — everything except built-ins is blocked.
Registries and allowlists do not govern cloud agent
Private MCP registries and registry-based restrictions apply to Copilot CLI and IDEs, not to
agents running on GitHub. For Copilot cloud agent, MCP servers are controlled through repository MCP
settings and custom agent profiles (including enterprise-level profiles), plus each
server's tools allowlist. "Private MCP registries" is explicitly listed among the policies that do not
apply to cloud agent.
2.3 Integrate agents within development environments
Evaluate the execution context
Execution context is the set of constraints that define where an agent runs and what it can reach: the repository, the branch, the workflow or machine it runs on, and the permissions it holds. It determines both visibility and capability.
| Context | Runs on | Identity and token | Network | Limits |
|---|---|---|---|---|
| Copilot cloud agent | Ephemeral GitHub Actions runner (Ubuntu x64 by default; Windows 64-bit supported; no macOS) | Its own scoped token; can push only to its branch | Agent firewall with a recommended allowlist | One repository, one branch, one PR per task, 59-minute session |
| Copilot CLI (local) | Your machine | Your account and your file-system rights | Your network; URL approvals | Whatever you approve — consider local sandboxing |
| Copilot CLI in Actions | A workflow runner | GITHUB_TOKEN (with copilot-requests: write) or a PAT in COPILOT_GITHUB_TOKEN | Runner network | Workflow permissions; -p denies anything not pre-approved |
| GitHub Agentic Workflows | Containerized Actions job | Read-only token by default; no secrets in the agent process | Firewalled, allowlisted egress | Writes only through safe-outputs, after threat detection |
| IDE agent mode | Your workspace | Your account | Your network | IDE settings and approvals |
Scope an agent to a specific repository
Repository scope is the first boundary. Copilot cloud agent can only change the repository specified when the task starts — it cannot change several repositories in one run — and by default it can only read context (issues, past PRs) from that repository through its GitHub MCP token.
- Availability — for Business and Enterprise, an administrator enables the cloud agent policy (disabled by default); organization owners can opt out some or all repositories; Pro, Pro+ and Max users have it on by default.
- Per-repository configuration — Settings → Copilot → Cloud agent (validation tools, workflow approval), MCP servers, Internet access (firewall), Memory.
- Custom agents — a profile in
.github/agents/applies to that repository; organization and enterprise profiles live in theagents/folder of a.githubor.github-privaterepository. On a name conflict, the lowest level wins: repository over organization over enterprise.
The Learn module's applyTo example
One Learn unit shows an agent profile with an applyTo glob. In the GitHub documentation,
applyTo belongs to path-specific instruction files
(.github/instructions/*.instructions.md), not to custom agent profiles, whose frontmatter properties are
name, description, target, tools, model,
disable-model-invocation, user-invocable, mcp-servers and
metadata. To narrow what an agent works on, restrict its tools and state its scope in the
prompt; to enforce path limits, use CODEOWNERS and hooks.
Configure an agent to be invoked in a CI workflow
Inside a workflow, the workflow is the execution boundary: the trigger, the runner, the
permissions block and the secrets define everything the agent can do. There are three ways in:
| Approach | When | Key details |
|---|---|---|
| GitHub Agentic Workflows (recommended) | Recurring, bounded repository tasks: triage, reports, docs upkeep, CI failure analysis | Markdown + frontmatter, compiled with gh aw compile to a .lock.yml; read-only by default; safe-outputs; engine defaults to Copilot |
| Copilot CLI in a job step | A one-off scripted task in an existing pipeline | npm install -g @github/copilot; copilot -p with minimal --allow-tool; --no-ask-user; -s for clean output; --agent to pick a custom agent |
| Hand off to cloud agent | The CI job should produce a PR, not do the work itself | REST API agent tasks, the GitHub MCP server's Copilot tools, or an automation |
flowchart TB
TRIG["Trigger<br/>schedule · workflow_dispatch · pull_request"] --> JOB
subgraph JOB["Workflow job — the execution boundary"]
direction TB
CO["Checkout"] --> INST["Install Copilot CLI<br/>npm install -g @github/copilot"]
INST --> RUN["copilot -p prompt<br/>--allow-tool · --no-ask-user · -s"]
RUN --> OUT["Publish result<br/>step summary · artifact · safe output"]
end name: Nightly repository summary
on:
schedule:
- cron: '30 17 * * *'
workflow_dispatch:
permissions:
contents: read
copilot-requests: write # lets GITHUB_TOKEN make Copilot requests (org billing)
jobs:
summary:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v7
- run: npm install -g @github/copilot
- name: Summarize today's changes
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
copilot -p "Summarize today's commits on main as a bullet list in summary.md" \
-s --no-ask-user \
--allow-tool='shell(git:*)' --allow-tool='write(summary.md)'
cat summary.md >> "$GITHUB_STEP_SUMMARY" copilot --agent security-reviewer \
-p "Review the diff against main for vulnerabilities" \
-s --no-ask-user --allow-tool='shell(git:*)' Authentication options for the CLI in Actions:
GITHUB_TOKEN— recommended in organization repositories: short-lived, no stored secret, usage billed to the organization. Requires the policy Allow use of Copilot CLI billed to the organization andcopilot-requests: writeinpermissions.- A fine-grained PAT with the Copilot Requests permission, stored as an Actions secret and passed as
COPILOT_GITHUB_TOKEN— the CLI-specific variable, so Copilot's access can differ from the workflow'sGITHUB_TOKEN. Usage is billed to the PAT owner's seat.
Fork pull requests are the risky trigger
Running an agent directly in a workflow step gives it broad access to that environment. Workflows triggered by pull requests from forks are the highest prompt-injection risk; keep permissions minimal and prefer Agentic Workflows, which isolate secrets from the agent process.
Configure an agent to use branch-based scope
- Cloud agent creates a new
copilot/branch for its work and can only push to that single branch. When triggered by@copiloton an existing PR, it gets write access to that PR's branch instead. - You can choose the base branch when starting a task from the Agents tab or panel, so the working branch is cut from, and the PR targets, something other than the default branch.
- The agent is subject to the repository's branch protections and required checks like any contributor.
- In CI, the workflow determines the branch — the agent runs in the checked-out ref.
- Custom agents define focus, tools and behaviour; they do not control branch creation, PR behaviour or execution isolation — that is always the execution context's job.
Enable autonomous actions: branches and pull requests
- Enable the agent — policy for Business/Enterprise, and the repository not opted out.
- Give it a task — assign an issue to Copilot (it reacts 👀 and opens a draft PR), prompt it from the Agents tab, mention
@copiloton a PR, delegate from the CLI with/delegateor an&prefix, or call the API. - Let it work — it researches, plans, creates the branch, commits and pushes, validating with tests and linters in its environment.
- Create the pull request — sessions started from the Agents tab do not open a PR automatically; click Create pull request when satisfied, or ask for one in the prompt. Issue assignment and
/delegateopen a draft PR. - Review and merge — a human marks it ready, approves and merges. Copilot cannot mark it ready for review, approve it or merge it.
When rulesets block the agent
If a ruleset is incompatible with the agent — for example, it only allows specific commit authors — access to cloud agent is blocked. The documented fix is to add Copilot as a bypass actor on that ruleset — which is one reason to express agent policy as rulesets rather than classic branch protection. Copilot can sign its commits, so "require signed commits" is not by itself a blocker.
Handle environment-specific constraints
Cloud agent can discover and install dependencies by trial and error, but that is slow, non-deterministic and fails for private packages. Make the environment deterministic with a Copilot setup steps workflow.
name: "Copilot Setup Steps"
on:
workflow_dispatch:
push:
paths: [.github/workflows/copilot-setup-steps.yml]
pull_request:
paths: [.github/workflows/copilot-setup-steps.yml]
jobs:
copilot-setup-steps: # MUST be named exactly this
runs-on: ubuntu-latest # or a larger / self-hosted / Windows runner label
timeout-minutes: 30 # maximum 59
permissions:
contents: read # Copilot gets its own token for its work
steps:
- uses: actions/checkout@v7
with:
lfs: true
- uses: actions/setup-node@v7
with:
node-version: "24"
cache: "npm"
- run: npm ci - The file must contain a single job named
copilot-setup-steps, and it only takes effect once it is on the default branch. - Only
steps,permissions,runs-on,services,snapshotandtimeout-minutes(max 59) can be customized; anything else is ignored. - If a step exits non-zero, the remaining setup steps are skipped and Copilot starts work with the environment as it is.
- Any
fetch-depthyou set onactions/checkoutis overridden so the agent can roll back commits on request. - The workflow's
GITHUB_TOKENis used by the setup steps only; Copilot receives its own token for the session.
| Constraint | Configuration |
|---|---|
| Private dependencies or internal registry | Agents secrets and variables (Settings → Secrets and variables → Agents), used by setup steps; firewall allowlist entry for the registry host |
| More CPU, memory or private networking | runs-on a larger runner label; with Azure private networking, allow the Copilot API hosts outbound |
| Access to internal resources | Self-hosted ephemeral runners (ARC or scale sets); disable the integrated firewall, which is not compatible; set proxy variables if needed |
| Windows toolchain | Windows runner label — the integrated firewall is not compatible with Windows, so pair it with your own network controls |
| Large files | actions/checkout with lfs: true in setup steps |
| Services such as a database | services: in the setup steps job |
| Blocked outbound request | Add the domain or URL to the custom allowlist (organization or repository, under Internet access) |
| Session too long | The 59-minute session limit cannot be raised — split the task |
The agent firewall
By default cloud agent's internet access is limited by a firewall, which reduces the risk of code or secrets being exfiltrated by unexpected behaviour or injected instructions. GitHub hosts are always reachable; a recommended allowlist (OS package repositories, container registries, language package registries, certificate authorities, Playwright browser downloads) is on by default. Blocked requests appear as a warning in the PR.
- Organization owners set Enable firewall and Recommended allowlist to Enabled, Disabled or Let repositories decide (the default), control whether repositories may add custom rules, and maintain an organization custom allowlist that repositories cannot remove.
- Allowlist entries are domains (the domain and its subdomains) or URLs (scheme, host and path prefix).
- Limitations: it applies only to processes the agent starts through its shell tool — not to MCP server processes or setup-step processes — only inside the Actions appliance, and sophisticated attacks may bypass it. It is a mitigation, not a complete security boundary.
2.4 Operate agents with safe execution paths and robust error handling
Reliable systems assume failure: agents misread tasks, tests fail, changes collide. The architecture should detect failure early, contain it, and hand it to a person with enough context to act.
flowchart TB
FAIL["Required check fails"] --> RETRY{"Attempts < 2?"}
RETRY -->|"yes"| FIX["Agent revises the branch<br/>and re-runs checks"]
FIX --> FAIL2{"Green?"}
FAIL2 -->|"yes"| REVIEW["Human review and merge"]
FAIL2 -->|"no"| RETRY
RETRY -->|"no"| ESC["Escalate<br/>what failed · what was tried · evidence · next step"]
ESC --> HUMAN["Human decides<br/>fix · close PR · revert"] Implement error handling
- Fail fast in workflows — leave
continue-on-errorfalse, useset -euo pipefailin scripts, and never let a failed agent step be reported as green. - Log meaningfully —
::error::annotations, step summaries and uploaded logs rather than silent failure. - Prevent partial changes — work on a branch so an aborted run leaves nothing on the default branch.
- Hook into errors — the
errorOccurredhook fires on agent errors (logging, alerting), andpostToolUseFailurecan add recovery guidance for the model after a failed tool call.
Implement retries
Retry transient failures — a network blip, a flaky runner — a bounded number of times, with backoff. Do not retry deterministic failures (a real test failure, a security block): that only burns time and AI credits.
- name: Run agent task with bounded retries
run: |
for attempt in 1 2 3; do
if copilot -p "Fix the failing unit tests" -s --no-ask-user \
--allow-tool='shell(npm:*)' --allow-tool=write; then
exit 0
fi
echo "::warning::Attempt $attempt failed"
sleep $((attempt * 10))
done
echo "::error::Agent failed after 3 attempts — escalating"
exit 1 - Copilot CLI autopilot has
--max-autopilot-continuesto cap automatic continuations; set it explicitly in automation. - An
agentStophook that keeps forcing continuation is overridden after 8 consecutive blocks — a runaway guard. - A common team policy: if the same required check fails twice, stop iterating and escalate.
Implement rollbacks
| Situation | Rollback |
|---|---|
| Agent output is wrong, not merged | Request changes, or close the PR and delete the branch — branch isolation means nothing reached main |
| Already merged | Revert the merge commit through a new PR; small, scoped PRs make this safe |
| Copilot CLI session went wrong | Press Esc twice when idle (or /rewind) and choose "rewind conversation" or "rewind and restore files" |
| Bad deployment | Redeploy an explicit earlier commit or tag — deploy by reference, not "latest on a branch" |
| Cloud agent should undo its own commit | Ask it — it keeps enough history to roll back commits on request |
CLI rewind restores only the files Copilot changed, skips files you edited since, and cannot restore files over 10 MB or turns that changed more than 500 files.
Implement escalation paths
- Required review — every agent PR reaches a human; CODEOWNERS auto-requests the right one.
- Notify on failure — an
if: failure()step that opens an issue or pings maintainers with the run link. - Deny with a reason — a
preToolUsehook denies a risky tool call and returnspermissionDecisionReason, which is surfaced for a human to act on. - Steer or stop — follow-up prompts on the Agents page, or Stop session.
- A clear escalation report — what failed, what was attempted, what evidence exists, and the suggested next step.
- name: Escalate to maintainers
if: failure()
env:
GH_TOKEN: ${{ github.token }}
run: |
gh issue create \
--title "Agent run ${{ github.run_id }} needs a human" \
--label needs-human \
--body "Failed step logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" Implement traceability and accountability
| Question | Where the answer lives |
|---|---|
| Who started this? | Commit co-author, the automation's creator, audit log user field |
| Was it really the agent? | Commits authored by Copilot and signed ("Verified"); audit events with actor_is_agent: true |
| Why was this change made? | The session log linked from every commit message; the plan in the PR |
| What did it run? | Session log tool calls; workflow run logs; hook audit logs |
| Which session produced this event? | agent_session_id on audit log events (filter with actor:Copilot, 180 days) |
| What is it doing right now? | The Agents page, or gh agent-task view --log --follow |
Make evidence traceable to a run and a commit
Name and label evidence so an auditor can answer "which run produced this artifact, against which code state?" — for example, include the commit SHA and run ID in the artifact name, and link both from the PR's Evidence section. The guardrails page covers audit and governance cadence.