6 Commits

Author SHA1 Message Date
egutierrez 71b3b2bca9 feat(api): status ring buffer (last 100) + GET /status/recent endpoint
Bus.Publish now also appends each event to a per-topic ring buffer of
size 100. Bus.Recent(topic, n) returns the tail. New endpoint:

  GET /status/recent?n=N    → JSON array of last N status-diff events

This lets a fresh client (agents_dashboard launching cold) populate its
Status Feed panel with historical activity before subscribing to
/sse/status for live updates. Until now, new SSE subscribers only saw
events emitted AFTER they connected — making the panel useless for
recent history review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:38:55 +02:00
egutierrez 261f96f71b feat(api): per-agent unified control + clear_memory + delete_cache
- Manager: RegisterUnifiedAgent/UnregisterUnifiedAgent/StopUnifiedAgent/
  IsUnifiedAgentRunning/UptimeSeconds — cancela goroutines individuales sin
  matar el launcher
- Manager: UptimeSeconds en AgentStatus via startedAt map
- api/server: AgentController interface + WithController/WithDataDir builders
  + rutas POST /agents/{id}/clear_memory y /agents/{id}/delete_cache
- api/handlers: handleStartAgent/Stop/Restart delegan a controller en modo
  unified; Messages24h enriquecido via queryMessages24h (cache 30s)
- api/handlers: handleClearMemory — para la goroutine, borra messages+facts de
  memory.db, responde {status,messages_deleted,facts_deleted}
- api/handlers: handleDeleteCache — para la goroutine, elimina crypto/ y cache/,
  responde {status,paths_deleted}
- launcher/registry: launchGoroutine extrae goroutine con contexto per-agente;
  deps.procMgr hookea RegisterUnified; startAgent permite relanzar via reload
- launcher/main: agentController implementa api.AgentController sobre registry;
  mgr compartido entre API y registry; WithController+WithDataDir cableados

Co-Authored-By: fn-orquestador <noreply@fn-registry>
2026-05-22 22:56:46 +02:00
egutierrez 3db4443b65 fix(sse): initial ping + periodic heartbeat unblocks "connecting" state
SSE clients (agents_dashboard) consider the stream connected only after
receiving the first byte of body. The previous implementation flushed
headers and then blocked waiting for status diffs (sse_status) or log
lines (sse_agents_logs) — which could be silent for minutes. UI sat
on "connecting" indefinitely.

Fix:
- After WriteHeader + Flush, emit ":ping\n\n" comment (SSE spec, valid
  no-op) and flush. Unblocks client fgets immediately → state flips
  to "connected" in < 1s.
- Add 15s ticker emitting ":ping\n\n" so idle streams stay alive
  through Traefik / CDN proxies and clients detect dead servers.
- Same treatment for /sse/status and /sse/agents/{id}/logs (tail.go).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:42:29 +02:00
egutierrez 4822208306 fix(api): statusWriter implements http.Flusher for SSE handlers
The logMiddleware wrapper (statusWriter) didn't forward Flush, so
`w.(http.Flusher)` in SSE handlers failed and returned the plain text
"streaming unsupported" with 500. SSE clients (agents_dashboard C++ app)
saw a closed connection with no events.

Add Flush() that delegates to the embedded ResponseWriter when it
implements Flusher. Required for /sse/status and /sse/agents/{id}/logs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:32:06 +02:00
egutierrez b3cf8b41aa fix(api): autodetect unified vs multi-process status for running field
handlers + poller llamaban Manager.StatusAll() siempre. En unified mode (launcher
arranca todos los agents como goroutines bajo 1 PID) no existen PID files
por agente, asi que StatusAll devolvia Running=false aunque los agents estaban
operativos.

Anade Server.statusAllAuto() que chequea IsUnifiedRunning() y delega a
StatusAllUnified() o StatusAll() segun corresponda. Reemplaza las 3 llamadas
directas en handlers.go (handleListAgents, handleGetAgent) y poller.go
(pollStatus seed + checkAndPublishDiffs).

Sin esto el frontend (0129) mostraria running=false universal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:31:41 +02:00
egutierrez 98839cd8a8 feat(api): HTTP API REST+SSE para gestion remota de agentes (issue 0128)
Nuevo paquete internal/api con servidor HTTP stdlib (sin gin/echo):
- Auth Bearer via AGENTS_API_KEY con subtle.ConstantTimeCompare
- REST: GET /health (sin auth), GET/POST /agents, /agents/{id}, /{id}/{start,stop,restart,logs}
- SSE: /sse/status (broadcast diffs cada 2s) y /sse/agents/{id}/logs (tail -f)
- Pubsub in-memory (TODO: NATS cuando haya 2do cliente)
- Tail de logfiles: retroalimenta ultimos 50KB + poll 200ms para streaming

Integracion en cmd/launcher/main.go:
- Flag --api-port (0=desactivado, 8487 en produccion)
- Flag --api-key (override de AGENTS_API_KEY env var)
- Si apiPort>0 y sin clave, WARN y deshabilita en vez de fallar

Systemd unit en systemd/agents_and_robots.service:
- Restart=always (no on-failure — evita que exit limpio mate el service)
- EnvironmentFile para AGENTS_API_KEY y demas tokens
- WorkingDirectory=/home/ubuntu/CodeProyects/agents_and_robots

app.md v0.2.0:
- port: 8487, health_endpoint: /health (fix drift anterior donde era null)
- e2e_checks: build, tests, smoke_health, smoke_auth
- Documentacion Traefik+DNS pendiente humano post-merge

Tests: 12 tests unitarios en internal/api (auth, health, bus, agents, logs)
Smoke: /health 200, /agents sin auth 401, /agents con key 200 — verificado local

Co-Authored-By: fn-constructor (agent)
2026-05-22 21:19:10 +02:00