6e1b820ecf
- new-agent.sh: reemplaza edicion del rulesRegistry map con insercion de un blank import simple. Ahora tambien sustituye AGENT_ID_PLACEHOLDER en agent.go con el ID real del agente. - create_agent.md: actualiza template de agent.go con patron init() + agents.Register(), secciones de registro en launcher y checklist. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
364 lines
9.5 KiB
Bash
Executable File
364 lines
9.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# new-agent.sh — genera el scaffold de un nuevo agente
|
|
#
|
|
# Uso:
|
|
# ./dev-scripts/agent/new-agent.sh <agent-id> [displayname]
|
|
#
|
|
# Ejemplo:
|
|
# ./dev-scripts/agent/new-agent.sh monitor-bot "Monitor Agent"
|
|
#
|
|
# Crea:
|
|
# agents/<agent-id>/config.yaml (copiado desde agents/_template/)
|
|
# agents/<agent-id>/agent.go (copiado desde agents/_template/)
|
|
# agents/<agent-id>/prompts/ (copiado desde agents/_template/prompts/)
|
|
# agents/<agent-id>/data/ (directorio de datos, en .gitignore)
|
|
#
|
|
# También te recuerda los dos pasos manuales que quedan.
|
|
|
|
source "$(dirname "$0")/../_common.sh"
|
|
load_env
|
|
|
|
need_arg "${1:-}"
|
|
|
|
ID="$1"
|
|
DISPLAYNAME="${2:-$ID}"
|
|
PACKAGE="$(echo "$ID" | tr '-' '_' | sed 's/_bot//')" # "monitor-bot" → "monitor"
|
|
NORM="$(normalize_id "$ID")" # "monitor-bot" → "MONITOR_BOT"
|
|
DIR="agents/$ID"
|
|
TEMPLATE="agents/_template"
|
|
|
|
[[ -d "$DIR" ]] && fail "Ya existe agents/$ID — ¿ya fue creado?"
|
|
[[ ! -d "$TEMPLATE" ]] && fail "No existe el directorio _template en agents/_template/"
|
|
|
|
info "Creando scaffold para $ID desde _template..."
|
|
|
|
mkdir -p "$DIR/prompts" "$DIR/data"
|
|
|
|
# ── Copiar config.yaml desde template y personalizar ─────────────────────
|
|
cp "$TEMPLATE/config.yaml" "$DIR/config.yaml"
|
|
sed -i "s/_template/$ID/g" "$DIR/config.yaml"
|
|
sed -i "s/Template Agent/$DISPLAYNAME/g" "$DIR/config.yaml"
|
|
sed -i "s/template: true/template: false/g" "$DIR/config.yaml"
|
|
sed -i "s/enabled: true/enabled: true/g" "$DIR/config.yaml"
|
|
sed -i "s/MATRIX_TOKEN_TEMPLATE/MATRIX_TOKEN_${NORM}/g" "$DIR/config.yaml"
|
|
sed -i "s/PICKLE_KEY_TEMPLATE/PICKLE_KEY_${NORM}/g" "$DIR/config.yaml"
|
|
sed -i "s/@template:matrix.example.com/@$ID:\${MATRIX_SERVER_NAME}/g" "$DIR/config.yaml"
|
|
sed -i "s|https://matrix.example.com|\${MATRIX_HOMESERVER}|g" "$DIR/config.yaml"
|
|
|
|
ok "config.yaml creado desde template"
|
|
|
|
# DEPRECATED: generacion inline — ahora copiamos desde _template
|
|
: <<'YAML'
|
|
# ============================================
|
|
# IDENTIDAD
|
|
# ============================================
|
|
agent:
|
|
id: $ID
|
|
name: "$DISPLAYNAME"
|
|
version: "1.0.0"
|
|
enabled: true
|
|
description: "Descripción del agente $DISPLAYNAME"
|
|
tags: [$(echo "$ID" | tr '-' ',')]
|
|
|
|
# ============================================
|
|
# PERSONALIDAD Y COMPORTAMIENTO
|
|
# ============================================
|
|
personality:
|
|
tone: friendly
|
|
verbosity: concise
|
|
language: es
|
|
languages_supported: [es, en]
|
|
emoji_style: minimal
|
|
prefix: "🤖"
|
|
error_style: helpful
|
|
|
|
templates:
|
|
greeting: "Hola, soy $DISPLAYNAME. ¿En qué puedo ayudarte?"
|
|
unknown_command: "No reconozco ese comando. Escríbeme directamente."
|
|
permission_denied: "No tengo permiso para hacer eso."
|
|
error: "Algo salió mal: {{.Error}}"
|
|
success: "{{.Summary}}"
|
|
busy: "Procesando, dame un momento..."
|
|
|
|
behavior:
|
|
proactive: false
|
|
ask_confirmation: false
|
|
show_reasoning: false
|
|
thread_replies: true
|
|
typing_indicator: true
|
|
acknowledge_receipt: false
|
|
|
|
# ============================================
|
|
# LLM
|
|
# ============================================
|
|
llm:
|
|
primary:
|
|
provider: openai
|
|
model: gpt-4o
|
|
api_key_env: OPENAI_API_KEY
|
|
base_url: ""
|
|
max_tokens: 4096
|
|
temperature: 0.7
|
|
|
|
fallback:
|
|
provider: ""
|
|
model: ""
|
|
api_key_env: ""
|
|
base_url: ""
|
|
max_tokens: 0
|
|
temperature: 0
|
|
|
|
reasoning:
|
|
system_prompt_file: "prompts/system.md"
|
|
context_window: 16384
|
|
memory_messages: 20
|
|
|
|
tool_use:
|
|
enabled: false
|
|
max_iterations: 3
|
|
parallel_calls: false
|
|
|
|
rate_limit:
|
|
requests_per_minute: 30
|
|
tokens_per_minute: 100000
|
|
concurrent_requests: 3
|
|
|
|
# ============================================
|
|
# TOOLS — ajustar según necesidades del agente
|
|
# ============================================
|
|
tools:
|
|
ssh:
|
|
enabled: false
|
|
allowed_targets: []
|
|
forbidden_commands: []
|
|
timeout: 0s
|
|
max_concurrent: 0
|
|
require_confirmation: []
|
|
http:
|
|
enabled: false
|
|
allowed_domains: []
|
|
timeout: 0s
|
|
max_retries: 0
|
|
scripts:
|
|
enabled: false
|
|
scripts_dir: ""
|
|
allowed: []
|
|
timeout: 0s
|
|
sandbox: false
|
|
file_ops:
|
|
enabled: false
|
|
allowed_paths: []
|
|
read_only: true
|
|
mcp:
|
|
enabled: false
|
|
servers: []
|
|
expose:
|
|
port: 0
|
|
tools: []
|
|
|
|
# ============================================
|
|
# MATRIX
|
|
# ============================================
|
|
matrix:
|
|
homeserver: "${MATRIX_HOMESERVER}"
|
|
user_id: "@$ID:${MATRIX_SERVER_NAME}"
|
|
access_token_env: MATRIX_TOKEN_${NORM}
|
|
device_id: ""
|
|
|
|
encryption:
|
|
enabled: true
|
|
store_path: "./agents/${ID}/data/crypto/"
|
|
pickle_key_env: PICKLE_KEY_${NORM}
|
|
trust_mode: tofu
|
|
recovery_key_env: SSSS_RECOVERY_KEY_${NORM}
|
|
|
|
rooms:
|
|
listen: []
|
|
respond: []
|
|
admin: []
|
|
|
|
filters:
|
|
command_prefix: "!"
|
|
mention_respond: true
|
|
dm_respond: true
|
|
ignore_bots: true
|
|
ignore_users: []
|
|
min_power_level: 0
|
|
|
|
threads:
|
|
enabled: true # responder en threads cuando el mensaje viene de un thread
|
|
auto_thread: false # true para crear thread automático por cada conversación nueva
|
|
|
|
# ============================================
|
|
# INTER-AGENTES
|
|
# ============================================
|
|
agents:
|
|
peers: []
|
|
delegation:
|
|
enabled: false
|
|
can_delegate_to: []
|
|
can_receive_from: []
|
|
max_delegation_depth: 1
|
|
timeout: 30s
|
|
protocol:
|
|
format: json
|
|
channel: matrix
|
|
heartbeat_interval: 60s
|
|
|
|
# ============================================
|
|
# SSH
|
|
# ============================================
|
|
ssh:
|
|
defaults:
|
|
user: ""
|
|
port: 22
|
|
key_file_env: ""
|
|
known_hosts: ""
|
|
keepalive_interval: 0s
|
|
timeout: 0s
|
|
targets: {}
|
|
|
|
# ============================================
|
|
# SEGURIDAD
|
|
# ============================================
|
|
security:
|
|
roles:
|
|
admin:
|
|
users: ["@admin:\${MATRIX_SERVER_NAME}"]
|
|
actions: ["*"]
|
|
user:
|
|
users: ["*"]
|
|
actions: ["help"]
|
|
audit:
|
|
enabled: false
|
|
log_file: "./data/audit.log"
|
|
log_to_room: ""
|
|
include: []
|
|
secrets:
|
|
provider: env
|
|
|
|
# ============================================
|
|
# SCHEDULING
|
|
# ============================================
|
|
schedules: []
|
|
|
|
# ============================================
|
|
# OBSERVABILIDAD
|
|
# ============================================
|
|
observability:
|
|
logging:
|
|
level: info
|
|
format: json
|
|
output: stdout
|
|
file: "./data/$ID.log"
|
|
metrics:
|
|
enabled: false
|
|
port: 0
|
|
path: /metrics
|
|
export: prometheus
|
|
health:
|
|
enabled: true
|
|
port: 0
|
|
path: /healthz
|
|
tracing:
|
|
enabled: false
|
|
provider: ""
|
|
endpoint: ""
|
|
|
|
# ============================================
|
|
# RESILIENCIA
|
|
# ============================================
|
|
resilience:
|
|
circuit_breaker:
|
|
failure_threshold: 5
|
|
timeout: 30s
|
|
half_open_max: 2
|
|
retry:
|
|
max_attempts: 2
|
|
backoff: exponential
|
|
initial_delay: 1s
|
|
max_delay: 10s
|
|
shutdown:
|
|
timeout: 10s
|
|
drain_messages: true
|
|
save_state: false
|
|
state_file: ""
|
|
queue:
|
|
enabled: true
|
|
max_size: 50
|
|
priority_users: ["@admin:\${MATRIX_SERVER_NAME}"]
|
|
|
|
# ============================================
|
|
# ALMACENAMIENTO
|
|
# ============================================
|
|
storage:
|
|
state:
|
|
backend: sqlite
|
|
path: "./data/$ID.db"
|
|
cache:
|
|
enabled: true
|
|
backend: memory
|
|
ttl: 5m
|
|
max_entries: 200
|
|
history:
|
|
backend: sqlite
|
|
path: "./data/history.db"
|
|
retention: 168h
|
|
YAML
|
|
|
|
# ── Copiar agent.go desde template y personalizar ────────────────────────
|
|
cp "$TEMPLATE/agent.go" "$DIR/agent.go"
|
|
sed -i "s/_template/$PACKAGE/g" "$DIR/agent.go"
|
|
sed -i "s/Package _template/Package $PACKAGE/g" "$DIR/agent.go"
|
|
sed -i "s/AGENT_ID_PLACEHOLDER/$ID/g" "$DIR/agent.go"
|
|
ok "agent.go creado desde template"
|
|
|
|
# ── Copiar prompts/system.md desde template y personalizar ───────────────
|
|
cp "$TEMPLATE/prompts/system.md" "$DIR/prompts/system.md"
|
|
sed -i "s/Template Agent/$DISPLAYNAME/g" "$DIR/prompts/system.md"
|
|
ok "prompts/system.md creado desde template"
|
|
|
|
ok "Scaffold creado en $DIR/"
|
|
echo ""
|
|
|
|
# ── Actualizar cmd/launcher/main.go — añadir blank import ────────────────
|
|
LAUNCHER="cmd/launcher/main.go"
|
|
BLANK_IMPORT="_ \"github.com/enmanuel/agents/agents/$ID\""
|
|
|
|
if grep -q "agents/$ID\"" "$LAUNCHER" 2>/dev/null; then
|
|
warn "$ID ya tiene blank import en $LAUNCHER — saltando"
|
|
else
|
|
TAB=$'\t'
|
|
IMPORT_LINE="${TAB}${BLANK_IMPORT}"
|
|
|
|
# Insertar blank import después del último blank import de agents/
|
|
if awk -v new_import="$IMPORT_LINE" '
|
|
{
|
|
lines[NR] = $0
|
|
if ($0 ~ /_ "github\.com\/enmanuel\/agents\/agents\//)
|
|
last_import = NR
|
|
}
|
|
END {
|
|
if (!last_import) { for (i=1;i<=NR;i++) print lines[i]; exit 1 }
|
|
for (i = 1; i <= NR; i++) {
|
|
print lines[i]
|
|
if (i == last_import) print new_import
|
|
}
|
|
}
|
|
' "$LAUNCHER" > /tmp/_launcher_tmp; then
|
|
mv /tmp/_launcher_tmp "$LAUNCHER"
|
|
ok "Blank import añadido en $LAUNCHER"
|
|
else
|
|
warn "No se pudo insertar el blank import — añádelo manualmente:"
|
|
echo -e " ${GRN}${IMPORT_LINE}${RST}"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YLW}Quedan 3 pasos:${RST}"
|
|
echo ""
|
|
echo -e " ${DIM}1. ./dev-scripts/agent/register.sh $ID \"$DISPLAYNAME\"${RST} # registra en Matrix + genera token, password, pickle key"
|
|
echo -e " ${DIM}2. ./dev-scripts/agent/verify.sh $ID${RST} # genera cross-signing keys + verifica device"
|
|
echo -e " ${DIM}3. ./dev-scripts/server/start.sh $ID${RST} # arranca el agente"
|
|
echo ""
|