Skip to content

Commit aed75d6

Browse files
author
Ambient Code Bot
committed
Merge remote-tracking branch 'origin/main' into feat/live-model-switching
2 parents 7e9ebe4 + 0c5a8e2 commit aed75d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2142
-270
lines changed

.github/workflows/amber-issue-handler.yml

100644100755
Lines changed: 157 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ jobs:
4242
runs-on: ubuntu-latest
4343
timeout-minutes: 30
4444
steps:
45+
- name: Acknowledge issue
46+
env:
47+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48+
run: |
49+
gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/reactions \
50+
-X POST -f content=eyes --silent || true
51+
4552
- name: Resolve issue details
4653
id: issue
4754
env:
@@ -66,6 +73,14 @@ jobs:
6673
echo "skip=false" >> $GITHUB_OUTPUT
6774
fi
6875
76+
- name: Post immediate acknowledgement
77+
if: steps.existing.outputs.skip != 'true'
78+
env:
79+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
80+
run: |
81+
gh issue comment ${{ steps.issue.outputs.number }} --repo "${{ github.repository }}" \
82+
--body "🤖 Amber is working on this issue. A PR will be created shortly with the \`ambient-code:managed\` label."
83+
6984
- name: Create session
7085
if: steps.existing.outputs.skip != 'true'
7186
id: session
@@ -100,16 +115,25 @@ jobs:
100115
first line of the PR body (read your session ID from the
101116
AGENTIC_SESSION_NAME environment variable):
102117
<!-- acp:session_id=$AGENTIC_SESSION_NAME source=#${{ steps.issue.outputs.number }} last_action=<ISO8601_NOW> retry_count=0 -->
118+
At the bottom of the PR body, add a session link:
119+
---
120+
🤖 [Ambient Session](<SESSION_URL>)
103121
6. Add the `ambient-code:managed` label to the PR.
104-
7. After creating the PR, send a Slack notification:
105-
```bash
106-
curl -X POST -H 'Content-type: application/json' \
107-
--data '{"text":"PR created for #${{ steps.issue.outputs.number }}\n*PR*: <PR_URL>\n*Session*: '"$PLATFORM_HOST/projects/$AGENTIC_SESSION_NAMESPACE/sessions/$AGENTIC_SESSION_NAME"'"}' \
108-
"$SLACK_WEBHOOK_URL"
109-
```
110-
Only send if SLACK_WEBHOOK_URL is set.
111-
8. Ensure CI passes. If it fails, investigate and fix.
112-
9. Do not merge. Leave the PR open for human review.
122+
7. Ensure CI passes. If it fails, investigate and fix.
123+
8. Do not merge. Leave the PR open for human review.
124+
9. When you comment on the PR, include this footer at the end:
125+
_🤖 [Session](<SESSION_URL>)_
126+
127+
## Session URL
128+
129+
IMPORTANT: Wherever a session link is needed (PR body, comments, Slack),
130+
you MUST first resolve the URL by reading the environment variables. Run:
131+
```bash
132+
echo "$PLATFORM_HOST/projects/$AGENTIC_SESSION_NAMESPACE/sessions/$AGENTIC_SESSION_NAME"
133+
```
134+
Use the resolved output as the actual URL. Do NOT write `$PLATFORM_HOST`,
135+
`$AGENTIC_SESSION_NAMESPACE`, or `$AGENTIC_SESSION_NAME` literally in any
136+
markdown link — always substitute their actual values.
113137
repos: >-
114138
[{"url": "https://github.com/${{ github.repository }}", "branch": "main"}]
115139
model: claude-opus-4-6
@@ -118,15 +142,32 @@ jobs:
118142
environment-variables: >-
119143
{"SLACK_WEBHOOK_URL": "${{ secrets.SLACK_WEBHOOK_URL }}", "PLATFORM_HOST": "${{ secrets.PLATFORM_HOST }}"}
120144
121-
- name: Post-session labels and comment
145+
- name: Post-session update comment
122146
if: steps.existing.outputs.skip != 'true'
123147
env:
124148
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
125149
SESSION_NAME: ${{ steps.session.outputs.session-name }}
126150
SESSION_PHASE: ${{ steps.session.outputs.session-phase }}
151+
SESSION_URL: ${{ steps.session.outputs.session-url }}
152+
PLATFORM_HOST: ${{ secrets.PLATFORM_HOST }}
153+
AMBIENT_PROJECT: ${{ secrets.AMBIENT_PROJECT }}
127154
run: |
128155
if [ -n "$SESSION_NAME" ]; then
129-
gh issue comment ${{ steps.issue.outputs.number }} --repo "${{ github.repository }}" --body "Session \`$SESSION_NAME\` created (phase: $SESSION_PHASE). PR will have the \`ambient-code:managed\` label."
156+
# Build session link — prefer session-url output, fall back to constructed URL
157+
if [ -n "$SESSION_URL" ]; then
158+
LINK="$SESSION_URL"
159+
elif [ -n "$PLATFORM_HOST" ] && [ -n "$AMBIENT_PROJECT" ]; then
160+
LINK="${PLATFORM_HOST%/}/projects/$AMBIENT_PROJECT/sessions/$SESSION_NAME"
161+
else
162+
LINK=""
163+
fi
164+
165+
if [ -n "$LINK" ]; then
166+
BODY="🤖 Amber session completed (phase: $SESSION_PHASE). [View session]($LINK)"
167+
else
168+
BODY="🤖 Amber session \`$SESSION_NAME\` completed (phase: $SESSION_PHASE)."
169+
fi
170+
gh issue comment ${{ steps.issue.outputs.number }} --repo "${{ github.repository }}" --body "$BODY"
130171
fi
131172
132173
- name: Session summary
@@ -158,6 +199,13 @@ jobs:
158199
runs-on: ubuntu-latest
159200
timeout-minutes: 30
160201
steps:
202+
- name: Acknowledge comment
203+
env:
204+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
205+
run: |
206+
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
207+
-X POST -f content=eyes --silent || true
208+
161209
- name: Resolve context
162210
id: context
163211
env:
@@ -231,13 +279,28 @@ jobs:
231279
4. Ensure the PR body contains this frontmatter as the first line
232280
(read your session ID from the AGENTIC_SESSION_NAME environment variable):
233281
<!-- acp:session_id=$AGENTIC_SESSION_NAME source=#${{ steps.context.outputs.number }} last_action=<ISO8601_NOW> retry_count=<N> -->
234-
Increment retry_count from whatever it was before. If retry_count reaches 3 or more,
235-
stop working, add `ambient-code:needs-human` label, remove `ambient-code:managed` label,
282+
Only increment retry_count if you actually had to fix something (CI failure,
283+
conflict, review comment). If the PR is already healthy, do NOT increment —
284+
just update last_action. If retry_count reaches 3 or more, stop working,
285+
add `ambient-code:needs-human` label, remove `ambient-code:managed` label,
236286
comment "AI was unable to resolve after 3 attempts. Needs human attention.",
237287
and send a Slack notification (see below).
238288
5. Add the `ambient-code:managed` label.
239289
6. Do not merge. Do not close. Do not force-push.
240290
7. If fundamentally broken beyond repair, add a comment explaining and stop.
291+
8. When you comment on the PR, include this footer at the end:
292+
_🤖 [Session](<SESSION_URL>)_
293+
294+
## Session URL
295+
296+
IMPORTANT: Wherever a session link is needed (PR body, comments, Slack),
297+
you MUST first resolve the URL by reading the environment variables. Run:
298+
```bash
299+
echo "$PLATFORM_HOST/projects/$AGENTIC_SESSION_NAMESPACE/sessions/$AGENTIC_SESSION_NAME"
300+
```
301+
Use the resolved output as the actual URL. Do NOT write `$PLATFORM_HOST`,
302+
`$AGENTIC_SESSION_NAMESPACE`, or `$AGENTIC_SESSION_NAME` literally in any
303+
markdown link — always substitute their actual values.
241304
242305
## Slack Notifications
243306
@@ -293,16 +356,25 @@ jobs:
293356
first line of the PR body (read your session ID from the
294357
AGENTIC_SESSION_NAME environment variable):
295358
<!-- acp:session_id=$AGENTIC_SESSION_NAME source=#${{ steps.context.outputs.number }} last_action=<ISO8601_NOW> retry_count=0 -->
359+
At the bottom of the PR body, add a session link:
360+
---
361+
🤖 [Ambient Session](<SESSION_URL>)
296362
6. Add the `ambient-code:managed` label to the PR.
297-
7. After creating the PR, send a Slack notification:
298-
```bash
299-
curl -X POST -H 'Content-type: application/json' \
300-
--data '{"text":"PR created for #${{ steps.context.outputs.number }}\n*PR*: <PR_URL>\n*Session*: '"$PLATFORM_HOST/projects/$AGENTIC_SESSION_NAMESPACE/sessions/$AGENTIC_SESSION_NAME"'"}' \
301-
"$SLACK_WEBHOOK_URL"
302-
```
303-
Only send if SLACK_WEBHOOK_URL is set.
304-
8. Ensure CI passes. If it fails, investigate and fix.
305-
9. Do not merge. Leave the PR open for human review.
363+
7. Ensure CI passes. If it fails, investigate and fix.
364+
8. Do not merge. Leave the PR open for human review.
365+
9. When you comment on the PR, include this footer at the end:
366+
_🤖 [Session](<SESSION_URL>)_
367+
368+
## Session URL
369+
370+
IMPORTANT: Wherever a session link is needed (PR body, comments, Slack),
371+
you MUST first resolve the URL by reading the environment variables. Run:
372+
```bash
373+
echo "$PLATFORM_HOST/projects/$AGENTIC_SESSION_NAMESPACE/sessions/$AGENTIC_SESSION_NAME"
374+
```
375+
Use the resolved output as the actual URL. Do NOT write `$PLATFORM_HOST`,
376+
`$AGENTIC_SESSION_NAMESPACE`, or `$AGENTIC_SESSION_NAME` literally in any
377+
markdown link — always substitute their actual values.
306378
repos: >-
307379
[{"url": "https://github.com/${{ github.repository }}", "branch": "main"}]
308380
model: claude-opus-4-6
@@ -552,10 +624,49 @@ jobs:
552624
print(f" Failed to create session: {e}")
553625
return None
554626
555-
# Get all open ambient-code:managed PRs (include updatedAt to avoid per-PR API calls)
627+
BOT_LOGINS = ["github-actions[bot]", "ambient-code[bot]", "ambient-bot"]
628+
629+
def needs_attention(pr_number):
630+
"""Check if a PR has actionable issues that need the fixer's attention.
631+
Returns (needs_work, reason) tuple."""
632+
# Check CI status
633+
checks_json = gh("pr", "checks", str(pr_number), "--repo", REPO,
634+
"--json", "name,state",
635+
"--jq", "[.[] | .state] | unique")
636+
try:
637+
states = json.loads(checks_json) if checks_json else []
638+
except json.JSONDecodeError:
639+
states = []
640+
641+
if not states:
642+
pass # No CI checks — nothing to fix
643+
elif "FAILURE" in states:
644+
return True, "CI failing"
645+
646+
# Check for merge conflicts
647+
mergeable = gh("pr", "view", str(pr_number), "--repo", REPO,
648+
"--json", "mergeable", "--jq", ".mergeable")
649+
if mergeable == "CONFLICTING":
650+
return True, "merge conflicts"
651+
652+
# Check for changes_requested from non-bot users
653+
bot_filter = " and ".join([f'.user.login != "{b}"' for b in BOT_LOGINS])
654+
try:
655+
reviews_raw = gh("api", f"repos/{REPO}/pulls/{pr_number}/reviews",
656+
"--jq", f'[.[] | select(.state == "CHANGES_REQUESTED" and {bot_filter})] | length')
657+
changes_requested = int(reviews_raw) if reviews_raw else 0
658+
except (ValueError, TypeError):
659+
changes_requested = 0
660+
661+
if changes_requested > 0:
662+
return True, "changes requested"
663+
664+
return False, "healthy"
665+
666+
# Get all open ambient-code:managed PRs
556667
prs_json = gh("pr", "list", "--repo", REPO, "--state", "open",
557668
"--label", "ambient-code:managed", "--limit", "200",
558-
"--json", "number,body,title,updatedAt")
669+
"--json", "number,body,title")
559670
prs = json.loads(prs_json) if prs_json else []
560671
print(f"Found {len(prs)} ambient-code:managed PRs")
561672
@@ -576,12 +687,13 @@ jobs:
576687
session_id = fm["session_id"]
577688
source = fm["source"]
578689
579-
# Check for changes using updatedAt from gh pr list (no extra API call)
580-
updated_at = pr.get("updatedAt", "")
581-
if updated_at and updated_at <= fm["last_action"]:
582-
print(f"PR #{number}: no changes since {fm['last_action']} (updatedAt={updated_at}), skipping")
690+
# Only trigger if the PR actually needs work
691+
needs_work, reason = needs_attention(number)
692+
if not needs_work:
693+
print(f"PR #{number}: {reason}, skipping")
583694
skipped += 1
584695
continue
696+
print(f"PR #{number}: {reason}")
585697
586698
# Trigger fix — reuse session if exists, create new if not
587699
print(f"PR #{number}: triggering fix (session_id={session_id or 'new'})")
@@ -602,12 +714,28 @@ jobs:
602714
4. Ensure the PR body contains this frontmatter as the first line
603715
(read your session ID from the AGENTIC_SESSION_NAME environment variable):
604716
<!-- acp:session_id=$AGENTIC_SESSION_NAME source={source} last_action=<ISO8601_NOW> retry_count=<N> -->
605-
The current retry_count is {current_retry}. Increment it by 1.
717+
The current retry_count is {current_retry}. Only increment retry_count if
718+
you actually had to fix something (CI failure, conflict, review comment).
719+
If the PR is already healthy (CI green, no conflicts, no open reviews),
720+
do NOT increment — just update last_action.
606721
If retry_count reaches 3 or more, stop working, add `ambient-code:needs-human` label,
607722
remove `ambient-code:managed` label, comment on the PR, and send a Slack notification.
608723
5. Add the `ambient-code:managed` label.
609724
6. Do not merge. Do not close. Do not force-push.
610725
7. If fundamentally broken beyond repair, add a comment explaining and stop.
726+
8. When you comment on the PR, include this footer at the end:
727+
_🤖 [Session](<SESSION_URL>)_
728+
729+
## Session URL
730+
731+
IMPORTANT: Wherever a session link is needed (PR body, comments, Slack),
732+
you MUST first resolve the URL by reading the environment variables. Run:
733+
```bash
734+
echo "$PLATFORM_HOST/projects/$AGENTIC_SESSION_NAMESPACE/sessions/$AGENTIC_SESSION_NAME"
735+
```
736+
Use the resolved output as the actual URL. Do NOT write `$PLATFORM_HOST`,
737+
`$AGENTIC_SESSION_NAMESPACE`, or `$AGENTIC_SESSION_NAME` literally in any
738+
markdown link — always substitute their actual values.
611739
612740
## Slack Notifications
613741

