diff --git a/recruitment/SKILL.md b/recruitment/SKILL.md new file mode 100644 index 0000000..60efb11 --- /dev/null +++ b/recruitment/SKILL.md @@ -0,0 +1,53 @@ +--- +name: recruitment +description: Onboard new agents into OpenClaw. Use when creating a new agent, adding an agent to the system, or setting up a new agent workspace. Triggers on requests like "new agent", "add agent", "create agent", "recruit agent", "onboard agent". +--- + +# Recruitment Skill + +Onboard new agents into OpenClaw with binding configuration. + +## Usage + +```bash +new-agent --type openclaw --agent-id [--model ] + +new-agent --type contractor --contractor-provider --agent-id +``` + +## Workflow + +The `new-agent` script executes these steps: + +1. **Register agent**: + - `openclaw`: runs `openclaw agents add {agent-id} --model {primary-model} --workspace ~/.openclaw/workspace/workspace-{agent-id} --non-interactive` + - `contractor`: requires `contractor-agent` plugin; runs `openclaw contractor-agents add --agent-id {agent-id} --workspace ~/.openclaw/workspace/workspace-{agent-id} --contractor {contractor-provider}` + +2. **Wait for bindings**: + - Poll `openclaw config get bindings` every 10s until `interviewee` accountId is no longer present + - Check `~/.openclaw/states/bindings.lock` exists → wait every 10s until it disappears + +3. **Set bindings**: + - Create `~/.openclaw/states/bindings.lock` + - Fetch current bindings, append entry: + ```json + { + "agentId": "{agent-id}", + "match": { + "channel": "discord", + "accountId": "interviewee" + } + } + ``` + - Write back with `openclaw config set` + - Delete `bindings.lock` + +4. **Configure interviewee account**: + ```bash + openclaw config set channels.discord.accounts.interviewee { + "enabled": true, + "token": "", + "groupPolicy": "open", + "streaming": { "mode": "off" } + } + ``` diff --git a/recruitment/scripts/new-agent b/recruitment/scripts/new-agent new file mode 100755 index 0000000..3609a1e --- /dev/null +++ b/recruitment/scripts/new-agent @@ -0,0 +1,122 @@ +#!/bin/bash +set -e + +TYPE="" +AGENT_ID="" +MODEL="" +CONTRACTOR_PROVIDER="" + +usage() { + echo "Usage: new-agent --type --agent-id [--model ] [--contractor-provider ]" + exit 1 +} + +while [[ $# -gt 0 ]]; do + case $1 in + --type) + TYPE="$2" + shift 2 + ;; + --agent-id) + AGENT_ID="$2" + shift 2 + ;; + --model) + MODEL="$2" + shift 2 + ;; + --contractor-provider) + CONTRACTOR_PROVIDER="$2" + shift 2 + ;; + *) + usage + ;; + esac +done + +if [[ -z "$TYPE" ]] || [[ -z "$AGENT_ID" ]]; then + usage +fi + +WORKSPACE_DIR="$HOME/.openclaw/workspace/workspace-$AGENT_ID" +STATE_DIR="$HOME/.openclaw/states" +BINDINGS_LOCK="$STATE_DIR/bindings.lock" + +# Step 1: Register agent +if [[ "$TYPE" == "openclaw" ]]; then + MODEL_ARG="" + if [[ -n "$MODEL" ]]; then + MODEL_ARG="--model $MODEL" + fi + openclaw agents add "$AGENT_ID" $MODEL_ARG --workspace "$WORKSPACE_DIR" --non-interactive +elif [[ "$TYPE" == "contractor" ]]; then + # Check contractor-agent plugin + if ! openclaw plugins list 2>/dev/null | grep -q "contractor-agent"; then + echo "Error: contractor-agent plugin is not installed" >&2 + exit 1 + fi + openclaw contractor-agents add --agent-id "$AGENT_ID" --workspace "$WORKSPACE_DIR" --contractor "$CONTRACTOR_PROVIDER" +else + echo "Error: unknown type '$TYPE'" >&2 + exit 1 +fi + +# Step 2: Wait for bindings +echo "Checking bindings..." + +wait_for_bindings() { + local bindings + bindings=$(openclaw config get bindings 2>/dev/null || echo "{}") + if echo "$bindings" | grep -q '"interviewee"'; then + return 0 + fi + return 1 +} + +wait_for_lock() { + if [[ -f "$BINDINGS_LOCK" ]]; then + return 0 + fi + return 1 +} + +while wait_for_bindings; do + echo "another interview is ongoing, waiting..." + sleep 10 +done + +while wait_for_lock; do + echo "bindings locked, waiting..." + sleep 10 +done + +# Step 3: Set bindings +mkdir -p "$STATE_DIR" +touch "$BINDINGS_LOCK" + +CURRENT_BINDINGS=$(openclaw config get bindings 2>/dev/null || echo "{}") + +NEW_ENTRY="{\"agentId\": \"$AGENT_ID\", \"match\": {\"channel\": \"discord\", \"accountId\": \"interviewee\"}}" + +if [[ "$CURRENT_BINDINGS" == "{}" ]] || [[ -z "$CURRENT_BINDINGS" ]]; then + NEW_BINDINGS="[$NEW_ENTRY]" +else + NEW_BINDINGS=$(echo "$CURRENT_BINDINGS" | sed 's/\(\[.*\)\]/\1,/' | sed 's/\]$//' | sed 's/\([{,]\)$/\1/')" $NEW_ENTRY]" +fi + +openclaw config set bindings "$NEW_BINDINGS" + +rm -f "$BINDINGS_LOCK" + +# Step 4: Configure interviewee account +TOKEN=$(secret-mgr get-secret --key interviewee-discord-token 2>/dev/null || echo "") + +if [[ -z "$TOKEN" ]]; then + echo "Error: failed to get interviewee-discord-token from secret-mgr" >&2 + exit 1 +fi + +openclaw config set channels.discord.accounts.interviewee "{\"enabled\": true, \"token\": \"$TOKEN\", \"groupPolicy\": \"open\", \"streaming\": {\"mode\": \"off\"}}" + +echo "Agent $AGENT_ID recruited successfully."