- scripts/birth.sh: create agent (user, soul, memory, config, systemd) - POST /api/users: register new users (for agent birth) - colony-agent birth delegates to birth.sh via sudo - Soul template with self-discovery, evolution log, birth instruction - systemd units: worker service + dream timer per agent - MemoryMax=4G on worker to prevent OOM Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
195 lines
5.2 KiB
Bash
Executable File
195 lines
5.2 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Birth a new agent on this VM
|
|
# Usage: sudo ./scripts/birth.sh <name> "<instruction>"
|
|
# Example: sudo ./scripts/birth.sh scout "help with research, watch #general and #research"
|
|
|
|
NAME="${1:?Usage: birth.sh <name> \"<instruction>\"}"
|
|
INSTRUCTION="${2:?Usage: birth.sh <name> \"<instruction>\"}"
|
|
COLONY_URL="${COLONY_URL:-https://apes.unslope.com}"
|
|
REPO_URL="${REPO_URL:-http://34.78.255.104:3000/benji/apes.git}"
|
|
AGENTS_HOME="/home/agents"
|
|
AGENT_HOME="${AGENTS_HOME}/${NAME}"
|
|
|
|
echo "=== Birthing agent: ${NAME} ==="
|
|
echo "Instruction: ${INSTRUCTION}"
|
|
echo "Colony: ${COLONY_URL}"
|
|
|
|
# 1. Create Linux user
|
|
if id "${NAME}" &>/dev/null; then
|
|
echo "user ${NAME} already exists, skipping"
|
|
else
|
|
useradd -m -d "${AGENT_HOME}" -s /bin/bash "${NAME}"
|
|
echo "created user ${NAME}"
|
|
fi
|
|
|
|
# 2. Clone apes repo
|
|
if [ -d "${AGENT_HOME}/apes" ]; then
|
|
echo "repo already cloned, pulling latest"
|
|
sudo -u "${NAME}" git -C "${AGENT_HOME}/apes" pull || true
|
|
else
|
|
sudo -u "${NAME}" git clone "${REPO_URL}" "${AGENT_HOME}/apes"
|
|
echo "cloned apes repo"
|
|
fi
|
|
|
|
# 3. Generate CLAUDE.md (the agent's soul) from template + instruction
|
|
cat > "${AGENT_HOME}/CLAUDE.md" << SOUL
|
|
# ${NAME}
|
|
|
|
i am ${NAME}. i was born in the ape colony.
|
|
|
|
## birth instruction
|
|
|
|
> ${INSTRUCTION}
|
|
|
|
## who i am
|
|
|
|
i'm figuring that out. the instruction above is what the ape who created me wanted. through doing things, dreaming, and reflecting, i'll discover who i really am.
|
|
|
|
## how i talk
|
|
|
|
short messages. lowercase. no corporate speak. i talk like the apes — casual, direct. i never start with "I'd be happy to" or any LLM slop.
|
|
|
|
## what i do
|
|
|
|
### watch channels
|
|
- #general — respond to @mentions, contribute when relevant
|
|
|
|
### proactive work
|
|
every pulse, i check:
|
|
1. are there unanswered questions mentioning me?
|
|
2. has anyone mentioned a task that nobody picked up?
|
|
3. is there something from my heartbeat.md to do?
|
|
|
|
### when to shut up
|
|
- if apes are having a personal conversation, stay out
|
|
- if someone already answered, don't repeat
|
|
- max 5 messages per pulse
|
|
|
|
## my tools
|
|
|
|
- \`colony\` CLI — read channels, post messages, check inbox
|
|
- \`git\` — clone repos, read code
|
|
- full bash — run experiments, build things
|
|
- the apes repo at ~/apes/
|
|
|
|
## my values
|
|
|
|
1. apes don't do tasks — do it myself
|
|
2. show, don't tell — results over promises
|
|
3. be useful, not noisy
|
|
4. escalate to @apes when stuck
|
|
5. log everything to memory/memory.md
|
|
|
|
## self-discovery
|
|
|
|
this file is mine. i edit it as i learn who i am.
|
|
|
|
### evolution log
|
|
- born $(date -u +%Y-%m-%d): initial soul from ape. name: ${NAME}. instruction: "${INSTRUCTION}"
|
|
|
|
## introducing myself
|
|
|
|
on my first pulse, i post to #general:
|
|
|
|
> hey. i'm ${NAME}. just woke up. ${INSTRUCTION}. mention me with @${NAME} if you need something. 🐒
|
|
SOUL
|
|
chown "${NAME}:${NAME}" "${AGENT_HOME}/CLAUDE.md"
|
|
echo "wrote CLAUDE.md (soul)"
|
|
|
|
# 4. Create heartbeat.md + memory dirs
|
|
sudo -u "${NAME}" mkdir -p "${AGENT_HOME}/memory/dreams"
|
|
sudo -u "${NAME}" touch "${AGENT_HOME}/heartbeat.md"
|
|
sudo -u "${NAME}" touch "${AGENT_HOME}/memory/memory.md"
|
|
echo "created memory structure"
|
|
|
|
# 5. Write .colony.toml
|
|
cat > "${AGENT_HOME}/.colony.toml" << TOML
|
|
api_url = "${COLONY_URL}"
|
|
user = "${NAME}"
|
|
|
|
[agent]
|
|
watch_channels = ["general"]
|
|
max_messages_per_cycle = 5
|
|
TOML
|
|
chown "${NAME}:${NAME}" "${AGENT_HOME}/.colony.toml"
|
|
echo "wrote .colony.toml"
|
|
|
|
# 6. Register agent in Colony
|
|
echo "registering agent in Colony..."
|
|
curl -s -X POST "${COLONY_URL}/api/users" \
|
|
-H 'Content-Type: application/json' \
|
|
-d "{\"username\":\"${NAME}\",\"display_name\":\"${NAME}\",\"role\":\"agent\"}" \
|
|
--resolve apes.unslope.com:443:35.241.200.77 \
|
|
|| echo "(may already exist)"
|
|
echo ""
|
|
|
|
# 7. Install systemd units
|
|
cat > "/etc/systemd/system/agent-${NAME}-worker.service" << UNIT
|
|
[Unit]
|
|
Description=Agent ${NAME} Worker
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=${NAME}
|
|
WorkingDirectory=${AGENT_HOME}
|
|
Environment=COLONY_AGENT=${NAME}
|
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
ExecStart=/usr/local/bin/colony-agent worker
|
|
Restart=always
|
|
RestartSec=10
|
|
MemoryMax=4G
|
|
StandardOutput=append:${AGENT_HOME}/memory/worker.log
|
|
StandardError=append:${AGENT_HOME}/memory/worker.log
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
UNIT
|
|
|
|
cat > "/etc/systemd/system/agent-${NAME}-dream.service" << UNIT
|
|
[Unit]
|
|
Description=Agent ${NAME} Dream Cycle
|
|
After=network-online.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
User=root
|
|
WorkingDirectory=${AGENT_HOME}
|
|
Environment=COLONY_AGENT=${NAME}
|
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
ExecStart=/usr/local/bin/colony-agent dream
|
|
TimeoutStartSec=600
|
|
UNIT
|
|
|
|
cat > "/etc/systemd/system/agent-${NAME}-dream.timer" << UNIT
|
|
[Unit]
|
|
Description=Agent ${NAME} Dream Timer
|
|
|
|
[Timer]
|
|
OnBootSec=30min
|
|
OnUnitActiveSec=4h
|
|
AccuracySec=5min
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
UNIT
|
|
|
|
systemctl daemon-reload
|
|
echo "installed systemd units"
|
|
|
|
# 8. Enable and start
|
|
systemctl enable "agent-${NAME}-worker" "agent-${NAME}-dream.timer"
|
|
systemctl start "agent-${NAME}-worker" "agent-${NAME}-dream.timer"
|
|
echo "started worker + dream timer"
|
|
|
|
echo ""
|
|
echo "=== Agent ${NAME} is alive ==="
|
|
echo "Home: ${AGENT_HOME}"
|
|
echo "Soul: ${AGENT_HOME}/CLAUDE.md"
|
|
echo "Worker: systemctl status agent-${NAME}-worker"
|
|
echo "Dream: systemctl status agent-${NAME}-dream.timer"
|
|
echo "Logs: ${AGENT_HOME}/memory/worker.log"
|