This repository was archived by the owner on Jun 3, 2026. It is now read-only.
Task 3.3: Implement Tool Result Status Auto-Detection for BedrockModelProvider #38
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: Strands Command Handler | |
| on: | |
| issue_comment: | |
| types: [created] | |
| workflow_dispatch: | |
| inputs: | |
| issue_id: | |
| description: 'Issue ID to process (can be issue or PR number)' | |
| required: true | |
| type: string | |
| command: | |
| description: 'Strands command to execute' | |
| required: false | |
| type: string | |
| default: '' | |
| jobs: | |
| authorization-check: | |
| if: startsWith(github.event.comment.body, '/strands') || github.event_name == 'workflow_dispatch' | |
| permissions: read-all | |
| runs-on: ubuntu-latest | |
| outputs: | |
| approval-env: ${{ steps.collab-check.outputs.result || steps.auto-approve.outputs.result }} | |
| steps: | |
| - name: Collaborator Check | |
| if: github.event_name != 'workflow_dispatch' | |
| uses: actions/github-script@v8 | |
| id: collab-check | |
| with: | |
| result-encoding: string | |
| script: | | |
| try { | |
| const permissionResponse = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| username: context.payload.comment.user.login, | |
| }); | |
| const permission = permissionResponse.data.permission; | |
| const hasWriteAccess = ['write', 'admin'].includes(permission); | |
| if (!hasWriteAccess) { | |
| console.log(`User ${context.payload.comment.user.login} does not have write access to the repository (permission: ${permission})`); | |
| return "manual-approval" | |
| } else { | |
| console.log(`Verified ${context.payload.comment.user.login} has write access. Auto Approving strands command.`) | |
| return "auto-approve" | |
| } | |
| } catch (error) { | |
| console.log(`${context.payload.comment.user.login} does not have write access. Requiring Manual Approval to run strands command.`) | |
| return "manual-approval" | |
| } | |
| - name: Auto-approve for workflow dispatch | |
| if: github.event_name == 'workflow_dispatch' | |
| id: auto-approve | |
| uses: actions/github-script@v8 | |
| with: | |
| result-encoding: string | |
| script: | | |
| return "auto-approve" | |
| execute: | |
| needs: [authorization-check] | |
| environment: ${{ needs.authorization-check.outputs.approval-env }} | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| id-token: write # Required for OIDC | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Add strands-running label | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: ${{ inputs.issue_id || github.event.issue.number }}, | |
| labels: ['strands-running'] | |
| }); | |
| - name: Determine PR context | |
| id: determine-context | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| try { | |
| const issueId = context.eventName === 'workflow_dispatch' | |
| ? '${{ inputs.issue_id }}' | |
| : context.payload.issue.number.toString(); | |
| const command = context.eventName === 'workflow_dispatch' | |
| ? '${{ inputs.command }}' | |
| : (context.payload.comment.body.match(/^\/strands\s*(.*)$/)?.[1]?.trim() || ''); | |
| console.log(`Event: ${context.eventName}, Issue ID: ${issueId}, Command: "${command}"`); | |
| const issue = await github.rest.issues.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueId | |
| }); | |
| const isPrContext = !!issue.data.pull_request; | |
| const mode = (isPrContext || command.startsWith('implement')) ? 'implementer' : 'reviewer'; | |
| console.log(`Is PR: ${isPrContext}, Mode: ${mode}`); | |
| let targetIssueId; | |
| if (!isPrContext) { | |
| targetIssueId = issueId; | |
| console.log(`Target issue ID: ${targetIssueId} (same as issue)`); | |
| } else { | |
| const closingIssuesData = await github.graphql(` | |
| query($owner: String!, $repo: String!, $number: Int!) { | |
| repository(owner: $owner, name: $repo) { | |
| pullRequest(number: $number) { | |
| closingIssuesReferences(first: 10) { | |
| nodes { number } | |
| } | |
| } | |
| } | |
| } | |
| `, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| number: parseInt(issueId) | |
| }); | |
| const issues = closingIssuesData?.repository?.pullRequest?.closingIssuesReferences?.nodes || []; | |
| console.log(`Found ${issues.length} closing issue(s)`); | |
| if (issues.length !== 1) { | |
| const errorMsg = issues.length > 1 | |
| ? `Pull request has ${issues.length} closing issues. Only one closing issue is allowed.` | |
| : 'Pull request must have exactly one closing issue.'; | |
| console.error(errorMsg); | |
| core.setFailed(errorMsg); | |
| return; | |
| } | |
| targetIssueId = issues[0].number.toString(); | |
| console.log(`Target issue ID: ${targetIssueId} (from closing issues)`); | |
| } | |
| console.log(`Setting outputs - issue_id: ${issueId}, target_issue_id: ${targetIssueId}, pr_id: ${isPrContext ? issueId : ''}, mode: ${mode}`); | |
| core.setOutput('issue_id', issueId); | |
| core.setOutput('target_issue_id', targetIssueId); | |
| core.setOutput('pr_id', isPrContext ? issueId : ''); | |
| core.setOutput('mode', mode); | |
| core.setOutput('command', command); | |
| } catch (error) { | |
| const errorMsg = `Failed to determine context: ${error.message}`; | |
| console.error(errorMsg); | |
| core.setFailed(errorMsg); | |
| } | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ steps.determine-context.outputs.pr_id && format('refs/pull/{0}/head', steps.determine-context.outputs.pr_id) || 'main' }} | |
| - name: Build prompts | |
| id: process | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| try { | |
| const mode = '${{ steps.determine-context.outputs.mode }}'; | |
| const targetIssueId = '${{ steps.determine-context.outputs.target_issue_id }}'; | |
| const issueId = '${{ steps.determine-context.outputs.issue_id }}'; | |
| const command = '${{ steps.determine-context.outputs.command }}'; | |
| const isPrContext = '${{ steps.determine-context.outputs.pr_id }}' !== ''; | |
| console.log(`Building prompts - mode: ${mode}, target issue: ${targetIssueId}, is PR: ${isPrContext}`); | |
| const sessionId = `${mode}-${targetIssueId}`; | |
| console.log(`Session ID: ${sessionId}`); | |
| const scriptFile = mode === 'implementer' | |
| ? '.github/agent-scripts/task-implementer.script.md' | |
| : '.github/agent-scripts/task-reviewer.script.md'; | |
| console.log(`Reading script file: ${scriptFile}`); | |
| let systemPrompt = fs.readFileSync(scriptFile, 'utf8'); | |
| systemPrompt = systemPrompt | |
| .replace(/\{\{ISSUE_NUMBER\}\}/g, targetIssueId); | |
| const first20Lines = systemPrompt.split('\n').slice(0, 20).join('\n'); | |
| console.log(`System prompt (first 20 lines):\n${first20Lines}`); | |
| let prompt = ''; | |
| if (isPrContext) prompt += `The pull request id is: ${issueId}\n`; | |
| prompt += `${command}\n`; | |
| prompt += 'review and continue'; | |
| console.log(`Task prompt: "${prompt}"`); | |
| core.setOutput('session_id', sessionId); | |
| core.setOutput('system_prompt', systemPrompt); | |
| core.setOutput('prompt', prompt); | |
| } catch (error) { | |
| const errorMsg = `Failed to build prompts: ${error.message}`; | |
| console.error(errorMsg); | |
| core.setFailed(errorMsg); | |
| } | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Execute strands command | |
| uses: strands-agents/strands-action@main | |
| timeout-minutes: 60 | |
| with: | |
| aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} | |
| system_prompt: ${{ steps.process.outputs.system_prompt }} | |
| session_id: ${{ steps.process.outputs.session_id }} | |
| session_s3_bucket: ${{ secrets.TYPESCRIPT_SESSIONS_BUCKET }} | |
| thinking_type: "enabled" | |
| budget_tokens: "8000" | |
| model: "global.anthropic.claude-sonnet-4-5-20250929-v1:0" | |
| anthropic_beta: "interleaved-thinking-2025-05-14" | |
| max_tokens: "64000" | |
| tools: "str_replace_based_edit_tool,shell,http_request,create_issue,get_issue,update_issue,list_issues,add_issue_comment,get_issue_comments,create_pull_request,get_pull_request,update_pull_request,list_pull_requests,get_pr_review_and_comments,reply_to_review_comment,notebook,handoff_to_user" | |
| task: ${{ steps.process.outputs.prompt }} | |
| - name: Remove strands-running label | |
| if: always() | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: ${{ inputs.issue_id || github.event.issue.number }}, | |
| name: 'strands-running' | |
| }); | |
| } catch (error) { | |
| console.log('Label removal failed (may not exist):', error.message); | |
| } | |