.github/workflows/claude-live-test.yml

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
-f components/operator/Dockerfile components/operator
8484
8585
docker build -t quay.io/ambient_code/vteam_claude_runner:claude-test \
86-
-f components/runners/ambient-runner/Dockerfile components/runners
86+
components/runners/ambient-runner
8787
8888
echo "✅ All images built"
8989

.github/workflows/components-build-deploy.yml

100644100755
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
{"name":"frontend","context":"./components/frontend","image":"quay.io/ambient_code/vteam_frontend","dockerfile":"./components/frontend/Dockerfile"},
5252
{"name":"backend","context":"./components/backend","image":"quay.io/ambient_code/vteam_backend","dockerfile":"./components/backend/Dockerfile"},
5353
{"name":"operator","context":"./components/operator","image":"quay.io/ambient_code/vteam_operator","dockerfile":"./components/operator/Dockerfile"},
54-
{"name":"ambient-runner","context":"./components/runners","image":"quay.io/ambient_code/vteam_claude_runner","dockerfile":"./components/runners/ambient-runner/Dockerfile"},
54+
{"name":"ambient-runner","context":"./components/runners/ambient-runner","image":"quay.io/ambient_code/vteam_claude_runner","dockerfile":"./components/runners/ambient-runner/Dockerfile"},
5555
{"name":"state-sync","context":"./components/runners/state-sync","image":"quay.io/ambient_code/vteam_state_sync","dockerfile":"./components/runners/state-sync/Dockerfile"},
5656
{"name":"public-api","context":"./components/public-api","image":"quay.io/ambient_code/vteam_public_api","dockerfile":"./components/public-api/Dockerfile"},
5757
{"name":"ambient-api-server","context":"./components/ambient-api-server","image":"quay.io/ambient_code/vteam_api_server","dockerfile":"./components/ambient-api-server/Dockerfile"}
@@ -206,7 +206,7 @@ jobs:
206206

