framework-agent actions-1-turn-1 #4
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # The workflow ActionsDriver dispatches (#610): one run is one `prompt()` turn. | |
| # | |
| # The driver cannot learn a run id from the dispatch API (it answers 204 with no body), so | |
| # the correlation id it generates is echoed into `run-name` and into the artifact name. That | |
| # is how the driver finds its own run, and it is why both must keep interpolating it. | |
| name: framework-agent | |
| run-name: framework-agent ${{ inputs.correlation_id }} | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| prompt: | |
| description: What the agent should do. Passed to the action verbatim, never through a shell. | |
| required: true | |
| correlation_id: | |
| description: How the driver finds this run. Must appear in run-name and in the artifact name. | |
| required: true | |
| model: | |
| description: Model id, e.g. claude-opus-4-8. Empty means the action's default. | |
| required: false | |
| resume_session_id: | |
| description: A prior agent session id to continue instead of starting fresh. | |
| required: false | |
| # The agent pushes a branch and may open a PR. `id-token: write` is not optional: the | |
| # action exchanges an OIDC token to authenticate the subscription OAuth token, and without | |
| # it every run fails with "Could not fetch an OIDC token". | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| id-token: write | |
| jobs: | |
| agent: | |
| runs-on: ubuntu-latest | |
| # Well under the 6h job cap. A turn that runs this long has gone wrong. | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| # Full history: the agent reads the log to understand what it is changing. | |
| fetch-depth: 0 | |
| # Built from environment variables rather than by interpolating ${{ }} into the shell, | |
| # so a crafted input cannot become a command. The driver validates them too. | |
| - name: Compose agent arguments | |
| id: args | |
| env: | |
| MODEL: ${{ inputs.model }} | |
| RESUME: ${{ inputs.resume_session_id }} | |
| run: | | |
| set -euo pipefail | |
| # Agent mode grants no permissions by default, so an unattended run cannot edit | |
| # or run anything without this. The runner is disposable, which is what makes it safe. | |
| args="--dangerously-skip-permissions" | |
| # Written as `if` rather than `[ .. ] && ..`, which returns non-zero when the input | |
| # is empty and would fail the step under `set -e`. | |
| if [ -n "$MODEL" ]; then args="$args --model $MODEL"; fi | |
| if [ -n "$RESUME" ]; then args="$args --resume $RESUME"; fi | |
| echo "value=$args" >> "$GITHUB_OUTPUT" | |
| - name: Run the agent | |
| id: claude | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| # A `claude setup-token` OAuth token: the run spends the subscription, not an API key (#495). | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| prompt: ${{ inputs.prompt }} | |
| claude_args: ${{ steps.args.outputs.value }} | |
| # The only REST-readable channel out of a run. `always()` so a failed turn still | |
| # returns its transcript, which is exactly when we most want to read it. | |
| - name: Collect the transcript | |
| if: always() | |
| env: | |
| EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} | |
| BRANCH: ${{ steps.claude.outputs.branch_name }} | |
| SESSION_ID: ${{ steps.claude.outputs.session_id }} | |
| run: | | |
| set -euo pipefail | |
| # Not dot-prefixed: upload-artifact@v4 defaults include-hidden-files to false and | |
| # drops every file under a hidden path segment, so a `.framework-run/` dir uploads | |
| # nothing and the driver finds no artifact to read. | |
| mkdir -p framework-run | |
| # An empty array still parses, so a crashed action yields an empty turn, not a driver error. | |
| if [ -n "$EXECUTION_FILE" ] && [ -f "$EXECUTION_FILE" ]; then | |
| cp "$EXECUTION_FILE" framework-run/execution.json | |
| else | |
| echo '[]' > framework-run/execution.json | |
| fi | |
| jq -n --arg branch "$BRANCH" --arg session_id "$SESSION_ID" \ | |
| '{branch: $branch, session_id: $session_id}' > framework-run/meta.json | |
| - uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: framework-run-${{ inputs.correlation_id }} | |
| path: framework-run | |
| retention-days: 7 |