-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathaction.yml
More file actions
298 lines (266 loc) · 12.1 KB
/
Copy pathaction.yml
File metadata and controls
298 lines (266 loc) · 12.1 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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
name: 'Strands Agent Runner'
description: 'Execute a Strands agent with the given prompts and configuration'
inputs:
aws_role_arn:
description: 'AWS IAM role ARN for authentication'
required: true
aws_secrets_manager_secret_id:
description: 'AWS Secrets Manager secret ID containing agent configuration. Will fetch sessions_bucket, langfuse_*, and evals_sqs_queue_arn from this secret.'
required: false
sessions_bucket:
description: 'S3 bucket for session storage (optional if using aws_secrets_manager_secret_id)'
required: false
default: ''
write_permission:
description: 'If this action runs with write permission. If this is false, you should run the `strands-write-executor` action after this one with write permission.'
required: true
default: 'false'
langfuse_public_key:
description: 'Langfuse public key for telemetry (optional, can be fetched from Secrets Manager)'
required: false
default: ''
langfuse_secret_key:
description: 'Langfuse secret key for telemetry (optional, can be fetched from Secrets Manager)'
required: false
default: ''
langfuse_host:
description: 'Langfuse host URL for telemetry (optional, can be fetched from Secrets Manager)'
required: false
default: ''
evals_sqs_queue_arn:
description: 'SQS queue ARN for eval triggers (optional, can be fetched from Secrets Manager)'
required: false
default: ''
runs:
using: 'composite'
steps:
- name: Download parsed input artifact
uses: actions/download-artifact@v4
with:
name: strands-parsed-input
- name: Read parsed input
id: read-input
shell: bash
run: |
echo "ref=$(jq -r .branch_name strands-parsed-input.json)" >> $GITHUB_OUTPUT
echo "session_id=$(jq -r .session_id strands-parsed-input.json)" >> $GITHUB_OUTPUT
echo "head_repo=$(jq -r '.head_repo // ""' strands-parsed-input.json)" >> $GITHUB_OUTPUT
echo "agent_mode=$(jq -r '.agent_mode // ""' strands-parsed-input.json)" >> $GITHUB_OUTPUT
echo "agent_type=$(jq -r '.agent_type // "standard"' strands-parsed-input.json)" >> $GITHUB_OUTPUT
echo "system_prompt<<EOF" >> $GITHUB_OUTPUT
jq -r .system_prompt strands-parsed-input.json >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "task_prompt<<EOF" >> $GITHUB_OUTPUT
jq -r .prompt strands-parsed-input.json >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Checkout devtools repo for scripts, SOPs, and agent skills
- name: Checkout devtools
uses: actions/checkout@v5
with:
repository: strands-agents/devtools
ref: main
sparse-checkout: |
strands-command/scripts
strands-command/agent-sops
strands-command/agent-skills
path: devtools
# Copy the devtools directory to the runner temp directory so the branch content cant overwrite the scripts executed here
- name: Copy devtools to safe directory
shell: bash
run: |
mkdir -p ${{ runner.temp }}/strands-agent-runner
cp -r devtools/strands-command ${{ runner.temp }}/strands-agent-runner/
# Checkout the branch repo to stage the directory for the agent
- name: Checkout repository
uses: actions/checkout@v6
with:
ref: ${{ steps.read-input.outputs.ref }}
repository: ${{ steps.read-input.outputs.head_repo || github.repository }}
# Copy agent-skills to working directory (beta agent only)
# The AgentSkills plugin looks for skills in the working directory
- name: Copy agent-skills to working directory
if: steps.read-input.outputs.agent_type == 'beta'
shell: bash
run: |
if [ -d "${{ runner.temp }}/strands-agent-runner/strands-command/agent-skills" ]; then
cp -r ${{ runner.temp }}/strands-agent-runner/strands-command/agent-skills ./agent-skills
echo "✅ Copied agent-skills to working directory"
if [ -d "${{ runner.temp }}/strands-agent-runner/strands-command/agent-sops" ]; then
cp -r ${{ runner.temp }}/strands-agent-runner/strands-command/agent-sops ./agent-sops
echo "✅ Copied agent-sops to working directory (for runtime skill conversion)"
fi
ls -la ./agent-skills/
else
echo "ℹ️ No agent-skills directory found (skills not available)"
fi
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: '${{ runner.temp }}/strands-agent-runner/strands-command/scripts/python/pyproject.toml'
- name: Install Strands Agents
shell: bash
run: |
echo "📦 Installing from pyproject.toml"
uv pip install --system ${{ runner.temp }}/strands-agent-runner/strands-command/scripts/python --quiet
- name: Configure Git
shell: bash
run: |
git config --global user.name "Strands Agent"
git config --global user.email "217235299+strands-agent@users.noreply.github.com"
git config --global core.pager cat
PAGER=cat
# Stage 1: Configure AWS credentials with minimal permissions to fetch secrets
- name: Configure AWS credentials (initial)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ inputs.aws_role_arn }}
role-session-name: GitHubActions-StrandsAgent-${{ github.run_id }}-init
aws-region: us-west-2
mask-aws-account-id: true
inline-session-policy: >-
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}
]
}
- name: Retrieve secrets from AWS Secrets Manager
id: secrets
shell: bash
run: |
if [ -n "${{ inputs.aws_secrets_manager_secret_id }}" ]; then
echo "Fetching configuration from AWS Secrets Manager..."
SECRET_JSON=$(aws secretsmanager get-secret-value --secret-id "${{ inputs.aws_secrets_manager_secret_id }}" --query SecretString --output text --region us-east-1)
SESSIONS_BUCKET=$(echo $SECRET_JSON | jq -r '.AGENT_SESSIONS_BUCKET // empty')
LANGFUSE_PUBLIC_KEY=$(echo $SECRET_JSON | jq -r '.LANGFUSE_PUBLIC_KEY // empty')
LANGFUSE_SECRET_KEY=$(echo $SECRET_JSON | jq -r '.LANGFUSE_SECRET_KEY // empty')
LANGFUSE_HOST=$(echo $SECRET_JSON | jq -r '.LANGFUSE_HOST // empty')
EVALS_SQS_QUEUE_ARN=$(echo $SECRET_JSON | jq -r '.EVALS_SQS_QUEUE_ARN // empty')
# Mask all secret values so they never appear in logs
[ -n "$SESSIONS_BUCKET" ] && echo "::add-mask::$SESSIONS_BUCKET"
[ -n "$LANGFUSE_PUBLIC_KEY" ] && echo "::add-mask::$LANGFUSE_PUBLIC_KEY"
[ -n "$LANGFUSE_SECRET_KEY" ] && echo "::add-mask::$LANGFUSE_SECRET_KEY"
[ -n "$LANGFUSE_HOST" ] && echo "::add-mask::$LANGFUSE_HOST"
[ -n "$EVALS_SQS_QUEUE_ARN" ] && echo "::add-mask::$EVALS_SQS_QUEUE_ARN"
echo "sessions_bucket=$SESSIONS_BUCKET" >> $GITHUB_OUTPUT
echo "langfuse_public_key=$LANGFUSE_PUBLIC_KEY" >> $GITHUB_OUTPUT
echo "langfuse_secret_key=$LANGFUSE_SECRET_KEY" >> $GITHUB_OUTPUT
echo "langfuse_host=$LANGFUSE_HOST" >> $GITHUB_OUTPUT
echo "evals_sqs_queue_arn=$EVALS_SQS_QUEUE_ARN" >> $GITHUB_OUTPUT
fi
- name: Build scoped IAM policy
id: policy
shell: bash
run: |
SESSIONS_BUCKET="${{ inputs.sessions_bucket || steps.secrets.outputs.sessions_bucket }}"
EVALS_SQS_QUEUE_ARN="${{ inputs.evals_sqs_queue_arn || steps.secrets.outputs.evals_sqs_queue_arn }}"
# Base statements for Bedrock and S3
STATEMENTS='[
{
"Effect": "Allow",
"Action": ["bedrock:InvokeModelWithResponseStream", "bedrock:InvokeModel"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::'"$SESSIONS_BUCKET"'/*"]
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": ["arn:aws:s3:::'"$SESSIONS_BUCKET"'"]
}
]'
# Conditionally add SQS statement if evals_sqs_queue_arn is set
if [ -n "$EVALS_SQS_QUEUE_ARN" ]; then
SQS_STATEMENT='{
"Effect": "Allow",
"Action": "sqs:SendMessage",
"Resource": "'"$EVALS_SQS_QUEUE_ARN"'"
}'
STATEMENTS=$(echo "$STATEMENTS" | jq --argjson sqs "$SQS_STATEMENT" '. + [$sqs]')
fi
POLICY=$(jq -n --argjson stmts "$STATEMENTS" '{"Version": "2012-10-17", "Statement": $stmts}')
echo "policy<<EOF" >> $GITHUB_OUTPUT
echo "$POLICY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Stage 2: Reconfigure AWS credentials with scoped permissions using fetched values
- name: Configure AWS credentials (scoped)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ inputs.aws_role_arn }}
role-session-name: GitHubActions-StrandsAgent-${{ github.run_id }}
aws-region: us-west-2
mask-aws-account-id: true
inline-session-policy: ${{ steps.policy.outputs.policy }}
- name: Execute strands command
shell: bash
env:
# Write Permission
GITHUB_WRITE: ${{ inputs.write_permission }}
# GitHub Configuration
GITHUB_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
# Task Configuration
INPUT_TASK: ${{ steps.read-input.outputs.task_prompt }}
INPUT_SYSTEM_PROMPT: ${{ steps.read-input.outputs.system_prompt }}
# AWS Configuration
AWS_REGION: 'us-west-2'
# Session Manager (input overrides Secrets Manager)
S3_SESSION_BUCKET: ${{ inputs.sessions_bucket || steps.secrets.outputs.sessions_bucket }}
SESSION_ID: ${{ steps.read-input.outputs.session_id }}
# Strands Env Vars
STRANDS_TOOL_CONSOLE_MODE: 'enabled'
BYPASS_TOOL_CONSENT: 'true'
# Langfuse Telemetry (input overrides Secrets Manager)
LANGFUSE_PUBLIC_KEY: ${{ inputs.langfuse_public_key || steps.secrets.outputs.langfuse_public_key }}
LANGFUSE_SECRET_KEY: ${{ inputs.langfuse_secret_key || steps.secrets.outputs.langfuse_secret_key }}
LANGFUSE_HOST: ${{ inputs.langfuse_host || steps.secrets.outputs.langfuse_host }}
# Evals Configuration (input overrides Secrets Manager)
EVALS_SQS_QUEUE_ARN: ${{ inputs.evals_sqs_queue_arn || steps.secrets.outputs.evals_sqs_queue_arn }}
# Agent type (standard or beta)
AGENT_TYPE: ${{ steps.read-input.outputs.agent_type }}
AGENT_MODE: ${{ steps.read-input.outputs.agent_mode }}
run: |
SCRIPTS_DIR="${{ runner.temp }}/strands-agent-runner/strands-command/scripts/python"
if [ "$AGENT_TYPE" = "beta" ]; then
echo "🧪 Running beta agent"
uv run --no-project "$SCRIPTS_DIR/beta_agent_runner.py" "$INPUT_TASK"
else
echo "🤖 Running standard agent"
uv run --no-project "$SCRIPTS_DIR/agent_runner.py" "$INPUT_TASK"
fi
- name: Capture repository state
shell: bash
run: |
mkdir -p .artifact
if git diff --quiet HEAD@{upstream} && git diff --quiet --cached; then
echo "📭 No changes to capture"
else
echo "📝 Capturing entire repository state"
tar -czf .artifact/repository_state.tar.gz --exclude='.artifact' .
fi
- name: Upload repository state artifact
uses: actions/upload-artifact@v4
with:
name: repository-state
path: .artifact/repository_state.tar.gz
retention-days: 1
if-no-files-found: ignore
- name: Upload artifact for write operations
uses: actions/upload-artifact@v4
with:
name: write-operations
path: .artifact/write_operations.jsonl
retention-days: 1
if-no-files-found: ignore