agent channels · kagent A2A · agentgateway models

Two ways to run the lab agents — chat or schedule.

Interactive multi-agent Telegram and a pure-GitOps agent scheduler share the same kagent fleet. Every model call still goes through Solo Enterprise AgentGateway — traced, priced, and key-injected upstream. Git remains the source of truth; the cluster only executes.

Telegram · multi-agent A2A Scheduler · PR → Argo → CronJob GitOps · no direct kubectl desired state 6 agent aliases

Channels at a glance

#Chat when you need a hand. Cron when you need a routine.

Both channels speak A2A to in-cluster agent Services. Neither holds provider API keys — kagent ModelConfigs point at agentgateway, and the gateway injects real upstream credentials from Vault-synced Secrets.

Telegram bot interactive

ns kagent · image telegram-kagent-bot:goose-0.1.0 · @KagentCorpAIbot

You message the bot; it polls Telegram, routes to the selected agent over A2A, and streams the reply (with HITL Approve/Reject when tools require approval).

  • Direction: you → agent
  • UI: Telegram mobile / desktop
  • Session state: per-chat conversation
  • Results: live chat replies

Agent scheduler unattended

UI NodePort :30955 · CronJobs in ns kagent · pure GitOps

Save a schedule in the web UI → GitHub PR auto-merges → Argo applies a CronJob → Job POSTs A2A → results land in ConfigMaps (and optionally Telegram).

  • Direction: cron → agent
  • UI: http://worker:30955
  • Source of truth: config/agent-schedules/
  • Results: ConfigMaps ags-result-* ± Telegram

Reference path

#How traffic reaches models

Telegram and the scheduler are edge adapters. The agents, tools, and LLM hops are the same stack documented on the platform page — kagent runtime, MCP tool servers, AgentGateway backends.

Interactive channel

#Telegram multi-agent bot

Same pattern as the k8s-iceman bot, adapted for goose: one polling Deployment, a JSON map of agent aliases to in-cluster A2A URLs, and HITL buttons when an agent needs approval.

You message

Open @KagentCorpAIbot — private chat or group where the bot is invited.

Bot polls

telegram-bot pod long-polls Telegram; token from Vault via ExternalSecret.

A2A send

JSON-RPC message/send to http://<agent>.kagent:8080/ with session continuity.

Reply / HITL

Agent answer returns as chat text. Destructive tools surface Approve / Reject inline buttons.

Commands

slash + @-prefix routing
CommandAction
/startHelp
/agentsList aliases + A2A URLs
/use <alias> [msg]Switch agent; optional message in same turn
/newReset session
/statusPing current agent
@forti …Switch + message in one line

Agent map

AGENTS_JSON on the Deployment
AliasService
k8sk8s-agent (default)
fortifortigate-agent
f5f5-bigip-agent
githubgithub-agent
dronedrone-agent
demodemo-agent

Repo layout

  • telegram-bot/ — source + Dockerfile
  • config/telegram-bot/ — Deployment + ExternalSecret
  • docs/telegram-bot.md — operator notes

Secrets

  • Vault path agentgateway/telegram
  • api_key → BotFather token
  • chat_id → numeric id for schedule notifies
  • K8s Secret telegram-bot-token (ns kagent)

Ops checks

  • ExternalSecret Ready / SecretSynced
  • Pod 1/1 Running (single replica)
  • Allowlist: @sebbycorp only
  • No dual getUpdates (409 Conflict)
  • /status after /use

Unattended channel

#Agent scheduler — pure GitOps

