-
-
Notifications
You must be signed in to change notification settings - Fork 1
153 lines (141 loc) · 7.35 KB
/
agent-spawn.yml
File metadata and controls
153 lines (141 loc) · 7.35 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
name: Trinity Agent Spawn
on:
issues:
types: [opened, labeled]
concurrency:
group: agent-pool-${{ github.event.issue.number % 2 }}
cancel-in-progress: false
jobs:
spawn-agent:
# Spawn if label agent:spawn AND agent is NOT manual
if: |
contains(github.event.issue.labels.*.name, 'agent:spawn') &&
!contains(github.event.issue.body, 'manual (no agent)')
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Check for free slot (no polling)
id: wait-for-slot
env:
RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
echo "🔍 Checking for active agent deployments..."
# Single check — no polling loop (P1.1)
DEPLOYMENTS=$(curl -s -X POST "https://backboard.railway.com/graphql/v2" \
-H "Authorization: Bearer ${RAILWAY_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"query(\$id: String!) { project(id: \$id) { services { edges { node { id name deployments(first: 1) { edges { node { status } } } } } } } }\",
\"variables\": { \"id\": \"${RAILWAY_PROJECT_ID}\" }
}")
ACTIVE_COUNT=$(echo "$DEPLOYMENTS" | jq '[.data.project.services.edges[].node | select(.name | startswith("agent-")) | .deployments.edges[0].node.status // ""] | map(select(. == "BUILDING" or . == "DEPLOYING")) | length')
if [ "$ACTIVE_COUNT" -eq 0 ]; then
echo "✅ Slot free, spawning immediately"
echo "slot_available=true" >> $GITHUB_OUTPUT
else
echo "⏳ $ACTIVE_COUNT active agent(s) — adding to queue"
# Add queued label instead of polling (queue-drain workflow will pick it up)
gh issue edit ${{ github.event.issue.number }} \
--repo ${{ github.repository }} \
--add-label "agent:queued"
gh issue comment ${{ github.event.issue.number }} \
--repo ${{ github.repository }} \
--body "⏳ **All agent slots busy** — queued via \`agent:queued\` label. Will auto-spawn when a slot frees up (~5 min check interval)."
echo "slot_available=false" >> $GITHUB_OUTPUT
fi
- name: Spawn container via Railway API
if: steps.wait-for-slot.outputs.slot_available == 'true'
env:
RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
RAILWAY_ENVIRONMENT_ID: ${{ secrets.RAILWAY_ENVIRONMENT_ID }}
AGENT_GH_TOKEN: ${{ secrets.AGENT_GH_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
WS_MONITOR_URL: ${{ secrets.WS_MONITOR_URL }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
MONITOR_TOKEN: ${{ secrets.MONITOR_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
AGENT_IMAGE: ghcr.io/ghashtag/trinity-agent:latest
run: |
set -e
echo "🚀 Spawning agent for issue #${ISSUE_NUMBER}..."
SERVICE_NAME="agent-${ISSUE_NUMBER}"
# 0. Check if service already exists (reuse instead of delete+create)
EXISTING=$(curl -s -X POST "https://backboard.railway.com/graphql/v2" \
-H "Authorization: Bearer ${RAILWAY_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"query(\$id: String!) { project(id: \$id) { services { edges { node { id name } } } } }\",
\"variables\": { \"id\": \"${RAILWAY_PROJECT_ID}\" }
}")
SERVICE_ID=$(echo "$EXISTING" | jq -r ".data.project.services.edges[].node | select(.name == \"${SERVICE_NAME}\") | .id" 2>/dev/null || true)
# Pool of reusable agent services (round-robin by issue number)
POOL_0="acfee27a-74e8-4436-961c-698ae93508ca" # ubuntu
POOL_1="12c2bdf9-d124-4a45-93ad-22921e842d1b" # Agents Anywhere
if [ -n "$SERVICE_ID" ]; then
echo "♻️ Reusing existing service ${SERVICE_NAME} (${SERVICE_ID})"
else
# Round-robin: even issues → ubuntu, odd → Agents Anywhere
SLOT=$((ISSUE_NUMBER % 2))
eval "SERVICE_ID=\$POOL_${SLOT}"
POOL_NAME=$([ "$SLOT" -eq 0 ] && echo "ubuntu" || echo "Agents Anywhere")
echo "♻️ No dedicated service — reusing ${POOL_NAME} (${SERVICE_ID}) for issue #${ISSUE_NUMBER}"
fi
# 2. Set environment variables via variableCollectionUpsert (batch)
VARS_RESPONSE=$(curl -s -X POST "https://backboard.railway.com/graphql/v2" \
-H "Authorization: Bearer ${RAILWAY_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"mutation(\$input: VariableCollectionUpsertInput!) { variableCollectionUpsert(input: \$input) }\",
\"variables\": {
\"input\": {
\"projectId\": \"${RAILWAY_PROJECT_ID}\",
\"environmentId\": \"${RAILWAY_ENVIRONMENT_ID}\",
\"serviceId\": \"${SERVICE_ID}\",
\"variables\": {
\"ISSUE_NUMBER\": \"${ISSUE_NUMBER}\",
\"GITHUB_TOKEN\": \"${AGENT_GH_TOKEN}\",
\"GH_TOKEN\": \"${AGENT_GH_TOKEN}\",
\"ANTHROPIC_API_KEY\": \"${ANTHROPIC_API_KEY}\",
\"ANTHROPIC_BASE_URL\": \"${ANTHROPIC_BASE_URL}\",
\"API_TIMEOUT_MS\": \"3000000\",
\"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\": \"1\",
\"NO_COLOR\": \"1\",
\"WS_MONITOR_URL\": \"${WS_MONITOR_URL}\",
\"TELEGRAM_BOT_TOKEN\": \"${TELEGRAM_BOT_TOKEN}\",
\"TELEGRAM_CHAT_ID\": \"${TELEGRAM_CHAT_ID}\",
\"MONITOR_TOKEN\": \"${MONITOR_TOKEN}\",
\"REPO_URL\": \"https://github.com/gHashTag/trinity.git\",
\"CLAUDE_MODEL\": \"glm-5\"
}
}
}
}")
echo "✅ Variables set: ${VARS_RESPONSE}"
# 3. Deploy the service
DEPLOY_RESPONSE=$(curl -s -X POST "https://backboard.railway.com/graphql/v2" \
-H "Authorization: Bearer ${RAILWAY_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"mutation(\$serviceId: String!, \$environmentId: String!) { serviceInstanceDeploy(serviceId: \$serviceId, environmentId: \$environmentId) }\",
\"variables\": {
\"serviceId\": \"${SERVICE_ID}\",
\"environmentId\": \"${RAILWAY_ENVIRONMENT_ID}\"
}
}")
echo "✅ Deploy triggered: ${DEPLOY_RESPONSE}"
- name: Comment confirmation
if: steps.wait-for-slot.outputs.slot_available == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh issue comment ${{ github.event.issue.number }} \
--repo ${{ github.repository }} \
--body "🚀 **Agent container spawned!** Deploying on Railway. Tracking in this issue."