Guía de Programación
🌐 Otros idiomas
Descripción General
El sistema de programación de Symbiont proporciona ejecución de tareas basada en cron de nivel de producción para agentes de IA. El sistema soporta:
- Programaciones cron: Sintaxis cron tradicional para tareas recurrentes
- Trabajos de una sola ejecución: Ejecutar una vez en un momento específico
- Patrón de latido (heartbeat): Ciclos continuos de evaluación-acción-pausa para agentes de monitoreo
- Aislamiento de sesión: Contextos de agente efímeros, compartidos o completamente aislados
- Enrutamiento de entrega: Múltiples canales de salida (Stdout, LogFile, Webhook, Slack, Email, Custom)
- Aplicación de políticas: Verificaciones de seguridad y cumplimiento antes de la ejecución
- Robustez para producción: Jitter, límites de concurrencia, colas de mensajes no entregados y verificación de AgentPin
Arquitectura
El sistema de programación está construido sobre tres componentes principales:
┌─────────────────────┐
│ CronScheduler │ Bucle de ticks en segundo plano (intervalos de 1 segundo)
│ (Tick Loop) │ Selección de trabajos y orquestación de ejecución
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ SqliteJobStore │ Almacenamiento persistente de trabajos
│ (Job Storage) │ Soporte de transacciones, gestión de estado
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│DefaultAgentScheduler│ Entorno de ejecución de agentes
│ (Execution Engine) │ Gestión del ciclo de vida de AgentContext
└─────────────────────┘
CronScheduler
El CronScheduler es el punto de entrada principal. Gestiona:
- Bucle de ticks en segundo plano ejecutándose a intervalos de 1 segundo
- Selección de trabajos basada en el siguiente tiempo de ejecución
- Control de concurrencia e inyección de jitter
- Recolección de métricas y monitoreo de salud
- Apagado gracioso con seguimiento de trabajos en curso
SqliteJobStore
El SqliteJobStore proporciona persistencia durable de trabajos con:
- Transacciones ACID para actualizaciones de estado de trabajos
- Seguimiento del ciclo de vida de trabajos (Active, Paused, Completed, Failed, DeadLetter)
- Historial de ejecuciones con pista de auditoría
- Capacidades de consulta para filtrar por estado, ID de agente, etc.
DefaultAgentScheduler
El DefaultAgentScheduler ejecuta los agentes programados:
- Crea instancias de
AgentContextaisladas o compartidas - Gestiona el ciclo de vida de la sesión (crear, ejecutar, destruir)
- Enruta la entrega a los canales configurados
- Aplica puertas de política antes de la ejecución
Sintaxis DSL
Estructura del Bloque de Programación
Los bloques de programación se definen en archivos DSL de Symbiont:
schedule {
name: "daily-report"
agent: "reporter-agent"
cron: "0 9 * * *"
session_mode: "ephemeral_with_summary"
delivery: ["stdout", "log_file"]
policy {
require_approval: false
max_runtime: "5m"
}
}
Sintaxis Cron
Sintaxis cron estándar con cinco campos:
┌───────────── minuto (0-59)
│ ┌─────────── hora (0-23)
│ │ ┌───────── día del mes (1-31)
│ │ │ ┌─────── mes (1-12)
│ │ │ │ ┌───── día de la semana (0-6, Domingo = 0)
│ │ │ │ │
* * * * *
Ejemplos:
# Todos los días a las 9 AM
cron: "0 9 * * *"
# Cada lunes a las 6 PM
cron: "0 18 * * 1"
# Cada 15 minutos
cron: "*/15 * * * *"
# Primer día de cada mes a medianoche
cron: "0 0 1 * *"
Trabajos de Una Sola Ejecución (Sintaxis At)
Para trabajos que se ejecutan una sola vez en un momento específico:
schedule {
name: "deployment-check"
agent: "health-checker"
at: "2026-02-15T14:30:00Z" # Marca de tiempo ISO 8601
delivery: ["webhook"]
webhook_url: "https://ops.example.com/hooks/deployment"
}
Patrón de Latido (Heartbeat)
Para agentes de monitoreo continuo que evalúan → actúan → duermen:
schedule {
name: "system-monitor"
agent: "heartbeat-agent"
cron: "*/5 * * * *" # Despertar cada 5 minutos
heartbeat: {
enabled: true
context_mode: "ephemeral_with_summary"
max_iterations: 100 # Límite de seguridad
}
}
El agente de latido sigue este ciclo:
- Evaluación: Evaluar el estado del sistema (por ejemplo, verificar métricas, registros)
- Acción: Tomar acción correctiva si es necesario (por ejemplo, reiniciar servicio, alertar a operaciones)
- Pausa: Esperar hasta el siguiente tick programado
Comandos CLI
El comando symbi cron proporciona gestión completa del ciclo de vida:
Listar Trabajos
# Listar todos los trabajos
symbi cron list
# Filtrar por estado
symbi cron list --status active
symbi cron list --status paused
# Filtrar por agente
symbi cron list --agent "reporter-agent"
# Salida en JSON
symbi cron list --format json
Agregar Trabajo
# Desde archivo DSL
symbi cron add --file agent.symbi --schedule "daily-report"
# Definición en línea (JSON)
symbi cron add --json '{
"name": "quick-task",
"agent_id": "agent-123",
"cron_expr": "0 * * * *"
}'
Eliminar Trabajo
# Por ID de trabajo
symbi cron remove <job-id>
# Por nombre
symbi cron remove --name "daily-report"
# Eliminación forzada (omitir confirmación)
symbi cron remove <job-id> --force
Pausar/Reanudar
# Pausar trabajo (detiene la programación, preserva el estado)
symbi cron pause <job-id>
# Reanudar trabajo pausado
symbi cron resume <job-id>
Estado
# Detalles del trabajo con próximo tiempo de ejecución
symbi cron status <job-id>
# Incluir los últimos 10 registros de ejecución
symbi cron status <job-id> --history 10
# Modo observación (actualización automática cada 5s)
symbi cron status <job-id> --watch
Ejecutar Ahora
# Activar ejecución inmediata (omite la programación)
symbi cron run <job-id>
# Con entrada personalizada
symbi cron run <job-id> --input "Check production database"
Historial
# Ver historial de ejecuciones de un trabajo
symbi cron history <job-id>
# Últimas 20 ejecuciones
symbi cron history <job-id> --limit 20
# Filtrar por estado
symbi cron history <job-id> --status failed
# Exportar a CSV
symbi cron history <job-id> --format csv > runs.csv
Patrón de Latido (Heartbeat)
HeartbeatContextMode
Controla cómo persiste el contexto entre iteraciones del heartbeat:
pub enum HeartbeatContextMode {
/// Contexto nuevo en cada iteración, resumen adjunto al historial de ejecuciones
EphemeralWithSummary,
/// Contexto compartido entre todas las iteraciones (la memoria se acumula)
SharedPersistent,
/// Contexto nuevo en cada iteración, sin resumen (sin estado)
FullyEphemeral,
}
EphemeralWithSummary (predeterminado):
- Nuevo
AgentContextpor iteración - Resumen de la iteración anterior adjunto al contexto
- Previene el crecimiento ilimitado de memoria
- Mantiene continuidad para acciones relacionadas
SharedPersistent:
- Un solo
AgentContextreutilizado en todas las iteraciones - Historial completo de conversación preservado
- Mayor uso de memoria
- Ideal para agentes que necesitan contexto profundo (por ejemplo, sesiones de depuración)
FullyEphemeral:
- Nuevo
AgentContextpor iteración, sin traspaso - Menor huella de memoria
- Ideal para verificaciones independientes (por ejemplo, sondas de salud de API)
Ejemplo de Agente Heartbeat
agent heartbeat_monitor {
model: "claude-sonnet-4.5"
system_prompt: """
You are a system monitoring agent. On each heartbeat:
1. Check system metrics (CPU, memory, disk)
2. Review recent error logs
3. If issues detected, take action:
- Restart services if safe
- Alert ops team via Slack
- Log incident details
4. Summarize findings
5. Return 'sleep' when done
"""
}
schedule {
name: "heartbeat-monitor"
agent: "heartbeat_monitor"
cron: "*/10 * * * *" # Cada 10 minutos
heartbeat: {
enabled: true
context_mode: "ephemeral_with_summary"
max_iterations: 50
}
delivery: ["log_file", "slack"]
slack_channel: "#ops-alerts"
}
Aislamiento de Sesión
Modos de Sesión
pub enum SessionIsolationMode {
/// Contexto efímero con traspaso de resumen (predeterminado)
EphemeralWithSummary,
/// Contexto persistente compartido entre todas las ejecuciones
SharedPersistent,
/// Completamente efímero, sin traspaso de estado
FullyEphemeral,
}
Configuración:
schedule {
name: "data-pipeline"
agent: "etl-agent"
cron: "0 2 * * *"
# Contexto nuevo por ejecución, resumen de la ejecución anterior incluido
session_mode: "ephemeral_with_summary"
}
Ciclo de Vida de la Sesión
Para cada ejecución programada:
- Pre-ejecución: Verificar límites de concurrencia, aplicar jitter
- Creación de sesión: Crear
AgentContextbasado ensession_mode - Puerta de política: Evaluar condiciones de política
- Ejecución: Ejecutar agente con entrada y contexto
- Entrega: Enrutar salida a los canales configurados
- Limpieza de sesión: Destruir o persistir contexto según el modo
- Post-ejecución: Actualizar registro de ejecución, recolectar métricas
Enrutamiento de Entrega
Canales Soportados
pub enum DeliveryChannel {
Stdout, // Imprimir en consola
LogFile, // Escribir al archivo de registro específico del trabajo
Webhook, // HTTP POST a URL
Slack, // Webhook o API de Slack
Email, // Correo electrónico SMTP
Custom(String), // Canal definido por el usuario
}
Ejemplos de Configuración
Canal único:
schedule {
name: "backup"
agent: "backup-agent"
cron: "0 3 * * *"
delivery: ["log_file"]
}
Múltiples canales:
schedule {
name: "security-scan"
agent: "scanner"
cron: "0 1 * * *"
delivery: ["log_file", "slack", "email"]
slack_channel: "#security"
email_recipients: ["ops@example.com", "security@example.com"]
}
Entrega por webhook:
schedule {
name: "metrics-report"
agent: "metrics-agent"
cron: "*/30 * * * *"
delivery: ["webhook"]
webhook_url: "https://metrics.example.com/ingest"
webhook_headers: {
"Authorization": "Bearer ${METRICS_API_KEY}"
"Content-Type": "application/json"
}
}
Trait DeliveryRouter
Los canales de entrega personalizados implementan:
#[async_trait]
pub trait DeliveryRouter: Send + Sync {
async fn route(
&self,
channel: &DeliveryChannel,
job: &CronJobDefinition,
run: &JobRunRecord,
output: &str,
) -> Result<(), SchedulerError>;
}
Aplicación de Políticas
PolicyGate
El PolicyGate evalúa políticas específicas de programación antes de la ejecución:
pub struct PolicyGate {
policy_engine: Arc<RealPolicyParser>,
}
impl PolicyGate {
pub async fn evaluate(
&self,
job: &CronJobDefinition,
context: &AgentContext,
) -> Result<SchedulePolicyDecision, SchedulerError>;
}
Condiciones de Política
schedule {
name: "production-deploy"
agent: "deploy-agent"
cron: "0 0 * * 0" # Domingo a medianoche
policy {
# Requerir aprobación humana antes de la ejecución
require_approval: true
# Tiempo máximo de ejecución antes de terminación forzada
max_runtime: "30m"
# Requerir capacidades específicas
require_capabilities: ["deployment", "production_write"]
# Aplicación de ventana de tiempo (UTC)
allowed_hours: {
start: "00:00"
end: "04:00"
}
# Restricciones de entorno
allowed_environments: ["staging", "production"]
# Verificación de AgentPin requerida
require_agent_pin: true
}
}
SchedulePolicyDecision
pub enum SchedulePolicyDecision {
Allow,
Deny { reason: String },
RequireApproval { approvers: Vec<String> },
}
Robustez para Producción
Jitter
Previene la estampida (thundering herd) cuando múltiples trabajos comparten una programación:
pub struct CronSchedulerConfig {
pub max_jitter_seconds: u64, // Retraso aleatorio de 0-N segundos
// ...
}
Ejemplo:
[scheduler]
max_jitter_seconds = 30 # Distribuir inicios de trabajos en una ventana de 30 segundos
Concurrencia por Trabajo
Limitar ejecuciones concurrentes por trabajo para prevenir el agotamiento de recursos:
schedule {
name: "data-sync"
agent: "sync-agent"
cron: "*/5 * * * *"
max_concurrent: 2 # Permitir máximo 2 ejecuciones concurrentes
}
Si un trabajo ya se está ejecutando al máximo de concurrencia, el programador omite el tick.
Cola de Mensajes No Entregados (Dead-Letter Queue)
Los trabajos que exceden max_retries pasan al estado DeadLetter para revisión manual:
schedule {
name: "flaky-job"
agent: "unreliable-agent"
cron: "0 * * * *"
max_retries: 3 # Después de 3 fallos, mover a dead-letter
}
Recuperación:
# Listar trabajos en dead-letter
symbi cron list --status dead_letter
# Revisar razones de fallo
symbi cron history <job-id> --status failed
# Restablecer trabajo a activo después de corregir
symbi cron reset <job-id>
Verificación de AgentPin
Verificar criptográficamente la identidad del agente antes de la ejecución:
schedule {
name: "secure-task"
agent: "trusted-agent"
cron: "0 * * * *"
agent_pin_jwt: "${AGENT_PIN_JWT}" # JWT ES256 de agentpin-cli
policy {
require_agent_pin: true
}
}
El programador verifica:
- Firma JWT usando ES256 (ECDSA P-256)
- El ID del agente coincide con el claim
iss - El ancla de dominio coincide con el origen esperado
- La expiración (
exp) es válida
Los fallos activan el evento de auditoría SecurityEventType::AgentPinVerificationFailed.
Endpoints de la API HTTP
Gestión de Programación
POST /api/v1/schedule Crear un nuevo trabajo programado.
curl -X POST http://localhost:8080/api/v1/schedule \
-H "Content-Type: application/json" \
-d '{
"name": "hourly-report",
"agent_id": "reporter",
"cron_expr": "0 * * * *",
"session_mode": "ephemeral_with_summary",
"delivery": ["stdout"]
}'
GET /api/v1/schedule Listar todos los trabajos (filtrable por estado, ID de agente).
curl "http://localhost:8080/api/v1/schedule?status=active&agent_id=reporter"
GET /api/v1/schedule/{job_id} Obtener detalles del trabajo.
curl http://localhost:8080/api/v1/schedule/job-123
PUT /api/v1/schedule/{job_id} Actualizar trabajo (expresión cron, entrega, etc.).
curl -X PUT http://localhost:8080/api/v1/schedule/job-123 \
-H "Content-Type: application/json" \
-d '{"cron_expr": "0 */2 * * *"}'
DELETE /api/v1/schedule/{job_id} Eliminar trabajo.
curl -X DELETE http://localhost:8080/api/v1/schedule/job-123
POST /api/v1/schedule/{job_id}/pause Pausar trabajo.
curl -X POST http://localhost:8080/api/v1/schedule/job-123/pause
POST /api/v1/schedule/{job_id}/resume Reanudar trabajo pausado.
curl -X POST http://localhost:8080/api/v1/schedule/job-123/resume
POST /api/v1/schedule/{job_id}/run Activar ejecución inmediata.
curl -X POST http://localhost:8080/api/v1/schedule/job-123/run \
-H "Content-Type: application/json" \
-d '{"input": "Run with custom input"}'
GET /api/v1/schedule/{job_id}/history Obtener historial de ejecuciones.
curl "http://localhost:8080/api/v1/schedule/job-123/history?limit=20&status=failed"
GET /api/v1/schedule/{job_id}/next_run Obtener el próximo tiempo de ejecución programado.
curl http://localhost:8080/api/v1/schedule/job-123/next_run
Monitoreo de Salud
GET /api/v1/health/scheduler Salud y métricas del programador.
curl http://localhost:8080/api/v1/health/scheduler
Respuesta:
{
"status": "healthy",
"active_jobs": 15,
"paused_jobs": 3,
"in_flight_jobs": 2,
"metrics": {
"runs_total": 1234,
"runs_succeeded": 1180,
"runs_failed": 54,
"avg_execution_time_ms": 850
}
}
Ejemplos del SDK
SDK de JavaScript
import { SymbiontClient } from '@symbiont/sdk-js';
const client = new SymbiontClient({
baseUrl: 'http://localhost:8080',
apiKey: process.env.SYMBI_API_KEY
});
// Crear trabajo programado
const job = await client.schedule.create({
name: 'daily-backup',
agentId: 'backup-agent',
cronExpr: '0 2 * * *',
sessionMode: 'ephemeral_with_summary',
delivery: ['webhook'],
webhookUrl: 'https://backup.example.com/notify'
});
console.log(`Created job: ${job.id}`);
// Listar trabajos activos
const activeJobs = await client.schedule.list({ status: 'active' });
console.log(`Active jobs: ${activeJobs.length}`);
// Obtener estado del trabajo
const status = await client.schedule.getStatus(job.id);
console.log(`Next run: ${status.next_run}`);
// Activar ejecución inmediata
await client.schedule.runNow(job.id, { input: 'Backup database' });
// Pausar trabajo
await client.schedule.pause(job.id);
// Ver historial
const history = await client.schedule.getHistory(job.id, { limit: 10 });
history.forEach(run => {
console.log(`Run ${run.id}: ${run.status} (${run.execution_time_ms}ms)`);
});
// Reanudar trabajo
await client.schedule.resume(job.id);
// Eliminar trabajo
await client.schedule.delete(job.id);
SDK de Python
from symbiont import SymbiontClient
client = SymbiontClient(
base_url='http://localhost:8080',
api_key=os.environ['SYMBI_API_KEY']
)
# Crear trabajo programado
job = client.schedule.create(
name='hourly-metrics',
agent_id='metrics-agent',
cron_expr='0 * * * *',
session_mode='ephemeral_with_summary',
delivery=['slack', 'log_file'],
slack_channel='#metrics'
)
print(f"Created job: {job.id}")
# Listar trabajos para un agente específico
jobs = client.schedule.list(agent_id='metrics-agent')
print(f"Found {len(jobs)} jobs for metrics-agent")
# Obtener detalles del trabajo
details = client.schedule.get(job.id)
print(f"Cron: {details.cron_expr}")
print(f"Next run: {details.next_run}")
# Actualizar expresión cron
client.schedule.update(job.id, cron_expr='*/30 * * * *')
# Activar ejecución inmediata
run = client.schedule.run_now(job.id, input='Generate metrics report')
print(f"Run ID: {run.id}")
# Pausar durante mantenimiento
client.schedule.pause(job.id)
print("Job paused for maintenance")
# Ver fallos recientes
history = client.schedule.get_history(
job.id,
status='failed',
limit=5
)
for run in history:
print(f"Failed run {run.id}: {run.error_message}")
# Reanudar después del mantenimiento
client.schedule.resume(job.id)
# Verificar salud del programador
health = client.schedule.health()
print(f"Scheduler status: {health.status}")
print(f"Active jobs: {health.active_jobs}")
print(f"In-flight jobs: {health.in_flight_jobs}")
Configuración
CronSchedulerConfig
pub struct CronSchedulerConfig {
/// Intervalo de tick en segundos (predeterminado: 1)
pub tick_interval_seconds: u64,
/// Jitter máximo para prevenir estampida (predeterminado: 0)
pub max_jitter_seconds: u64,
/// Límite global de concurrencia (predeterminado: 10)
pub max_concurrent_jobs: usize,
/// Habilitar recolección de métricas (predeterminado: true)
pub enable_metrics: bool,
/// Umbral de reintentos para dead-letter (predeterminado: 3)
pub default_max_retries: u32,
/// Tiempo de espera para apagado gracioso (predeterminado: 30s)
pub shutdown_timeout_seconds: u64,
}
Configuración TOML
[scheduler]
tick_interval_seconds = 1
max_jitter_seconds = 30
max_concurrent_jobs = 20
enable_metrics = true
default_max_retries = 3
shutdown_timeout_seconds = 60
[scheduler.delivery]
# Configuración de webhook
webhook_timeout_seconds = 30
webhook_retry_attempts = 3
# Configuración de Slack
slack_api_token = "${SLACK_API_TOKEN}"
slack_default_channel = "#ops"
# Configuración de correo electrónico
smtp_host = "smtp.example.com"
smtp_port = 587
smtp_username = "${SMTP_USER}"
smtp_password = "${SMTP_PASS}"
email_from = "symbiont@example.com"
Variables de Entorno
# Configuración del programador
SYMBI_SCHEDULER_MAX_JITTER=30
SYMBI_SCHEDULER_MAX_CONCURRENT=20
# Configuración de entrega
SYMBI_SLACK_TOKEN=xoxb-...
SYMBI_WEBHOOK_AUTH_HEADER="Bearer secret-token"
# Verificación de AgentPin
SYMBI_AGENTPIN_REQUIRED=true
SYMBI_AGENTPIN_DOMAIN=agent.example.com
Observabilidad
Métricas (compatibles con Prometheus)
# Ejecuciones totales
symbiont_cron_runs_total{job_name="daily-report",status="succeeded"} 450
# Ejecuciones fallidas
symbiont_cron_runs_total{job_name="daily-report",status="failed"} 5
# Histograma de tiempo de ejecución
symbiont_cron_execution_duration_seconds{job_name="daily-report"} 1.234
# Indicador de trabajos en curso
symbiont_cron_in_flight_jobs 3
# Trabajos en dead-letter
symbiont_cron_dead_letter_total{job_name="flaky-job"} 2
Eventos de Auditoría
Todas las acciones del programador emiten eventos de seguridad:
pub enum SecurityEventType {
CronJobCreated,
CronJobUpdated,
CronJobDeleted,
CronJobPaused,
CronJobResumed,
CronJobExecuted,
CronJobFailed,
CronJobDeadLettered,
AgentPinVerificationFailed,
}
Consultar el registro de auditoría:
symbi audit query --type CronJobFailed --since "2026-02-01" --limit 50
Mejores Prácticas
- Usar jitter para programaciones compartidas: Prevenir que múltiples trabajos inicien simultáneamente
- Establecer límites de concurrencia: Proteger contra el agotamiento de recursos
- Monitorear la cola de dead-letter: Revisar y corregir trabajos fallidos regularmente
- Usar EphemeralWithSummary: Previene el crecimiento ilimitado de memoria en heartbeats de larga duración
- Habilitar verificación de AgentPin: Verificar criptográficamente la identidad del agente
- Configurar enrutamiento de entrega: Usar canales apropiados para diferentes tipos de trabajos
- Establecer puertas de política: Aplicar ventanas de tiempo, aprobaciones y verificaciones de capacidades
- Usar patrón de heartbeat para monitoreo: Ciclos continuos de evaluación-acción-pausa
- Probar programaciones en staging: Validar expresiones cron y lógica de trabajos antes de producción
- Exportar métricas: Integrar con Prometheus/Grafana para visibilidad operativa
Solución de Problemas
El Trabajo No Se Ejecuta
- Verificar el estado del trabajo:
symbi cron status <job-id> - Verificar la expresión cron: Usar crontab.guru
- Verificar la salud del programador:
curl http://localhost:8080/api/v1/health/scheduler - Revisar los registros:
symbi logs --filter scheduler --level debug
El Trabajo Falla Repetidamente
- Ver historial:
symbi cron history <job-id> --status failed - Verificar mensajes de error en los registros de ejecución
- Verificar la configuración y capacidades del agente
- Probar el agente fuera del programador:
symbi run <agent-id> --input "test" - Verificar puertas de política: Asegurar que las ventanas de tiempo y capacidades coincidan
Trabajo en Dead-Letter
- Listar trabajos en dead-letter:
symbi cron list --status dead_letter - Revisar el patrón de fallos:
symbi cron history <job-id> - Corregir la causa raíz (código del agente, permisos, dependencias externas)
- Restablecer el trabajo:
symbi cron reset <job-id>
Alto Uso de Memoria
- Verificar el modo de sesión: Cambiar a
ephemeral_with_summaryofully_ephemeral - Reducir iteraciones de heartbeat: Disminuir
max_iterations - Monitorear el tamaño del contexto: Revisar la verbosidad de salida del agente
- Habilitar archivado de contexto: Configurar políticas de retención
Migración desde v0.9.0
La versión v1.0.0 agrega características de robustez para producción. Actualice sus definiciones de trabajos:
schedule {
name: "my-job"
agent: "my-agent"
cron: "0 * * * *"
+
+ # Agregar límite de concurrencia
+ max_concurrent: 2
+
+ # Agregar AgentPin para verificación de identidad
+ agent_pin_jwt: "${AGENT_PIN_JWT}"
+
+ policy {
+ require_agent_pin: true
+ }
}
Actualizar configuración:
[scheduler]
tick_interval_seconds = 1
+ max_jitter_seconds = 30
+ default_max_retries = 3
+ shutdown_timeout_seconds = 60
Sin cambios de API incompatibles. Todos los trabajos de v0.9.0 continúan funcionando.