AI Engines (aka Coding Agents)
GitHub Agentic Workflows use AI Engines (normally a coding agent) to interpret and execute natural language instructions.
Available Coding Agents
Section titled “Available Coding Agents”Set engine: in your workflow frontmatter and configure the corresponding secret:
| Engine | engine: value | Required Secret |
|---|---|---|
| GitHub Copilot CLI (default) | copilot | COPILOT_GITHUB_TOKEN |
| Claude by Anthropic (Claude Code) | claude | ANTHROPIC_API_KEY |
| OpenAI Codex | codex | OPENAI_API_KEY |
| Google Gemini CLI | gemini | GEMINI_API_KEY |
| Crush (experimental) | crush | COPILOT_GITHUB_TOKEN |
| OpenCode (experimental) | opencode | COPILOT_GITHUB_TOKEN |
| Pi (experimental) | pi | COPILOT_GITHUB_TOKEN (default); switches to provider-specific secret when model: uses provider/model format |
Copilot CLI is the default — engine: can be omitted when using Copilot. See the linked authentication docs for secret setup instructions.
Which engine should I choose?
Section titled “Which engine should I choose?”Choose the engine that best matches your needs and existing AI account: Copilot supports the broadest gh-aw feature set, including custom agents and autopilot-style continuations; Claude offers stronger control over turn limits (max-turns) for long reasoning sessions; and Gemini or Codex fit well when those models are already part of existing tooling or budget decisions. You can switch later by changing only engine: and the corresponding secret.
Engine Feature Comparison
Section titled “Engine Feature Comparison”Not all features are available across all engines. The table below summarizes per-engine support for commonly used workflow options:
| Feature | Copilot | Claude | Codex | Gemini | Crush | OpenCode | Pi |
|---|---|---|---|---|---|---|---|
max-runs (AWF invocation cap) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
max-turns | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
max-continuations | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
tools.web-fetch | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
tools.web-search | via MCP | via MCP | ✓ (opt-in) | via MCP | via MCP | via MCP | via MCP |
engine.agent (custom agent file) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
engine.api-target (custom endpoint) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
engine.bare (disable context loading) | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
engine.harness (custom harness script) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Tools allowlist | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
max-runs (default 500) and max-effective-tokens (default 25M) are top-level frontmatter fields supported by all engines. max-turns limits Claude iterations per run; max-continuations enables Copilot autopilot mode. Codex web-search is opt-in via tools: web-search:; other engines use a third-party MCP server — see Using Web Search. engine.agent, engine.bare, and engine.harness are described below.
Extended Coding Agent Configuration
Section titled “Extended Coding Agent Configuration”Workflows can specify extended configuration for the coding agent:
engine: id: copilot version: latest # defaults to latest model: gpt-5 # example override; omit to use engine default command: /usr/local/bin/copilot # custom executable path args: ["--add-dir", "/workspace"] # custom CLI arguments agent: agent-id # custom agent file identifier api-target: api.acme.ghe.com # custom API endpoint hostname (GHEC/GHES)Pinning a Specific Engine Version
Section titled “Pinning a Specific Engine Version”By default, workflows install the latest available version of each engine CLI. To pin to a specific version, set version to the desired release:
| Engine | id | Example version |
|---|---|---|
| GitHub Copilot CLI | copilot | "0.0.422" |
| Claude Code | claude | "2.1.70" |
| Codex | codex | "0.111.0" |
| Gemini CLI | gemini | "0.31.0" |
| Crush | crush | "1.2.14" |
| OpenCode | opencode | "0.1.0" |
| Pi | pi | "0.72.1" |
engine: id: copilot version: "0.0.422"Pinning is useful when you need reproducible builds or want to avoid breakage from a new CLI release while testing. Remember to update the pinned version periodically to pick up bug fixes and new features.
version also accepts a GitHub Actions expression string, enabling workflow_call reusable workflows to parameterize the engine version via caller inputs. Expressions are passed injection-safely through an environment variable rather than direct shell interpolation:
on: workflow_call: inputs: engine-version: type: string default: latest
---
engine: id: copilot version: ${{ inputs.engine-version }}Copilot Custom Configuration
Section titled “Copilot Custom Configuration”Use agent to reference a custom agent file in .github/agents/ (omit the .agent.md extension):
engine: id: copilot agent: technical-doc-writer # .github/agents/technical-doc-writer.agent.mdSee Copilot Agent Files for details.
Engine Environment Variables
Section titled “Engine Environment Variables”All engines support custom environment variables through the env field:
engine: id: copilot env: DEBUG_MODE: "true" AWS_REGION: us-west-2 CUSTOM_API_ENDPOINT: https://api.example.comEnvironment variables can also be defined at workflow, job, step, and other scopes. See Environment Variables for complete documentation on precedence and all 13 env scopes.
Enterprise API Endpoint (api-target)
Section titled “Enterprise API Endpoint (api-target)”The api-target field specifies a custom API endpoint hostname for the agentic engine. Use this when running workflows against GitHub Enterprise Cloud (GHEC), GitHub Enterprise Server (GHES), or any custom AI endpoint.
For a complete setup and debugging walkthrough for GHE Cloud with data residency, see Debugging GHE Cloud with Data Residency.
The value must be a hostname only — no protocol or path (e.g., api.acme.ghe.com, not https://api.acme.ghe.com/v1). The field works with any engine.
Example — specify a GHEC or GHES Copilot endpoint (use api.enterprise.githubcopilot.com for GHES):
engine: id: copilot api-target: api.acme.ghe.comnetwork: allowed: - defaults - acme.ghe.com - api.acme.ghe.comThe specified hostname must also be listed in network.allowed for the firewall to permit outbound requests.
Custom API Endpoints via Environment Variables
Section titled “Custom API Endpoints via Environment Variables”Set a base URL environment variable in engine.env to route API calls to an internal LLM router, Azure OpenAI deployment, or corporate proxy. AWF automatically extracts the hostname and applies it to the API proxy. The target domain must also appear in network.allowed.
| Engine | Environment variable |
|---|---|
codex, crush | OPENAI_BASE_URL |
claude | ANTHROPIC_BASE_URL |
copilot | GITHUB_COPILOT_BASE_URL |
gemini | GEMINI_API_BASE_URL |
engine: id: codex model: gpt-4o env: OPENAI_BASE_URL: "https://llm-router.internal.example.com/v1" OPENAI_API_KEY: ${{ secrets.LLM_ROUTER_KEY }}
network: allowed: - github.com - llm-router.internal.example.comGITHUB_COPILOT_BASE_URL is a fallback — if both it and engine.api-target are set, engine.api-target takes precedence. Crush uses OpenAI-compatible API format; its model field uses provider/model format (e.g., openai/gpt-4o).
Copilot Bring Your Own Key (BYOK) Mode
Section titled “Copilot Bring Your Own Key (BYOK) Mode”The Copilot engine supports routing requests to an external LLM provider instead of GitHub’s default routing. This is useful when you want to use a different model or provider (e.g., OpenAI, Anthropic, Azure OpenAI, or a local Ollama/vLLM instance) while still using the Copilot CLI tooling.
Set COPILOT_PROVIDER_BASE_URL in engine.env to activate BYOK mode. The credential variables COPILOT_PROVIDER_BASE_URL, COPILOT_PROVIDER_API_KEY, and COPILOT_PROVIDER_BEARER_TOKEN are explicitly allowed to carry ${{ secrets.* }} references in engine.env under strict mode — they are not leaked to the agent container. Other COPILOT_PROVIDER_* variables hold non-sensitive configuration and can be set as plain strings.
| Variable | Required | Description |
|---|---|---|
COPILOT_PROVIDER_BASE_URL | ✓ for BYOK | Base URL of the external provider (e.g. https://api.openai.com/v1) |
COPILOT_MODEL | ✓ for BYOK | Model to use (e.g. claude-sonnet-4, gpt-4o); required by most providers |
COPILOT_PROVIDER_API_KEY | Optional | API key for cloud providers (OpenAI, Anthropic, etc.); not needed for local providers |
COPILOT_PROVIDER_BEARER_TOKEN | Optional | Bearer token alternative to COPILOT_PROVIDER_API_KEY; takes precedence when set |
COPILOT_PROVIDER_TYPE | Optional | Provider format: openai (default), azure, or anthropic |
COPILOT_PROVIDER_WIRE_API | Optional | Wire API variant: completions (default) or responses (for GPT-5 series) |
COPILOT_PROVIDER_MODEL_ID | Optional | Model ID sent on the wire when it differs from COPILOT_MODEL (e.g. an Azure deployment name) |
COPILOT_PROVIDER_WIRE_MODEL | Optional | Alternative to COPILOT_PROVIDER_MODEL_ID for overriding the wire model |
COPILOT_PROVIDER_MAX_PROMPT_TOKENS | Optional | Override the maximum prompt token limit (otherwise resolved from model catalog) |
COPILOT_PROVIDER_MAX_OUTPUT_TOKENS | Optional | Override the maximum output token limit |
Example:
engine: id: copilot env: COPILOT_PROVIDER_BASE_URL: ${{ secrets.PROVIDER_BASE_URL }} # REQUIRED — activates BYOK COPILOT_MODEL: claude-sonnet-4 # REQUIRED for most providers COPILOT_PROVIDER_API_KEY: ${{ secrets.PROVIDER_API_KEY }} # OPTIONAL for local providers COPILOT_PROVIDER_TYPE: anthropic # OPTIONAL — default: openai
network: allowed: - defaults - your-provider-domain.example.com[!NOTE] Credentials are kept out of the agent container — only a dummy API key activating the AWF BYOK detection path is visible to the agent process; the real credential is isolated in the AWF API proxy sidecar. See AWF sandbox architecture.
Engine Command-Line Arguments
Section titled “Engine Command-Line Arguments”All engines support custom command-line arguments through the args field, injected before the prompt:
engine: id: copilot args: ["--add-dir", "/workspace", "--verbose"]Arguments are added in order and placed before the --prompt flag. Consult the specific engine’s CLI documentation for available flags.
Custom Engine Command
Section titled “Custom Engine Command”Override the default engine executable using the command field. Useful for testing pre-release versions, custom builds, or non-standard installations. Installation steps are automatically skipped.
engine: id: copilot command: /usr/local/bin/copilot-dev # absolute path args: ["--verbose"]Custom Harness Script (harness)
Section titled “Custom Harness Script (harness)”The harness field lets you replace the built-in Node.js harness wrapper that the Copilot engine uses to launch the CLI. Use this when you need to customize startup behavior, inject pre/post hooks, or test an alternative harness implementation.
engine: id: copilot harness: custom_copilot_harness.cjsThe value must be a bare filename — no directory separators, no .., and no shell metacharacters. It must end with .js, .cjs, or .mjs. When harness is set, AWF automatically ensures Node 24 is available in the runner environment.
[!NOTE]
engine.harnessis currently only applied during Copilot engine execution. Setting it on other engines has no effect.
Validation rules:
| Rule | Valid example | Invalid example |
|---|---|---|
| Bare filename only | my_harness.cjs | subdir/harness.cjs |
| No path traversal | harness.mjs | ../harness.cjs |
Must start with [A-Za-z0-9_] | harness.js | -harness.cjs |
Must end with .js, .cjs, or .mjs | wrapper.cjs | harness.sh |
Copilot SDK Support
Section titled “Copilot SDK Support”Enable engine.copilot-sdk: true to run Copilot in SDK mode.
In this mode, the harness starts a local sidecar and runs the
SDK driver process instead of the default CLI-only flow.
Use engine.copilot-sdk-driver to replace the built-in
copilot_sdk_driver.cjs implementation:
engine: id: copilot copilot-sdk: true copilot-sdk-driver: .github/drivers/custom-copilot-driver.jscopilot-sdk-driver must be a relative path from the workspace root
(no absolute paths, .., backslashes, or shell metacharacters). It supports:
- script filenames ending with
.js,.cjs,.mjs,.py,.ts,.mts, or.rb - bare command names without an extension (resolved from
PATH)
See Copilot SDK Driver Specification for the full driver contract.
SDK driver environment variables
Section titled “SDK driver environment variables”The specification defines the driver environment contract. In SDK mode, gh-aw injects required runtime values:
GH_AW_PROMPTCOPILOT_SDK_URICOPILOT_CONNECTION_TOKEN
COPILOT_MODEL is required and must be set to the model to use
(e.g. gpt-4o, claude-sonnet-4). Drivers MUST fail fast when
it is not set.
For runtime controls, the driver should consume:
COPILOT_SDK_SEND_TIMEOUT_MSCOPILOT_SDK_LOG_LEVEL
In gh-aw, COPILOT_SDK_SEND_TIMEOUT_MS is usually injected
automatically from workflow timeout-minutes (via
GH_AW_TIMEOUT_MINUTES) with safety headroom. Override it in
engine.env only when you need a custom SDK send timeout.
COPILOT_SDK_LOG_LEVEL is a host-provided driver control and
should be honored when gh-aw passes it to the driver process.
Do not set COPILOT_CONNECTION_TOKEN manually. The harness
generates it per run and passes the same token to both the
sidecar and driver process.
engine: id: copilot copilot-sdk: true copilot-sdk-driver: .github/drivers/my_driver.ts model: gpt-5 env: COPILOT_SDK_SEND_TIMEOUT_MS: "900000" COPILOT_SDK_LOG_LEVEL: infoBare Mode (bare)
Section titled “Bare Mode (bare)”Set engine.bare: true to disable automatic loading of context and custom instructions by the engine. Use this when the workflow prompt is fully self-contained and you want to prevent the engine from reading memory files, AGENTS.md, or built-in system prompts that would otherwise be loaded automatically.
engine: id: claude bare: trueThe underlying mechanism is engine-specific:
| Engine | Effect |
|---|---|
| Copilot | Passes --no-custom-instructions — suppresses .github/AGENTS.md and user-level custom instructions |
| Claude | Passes --bare — suppresses CLAUDE.md memory files |
| Codex | Passes --no-system-prompt — suppresses the default system prompt |
| Gemini | Sets GEMINI_SYSTEM_MD=/dev/null — overrides the built-in system prompt with an empty file |
Defaults to false.
Custom Token Weights (token-weights)
Section titled “Custom Token Weights (token-weights)”Override the built-in token cost multipliers used when computing Effective Tokens. Useful when your workflow uses a custom model not in the built-in list, or when you want to adjust the relative cost ratios for your use case.
engine: id: claude token-weights: multipliers: my-custom-model: 2.5 # 2.5x the cost of claude-sonnet-4.5 experimental-llm: 0.8 # Override an existing model's multiplier token-class-weights: output: 6.0 # Override output token weight (default: 4.0) cached-input: 0.05 # Override cached input weight (default: 0.1)multipliers is a map of model names to numeric multipliers relative to claude-sonnet-4.5 (= 1.0). Keys are case-insensitive and support prefix matching. token-class-weights overrides the per-class weights applied before the model multiplier; the defaults are input: 1.0, cached-input: 0.1, output: 4.0, reasoning: 4.0, cache-write: 1.0.
Custom weights are embedded in the compiled workflow YAML and read by gh aw logs and gh aw audit when analyzing runs.
Timeout Configuration
Section titled “Timeout Configuration”Repositories with long build or test cycles require careful timeout tuning at multiple levels. This section documents the timeout knobs available for each engine.
Job-Level Timeout (timeout-minutes)
Section titled “Job-Level Timeout (timeout-minutes)”timeout-minutes sets the maximum wall-clock time for the entire agent job. This is the primary knob for repositories with long build times. The default is 20 minutes.
timeout-minutes: 60 # allow up to 60 minutes for the agent jobSee Long Build Times in the Sandbox reference for recommended values and concrete examples, including a 30-minute C++ workflow.
Per-Tool-Call Timeout (tools.timeout)
Section titled “Per-Tool-Call Timeout (tools.timeout)”tools.timeout limits how long any single tool invocation may run, in seconds. Useful when individual bash commands (builds, test suites) take longer than an engine’s default:
tools: timeout: 300 # 5 minutes per tool callDefaults: Claude 60s, Codex 120s. Other engines (Copilot, Gemini, Crush) are engine-managed and not enforced by gh-aw. See Tool Timeout Configuration for full documentation including tools.startup-timeout.
Per-Engine Timeout Controls
Section titled “Per-Engine Timeout Controls”| Knob | Copilot | Claude | Codex/Gemini/Crush/OpenCode | Purpose |
|---|---|---|---|---|
timeout-minutes | ✓ | ✓ | ✓ | Job-level wall clock |
tools.timeout | ✓ | ✓ | ✓ | Per tool-call limit (seconds) |
tools.startup-timeout | ✓ | ✓ | ✓ | MCP server startup limit |
max-turns | ✗ | ✓ | ✗ | Iteration budget |
max-continuations | ✓ | ✗ | ✗ | Autopilot run budget |
Copilot uses max-continuations for autopilot runs; Claude uses max-turns to cap iterations. Codex, Gemini, Crush, and OpenCode rely solely on timeout-minutes and tools.timeout.
# Claude — combine iteration cap with per-tool timeoutengine: id: claudemax-turns: 20tools: timeout: 600timeout-minutes: 60When max-turns is set in frontmatter, gh-aw passes it to Claude automatically — no need to also set the CLAUDE_CODE_MAX_TURNS env var.
Claude Tool Enforcement Security Model
Section titled “Claude Tool Enforcement Security Model”Claude Code accepts a --permission-mode flag that determines whether the declared tools: allowlist is enforced. Set engine.permission-mode to one of auto, acceptEdits, plan, or bypassPermissions:
engine: id: claude permission-mode: autoengine.permission-mode takes precedence over any --permission-mode flag supplied through engine.args. When unset, the default is acceptEdits (or auto when tools.edit: false). gh-aw does not derive bypassPermissions implicitly from unrestricted bash — set it explicitly.
engine.permission-mode | Effective mode | --allowed-tools enforced? | Gateway allowed: enforced? |
|---|---|---|---|
| unset (default) | acceptEdits | ✓ Yes | ✓ Yes |
unset, with tools.edit: false | auto | ✓ Yes | ✓ Yes |
auto | auto | ✓ Yes | ✓ Yes |
acceptEdits | acceptEdits | ✓ Yes | ✓ Yes |
plan | plan | ✓ Yes | ✓ Yes |
bypassPermissions | bypassPermissions | ✗ No | ✓ Yes |
Gateway-side enforcement
Section titled “Gateway-side enforcement”The MCP gateway’s allowed: filter is the sole effective tool boundary in bypassPermissions mode (and a second layer of enforcement otherwise). Always specify allowed: on each mcp-servers: entry to restrict which MCP tools are reachable:
mcp-servers: notion: container: "mcp/notion" allowed: ["search_pages", "get_page"] # enforced at gateway level[!WARNING] Do not rely on
tools:ormcp-servers: allowed:for security guarantees inbypassPermissionsmode. The agent can already run arbitrary shell commands when unrestricted bash is granted, so--allowed-toolsprovides no meaningful additional boundary.
Related Documentation
Section titled “Related Documentation”- Frontmatter - Complete configuration reference
- Tools - Available tools and MCP servers
- Security Guide - Security considerations for AI engines
- MCPs - Model Context Protocol setup and configuration
- Long Build Times - Timeout tuning for large repositories
- Self-Hosted Runners - Fast hardware for long-running workflows