From c7cd7fb27553cee3e7d26d5ea668c2dc1474af47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:02:37 +0000 Subject: [PATCH 1/5] Initial plan From f12ece2e849f64da979014d85990f3d76d8422d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:12:22 +0000 Subject: [PATCH 2/5] feat: map engine max-turns to --max-turns Copilot CLI argument Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/75070f2d-6d49-4a55-b510-334a170b04af Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- src/compile/common.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/compile/common.rs b/src/compile/common.rs index 26c1d33..e97ca5b 100644 --- a/src/compile/common.rs +++ b/src/compile/common.rs @@ -310,6 +310,9 @@ pub fn generate_copilot_params(front_matter: &FrontMatter) -> String { let mut params = Vec::new(); params.push(format!("--model {}", front_matter.engine.model())); + if let Some(max_turns) = front_matter.engine.max_turns() { + params.push(format!("--max-turns {}", max_turns)); + } params.push("--disable-builtin-mcps".to_string()); params.push("--no-ask-user".to_string()); @@ -894,6 +897,23 @@ mod tests { assert!(params.contains("--mcp ado")); } + #[test] + fn test_copilot_params_max_turns() { + let (fm, _) = parse_markdown( + "---\nname: test\ndescription: test\nengine:\n model: claude-opus-4.5\n max-turns: 50\n---\n", + ) + .unwrap(); + let params = generate_copilot_params(&fm); + assert!(params.contains("--max-turns 50")); + } + + #[test] + fn test_copilot_params_no_max_turns_when_simple_engine() { + let fm = minimal_front_matter(); + let params = generate_copilot_params(&fm); + assert!(!params.contains("--max-turns")); + } + // ─── sanitize_filename ──────────────────────────────────────────────────── #[test] From 2b3249caf2d66e28925688f06201d3d841d2e09a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:22:47 +0000 Subject: [PATCH 3/5] docs: document engine max-turns in AGENTS.md Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/8c7be6ee-473d-4cde-96ca-67ec8b8ca65c Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- AGENTS.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 6d7cecd..2b29276 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -285,6 +285,39 @@ schedule: - release/* ``` +### Engine Configuration + +The `engine` field specifies which AI model to use and optional execution parameters. It accepts both a simple string format (model name only) and an object format with additional options. + +```yaml +# Simple string format (just a model name) +engine: claude-opus-4.5 + +# Object format with additional options +engine: + model: claude-opus-4.5 + max-turns: 50 + timeout-minutes: 30 +``` + +#### Fields + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `model` | string | `claude-opus-4.5` | AI model to use. Options include `claude-sonnet-4.5`, `gpt-5.2-codex`, `gemini-3-pro-preview`, etc. | +| `max-turns` | integer | *(none)* | Maximum number of agentic turns (tool-use iterations) the model is allowed per run. Maps to the `--max-turns` Copilot CLI argument. Use this to cap compute and prevent runaway loops. | +| `timeout-minutes` | integer | *(none)* | Workflow timeout in minutes. | + +#### `max-turns` + +Each "turn" is one iteration of the model calling a tool and receiving its output. Setting `max-turns` places an upper bound on how many such iterations the agent can perform in a single pipeline run. This is useful for: + +- **Cost control** — limiting expensive model invocations. +- **Safety** — preventing infinite loops where the agent repeatedly calls tools without converging on a result. +- **Predictability** — ensuring the pipeline completes within a reasonable time frame. + +When omitted, the Copilot CLI uses its built-in default. When set, the compiler emits `--max-turns ` in the generated pipeline's agency params. + ### Tools Configuration The `tools` field controls which tools are available to the agent. Both sub-fields are optional and have sensible defaults. @@ -439,6 +472,7 @@ Should be replaced with the human-readable name from the front matter (e.g., "Da Additional params provided to agency CLI. The compiler generates: - `--model ` - AI model from `engine` front matter field (default: claude-opus-4.5) +- `--max-turns ` - Maximum agentic turns from `engine.max-turns` (omitted when not set) - `--disable-builtin-mcps` - Disables all built-in MCPs initially - `--no-ask-user` - Prevents interactive prompts - `--allow-tool ` - Explicitly allows specific tools (github, safeoutputs, write, shell commands like cat, date, echo, grep, head, ls, pwd, sort, tail, uniq, wc, yq) From 35923231342b0bcf1377b3cccc055cc2027ba9a4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:31:10 +0000 Subject: [PATCH 4/5] feat: map engine timeout-minutes to --max-timeout Copilot CLI argument Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/37d8db70-af7e-4c5b-bbab-e3c097d5c2e2 Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- AGENTS.md | 13 ++++++++++++- src/compile/common.rs | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 2b29276..b41575d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -306,7 +306,7 @@ engine: |-------|------|---------|-------------| | `model` | string | `claude-opus-4.5` | AI model to use. Options include `claude-sonnet-4.5`, `gpt-5.2-codex`, `gemini-3-pro-preview`, etc. | | `max-turns` | integer | *(none)* | Maximum number of agentic turns (tool-use iterations) the model is allowed per run. Maps to the `--max-turns` Copilot CLI argument. Use this to cap compute and prevent runaway loops. | -| `timeout-minutes` | integer | *(none)* | Workflow timeout in minutes. | +| `timeout-minutes` | integer | *(none)* | Maximum time in minutes the agent workflow is allowed to run. Maps to the `--max-timeout` Copilot CLI argument. Use this to cap long-running agent sessions. | #### `max-turns` @@ -318,6 +318,16 @@ Each "turn" is one iteration of the model calling a tool and receiving its outpu When omitted, the Copilot CLI uses its built-in default. When set, the compiler emits `--max-turns ` in the generated pipeline's agency params. +#### `timeout-minutes` + +The `timeout-minutes` field sets a wall-clock limit (in minutes) for the entire agent session. It maps to the `--max-timeout` Copilot CLI argument. This is useful for: + +- **Budget enforcement** — hard-capping the total runtime of an agent to control compute costs. +- **Pipeline hygiene** — preventing agents from occupying a runner indefinitely if they stall or enter long retry loops. +- **SLA compliance** — ensuring scheduled agents complete within a known window. + +When omitted, the Copilot CLI uses its built-in default. When set, the compiler emits `--max-timeout ` in the generated pipeline's agency params. + ### Tools Configuration The `tools` field controls which tools are available to the agent. Both sub-fields are optional and have sensible defaults. @@ -473,6 +483,7 @@ Should be replaced with the human-readable name from the front matter (e.g., "Da Additional params provided to agency CLI. The compiler generates: - `--model ` - AI model from `engine` front matter field (default: claude-opus-4.5) - `--max-turns ` - Maximum agentic turns from `engine.max-turns` (omitted when not set) +- `--max-timeout ` - Workflow timeout in minutes from `engine.timeout-minutes` (omitted when not set) - `--disable-builtin-mcps` - Disables all built-in MCPs initially - `--no-ask-user` - Prevents interactive prompts - `--allow-tool ` - Explicitly allows specific tools (github, safeoutputs, write, shell commands like cat, date, echo, grep, head, ls, pwd, sort, tail, uniq, wc, yq) diff --git a/src/compile/common.rs b/src/compile/common.rs index e97ca5b..ac7537a 100644 --- a/src/compile/common.rs +++ b/src/compile/common.rs @@ -313,6 +313,9 @@ pub fn generate_copilot_params(front_matter: &FrontMatter) -> String { if let Some(max_turns) = front_matter.engine.max_turns() { params.push(format!("--max-turns {}", max_turns)); } + if let Some(timeout_minutes) = front_matter.engine.timeout_minutes() { + params.push(format!("--max-timeout {}", timeout_minutes)); + } params.push("--disable-builtin-mcps".to_string()); params.push("--no-ask-user".to_string()); @@ -914,6 +917,23 @@ mod tests { assert!(!params.contains("--max-turns")); } + #[test] + fn test_copilot_params_max_timeout() { + let (fm, _) = parse_markdown( + "---\nname: test\ndescription: test\nengine:\n model: claude-opus-4.5\n timeout-minutes: 30\n---\n", + ) + .unwrap(); + let params = generate_copilot_params(&fm); + assert!(params.contains("--max-timeout 30")); + } + + #[test] + fn test_copilot_params_no_max_timeout_when_simple_engine() { + let fm = minimal_front_matter(); + let params = generate_copilot_params(&fm); + assert!(!params.contains("--max-timeout")); + } + // ─── sanitize_filename ──────────────────────────────────────────────────── #[test] From b61c58bdfdf2b9373e3a32a1dcc90559c0b692ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:13:49 +0000 Subject: [PATCH 5/5] fix: add zero-value warnings for max-turns/timeout-minutes and use copilot params terminology Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/8826b5cb-7d91-4b7b-9e18-6b5f9e1d02f7 Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- AGENTS.md | 4 ++-- src/compile/common.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b41575d..d5fc0b1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -316,7 +316,7 @@ Each "turn" is one iteration of the model calling a tool and receiving its outpu - **Safety** — preventing infinite loops where the agent repeatedly calls tools without converging on a result. - **Predictability** — ensuring the pipeline completes within a reasonable time frame. -When omitted, the Copilot CLI uses its built-in default. When set, the compiler emits `--max-turns ` in the generated pipeline's agency params. +When omitted, the Copilot CLI uses its built-in default. When set, the compiler emits `--max-turns ` in the generated pipeline's copilot params. #### `timeout-minutes` @@ -326,7 +326,7 @@ The `timeout-minutes` field sets a wall-clock limit (in minutes) for the entire - **Pipeline hygiene** — preventing agents from occupying a runner indefinitely if they stall or enter long retry loops. - **SLA compliance** — ensuring scheduled agents complete within a known window. -When omitted, the Copilot CLI uses its built-in default. When set, the compiler emits `--max-timeout ` in the generated pipeline's agency params. +When omitted, the Copilot CLI uses its built-in default. When set, the compiler emits `--max-timeout ` in the generated pipeline's copilot params. ### Tools Configuration diff --git a/src/compile/common.rs b/src/compile/common.rs index ac7537a..60522f0 100644 --- a/src/compile/common.rs +++ b/src/compile/common.rs @@ -311,9 +311,25 @@ pub fn generate_copilot_params(front_matter: &FrontMatter) -> String { params.push(format!("--model {}", front_matter.engine.model())); if let Some(max_turns) = front_matter.engine.max_turns() { + if max_turns == 0 { + eprintln!( + "Warning: Agent '{}' has max-turns: 0, which means zero turns allowed. \ + The agent will not be able to perform any tool calls. \ + Consider setting max-turns to at least 1.", + front_matter.name + ); + } params.push(format!("--max-turns {}", max_turns)); } if let Some(timeout_minutes) = front_matter.engine.timeout_minutes() { + if timeout_minutes == 0 { + eprintln!( + "Warning: Agent '{}' has timeout-minutes: 0, which means no time is allowed. \ + The agent session will time out immediately. \ + Consider setting timeout-minutes to at least 1.", + front_matter.name + ); + } params.push(format!("--max-timeout {}", timeout_minutes)); } params.push("--disable-builtin-mcps".to_string()); @@ -934,6 +950,26 @@ mod tests { assert!(!params.contains("--max-timeout")); } + #[test] + fn test_copilot_params_max_turns_zero_still_emitted() { + let (fm, _) = parse_markdown( + "---\nname: test\ndescription: test\nengine:\n model: claude-opus-4.5\n max-turns: 0\n---\n", + ) + .unwrap(); + let params = generate_copilot_params(&fm); + assert!(params.contains("--max-turns 0")); + } + + #[test] + fn test_copilot_params_max_timeout_zero_still_emitted() { + let (fm, _) = parse_markdown( + "---\nname: test\ndescription: test\nengine:\n model: claude-opus-4.5\n timeout-minutes: 0\n---\n", + ) + .unwrap(); + let params = generate_copilot_params(&fm); + assert!(params.contains("--max-timeout 0")); + } + // ─── sanitize_filename ──────────────────────────────────────────────────── #[test]