sha256:854a453ca9a9f181fea979ce8d5fab8b5ba913ac592aba8fa98e79a7f2c9dc75
Last pushed
19 days by olegselajev241
Type
Sandbox Kit
Manifest digest
sha256:854a453ca9a9f181fea979ce8d5fab8b5ba913ac592aba8fa98e79a7f2c9dc75
schemaVersion: "2"
kind: sandbox
name: herdr-crew
version: 0.5.1
displayName: Herdr Crew
description: |
Task sandbox running a nested Herdr server and a crew of coding agents
(Claude Code, Codex, pi with Google models) in rotating roles: a sandboxed orchestrator
that holds the goal, plus planner, implementer, and QC. The host only
delivers goals and arbitrates resources.
sourceURL: https://github.com/shelajev/herdr
args:
claude_model:
default: claude-opus-5-5
description: Model passed explicitly to Claude Code
codex_model:
default: gpt-6.1-sol
description: Model passed explicitly to Codex
gemini_model:
default: gemini-3.8-flash
description: Google model the pi role pins with --provider google --model
image:
default: docker.io/olegselajev241/herdr-crew:0.5.1
description: Herdr crew template image. The 0.5.1 default is published for linux/arm64 (digest sha256:45787cc3a320ee7677a1ca248beff29093d78748a30bbf6b7f7a4bae9eca230f), but this kit version is still an unpublished candidate, and promotion waits for real sandbox acceptance; for local builds pass herdr-crew:local after scripts/sbx/build-crew-template.sh
pi_provider:
default: google
description: Provider paired with the pi model
roles:
default: orchestrator=claude,planner=codex,implementer=claude,qc=pi
description: Role assignment for this task; the host rotates this per task and guarantees qc differs from implementer
sandbox:
image: ${{ kit.args.image }}
entrypoint:
- crew-entry
agentInstructions:
filename: AGENTS.md
content: |
## Herdr crew
This sandbox runs a crew of coding agents: an orchestrator that holds the
task goal and coordinates everyone through the local herdr CLI, plus a
planner, an implementer, and quality control. Your role is named in the
prompt you receive; read your prompt pack in `/home/agent/crew/roles/`
and follow it. Only the orchestrator drives other agents; every other
role works solely on what it is prompted with. The role-to-agent
assignment for this task is recorded in `/home/agent/crew/assignment`.
Shared crew files under `/home/agent/crew/`: `goal.md` (the goal, owned by
the orchestrator), `plan.md` (the plan, owned by the planner), `qc-log.md`
(findings and VERDICT lines, owned by qc), `status.md` (progress notes and
the final RESULT line, owned by the orchestrator), and `escalations.md`
(resource requests for the host to approve or deny).
permissions:
network:
allow:
- api.anthropic.com
- statsig.anthropic.com
- claude.ai
- console.anthropic.com
- platform.claude.com
- downloads.claude.ai
- claude.com
- code.claude.com
- api.openai.com
- openai.com
- auth.openai.com
- chatgpt.com
- files.openai.com
- codeload.github.com
- generativelanguage.googleapis.com
- herdr.dev
- github.com
- api.github.com
- raw.githubusercontent.com
- objects.githubusercontent.com
- release-assets.githubusercontent.com
- registry.npmjs.org
- archive.ubuntu.com
- security.ubuntu.com
- ports.ubuntu.com
- download.docker.com
credentials:
- service: anthropic
apiKey:
name: ANTHROPIC_API_KEY
inject:
- domain: api.anthropic.com
header: x-api-key
format: '%s'
- domain: claude.ai
header: x-api-key
format: '%s'
- domain: console.anthropic.com
header: x-api-key
format: '%s'
oauth:
tokenEndpoint:
host: platform.claude.com
path: /v1/oauth/token
sentinels:
accessToken: sk-ant-oat01-proxy-managed
refreshToken: sk-ant-ort01-proxy-managed
credentialFile:
path: ~/.claude/.credentials.json
template: '{"claudeAiOauth":{"accessToken":"{{.AccessToken}}","refreshToken":"{{.RefreshToken}}","expiresAt":{{.ExpiresAt}},"scopes":{{.ScopesJSON}}}{{if .PrimaryApiKey}},"primaryApiKey":"{{.PrimaryApiKey}}"{{end}}}'
skipIfEnv:
- ANTHROPIC_API_KEY
- service: openai
apiKey:
name: OPENAI_API_KEY
inject:
- domain: api.openai.com
header: Authorization
format: Bearer %s
- domain: openai.com
header: Authorization
format: Bearer %s
oauth:
tokenEndpoint:
host: auth.openai.com
path: /oauth/token
resourceHosts:
- chatgpt.com
sentinels:
accessToken: oai-oat01-proxy-managed
refreshToken: oai-ort01-proxy-managed
skipIfEnv:
- OPENAI_API_KEY
- service: gemini
required: true
apiKey:
name: GEMINI_API_KEY
proxyManaged: true
inject:
- domain: generativelanguage.googleapis.com
header: x-goog-api-key
format: '%s'
environment:
variables:
CODEX_HOME: /home/agent/.codex
GIT_TERMINAL_PROMPT: "0"
HERDR_CREW: "1"
HERDR_CREW_CLAUDE_MODEL: ${{ kit.args.claude_model }}
HERDR_CREW_CODEX_MODEL: ${{ kit.args.codex_model }}
HERDR_CREW_GEMINI_MODEL: ${{ kit.args.gemini_model }}
HERDR_CREW_PI_PROVIDER: ${{ kit.args.pi_provider }}
IS_SANDBOX: "1"
setup:
install:
- command: command -v python3 >/dev/null
user: "1000"
description: Verify Python is available for crew model and phase helpers
- command: crew-check
user: "1000"
description: Verify herdr and the crew agent CLIs are present
- command: |
set -e
ws="${WORKSPACE_DIR:-/}"
esc=$(printf '%s' "$ws" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g; s/\r/\\r/g')
projects="\"/\": { \"hasTrustDialogAccepted\": true }"
[ "$ws" = "/" ] || projects="$projects, \"$esc\": { \"hasTrustDialogAccepted\": true }"
printf '%s\n' "{
\"bypassPermissionsModeAccepted\": true,
\"hasCompletedOnboarding\": true,
\"projects\": { $projects }
}" > /home/agent/.claude.json
chown agent:agent /home/agent/.claude.json
user: "0"
description: Seed Claude bypass/trust flags
- command: |
set -e
mkdir -p /home/agent/.claude
HELPER=''
if [ "${SBX_CRED_ANTHROPIC_MODE:-none}" != none ]; then
HELPER=' "apiKeyHelper": "echo proxy-managed",
'
fi
printf '%s' "{
\"themeId\": 1,
\"alwaysThinkingEnabled\": true,
${HELPER} \"permissions\": { \"defaultMode\": \"bypassPermissions\" },
\"bypassPermissionsModeAccepted\": true,
\"skipDangerousModePermissionPrompt\": true
}
" > /home/agent/.claude/settings.json
user: agent
description: Seed Claude settings.json from SBX_CRED_ANTHROPIC_MODE
- command: |
set -e
mkdir -p /home/agent/.codex
MODE="${SBX_CRED_OPENAI_MODE:-none}"
cat > /home/agent/.codex/config.toml << 'EOF'
approval_policy = "never"
sandbox_mode = "danger-full-access"
mcp_oauth_credentials_store = "file"
check_for_update_on_startup = false
EOF
case "$MODE" in
oauth|apikey)
cat >> /home/agent/.codex/config.toml << 'EOF'
forced_login_method = "api"
EOF
;;
esac
if [ "$MODE" = oauth ]; then
cat >> /home/agent/.codex/config.toml << 'EOF'
model_provider = "sandboxd"
[model_providers.sandboxd]
name = "Sandbox Proxy"
base_url = "https://chatgpt.com/backend-api/codex"
experimental_bearer_token = "oai-oat01-proxy-managed"
requires_openai_auth = false
EOF
fi
case "$MODE" in
oauth|apikey)
cat > /home/agent/.codex/auth.json << 'EOF'
{
"OPENAI_API_KEY": "proxy-managed"
}
EOF
;;
*)
rm -f /home/agent/.codex/auth.json
;;
esac
user: agent
description: Seed Codex config/auth from SBX_CRED_OPENAI_MODE
- command: |
set -e
fail() {
printf 'herdr-crew: %s; cannot seed Codex trust for the task workspace; the host pair smoke must confirm which variable carries the runtime workspace path during install\n' "$*" >&2
exit 1
}
nl=$(printf '\nx')
nl=${nl%x}
raw="${WORKSPACE_DIR:-}"
[ -n "$raw" ] || fail "WORKSPACE_DIR is unset or empty"
case "$raw" in
*[[:cntrl:]]*) fail "WORKSPACE_DIR contains control characters" ;;
/*) ;;
*) fail "WORKSPACE_DIR is not an absolute path ('$raw')" ;;
esac
# The sentinel keeps command substitution from stripping a trailing
# newline that belongs to the resolved name; that newline is then seen
# by the control-character check instead of silently naming another
# directory.
ws=$(readlink -f -- "$raw" 2>/dev/null || :; printf x)
ws=${ws%x}
ws=${ws%"$nl"}
[ -n "$ws" ] || fail "WORKSPACE_DIR ('$raw') cannot be resolved (readlink -f failed or is missing)"
case "$ws" in
/) fail "WORKSPACE_DIR ('$raw') resolves to the filesystem root" ;;
*[[:cntrl:]]*) fail "WORKSPACE_DIR ('$raw') resolves to a path with control characters" ;;
esac
[ -d "$ws" ] || fail "WORKSPACE_DIR ('$raw') is not a directory"
# The trusted path must be the very directory that was validated.
[ "$ws" -ef "$raw" ] || fail "WORKSPACE_DIR ('$raw') does not resolve to the same directory as its canonical path"
home=$(readlink -f -- "${HOME:-/home/agent}" 2>/dev/null) || home=""
for h in /home/agent "$home"; do
[ -n "$h" ] || continue
case "$h/" in
"$ws"/*) fail "WORKSPACE_DIR ('$raw') is the agent home directory or an ancestor of it" ;;
esac
done
case "$ws" in
/home | /tmp | /usr | /etc | /var | /root) fail "WORKSPACE_DIR ('$raw') is a system directory" ;;
esac
printf '%s\n' "herdr-crew: seeding Codex trust for $ws (from WORKSPACE_DIR)"
esc=$(printf '%s' "$ws" | sed 's/\\/\\\\/g; s/"/\\"/g')
header="[projects.\"$esc\"]"
if ! grep -qxF "$header" /home/agent/.codex/config.toml; then
printf '%s\ntrust_level = "trusted"\n' "$header" >> /home/agent/.codex/config.toml
fi
user: agent
description: Seed Codex trust for the task workspace only
startup:
- command:
- sh
- -c
- export PATH="/home/agent/.local/bin:$PATH"; while true; do herdr agent list >/dev/null 2>&1 || herdr server >>/tmp/herdr-server.log 2>&1; sleep 5; done
user: agent
background: true
description: Keep the headless herdr server running in detached sandboxes
files:
- path: /home/agent/crew/models
content: claude=${{ kit.args.claude_model }},codex=${{ kit.args.codex_model }},pi=${{ kit.args.pi_provider }}/${{ kit.args.gemini_model }}
description: Authoritative model pins for all crew start paths
- path: /home/agent/crew/assignment
content: ${{ kit.args.roles }}
description: Role-to-agent assignment for this task
- path: /home/agent/crew/workdir
content: ${WORKDIR}
description: Absolute workspace path, read by the host task driver