Configuration¶
CopilotTask configures a session; the task is passed separately to
copilot_run. This page covers the common settings. The
API reference contains the full signature.
Authentication¶
Supported authentication paths:
gh auth login --hostname github.comGITHUB_TOKENorGH_TOKENin CI or automation
Task sessions select GITHUB_TOKEN first, then GH_TOKEN. If neither
is set, the SDK uses its signed-in user. Tokens must authorize the intended
GitHub account and Copilot access.
The framework uses GitHub Copilot. There is no separate provider configuration.
CopilotTask¶
from pytest_copilot.copilot import CopilotTask
agent = CopilotTask(
name="my-task",
model="gpt-5.6-sol",
instructions="Use the supplied tools to complete the user's task.",
timeout_s=120,
max_tool_calls=10,
)
Common settings¶
| Field | Meaning |
|---|---|
name |
Label identifying the configuration in saved evidence |
model |
Copilot model name; None uses Copilot's default |
instructions |
System prompt content; distinct from the user task |
system_message_mode |
append by default; replace replaces the SDK system message content |
response_schema |
Optional Pydantic model class defining the final JSON answer |
working_directory |
Session workspace; not a security sandbox |
mcp_servers |
Copilot SDK server config mapping |
allowed_tools |
Optional global tool allow-list |
excluded_tools |
Tool block-list |
custom_agents |
Loaded .agent.md definitions |
active_agent |
Custom agent to activate at session start |
skill_directories |
Explicit individual skill directories or parents; enable loading and check SDK availability before sending |
disabled_skills |
Skill names deliberately disabled, retained in discovery evidence |
max_turns |
Advisory top-level turn budget; used for subagent turn caps |
timeout_s |
Hard wall-clock limit for the run |
max_tool_calls |
Optional hard tool-admission budget; nonnegative integer |
auto_confirm |
Auto-approve permission requests by default |
audit_requests |
Capture actual outgoing request metadata when enabled |
client_mode |
copilot-cli by default; empty disables ambient configuration and persona injection |
extra_config |
SDK settings not exposed as direct fields; overrides subject to validation |
Limits and permissions¶
max_turns is not a hard top-level limit. Use timeout_s and, where appropriate,
max_tool_calls to bound execution. Tool admission does not prove a tool
completed or the application operation succeeded.
auto_confirm=True is convenient for deterministic tests, but it is not an
isolation boundary. Restrict tools and use guarded adapters and an approved
execution environment. A temporary working directory alone does not prevent
filesystem or network access.
One attempt per call¶
Each copilot_run call makes one execution attempt. Startup, connection, and
task failures are recorded without automatically starting another session.
Rerun a selected pytest case explicitly when appropriate.
JSON answers with a required shape¶
Pass a Pydantic model class as response_schema to request a JSON answer with
specific fields and types. The SDK checks the final answer against that model.
An invalid answer fails the run; the framework does not retry or repair it.
from pydantic import BaseModel, ConfigDict
from pytest_copilot.copilot import CopilotTask
class Answer(BaseModel):
model_config = ConfigDict(extra="forbid", strict=True)
count: int
async def test_count(copilot_run):
agent = CopilotTask(
name="count",
client_mode="empty",
response_schema=Answer,
)
result = await copilot_run(agent, "What is 19 plus 23? Return the count.")
assert result.success, result.error
assert result.final_response is not None
answer = Answer.model_validate_json(result.final_response)
assert answer.count == 42
final_response stays the original answer text. The configuration snapshot and
saved JSON contain the requested JSON schema, not the Python class. The setting
applies only to the parent's final answer; framework-owned child sessions do not
inherit it. Omit it for ordinary text answers.
A matching shape does not establish that the answer is correct. Keep ordinary assertions against the expected values or actual application output.
Explicit skills and empty mode¶
Nonempty skill_directories set the public SDK enable_skills=True by default.
enable_config_discovery remains false. In empty mode, personal/project discovery,
custom instructions, file hooks, managed settings, and persona injection stay
disabled. Explicit tools and system prompt content remain supported.
Explicit directories combined with extra_config={"enable_skills": False} are
rejected, not silently ignored. None leaves the explicit-skills default enabled.
An empty directory list plus enable_skills=False is allowed. Use disabled_skills
to deliberately disable named skills within an otherwise loaded set.
Every requested directory must contain a valid SKILL.md or immediate child
skill directories. Relative paths use the Python caller's current directory.
Validation and public SDK discovery run before the task is sent; setup failure
is recorded as an execution error with no model send. SDK metadata and diagnostics
are available as result.skill_discovery and in native JSON, separately from reads.
See Skills for input and evidence details.
MCP server mappings¶
Attach servers directly to CopilotTask(mcp_servers={...}).
SDK configuration discovery is disabled by default. Personal or ambient MCP configuration is not automatically attached to task sessions. Supply servers, skills, and custom agents explicitly, or use the configuration loaders for the project under test.
To test SDK discovery deliberately, opt in:
agent = CopilotTask(
name="configuration-discovery",
extra_config={"enable_config_discovery": True},
)
This opt-in can expose machine-specific tools and configuration; avoid it in tests intended to be reproducible across developer machines and CI.
For example, attach the bundled Banking server over stdio:
import sys
BANKING_MCP = {
"banking": {
"command": sys.executable,
"args": ["-m", "pytest_copilot.testing.banking_mcp"],
"tools": ["*"],
}
}
Remote transports use the same mapping:
REMOTE_MCP = {
"crm": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": {"Authorization": "Bearer ${CRM_TOKEN}"},
"tools": ["*"],
}
}
Command-line tools¶
Use create_command_tool from pytest_copilot.copilot for direct
executable calls with separate arguments, including native help, setup, and
supporting commands. Attach tools through extra_config={"tools": [tool]};
use client_mode="empty" and allowed_tools to expose only the intended tools.
No shell or MCP wrapper is needed. Consumers still own application permissions
and independent checks. See Test CLI tools for
the shared helper's input, output, time limits, and shell-specific exceptions.
pytest configuration¶
[tool.pytest.ini_options]
asyncio_mode = "auto"
addopts = """
--aitest-json=aitest-reports/results.json
"""
pytest-copilot init adds these settings to an existing
project. It reports conflicting values instead of replacing them.
Related references¶
Use CLI options for repetition and evidence flags, CopilotResult for returned fields, and assertions for independent output checks. Saved evidence requires the current schema exactly; loading it does not execute a model.