Skip to content

Commit 57d8fbf

Browse files
MagnetonIOclaude
andcommitted
Phase 12: Universal agent runtime + LLM-driven tool selection + live Vercel dashboard
Replaced hardcoded shell scripts with a universal LLM-driven agent runtime. The contract defines WHAT to do — the LLM agent decides HOW, including which tools to use. One runtime script for all stages. Universal agent-runtime.sh: - Reads /context/brief.md (contract instructions) - Sends to LLM: "plan your approach, here are your tools" - LLM returns JSON array of shell commands - Executes step by step with self-repair on failure - No hardcoded tool usage — LLM decides everything Validated end-to-end: 3-stage oil & gas market dashboard pipeline - Stage 1 (researcher): LLM generated market data, news, analysis - Stage 2 (developer): LLM wrote Python script to generate Chart.js dashboard - Stage 3 (deployer): deployed to Vercel Live dashboard: https://app-theta-umber.vercel.app Changes: - Created sandbox/scripts/agent-runtime.sh (universal LLM-driven runtime) - Deleted hardcoded scripts (researcher.sh, writer.sh, publisher.sh, developer.sh, deployer.sh) - Pipeline always uses agent-runtime.sh — LLM plans tool usage from contract - ContextBridge tags with contract:{name}, stage:{stage} for scoped memory - Pipeline.build_env injects vercel_token from env when contract declares it - market-dashboard.yaml updated with vercel_token credential Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eaf0f2e commit 57d8fbf

9 files changed

Lines changed: 335 additions & 314 deletions

File tree