The UI never creates CronJobs as desired state via the Kubernetes API. It only opens a pull request on sebbycorp/k8s-goose; ArgoCD applies config/agent-schedules/*.

Save in UI

Name, agent, cron (UTC), prompt, optional Telegram notify. POST /api/schedules.

PR + merge

GitHub REST with PAT from Vault (github/mcp). Auto-merge when allowed.

Argo apply

agentgateway-config syncs → CronJob/ags-<name> in ns kagent.

Fire + store

Job POSTs A2A, writes ConfigMaps, optionally posts to Telegram using the shared bot token.

Where things live

repo paths
  • agent-scheduler/ — FastAPI UI + Dockerfile
  • config/agent-scheduler/ — deploy, NodePort 30955, RBAC, ESO
  • config/agent-schedules/ — one CronJob YAML per schedule
  • docs/agent-scheduler.md — full operator guide

Results storage

ConfigMaps in ns kagent
  • ags-result-<name> — latest (overwrite)
  • ags-hist-<name>-<ts> — history snapshot
  • Keys: text, raw.json, status, httpCode, …
  • UI Results panel + GET /api/results/…

API surface

MethodPathDescription
GET/healthzLiveness + GitHub token present
GET/api/agentsLive kagent Agents
GET / POST/api/schedulesList / upsert via PR
DELETE/api/schedules/{name}Delete via PR
GET/api/results[/{name}]List or load result ConfigMaps
GET/api/presetsCron presets (hourly, daily, …)

Telegram notifications from schedules

When Send result to Telegram is checked, each Job posts a short summary to TELEGRAM_CHAT_ID using the same Vault bot token as the chat bot. Re-save schedules after enabling so CronJobs pick up the notify step. Message format: schedule name, agent, status, timestamp, and a reply preview.

Operational caveats

HITL requireApproval can block unattended cron for destructive tools — start with read-only prompts. Cron is UTC. Result bodies are truncated for ConfigMap size limits. History ConfigMaps are not auto-pruned. The UI requests minimal CPU so it still schedules on a tight worker.

Shared foundation

#kagent models through AgentGateway

Whether you type in Telegram or a CronJob fires, the agent still calls an OpenAI-compatible base URL that is an in-cluster gateway. Provider keys stay in Vault → ESO → gateway backend Secrets.

ModelConfigModelGateway baseUrl (in-cluster)
default-model-config gpt-4o / lab default …/openai/v1 · agentgateway-proxy
xai-grok grok-4.5 …/grok/v1 · xai-grok-gateway
dgx-spark Qwen3.8-27B (NVFP4), local …/spark/v1 · dgx-spark-gateway
gpt-5-6 gpt-5.6 …/gpt56/v1 · dedicated gateway

Why this design

  • One control plane for spend, traces, and budgets
  • Agents never need real upstream keys
  • Swap models without redeploying Telegram or the scheduler
  • Cost UI + Langfuse stay accurate across channels

Related lab surfaces

Operator runbook

#Seed secrets & verify

Lab-only: NodePorts are plain HTTP. Confirm the worker IP with kubectl get nodes -o wide if DHCP moved it.

vault — telegram bot token + chat_id
# api_key = BotFather token · chat_id from getUpdates after /start
kubectl --context maniak-goose -n vault exec vault-0 -- \
  vault kv put agentgateway/telegram \
    api_key='' \
    chat_id=''

kubectl --context maniak-goose -n kagent annotate externalsecret telegram-bot-token \
  force-sync=$(date +%s) --overwrite

kubectl --context maniak-goose -n kagent get externalsecret telegram-bot-token
kubectl --context maniak-goose -n kagent get pods -l app.kubernetes.io/name=telegram-bot
scheduler — list CronJobs, results, manual fire
# UI (worker NodePort)
open http://172.16.10.155:30955/

kubectl --context maniak-goose get cronjobs -n kagent \
  -l app.kubernetes.io/managed-by=agent-scheduler

kubectl --context maniak-goose get cm -n kagent \
  -l app.kubernetes.io/managed-by=agent-scheduler

# one-shot without waiting for cron
kubectl --context maniak-goose create job -n kagent \
  --from=cronjob/ags-<name> manual-<name>-1
kubectl --context maniak-goose logs -n kagent -f job/manual-<name>-1
smoke — Telegram chat path
# In Telegram:
#   /start
#   /agents
#   /use k8s
#   /status
#   ask a read-only question first

kubectl --context maniak-goose -n kagent logs \
  -l app.kubernetes.io/name=telegram-bot --tail=50

Access

  • Bot: t.me/KagentCorpAIbot
  • Scheduler UI: http://172.16.10.155:30955/
  • Solo UI: :30854 (kagent + AgentGateway)
  • Context: maniak-goose

Images

  • sebbycorp/telegram-kagent-bot:goose-0.1.0
  • sebbycorp/agent-scheduler:0.1.3
  • Rebuild with docker buildx … --platform linux/amd64

Source docs in repo

  • docs/telegram-bot.md
  • docs/agent-scheduler.md
  • Design: docs/superpowers/specs/…agent-scheduler-design.md