2026-04-14 18:02:25 +00:00
|
|
|
// Package api exposes the HTTP control-plane used by the MCP gateway
|
|
|
|
|
// and the orchestrator dashboard.
|
|
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
"encoding/json"
|
2026-04-14 18:02:25 +00:00
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
|
|
|
|
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
"forge.secuaas.ovh/olivier/claude-failover/internal/delegation"
|
2026-04-14 18:02:25 +00:00
|
|
|
"forge.secuaas.ovh/olivier/claude-failover/internal/state"
|
|
|
|
|
)
|
|
|
|
|
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
const version = "0.2.0"
|
2026-04-14 18:02:25 +00:00
|
|
|
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
// DelegationProvider is the slice of delegation.Manager used by the HTTP
|
|
|
|
|
// server. Kept as an interface so tests don't have to spin up a real
|
|
|
|
|
// secutools client.
|
|
|
|
|
type DelegationProvider interface {
|
|
|
|
|
Active() []delegation.ActiveJob
|
|
|
|
|
CountersSnapshot() delegation.Snapshot
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Server is a minimal HTTP server exposing /health, /status,
|
|
|
|
|
// /watchdog/status and /api/delegated/status.
|
2026-04-14 18:02:25 +00:00
|
|
|
type Server struct {
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
addr string
|
|
|
|
|
state *state.State
|
|
|
|
|
delegation DelegationProvider
|
2026-04-14 18:02:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New creates a Server listening on addr.
|
|
|
|
|
func New(addr string, s *state.State) *Server {
|
|
|
|
|
return &Server{addr: addr, state: s}
|
|
|
|
|
}
|
|
|
|
|
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
// WithDelegation enables /api/delegated/* endpoints. Pass nil (or skip
|
|
|
|
|
// the call) to keep them disabled — those paths return 404.
|
|
|
|
|
func (s *Server) WithDelegation(d DelegationProvider) *Server {
|
|
|
|
|
s.delegation = d
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 18:02:25 +00:00
|
|
|
// Start registers routes and begins serving. Blocks until the listener fails.
|
|
|
|
|
func (s *Server) Start() error {
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
mux.HandleFunc("/health", s.handleHealth)
|
|
|
|
|
mux.HandleFunc("/status", s.handleStatus)
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
mux.HandleFunc("/watchdog/status", s.handleWatchdogStatus)
|
|
|
|
|
mux.HandleFunc("/api/delegated/status", s.handleDelegatedStatus)
|
2026-04-14 18:02:25 +00:00
|
|
|
return http.ListenAndServe(s.addr, mux)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
fmt.Fprintf(w, `{"status":"ok","version":%q}`, version)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
feat(phase2-E): multi-provider routing via secutools delegation
Adds optional delegation of agent-queue tasks to the SecuAAS secutools
AI platform (GPU / Gemini / Claude API) instead of dispatching to a
local Claude Code tmux session. Per-task opt-in via YAML frontmatter
fields preferred_ai, allow_delegation, complexity_hint — absence keeps
the Phase 1 behaviour exactly (zero breaking change).
Go side:
- internal/secutools: HTTP client with exponential-backoff retries
(SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI
use, table tests.
- internal/router: struct-typed Decide() with strict precedence
(needs_claude_code > preferred_ai=claude-code > allow_delegation=false
> preferred_ai > fail-safe local on unknown).
- internal/delegation: Manager submits jobs, writes .md.delegated
markers for on-restart recovery, runs a periodic reaper that moves
completed jobs into done/ with provider/cost footer and failed jobs
into failed/.
- internal/dispatcher: WithDelegation() opt-in, routeTask hook before
findFreeSession, skips .md.delegated in assignNextTask.
- internal/api: /api/delegated/status (active jobs + counters),
/watchdog/status extended with delegation counters.
- cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the
bash dispatcher can call the same contract without duplicating logic.
- cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY.
Tests:
- 29+ new unit tests across router, secutools, delegation, dispatcher,
api packages. go test -race -count=1 clean.
- tests/phase2-E-integration.sh: bash end-to-end against a Python
stdlib mock HTTP server, exercising the dev-management scripts.
Forward-compat with watchdog (Phase 1 B1 already ignores
state=delegated_to_secutools) so delegated tasks aren't flagged stale.
2026-04-17 02:17:19 +00:00
|
|
|
_, _ = w.Write(s.state.JSON())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleWatchdogStatus returns operational counters consumed by the
|
|
|
|
|
// orchestrator dashboard. Includes delegation metrics when wired.
|
|
|
|
|
func (s *Server) handleWatchdogStatus(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
out := map[string]any{
|
|
|
|
|
"version": version,
|
|
|
|
|
}
|
|
|
|
|
if s.delegation != nil {
|
|
|
|
|
out["delegation"] = s.delegation.CountersSnapshot()
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleDelegatedStatus returns the list of in-flight delegated jobs.
|
|
|
|
|
func (s *Server) handleDelegatedStatus(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
if s.delegation == nil {
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
_, _ = w.Write([]byte(`{"error":"delegation disabled"}`))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
out := map[string]any{
|
|
|
|
|
"active": s.delegation.Active(),
|
|
|
|
|
"counters": s.delegation.CountersSnapshot(),
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(out)
|
2026-04-14 18:02:25 +00:00
|
|
|
}
|