207207
- name: Log in to OpenShift Cluster
208208
run: |
209-
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }} --insecure-skip-tls-verify
209+
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }}
210210
211211
- name: Deploy Service Mesh operator via OLM
212212
run: |
@@ -327,7 +327,7 @@ jobs:
327327

328328
- name: Log in to OpenShift Cluster
329329
run: |
330-
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }} --insecure-skip-tls-verify
330+
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }}
331331
332332
- name: Apply RBAC and CRD manifests
333333
run: |
@@ -360,7 +360,7 @@ jobs:
360360
361361
- name: Log in to OpenShift Cluster
362362
run: |
363-
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }} --insecure-skip-tls-verify
363+
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }}
364364
365365
- name: Update kustomization with SHA image tags
366366
working-directory: components/manifests/overlays/production
@@ -435,7 +435,7 @@ jobs:
435435
436436
- name: Log in to OpenShift Cluster
437437
run: |
438-
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }} --insecure-skip-tls-verify
438+
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }}
439439
440440
- name: Update kustomization with SHA image tags
441441
working-directory: components/manifests/overlays/production

.github/workflows/e2e.yml

100644100755
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,7 @@ jobs:
135135
if [ "${{ needs.detect-changes.outputs.claude-runner }}" == "true" ]; then
136136
echo "Building ambient-runner (changed)..."
137137
docker build -t quay.io/ambient_code/vteam_claude_runner:e2e-test \
138-
-f components/runners/ambient-runner/Dockerfile \
139-
components/runners
138+
components/runners/ambient-runner
140139
else
141140
echo "Claude-runner unchanged, pulling latest..."
142141
docker pull quay.io/ambient_code/vteam_claude_runner:latest

0 commit comments

Comments
 (0)