Agent / Orchestrator #3
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
| name: Agent / Orchestrator | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| source_action: | |
| description: "Agent action that handed back to the orchestrator" | |
| required: true | |
| source_conclusion: | |
| description: "Normalized source action conclusion" | |
| required: true | |
| source_recommended_next_step: | |
| description: "Optional source action recommended next step" | |
| required: false | |
| default: "" | |
| source_run_id: | |
| description: "Workflow run ID of the source action, used for handoff dedupe" | |
| required: false | |
| default: "" | |
| target_number: | |
| description: "Issue or pull request number currently being handled" | |
| required: true | |
| target_kind: | |
| description: "Issue or pull request kind currently being handled" | |
| required: false | |
| default: "" | |
| author_association: | |
| description: "Original requester author association from router context" | |
| required: false | |
| default: "" | |
| access_policy: | |
| description: "Route access policy JSON used by router authorization" | |
| required: false | |
| default: "" | |
| repository_private: | |
| description: "Repository visibility flag for access policy defaults" | |
| required: false | |
| default: "" | |
| next_target_number: | |
| description: "Optional next target number produced by the source action" | |
| required: false | |
| default: "" | |
| source_handoff_context: | |
| description: "Optional action-oriented context derived by the source action" | |
| required: false | |
| default: "" | |
| requested_by: | |
| description: "GitHub login that requested the original run" | |
| required: false | |
| request_text: | |
| description: "Original user request text forwarded from the source action" | |
| required: false | |
| session_bundle_mode: | |
| description: "Session bundle persistence mode" | |
| required: false | |
| default: "" | |
| base_branch: | |
| description: "Branch to pass to agent-implement when dispatching an implementation" | |
| required: false | |
| default: "" | |
| base_pr: | |
| description: "Open PR number whose head branch agent-implement should stack on" | |
| required: false | |
| default: "" | |
| automation_mode: | |
| description: "Post-action orchestration mode (disabled, heuristics, agent)" | |
| required: false | |
| default: "disabled" | |
| automation_current_round: | |
| description: "Current automation handoff round" | |
| required: false | |
| default: "1" | |
| automation_max_rounds: | |
| description: "Maximum automation handoff rounds" | |
| required: false | |
| default: "12" | |
| permissions: | |
| actions: write | |
| contents: read | |
| issues: write | |
| pull-requests: read | |
| id-token: write # required for GitHub Actions OIDC broker exchange | |
| concurrency: | |
| group: agent-orchestrator-${{ github.repository }}-${{ inputs.target_number }}-${{ inputs.source_action }}-${{ inputs.automation_current_round }} | |
| cancel-in-progress: false | |
| jobs: | |
| orchestrate: | |
| if: vars.AGENT_ENABLED != 'false' | |
| runs-on: ${{ fromJson(vars.AGENT_RUNS_ON || '["ubuntu-latest"]') }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.repository.default_branch }} | |
| token: ${{ github.token }} | |
| - name: Resolve GitHub auth | |
| id: auth | |
| uses: ./.github/actions/resolve-github-auth | |
| with: | |
| app_id: ${{ secrets.AGENT_APP_ID }} | |
| app_private_key: ${{ secrets.AGENT_APP_PRIVATE_KEY }} | |
| pat: ${{ secrets.AGENT_PAT }} | |
| fallback_token: ${{ github.token }} | |
| - name: Setup agent runtime | |
| uses: ./.github/actions/setup-agent-runtime | |
| - name: Check handoff preflight | |
| id: preflight | |
| env: | |
| AUTOMATION_CURRENT_ROUND: ${{ inputs.automation_current_round }} | |
| AUTOMATION_MAX_ROUNDS: ${{ inputs.automation_max_rounds }} | |
| AUTOMATION_MODE: ${{ inputs.automation_mode }} | |
| SOURCE_ACTION: ${{ inputs.source_action }} | |
| SOURCE_CONCLUSION: ${{ inputs.source_conclusion }} | |
| TARGET_KIND: ${{ inputs.target_kind || (inputs.source_action == 'implement' && 'issue' || 'pull_request') }} | |
| AGENT_ALLOW_SELF_APPROVE: ${{ vars.AGENT_ALLOW_SELF_APPROVE || 'false' }} | |
| AGENT_ALLOW_SELF_MERGE: ${{ vars.AGENT_ALLOW_SELF_MERGE || 'false' }} | |
| AUTHOR_ASSOCIATION: ${{ inputs.author_association }} | |
| ACCESS_POLICY: ${{ inputs.access_policy }} | |
| REPOSITORY_PRIVATE: ${{ inputs.repository_private || (github.event.repository.private && 'true' || 'false') }} | |
| run: node .agent/dist/cli/orchestrator-preflight.js | |
| - name: Resolve orchestrator provider | |
| id: provider | |
| if: ${{ steps.preflight.outputs.planner_enabled == 'true' }} | |
| uses: ./.github/actions/resolve-agent-provider | |
| with: | |
| route: orchestrator | |
| default_provider: ${{ vars.AGENT_DEFAULT_PROVIDER || 'auto' }} | |
| openai_api_key: ${{ secrets.OPENAI_API_KEY }} | |
| claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| model_policy: ${{ vars.AGENT_MODEL_POLICY || '' }} | |
| - name: Install orchestrator provider | |
| if: ${{ steps.preflight.outputs.planner_enabled == 'true' }} | |
| uses: ./.github/actions/setup-agent-runtime | |
| with: | |
| install_codex: ${{ steps.provider.outputs.install_codex }} | |
| install_claude: ${{ steps.provider.outputs.install_claude }} | |
| - name: Resolve task timeout | |
| if: ${{ steps.preflight.outputs.planner_enabled == 'true' }} | |
| id: task_timeout | |
| env: | |
| AGENT_TASK_TIMEOUT_POLICY: ${{ vars.AGENT_TASK_TIMEOUT_POLICY || '' }} | |
| ROUTE: orchestrator | |
| run: node .agent/dist/cli/resolve-task-timeout.js | |
| - name: Plan next action with agent | |
| id: planner | |
| if: ${{ steps.preflight.outputs.planner_enabled == 'true' }} | |
| continue-on-error: true | |
| timeout-minutes: ${{ fromJson(steps.task_timeout.outputs.minutes || '30') }} | |
| uses: ./.github/actions/run-agent-task | |
| env: | |
| ORCHESTRATOR_SOURCE_ACTION: ${{ inputs.source_action }} | |
| ORCHESTRATOR_SOURCE_CONCLUSION: ${{ inputs.source_conclusion }} | |
| ORCHESTRATOR_SOURCE_RECOMMENDED_NEXT_STEP: ${{ inputs.source_recommended_next_step }} | |
| ORCHESTRATOR_SOURCE_RUN_ID: ${{ inputs.source_run_id || github.run_id }} | |
| ORCHESTRATOR_NEXT_TARGET_NUMBER: ${{ inputs.next_target_number }} | |
| ORCHESTRATOR_SOURCE_HANDOFF_CONTEXT: ${{ inputs.source_handoff_context }} | |
| ORCHESTRATOR_SELF_APPROVE_ENABLED: ${{ vars.AGENT_ALLOW_SELF_APPROVE || 'false' }} | |
| ORCHESTRATOR_SELF_MERGE_ENABLED: ${{ vars.AGENT_ALLOW_SELF_MERGE || 'false' }} | |
| ORCHESTRATOR_CURRENT_ROUND: ${{ inputs.automation_current_round }} | |
| ORCHESTRATOR_MAX_ROUNDS: ${{ inputs.automation_max_rounds }} | |
| with: | |
| agent: ${{ steps.provider.outputs.provider }} | |
| github_token: ${{ steps.auth.outputs.token }} | |
| secondary_github_token: ${{ secrets.AGENT_SECONDARY_GITHUB_TOKEN }} | |
| openai_api_key: ${{ secrets.OPENAI_API_KEY }} | |
| claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| model: ${{ steps.provider.outputs.model }} | |
| display_model: ${{ vars.AGENT_DISPLAY_MODEL || '' }} | |
| permission_mode: approve-all | |
| prompt: orchestrator | |
| reasoning_effort: ${{ steps.provider.outputs.reasoning_effort || 'high' }} | |
| lane: planner | |
| memory_mode_override: read-only | |
| memory_ref: ${{ vars.AGENT_MEMORY_REF || 'agent/memory' }} | |
| memory_policy: ${{ vars.AGENT_MEMORY_POLICY || '' }} | |
| rubrics_ref: ${{ vars.AGENT_RUBRICS_REF || 'agent/rubrics' }} | |
| rubrics_policy: ${{ vars.AGENT_RUBRICS_POLICY || '' }} | |
| rubrics_mode_override: read-only | |
| rubrics_limit: ${{ vars.AGENT_RUBRICS_LIMIT || '10' }} | |
| session_bundle_mode: ${{ inputs.session_bundle_mode || vars.AGENT_SESSION_BUNDLE_MODE || 'auto' }} | |
| session_policy: resume-best-effort | |
| request_text: ${{ inputs.request_text }} | |
| requested_by: ${{ inputs.requested_by || github.actor }} | |
| route: orchestrator | |
| source_kind: workflow_dispatch | |
| target_kind: ${{ inputs.target_kind || (inputs.source_action == 'implement' && 'issue' || 'pull_request') }} | |
| target_number: ${{ inputs.target_number }} | |
| target_url: ${{ (inputs.target_kind || (inputs.source_action == 'implement' && 'issue' || 'pull_request')) == 'issue' && format('{0}/{1}/issues/{2}', github.server_url, github.repository, inputs.target_number) || format('{0}/{1}/pull/{2}', github.server_url, github.repository, inputs.target_number) }} | |
| workflow: agent-orchestrator.yml | |
| - name: Decide and dispatch next action | |
| env: | |
| AUTOMATION_CURRENT_ROUND: ${{ inputs.automation_current_round }} | |
| AUTOMATION_MAX_ROUNDS: ${{ inputs.automation_max_rounds }} | |
| AUTOMATION_MODE: ${{ inputs.automation_mode }} | |
| AGENT_COLLAPSE_OLD_REVIEWS: ${{ vars.AGENT_COLLAPSE_OLD_REVIEWS }} | |
| BASE_BRANCH: ${{ inputs.base_branch }} | |
| BASE_PR: ${{ inputs.base_pr }} | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| GH_TOKEN: ${{ steps.auth.outputs.token }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| NEXT_TARGET_NUMBER: ${{ inputs.next_target_number }} | |
| REQUESTED_BY: ${{ inputs.requested_by || github.actor }} | |
| REQUEST_TEXT: ${{ inputs.request_text }} | |
| SESSION_BUNDLE_MODE: ${{ inputs.session_bundle_mode || vars.AGENT_SESSION_BUNDLE_MODE || 'auto' }} | |
| SOURCE_ACTION: ${{ inputs.source_action }} | |
| SOURCE_CONCLUSION: ${{ inputs.source_conclusion }} | |
| SOURCE_RECOMMENDED_NEXT_STEP: ${{ inputs.source_recommended_next_step }} | |
| SOURCE_HANDOFF_CONTEXT: ${{ inputs.source_handoff_context }} | |
| SOURCE_RUN_ID: ${{ inputs.source_run_id || github.run_id }} | |
| AGENT_ALLOW_SELF_APPROVE: ${{ vars.AGENT_ALLOW_SELF_APPROVE || 'false' }} | |
| AGENT_ALLOW_SELF_MERGE: ${{ vars.AGENT_ALLOW_SELF_MERGE || 'false' }} | |
| AUTHOR_ASSOCIATION: ${{ inputs.author_association }} | |
| ACCESS_POLICY: ${{ inputs.access_policy }} | |
| REPOSITORY_PRIVATE: ${{ inputs.repository_private || (github.event.repository.private && 'true' || 'false') }} | |
| TARGET_KIND: ${{ inputs.target_kind || (inputs.source_action == 'implement' && 'issue' || 'pull_request') }} | |
| TARGET_NUMBER: ${{ inputs.target_number }} | |
| PLANNER_RESPONSE_FILE: ${{ steps.planner.outputs.response_file }} | |
| run: node .agent/dist/cli/orchestrate-handoff.js |