Files
m 73d7ffd012
CI / test (push) Failing after 37s
CI / test (pull_request) Failing after 35s
feat: execute generic local work graph candidates
2026-08-24 02:56:19 +03:00

130 lines
4.2 KiB
Go

package main
import (
"encoding/json"
"net/http"
"sync"
"time"
)
type workerStatusResponse struct {
Runtime string `json:"runtime"`
ExecutorConfigured bool `json:"executor_configured"`
Mode string `json:"mode"`
Ready bool `json:"ready"`
Reason string `json:"reason"`
LocalFallback bool `json:"local_fallback"`
LocalExecutor bool `json:"local_executor"`
Capabilities []string `json:"capabilities,omitempty"`
StartedAt time.Time `json:"started_at,omitempty"`
LastCycleAt time.Time `json:"last_cycle_at,omitempty"`
LastCompletedAt time.Time `json:"last_completed_at,omitempty"`
LastRecoveredClaims int64 `json:"last_recovered_claims"`
TotalRecoveredClaims int64 `json:"total_recovered_claims"`
LastDispatchID string `json:"last_dispatch_id,omitempty"`
LastResult string `json:"last_result,omitempty"`
LastError string `json:"last_error,omitempty"`
}
type agentWorkerRuntimeStatus struct {
mu sync.RWMutex
startedAt time.Time
lastCycleAt time.Time
lastCompletedAt time.Time
lastRecoveredClaims int64
totalRecoveredClaims int64
lastDispatchID string
lastResult string
lastError string
}
func newAgentWorkerRuntimeStatus() *agentWorkerRuntimeStatus {
return &agentWorkerRuntimeStatus{startedAt: time.Now().UTC()}
}
func (s *agentWorkerRuntimeStatus) cycleStarted(at time.Time) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCycleAt = at.UTC()
s.lastResult = "running"
s.lastError = ""
}
func (s *agentWorkerRuntimeStatus) cycleFinished(at time.Time, recoveredClaims int64, dispatchID string, result string) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCompletedAt = at.UTC()
s.lastRecoveredClaims = recoveredClaims
s.totalRecoveredClaims += recoveredClaims
s.lastDispatchID = dispatchID
s.lastResult = result
s.lastError = ""
}
func (s *agentWorkerRuntimeStatus) cycleFailed(at time.Time, err error) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCompletedAt = at.UTC()
s.lastResult = "failed"
s.lastError = err.Error()
}
func workerStatusHandler(executorConfigured bool, localFallback ...interface{}) http.Handler {
local := false
var runtime *agentWorkerRuntimeStatus
for _, argument := range localFallback {
switch value := argument.(type) {
case bool:
local = value
case *agentWorkerRuntimeStatus:
runtime = value
}
}
if runtime == nil {
runtime = newAgentWorkerRuntimeStatus()
}
baseStatus := workerStatusResponse{
Runtime: "agent-worker",
ExecutorConfigured: executorConfigured,
Mode: "recovery-only",
Ready: false,
Reason: "protected agent execution bridge URL is not configured",
LocalFallback: local,
LocalExecutor: local,
}
if local {
baseStatus.Mode = "local-executor"
baseStatus.Ready = true
baseStatus.Reason = "bounded local task-evaluation capability is ready; unsupported work remains durably waiting"
baseStatus.Capabilities = []string{"task-evaluation"}
}
if executorConfigured {
baseStatus.Mode = "execution"
baseStatus.Ready = true
baseStatus.Reason = "typed agent execution bridge configured"
baseStatus.Capabilities = []string{"task-evaluation", "generic-cognitive-work"}
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet || (r.URL.Path != "/healthz" && r.URL.Path != "/readyz" && r.URL.Path != "/status") {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "application/json")
if r.URL.Path == "/readyz" && !baseStatus.Ready {
w.WriteHeader(http.StatusServiceUnavailable)
}
response := baseStatus
runtime.mu.RLock()
response.StartedAt = runtime.startedAt
response.LastCycleAt = runtime.lastCycleAt
response.LastCompletedAt = runtime.lastCompletedAt
response.LastRecoveredClaims = runtime.lastRecoveredClaims
response.TotalRecoveredClaims = runtime.totalRecoveredClaims
response.LastDispatchID = runtime.lastDispatchID
response.LastResult = runtime.lastResult
response.LastError = runtime.lastError
runtime.mu.RUnlock()
_ = json.NewEncoder(w).Encode(response)
})
}