sandbox/scripts/agent-runtime.sh

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#!/bin/sh
2+
# Universal Agent Runtime — LLM-driven plan+execute+fix loop
3+
#
4+
# This is the ONLY script that runs inside microVMs. It reads the contract
5+
# instructions from /context/brief.md, asks the LLM to plan which tools
6+
# to use, executes the plan step by step, and self-repairs on failure.
7+
#
8+
# The contract defines WHAT. The LLM decides HOW.
9+
set -e
10+
11+
# Install base tools (Alpine, ~3 seconds)
12+
apk add --no-cache -q curl jq git bash nodejs npm python3 2>/dev/null || true
13+
14+
MAX_ITERATIONS=10
15+
LLM_PROXY="http://localhost:4000/api/v1/vm/llm/chat"
16+
17+
echo "=== Agent Runtime Starting ==="
18+
19+
# 1. Read the brief (contract instructions for this stage)
20+
BRIEF=""
21+
if [ -f /context/brief.md ]; then
22+
BRIEF=$(cat /context/brief.md)
23+
else
24+
echo "ERROR: No /context/brief.md found"
25+
exit 1
26+
fi
27+
28+
# 2. Discover available context files
29+
CONTEXT_FILES=$(ls -1 /context/ 2>/dev/null | grep -v brief.md | head -20)
30+
CONTEXT_CONTENTS=""
31+
for f in $CONTEXT_FILES; do
32+
if [ -f "/context/$f" ]; then
33+
SIZE=$(wc -c < "/context/$f" | tr -d ' ')
34+
if [ "$SIZE" -lt 8000 ]; then
35+
CONTEXT_CONTENTS="$CONTEXT_CONTENTS
36+
--- /context/$f ---
37+
$(cat /context/$f)"
38+
else
39+
CONTEXT_CONTENTS="$CONTEXT_CONTENTS
40+
--- /context/$f (${SIZE} bytes, showing first 2000) ---
41+
$(head -c 2000 /context/$f)"
42+
fi
43+
fi
44+
done
45+
46+
# 3. Discover available tools
47+
TOOLS="curl, jq, git, bash, node, npm, python3"
48+
if command -v vercel > /dev/null 2>&1; then TOOLS="$TOOLS, vercel"; fi
49+
if command -v pdflatex > /dev/null 2>&1; then TOOLS="$TOOLS, pdflatex"; fi
50+
51+
# Check for injected credentials
52+
CREDS=""
53+
if [ -n "$GH_TOKEN" ]; then
54+
CREDS="$CREDS GitHub (GH_TOKEN set),"
55+
git config --global user.email "agent-os@agenthero.work"
56+
git config --global user.name "Agent-OS Pipeline"
57+
git config --global init.defaultBranch main
58+
git config --global url."https://x-access-token:${GH_TOKEN}@github.com/".insteadOf "https://github.com/"
59+
fi
60+
if [ -n "$VERCEL_TOKEN" ]; then
61+
CREDS="$CREDS Vercel (VERCEL_TOKEN set),"
62+
npm install -g vercel 2>/dev/null || true
63+
fi
64+
65+
echo "Tools: $TOOLS"
66+
echo "Credentials: ${CREDS:-none}"
67+
echo "Context files: $(echo $CONTEXT_FILES | tr '\n' ', ')"
68+
69+
# 4. Ask LLM to plan the approach
70+
echo ""
71+
echo "=== Planning ==="
72+
73+
PLAN_PROMPT="You are an autonomous agent running inside an isolated Linux container.
74+
75+
YOUR TASK:
76+
$BRIEF
77+
78+
AVAILABLE TOOLS: $TOOLS
79+
AVAILABLE CREDENTIALS: ${CREDS:-none}
80+
OUTPUT DIRECTORY: /shared/output/ (write ALL output files here)
81+
82+
CONTEXT FILES AVAILABLE:
83+
$CONTEXT_CONTENTS
84+
85+
CRITICAL RULES:
86+
- You have NO access to external APIs or websites. Do NOT use curl to fetch data from APIs.
87+
- Generate all content from YOUR OWN KNOWLEDGE. You are an expert — write the data yourself.
88+
- For JSON data files, write them directly using cat/heredoc or a python/node script.
89+
- For markdown files, write them directly using cat/heredoc.
90+
- For HTML/CSS/JS dashboards, write a python script that generates the complete file.
91+
- Use real, specific, accurate data from your training knowledge.
92+
- If credentials are available (GH_TOKEN, VERCEL_TOKEN), use git/vercel CLI tools for deployment.
93+
94+
INSTRUCTIONS:
95+
1. Plan your approach step by step
96+
2. For each step, provide the EXACT shell command to run
97+
3. Use the context files to inform your work
98+
4. Write all output to /shared/output/
99+
5. If you need to create directories, use mkdir -p
100+
6. For large file generation, write a python3 script to /tmp/gen.py then run it
101+
102+
OUTPUT FORMAT — respond with ONLY a JSON array of steps:
103+
[
104+
{\"description\": \"what this step does\", \"command\": \"the exact shell command\"},
105+
{\"description\": \"next step\", \"command\": \"next command\"},
106+
...
107+
]
108+
109+
IMPORTANT:
110+
- Output ONLY the JSON array, no markdown fences, no explanation
111+
- Each command must be a single shell command (use && to chain if needed)
112+
- For multi-line file writes, use heredoc: cat > /shared/output/file.md << 'HEREDOC' ... HEREDOC
113+
- For complex file generation, write a python3 or node script to /tmp/ first, then execute it
114+
- NEVER try to curl/fetch external URLs for data — generate everything from your knowledge"
115+
116+
PLAN_RESPONSE=$(curl -s -X POST "$LLM_PROXY" \
117+
-H "Content-Type: application/json" \
118+
-H "Authorization: Bearer $JOB_TOKEN" \
119+
-d "$(jq -n \
120+
--arg prompt "$PLAN_PROMPT" \
121+
'{messages: [{role: "user", content: $prompt}], model: "gpt-4o", max_tokens: 8192, temperature: 0.2}')" 2>&1)
122+
123+
# Extract content from LLM response
124+
PLAN=""
125+
if echo "$PLAN_RESPONSE" | jq -e '.content' > /dev/null 2>&1; then
126+
PLAN=$(echo "$PLAN_RESPONSE" | jq -r '.content')
127+
else
128+
echo "ERROR: LLM proxy not available or returned invalid response"
129+
echo "Response: $(echo "$PLAN_RESPONSE" | head -c 500)"
130+
exit 1
131+
fi
132+
133+
# Strip markdown code fences if present
134+
PLAN=$(echo "$PLAN" | sed 's/^```json//' | sed 's/^```//' | sed 's/```$//')
135+
136+
# Validate JSON
137+
if ! echo "$PLAN" | jq -e '.' > /dev/null 2>&1; then
138+
echo "ERROR: LLM returned invalid JSON plan"
139+
echo "Plan: $(echo "$PLAN" | head -c 500)"
140+
exit 1
141+
fi
142+
143+
STEP_COUNT=$(echo "$PLAN" | jq 'length')
144+
echo "Plan: $STEP_COUNT steps"
145+
146+
# 5. Execute plan step by step
147+
echo ""
148+
echo "=== Executing ==="
149+
150+
ITERATION=0
151+
STEP_INDEX=0
152+
153+
while [ "$STEP_INDEX" -lt "$STEP_COUNT" ] && [ "$ITERATION" -lt "$MAX_ITERATIONS" ]; do
154+
ITERATION=$((ITERATION + 1))
155+
DESC=$(echo "$PLAN" | jq -r ".[$STEP_INDEX].description")
156+
CMD=$(echo "$PLAN" | jq -r ".[$STEP_INDEX].command")
157+
158+
echo ""
159+
echo "--- Step $((STEP_INDEX + 1))/$STEP_COUNT: $DESC ---"
160+
echo "\$ $CMD"
161+
162+
# Execute the command
163+
OUTPUT=$(eval "$CMD" 2>&1) || {
164+
EXIT_CODE=$?
165+
echo "FAILED (exit $EXIT_CODE): $OUTPUT"
166+
167+
# Self-repair: ask LLM to fix
168+
echo "Asking LLM to fix..."
169+
170+
FIX_PROMPT="A command failed during autonomous execution.
171+
172+
Step description: $DESC
173+
Command: $CMD
174+
Error output: $OUTPUT
175+
Exit code: $EXIT_CODE
176+
177+
Available tools: $TOOLS
178+
Working directory: $(pwd)
179+
180+
Provide a FIXED command that accomplishes the same goal. Output ONLY the shell command, nothing else."
181+
182+
FIX_RESPONSE=$(curl -s -X POST "$LLM_PROXY" \
183+
-H "Content-Type: application/json" \
184+
-H "Authorization: Bearer $JOB_TOKEN" \
185+
-d "$(jq -n \
186+
--arg prompt "$FIX_PROMPT" \
187+
'{messages: [{role: "user", content: $prompt}], model: "gpt-4o", max_tokens: 2048, temperature: 0.1}')" 2>&1)
188+
189+
if echo "$FIX_RESPONSE" | jq -e '.content' > /dev/null 2>&1; then
190+
FIXED_CMD=$(echo "$FIX_RESPONSE" | jq -r '.content' | sed 's/^```sh//' | sed 's/^```bash//' | sed 's/^```//' | sed 's/```$//' | tr -d '\n')
191+
echo "Fix: $FIXED_CMD"
192+
eval "$FIXED_CMD" 2>&1 || echo "Fix also failed — continuing"
193+
else
194+
echo "Could not get fix from LLM — continuing"
195+
fi
196+
}
197+
198+
# Show truncated output
199+
if [ -n "$OUTPUT" ]; then
200+
echo "$(echo "$OUTPUT" | head -5)"
201+
LINES=$(echo "$OUTPUT" | wc -l | tr -d ' ')
202+
if [ "$LINES" -gt 5 ]; then echo "... ($LINES lines total)"; fi
203+
fi
204+
205+
STEP_INDEX=$((STEP_INDEX + 1))
206+
done
207+
208+
# 6. Verify output
209+
echo ""
210+
echo "=== Output Verification ==="
211+
if [ -d /shared/output ]; then
212+
echo "Files in /shared/output/:"
213+
find /shared/output -type f | while read f; do
214+
SIZE=$(wc -c < "$f" | tr -d ' ')
215+
echo " $f ($SIZE bytes)"
216+
done
217+
else
218+
echo "WARNING: /shared/output/ is empty"
219+
fi
220+
221+
echo ""
222+
echo "=== Agent Runtime Complete ($ITERATION iterations) ==="

sandbox/scripts/publisher.sh

Lines changed: 0 additions & 85 deletions
This file was deleted.

sandbox/scripts/researcher.sh

Lines changed: 0 additions & 66 deletions
This file was deleted.

0 commit comments

Comments
 (0)