-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathaca-coding-agent.yaml
More file actions
179 lines (168 loc) · 8.39 KB
/
Copy pathaca-coding-agent.yaml
File metadata and controls
179 lines (168 loc) · 8.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# Coding agent running entirely inside an Azure Container Apps (ACA)
# dynamic-sessions sandbox instead of on the host (issue #284, epic E7).
#
# This is the verified, runnable successor to the design-time preview at
# docs/projects/aca/aca-provider-example.yaml (removed once this file
# passed `conductor validate` — see aca-provider.plan.md, epic E7).
#
# Prerequisites:
# 1. An operator-provisioned ACA custom-container session pool running
# the `conductor-agent-runner` image (see docker/aca-runner/Dockerfile
# and scripts/aca/provision-pool.sh — Conductor does not provision ACA
# infrastructure itself; DD6, bring-your-own pool). The pool must allow
# egress (see the `egress:` note below — cloning the repo and reaching
# the Copilot model backend both require outbound network access from
# the sandbox).
# 2. `pip install 'conductor-cli[aca]'` on the host (pins azure-identity).
# 3. `az login` (or another `DefaultAzureCredential`-compatible identity)
# with the *Session Executor* role on the pool.
# 4. Inner Copilot authentication: the in-sandbox runner drives a real
# CopilotProvider but cannot do interactive OAuth login inside a
# headless container, so the HOST must forward either a GitHub
# token or BYOK routing settings (design DD4). Default: nothing to
# do — if you are signed in with the GitHub CLI (`gh auth login`),
# Conductor picks that token up automatically via `gh auth token`.
# To use a different credential, export it as COPILOT_GITHUB_TOKEN
# (GH_TOKEN / GITHUB_TOKEN also work), which takes precedence; a
# fine-grained PAT scoped to only the "Copilot Requests" permission
# is the narrowest option and is recommended for CI/service
# accounts. Either way the sandbox runs on your own Copilot capacity
# (the capacity of the GitHub account owning that token, independent
# of the Azure identity used to reach the pool). Fallback: BYOK
# custom routing, activated by COPILOT_PROVIDER_BASE_URL alone —
# COPILOT_PROVIDER_BEARER_TOKEN / COPILOT_PROVIDER_API_KEY are
# optional, only needed if the endpoint itself requires a
# credential. Whatever is resolved is delivered in-memory per
# request, never as a persisted sandbox secret — but it IS
# readable inside the session, so prefer a narrowly-scoped token
# with a short expiry. This mechanism is only
# acceptable for TRUSTED workloads, not untrusted/multi-tenant use
# (see docs/providers/aca.md#security; off-sandbox isolation is
# future work). Failure means no GitHub token could be resolved
# from any source AND no BYOK base URL is set — in that case the
# host fails while building the
# request, before the sandbox is ever contacted, not inside it. See
# docs/providers/aca.md#authentication.
# 5. Set POOL_ENDPOINT below to your pool's management endpoint.
#
# Full architecture, security model, and API contract:
# docs/providers/aca.md
# docs/projects/aca/aca-provider.design.md
#
# Run (assuming `gh auth login` has been done at some point):
# conductor run examples/aca-coding-agent.yaml \
# --input repo_url="https://github.com/octocat/Hello-World" \
# --input task="Add a CONTRIBUTING.md with a one-paragraph guide"
workflow:
name: aca-coding-agent
description: >
Coding agent (clone -> implement -> test -> loop back on failure)
running entirely inside an ACA dynamic-session sandbox instead of the
host.
version: "1.0.0"
entry_point: implement
runtime:
provider:
name: aca
# Operator-provisioned pool (DD6, bring-your-own — Conductor does not
# provision ACA infrastructure). See scripts/aca/provision-pool.sh
# for a throwaway provisioning example.
# ${ACA_POOL_ENDPOINT:-default} interpolation: falls back to a
# placeholder so `conductor validate` (and CI's examples-validation
# set) never requires a real pool endpoint — override with a real
# `az containerapp sessionpool show` endpoint before `conductor run`.
pool_endpoint: "${ACA_POOL_ENDPOINT:-https://my-agent-pool.example.westus2.azurecontainerapps.io}"
api_version: "2025-07-01"
# MVP: the in-container runner drives a real CopilotProvider only.
inner_provider: copilot
# Default granularity for *sequential* session reuse; concurrent
# units (parallel / for_each) always diverge via the mandatory
# concurrency discriminator regardless of this setting (DD5).
identifier_scope: agent
# Advisory mirrors of the pool's own settings (the pool governs).
# egress MUST be enabled here: this workflow clones a repo over the
# network and its inner_provider (copilot) always needs outbound
# access to reach the model backend, regardless of what this
# workflow-level field says — the pool's own network configuration is
# what actually gates it.
egress: enabled
lifecycle: timed
auth: azure_default
default_model: gpt-5-mini
mcp_servers:
# Baked into the conductor-agent-runner image (docker/aca-runner) —
# stdio MCP servers cannot be provisioned by Conductor itself once
# execution has moved into the sandbox (the "runner-image contract").
git:
command: git-mcp-server
tools: ["*"]
input:
repo_url:
type: string
required: true
description: Repository to clone inside the sandbox
task:
type: string
required: true
description: The change to implement
limits:
max_iterations: 5 # caps the implement <-> self loop-back below
agents:
- name: implement
description: >
Clones the repo on first run, implements the task, and runs the test
suite — all inside the sandbox. On failure, loops back into itself:
identifier_scope: agent means this re-execution reuses the SAME ACA
session, so the clone and any partial edits are still there.
# No per-agent `model:` override — this inherits `runtime.default_model`
# (gpt-5-mini), which is verified working end-to-end in the sandbox.
# Don't pin a model here unless you know your Copilot account has it:
# an unavailable model fails inside the sandbox at session.create time
# with `Model "<name>" is not available.` (gpt-4.1 and gpt-4o are NOT
# available on all accounts; gpt-5-mini and claude-sonnet-4.5 are).
# Per-agent sandbox override block (SandboxConfig). working_dir is
# container-relative — a path inside the remote session filesystem,
# never resolved against the host workflow directory. Set to /workspace
# (created by docker/aca-runner/Dockerfile at image build time) rather
# than /workspace/repo: the latter doesn't exist until the clone below
# runs, and a working_dir that's absent when the session starts is a
# runtime error (never a silent host fallback) — see
# docs/providers/aca.md#workflow-configuration. The agent clones into
# the `repo` subdirectory itself on first run.
sandbox:
working_dir: /workspace
identifier_scope: agent # explicit here for clarity; matches the default
# Comfortably under the ~30-min measured per-request cap (issue #312) so
# one attempt can't outrun the streaming transport (DD3).
max_session_seconds: 900
prompt: |
Working directory: /workspace
If the `repo` subdirectory does not exist yet, clone
{{ workflow.input.repo_url }} into `repo` (e.g. `git clone <url> repo`).
Then `cd repo` and implement the following change there:
{{ workflow.input.task }}
{% if implement is defined and implement.output and implement.output.tests_passed == false %}
Your previous attempt failed:
{{ implement.output.failure_notes }}
Fix the issue (the repo and your prior edits are still present in this
same sandbox session, under ./repo) and try again.
{% endif %}
Run the test suite and report the outcome.
output:
summary:
type: string
description: What changed in this attempt
tests_passed:
type: boolean
description: Whether the test suite passed
failure_notes:
type: string
description: Populated only when tests_passed is false
routes:
- to: implement
when: "{{ not output.tests_passed }}"
- to: $end
output:
summary: "{{ implement.output.summary }}"
tests_passed: "{{ implement.output.tests_passed }}"
attempts: "{{ context.iteration }}"