agent/pkg/agentic/platform.go
Snider 39914fbf14 refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.

Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00

1297 lines
39 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
"net/http"
"time"
core "dappco.re/go/core"
)
// node := agentic.FleetNode{AgentID: "charon", Platform: "linux", Status: "online"}
type ComputeBudget struct {
MaxDailyHours float64 `json:"max_daily_hours,omitempty"`
MaxWeeklyCostUSD float64 `json:"max_weekly_cost_usd,omitempty"`
QuietStart string `json:"quiet_start,omitempty"`
QuietEnd string `json:"quiet_end,omitempty"`
PreferModels []string `json:"prefer_models,omitempty"`
AvoidModels []string `json:"avoid_models,omitempty"`
}
// node := agentic.FleetNode{AgentID: "charon", Platform: "linux", Status: "online"}
type FleetNode struct {
ID int `json:"id"`
WorkspaceID int `json:"workspace_id,omitempty"`
AgentID string `json:"agent_id"`
Platform string `json:"platform"`
Models []string `json:"models,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
Status string `json:"status"`
ComputeBudget *ComputeBudget `json:"compute_budget,omitempty"`
CurrentTaskID *int `json:"current_task_id,omitempty"`
LastHeartbeatAt string `json:"last_heartbeat_at,omitempty"`
RegisteredAt string `json:"registered_at,omitempty"`
}
// task := agentic.FleetTask{ID: 7, Repo: "go-io", Task: "Fix tests", Status: "assigned"}
type FleetTask struct {
ID int `json:"id"`
WorkspaceID int `json:"workspace_id,omitempty"`
FleetNodeID int `json:"fleet_node_id,omitempty"`
Repo string `json:"repo"`
Branch string `json:"branch,omitempty"`
Task string `json:"task"`
Template string `json:"template,omitempty"`
AgentModel string `json:"agent_model,omitempty"`
Status string `json:"status"`
Result map[string]any `json:"result,omitempty"`
Findings []map[string]any `json:"findings,omitempty"`
Changes map[string]any `json:"changes,omitempty"`
Report map[string]any `json:"report,omitempty"`
StartedAt string `json:"started_at,omitempty"`
CompletedAt string `json:"completed_at,omitempty"`
}
// out := agentic.FleetNodesOutput{Total: 2, Nodes: []agentic.FleetNode{{AgentID: "charon"}}}
type FleetNodesOutput struct {
Total int `json:"total"`
Nodes []FleetNode `json:"nodes"`
}
// stats := agentic.FleetStats{NodesOnline: 2, TasksToday: 5}
type FleetStats struct {
NodesOnline int `json:"nodes_online"`
TasksToday int `json:"tasks_today"`
TasksWeek int `json:"tasks_week"`
ReposTouched int `json:"repos_touched"`
FindingsTotal int `json:"findings_total"`
ComputeHours int `json:"compute_hours"`
}
// event := agentic.FleetEvent{Type: "task.assigned", AgentID: "charon", Repo: "core/go-io"}
type FleetEvent struct {
Type string `json:"type,omitempty"`
Event string `json:"event,omitempty"`
AgentID string `json:"agent_id,omitempty"`
TaskID int `json:"task_id,omitempty"`
Repo string `json:"repo,omitempty"`
Branch string `json:"branch,omitempty"`
Status string `json:"status,omitempty"`
ReceivedAt string `json:"received_at,omitempty"`
Payload map[string]any `json:"payload,omitempty"`
}
// out := agentic.FleetEventOutput{Success: true, Event: agentic.FleetEvent{Type: "task.assigned"}}
type FleetEventOutput struct {
Success bool `json:"success"`
Event FleetEvent `json:"event"`
Raw string `json:"raw,omitempty"`
}
// status := agentic.SyncStatusOutput{AgentID: "charon", Status: "online"}
type SyncStatusOutput struct {
AgentID string `json:"agent_id"`
Status string `json:"status"`
LastPushAt string `json:"last_push_at,omitempty"`
LastPullAt string `json:"last_pull_at,omitempty"`
Queued int `json:"queued"`
ContextCount int `json:"context_count"`
RemoteError string `json:"remote_error,omitempty"`
}
// balance := agentic.CreditBalance{AgentID: "charon", Balance: 12}
type CreditBalance struct {
AgentID string `json:"agent_id"`
Balance int `json:"balance"`
Entries int `json:"entries"`
}
// entry := agentic.CreditEntry{ID: 4, TaskType: "fleet-task", Amount: 2}
type CreditEntry struct {
ID int `json:"id"`
WorkspaceID int `json:"workspace_id,omitempty"`
FleetNodeID int `json:"fleet_node_id,omitempty"`
TaskType string `json:"task_type"`
Amount int `json:"amount"`
BalanceAfter int `json:"balance_after"`
Description string `json:"description,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
// out := agentic.CreditsHistoryOutput{Total: 1, Entries: []agentic.CreditEntry{{ID: 1}}}
type CreditsHistoryOutput struct {
Total int `json:"total"`
Entries []CreditEntry `json:"entries"`
}
// caps := agentic.SubscriptionCapabilities{Available: []string{"claude", "openai"}}
type SubscriptionCapabilities struct {
Providers map[string]bool `json:"providers,omitempty"`
Available []string `json:"available,omitempty"`
}
// result := c.Action("agentic.sync.status").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleSyncStatus(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
agentID = AgentName()
}
output := SyncStatusOutput{
AgentID: agentID,
Status: "offline",
Queued: len(readSyncQueue()),
ContextCount: len(readSyncContext()),
}
localStatus := readSyncStatusState()
if !localStatus.LastPushAt.IsZero() {
output.LastPushAt = localStatus.LastPushAt.Format(time.RFC3339)
}
if !localStatus.LastPullAt.IsZero() {
output.LastPullAt = localStatus.LastPullAt.Format(time.RFC3339)
}
if s.syncToken() == "" {
return core.Result{Value: output, OK: true}
}
path := appendQueryParam("/v1/agent/status", "agent_id", agentID)
result := s.platformPayload(ctx, "agentic.sync.status", "GET", path, nil)
if !result.OK {
err, _ := result.Value.(error)
if err != nil {
output.RemoteError = err.Error()
}
return core.Result{Value: output, OK: true}
}
data := payloadResourceMap(result.Value.(map[string]any), "status")
if len(data) == 0 {
return core.Result{Value: output, OK: true}
}
if remoteAgentID := stringValue(data["agent_id"]); remoteAgentID != "" {
output.AgentID = remoteAgentID
}
output.Status = stringValue(data["status"])
if lastPushAt := stringValue(data["last_push_at"]); lastPushAt != "" {
output.LastPushAt = lastPushAt
}
if lastPullAt := stringValue(data["last_pull_at"]); lastPullAt != "" {
output.LastPullAt = lastPullAt
}
if queued, ok := intValueOK(data["queued"]); ok {
output.Queued = queued
}
if contextCount, ok := intValueOK(data["context_count"]); ok {
output.ContextCount = contextCount
}
if output.Status == "" {
output.Status = "online"
}
return core.Result{Value: output, OK: true}
}
// result := c.Action("agentic.fleet.register").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetRegister(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agentic.fleet.register", "agent_id is required", nil), OK: false}
}
platform := optionStringValue(options, "platform")
if platform == "" {
platform = "unknown"
}
body := map[string]any{
"agent_id": agentID,
"platform": platform,
}
if models := optionStringSliceValue(options, "models"); len(models) > 0 {
body["models"] = models
}
if capabilities := optionStringSliceValue(options, "capabilities"); len(capabilities) > 0 {
body["capabilities"] = capabilities
}
result := s.platformPayload(ctx, "agentic.fleet.register", "POST", "/v1/fleet/register", body)
if !result.OK {
return result
}
return core.Result{Value: parseFleetNode(payloadResourceMap(result.Value.(map[string]any), "node")), OK: true}
}
// result := c.Action("agentic.fleet.heartbeat").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetHeartbeat(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
status := optionStringValue(options, "status")
if agentID == "" || status == "" {
return core.Result{Value: core.E("agentic.fleet.heartbeat", "agent_id and status are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"status": status,
}
if budget := optionAnyMapValue(options, "compute_budget", "compute-budget"); len(budget) > 0 {
body["compute_budget"] = budget
}
result := s.platformPayload(ctx, "agentic.fleet.heartbeat", "POST", "/v1/fleet/heartbeat", body)
if !result.OK {
return result
}
return core.Result{Value: parseFleetNode(payloadResourceMap(result.Value.(map[string]any), "node")), OK: true}
}
// result := c.Action("agentic.fleet.deregister").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetDeregister(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agentic.fleet.deregister", "agent_id is required", nil), OK: false}
}
result := s.platformPayload(ctx, "agentic.fleet.deregister", "POST", "/v1/fleet/deregister", map[string]any{
"agent_id": agentID,
})
if !result.OK {
return result
}
return core.Result{Value: map[string]any{
"agent_id": agentID,
"deregistered": true,
}, OK: true}
}
// result := c.Action("agentic.fleet.nodes").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleFleetNodes(ctx context.Context, options core.Options) core.Result {
path := "/v1/fleet/nodes"
path = appendQueryParam(path, "status", optionStringValue(options, "status"))
path = appendQueryParam(path, "platform", optionStringValue(options, "platform"))
result := s.platformPayload(ctx, "agentic.fleet.nodes", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: parseFleetNodesOutput(result.Value.(map[string]any)), OK: true}
}
// result := c.Action("agentic.fleet.task.assign").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetAssignTask(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
repo := optionStringValue(options, "repo")
task := optionStringValue(options, "task")
if agentID == "" || repo == "" || task == "" {
return core.Result{Value: core.E("agentic.fleet.task.assign", "agent_id, repo, and task are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"repo": repo,
"task": task,
}
if branch := optionStringValue(options, "branch"); branch != "" {
body["branch"] = branch
}
if template := optionStringValue(options, "template"); template != "" {
body["template"] = template
}
if agentModel := optionStringValue(options, "agent_model", "agent-model"); agentModel != "" {
body["agent_model"] = agentModel
}
result := s.platformPayload(ctx, "agentic.fleet.task.assign", "POST", "/v1/fleet/task/assign", body)
if !result.OK {
return result
}
return core.Result{Value: parseFleetTask(payloadResourceMap(result.Value.(map[string]any), "task")), OK: true}
}
// result := c.Action("agentic.fleet.task.complete").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetCompleteTask(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
taskID := optionIntValue(options, "task_id", "task-id")
if agentID == "" || taskID == 0 {
return core.Result{Value: core.E("agentic.fleet.task.complete", "agent_id and task_id are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"task_id": taskID,
}
if resultMap := optionAnyMapValue(options, "result"); len(resultMap) > 0 {
body["result"] = resultMap
}
if findings := optionAnyMapSliceValue(options, "findings"); len(findings) > 0 {
body["findings"] = findings
}
if changes := optionAnyMapValue(options, "changes"); len(changes) > 0 {
body["changes"] = changes
}
if report := optionAnyMapValue(options, "report"); len(report) > 0 {
body["report"] = report
}
result := s.platformPayload(ctx, "agentic.fleet.task.complete", "POST", "/v1/fleet/task/complete", body)
if !result.OK {
return result
}
task := parseFleetTask(payloadResourceMap(result.Value.(map[string]any), "task"))
awardOptions := core.NewOptions(
core.Option{Key: "agent_id", Value: agentID},
core.Option{Key: "task_type", Value: "fleet-task"},
core.Option{Key: "amount", Value: 2},
core.Option{Key: "description", Value: "Fleet task completed"},
)
if task.FleetNodeID > 0 {
awardOptions.Set("fleet_node_id", task.FleetNodeID)
}
if awardResult := s.handleCreditsAward(ctx, awardOptions); !awardResult.OK {
if s.Core() != nil {
core.Print(nil, "warning: %v", commandResultError("agentic.fleet.task.complete", awardResult))
}
}
return core.Result{Value: task, OK: true}
}
// result := c.Action("agentic.fleet.task.next").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetNextTask(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agentic.fleet.task.next", "agent_id is required", nil), OK: false}
}
path := appendQueryParam("/v1/fleet/task/next", "agent_id", agentID)
path = appendQuerySlice(path, "capabilities[]", optionStringSliceValue(options, "capabilities"))
result := s.platformPayload(ctx, "agentic.fleet.task.next", "GET", path, nil)
if !result.OK {
return result
}
data := payloadResourceMap(result.Value.(map[string]any), "task")
if len(data) == 0 {
var task *FleetTask
return core.Result{Value: task, OK: true}
}
task := parseFleetTask(data)
return core.Result{Value: &task, OK: true}
}
// result := c.Action("agentic.fleet.stats").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleFleetStats(ctx context.Context, options core.Options) core.Result {
result := s.platformPayload(ctx, "agentic.fleet.stats", "GET", "/v1/fleet/stats", nil)
if !result.OK {
return result
}
return core.Result{Value: parseFleetStats(payloadResourceMap(result.Value.(map[string]any), "stats")), OK: true}
}
// result := c.Action("agentic.fleet.events").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetEvents(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
path := "/v1/fleet/events"
path = appendQueryParam(path, "agent_id", agentID)
result := s.platformEventPayload(ctx, "agentic.fleet.events", path)
if result.OK {
output, err := parseFleetEventOutput(result.Value.(map[string]any))
if err == nil {
return core.Result{Value: output, OK: true}
}
}
fallbackResult := s.handleFleetNextTask(ctx, core.NewOptions(
core.Option{Key: "agent_id", Value: agentID},
core.Option{Key: "capabilities", Value: optionStringSliceValue(options, "capabilities")},
))
if !fallbackResult.OK {
if result.OK {
return result
}
return fallbackResult
}
task, ok := fallbackResult.Value.(*FleetTask)
if !ok {
return core.Result{Value: core.E("agentic.fleet.events", "invalid fleet task output", nil), OK: false}
}
if task == nil {
if result.OK {
return core.Result{Value: core.E("agentic.fleet.events", "no fleet event payload returned", nil), OK: false}
}
return core.Result{Value: core.E("agentic.fleet.events", "no fleet task available", nil), OK: false}
}
return core.Result{Value: fleetEventOutputFromTask(agentID, task), OK: true}
}
// result := c.Action("agentic.credits.award").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleCreditsAward(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
taskType := optionStringValue(options, "task_type", "task-type")
amount := optionIntValue(options, "amount")
if agentID == "" || taskType == "" || amount == 0 {
return core.Result{Value: core.E("agentic.credits.award", "agent_id, task_type, and amount are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"task_type": taskType,
"amount": amount,
}
if fleetNodeID := optionIntValue(options, "fleet_node_id", "fleet-node-id"); fleetNodeID > 0 {
body["fleet_node_id"] = fleetNodeID
}
if description := optionStringValue(options, "description"); description != "" {
body["description"] = description
}
result := s.platformPayload(ctx, "agentic.credits.award", "POST", "/v1/credits/award", body)
if !result.OK {
return result
}
return core.Result{Value: parseCreditEntry(payloadResourceMap(result.Value.(map[string]any), "entry")), OK: true}
}
// result := c.Action("agentic.credits.balance").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleCreditsBalance(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agentic.credits.balance", "agent_id is required", nil), OK: false}
}
path := core.Concat("/v1/credits/balance/", agentID)
result := s.platformPayload(ctx, "agentic.credits.balance", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: parseCreditBalance(payloadResourceMap(result.Value.(map[string]any), "balance")), OK: true}
}
// result := c.Action("agentic.credits.history").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleCreditsHistory(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agentic.credits.history", "agent_id is required", nil), OK: false}
}
path := core.Concat("/v1/credits/history/", agentID)
if limit := optionIntValue(options, "limit"); limit > 0 {
path = appendQueryParam(path, "limit", core.Sprint(limit))
}
result := s.platformPayload(ctx, "agentic.credits.history", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: parseCreditsHistoryOutput(result.Value.(map[string]any)), OK: true}
}
// result := c.Action("agentic.subscription.detect").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleSubscriptionDetect(ctx context.Context, options core.Options) core.Result {
body := map[string]any{}
if apiKeys := optionStringMapValue(options, "api_keys", "api-keys"); len(apiKeys) > 0 {
body["api_keys"] = apiKeys
}
result := s.platformPayload(ctx, "agentic.subscription.detect", "POST", "/v1/subscription/detect", body)
if !result.OK {
return result
}
return core.Result{Value: parseSubscriptionCapabilities(payloadResourceMap(result.Value.(map[string]any), "capabilities", "subscription")), OK: true}
}
// result := c.Action("agentic.subscription.budget").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleSubscriptionBudget(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agentic.subscription.budget", "agent_id is required", nil), OK: false}
}
path := core.Concat("/v1/subscription/budget/", agentID)
result := s.platformPayload(ctx, "agentic.subscription.budget", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: payloadResourceMap(result.Value.(map[string]any), "budget"), OK: true}
}
// result := c.Action("agentic.subscription.budget.update").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleSubscriptionBudgetUpdate(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
limits := optionAnyMapValue(options, "limits")
if agentID == "" || len(limits) == 0 {
return core.Result{Value: core.E("agentic.subscription.budget.update", "agent_id and limits are required", nil), OK: false}
}
path := core.Concat("/v1/subscription/budget/", agentID)
result := s.platformPayload(ctx, "agentic.subscription.budget.update", "PUT", path, map[string]any{
"limits": limits,
})
if !result.OK {
return result
}
return core.Result{Value: payloadResourceMap(result.Value.(map[string]any), "budget"), OK: true}
}
func (s *PrepSubsystem) platformPayload(ctx context.Context, action, method, path string, body any) core.Result {
token := s.syncToken()
if token == "" {
return core.Result{Value: core.E(action, "no platform API key configured", nil), OK: false}
}
bodyString := ""
if body != nil {
bodyString = core.JSONMarshalString(body)
}
requestResult := HTTPDo(ctx, method, core.Concat(s.syncAPIURL(), path), bodyString, token, "Bearer")
if !requestResult.OK {
return core.Result{Value: platformResultError(action, requestResult), OK: false}
}
var payload map[string]any
parseResult := core.JSONUnmarshalString(requestResult.Value.(string), &payload)
if !parseResult.OK {
err, _ := parseResult.Value.(error)
return core.Result{Value: core.E(action, "failed to parse platform response", err), OK: false}
}
return core.Result{Value: payload, OK: true}
}
func (s *PrepSubsystem) platformEventPayload(ctx context.Context, action, path string) core.Result {
token := s.syncToken()
if token == "" {
return core.Result{Value: core.E(action, "no platform API key configured", nil), OK: false}
}
request, err := http.NewRequestWithContext(ctx, "GET", core.Concat(s.syncAPIURL(), path), nil)
if err != nil {
return core.Result{Value: core.E(action, "create request", err), OK: false}
}
request.Header.Set("Accept", "text/event-stream, application/json")
request.Header.Set("Authorization", core.Concat("Bearer ", token))
response, err := defaultClient.Do(request)
if err != nil {
return core.Result{Value: core.E(action, "request failed", err), OK: false}
}
defer response.Body.Close()
if response.StatusCode >= 400 {
readResult := core.ReadAll(response.Body)
if !readResult.OK {
return core.Result{Value: core.E(action, core.Sprintf("HTTP %d", response.StatusCode), nil), OK: false}
}
body := core.Trim(readResult.Value.(string))
if body == "" {
return core.Result{Value: core.E(action, core.Sprintf("HTTP %d", response.StatusCode), nil), OK: false}
}
return core.Result{Value: platformResultError(action, core.Result{Value: body, OK: false}), OK: false}
}
eventBody, readErr := readFleetEventBody(response.Body)
if readErr != nil {
return core.Result{Value: core.E(action, "failed to read event stream", readErr), OK: false}
}
payload := s.eventPayloadValue(eventBody)
if len(payload) == 0 {
return core.Result{Value: core.E(action, "no fleet event payload returned", nil), OK: false}
}
return core.Result{Value: payload, OK: true}
}
func platformResultError(action string, result core.Result) error {
if err, ok := result.Value.(error); ok && err != nil {
return core.E(action, "platform request failed", err)
}
body := core.Trim(stringValue(result.Value))
if body == "" {
return core.E(action, "platform request failed", nil)
}
var payload map[string]any
if parseResult := core.JSONUnmarshalString(body, &payload); parseResult.OK {
if message := stringValue(payload["error"]); message != "" {
return core.E(action, message, nil)
}
}
return core.E(action, body, nil)
}
func payloadDataMap(payload map[string]any) map[string]any {
return anyMapValue(payload["data"])
}
func payloadDataSlice(payload map[string]any, keys ...string) []map[string]any {
if values := anyMapSliceValue(payload["data"]); len(values) > 0 {
return values
}
if data := payloadDataMap(payload); len(data) > 0 {
for _, key := range keys {
if values := anyMapSliceValue(data[key]); len(values) > 0 {
return values
}
}
}
for _, key := range keys {
if values := anyMapSliceValue(payload[key]); len(values) > 0 {
return values
}
}
return nil
}
func payloadResourceMap(payload map[string]any, keys ...string) map[string]any {
if data := payloadDataMap(payload); len(data) > 0 {
for _, key := range keys {
if values := anyMapValue(data[key]); len(values) > 0 {
return values
}
}
return data
}
for _, key := range keys {
if values := anyMapValue(payload[key]); len(values) > 0 {
return values
}
}
for key, value := range payload {
switch key {
case "data", "error", "code", "message":
continue
}
if value != nil {
return payload
}
}
return nil
}
func mapIntValue(values map[string]any, keys ...string) int {
for _, key := range keys {
if value, ok := values[key]; ok {
return intValue(value)
}
}
return 0
}
func intValueOK(value any) (int, bool) {
switch typed := value.(type) {
case int:
return typed, true
case int64:
return int(typed), true
case float64:
return int(typed), true
case string:
trimmed := core.Trim(typed)
parsed := parseInt(trimmed)
if parsed != 0 || trimmed == "0" {
return parsed, true
}
}
return 0, false
}
func parseFleetNode(values map[string]any) FleetNode {
return FleetNode{
ID: intValue(values["id"]),
WorkspaceID: intValue(values["workspace_id"]),
AgentID: stringValue(values["agent_id"]),
Platform: stringValue(values["platform"]),
Models: listValue(values["models"]),
Capabilities: listValue(values["capabilities"]),
Status: stringValue(values["status"]),
ComputeBudget: computeBudgetFromValue(values["compute_budget"]),
CurrentTaskID: intPointerValue(values["current_task_id"]),
LastHeartbeatAt: stringValue(values["last_heartbeat_at"]),
RegisteredAt: stringValue(values["registered_at"]),
}
}
func intPointerValue(value any) *int {
number, ok := intValueOK(value)
if !ok {
return nil
}
return &number
}
func computeBudgetFromValue(value any) *ComputeBudget {
switch typed := value.(type) {
case *ComputeBudget:
if typed == nil || computeBudgetIsZero(*typed) {
return nil
}
return typed
case ComputeBudget:
if computeBudgetIsZero(typed) {
return nil
}
return &typed
case map[string]any:
return computeBudgetFromMap(typed)
case map[string]string:
values := make(map[string]any, len(typed))
for key, item := range typed {
values[key] = item
}
return computeBudgetFromMap(values)
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return nil
}
if core.HasPrefix(trimmed, "{") {
var values map[string]any
if result := core.JSONUnmarshalString(trimmed, &values); result.OK {
return computeBudgetFromMap(values)
}
}
}
return nil
}
func computeBudgetFromMap(values map[string]any) *ComputeBudget {
if len(values) == 0 {
return nil
}
budget := &ComputeBudget{
MaxDailyHours: floatValue(values["max_daily_hours"]),
MaxWeeklyCostUSD: floatValue(values["max_weekly_cost_usd"]),
QuietStart: stringValue(values["quiet_start"]),
QuietEnd: stringValue(values["quiet_end"]),
PreferModels: listValue(values["prefer_models"]),
AvoidModels: listValue(values["avoid_models"]),
}
if computeBudgetIsZero(*budget) {
return nil
}
return budget
}
func computeBudgetIsZero(budget ComputeBudget) bool {
return budget.MaxDailyHours == 0 &&
budget.MaxWeeklyCostUSD == 0 &&
core.Trim(budget.QuietStart) == "" &&
core.Trim(budget.QuietEnd) == "" &&
len(budget.PreferModels) == 0 &&
len(budget.AvoidModels) == 0
}
func parseFleetTask(values map[string]any) FleetTask {
return FleetTask{
ID: intValue(values["id"]),
WorkspaceID: intValue(values["workspace_id"]),
FleetNodeID: intValue(values["fleet_node_id"]),
Repo: stringValue(values["repo"]),
Branch: stringValue(values["branch"]),
Task: stringValue(values["task"]),
Template: stringValue(values["template"]),
AgentModel: stringValue(values["agent_model"]),
Status: stringValue(values["status"]),
Result: anyMapValue(values["result"]),
Findings: anyMapSliceValue(values["findings"]),
Changes: anyMapValue(values["changes"]),
Report: anyMapValue(values["report"]),
StartedAt: stringValue(values["started_at"]),
CompletedAt: stringValue(values["completed_at"]),
}
}
func parseFleetNodesOutput(payload map[string]any) FleetNodesOutput {
nodesData := payloadDataSlice(payload, "nodes")
nodes := make([]FleetNode, 0, len(nodesData))
for _, values := range nodesData {
nodes = append(nodes, parseFleetNode(values))
}
total := mapIntValue(payload, "total", "count")
if total == 0 {
total = mapIntValue(payloadDataMap(payload), "total", "count")
}
if total == 0 {
total = len(nodes)
}
return FleetNodesOutput{
Total: total,
Nodes: nodes,
}
}
func parseFleetStats(values map[string]any) FleetStats {
return FleetStats{
NodesOnline: intValue(values["nodes_online"]),
TasksToday: intValue(values["tasks_today"]),
TasksWeek: intValue(values["tasks_week"]),
ReposTouched: intValue(values["repos_touched"]),
FindingsTotal: intValue(values["findings_total"]),
ComputeHours: intValue(values["compute_hours"]),
}
}
func parseFleetEventOutput(values map[string]any) (FleetEventOutput, error) {
eventValues := payloadResourceMap(values, "event")
if len(eventValues) == 0 {
eventValues = values
}
event := parseFleetEvent(eventValues)
if event.Event == "" && event.Type == "" && event.AgentID == "" && event.Repo == "" {
return FleetEventOutput{}, core.E("parseFleetEventOutput", "fleet event payload is empty", nil)
}
return FleetEventOutput{
Success: true,
Event: event,
Raw: core.Trim(stringValue(values["raw"])),
}, nil
}
func (s *PrepSubsystem) eventPayloadValue(body string) map[string]any {
trimmed := core.Trim(body)
if trimmed == "" {
return nil
}
payload := map[string]any{
"raw": trimmed,
}
lines := core.Split(trimmed, "\n")
dataLines := make([]string, 0, len(lines))
for _, line := range lines {
line = core.Trim(line)
if line == "" {
continue
}
switch {
case core.HasPrefix(line, "event:"):
eventName := core.Trim(core.TrimPrefix(line, "event:"))
if eventName != "" {
payload["event"] = eventName
payload["type"] = eventName
}
case core.HasPrefix(line, "data:"):
dataLines = append(dataLines, core.Trim(core.TrimPrefix(line, "data:")))
}
}
dataBody := core.Join("\n", dataLines...)
if dataBody != "" {
var dataPayload map[string]any
if parseResult := core.JSONUnmarshalString(dataBody, &dataPayload); parseResult.OK {
for key, value := range dataPayload {
payload[key] = value
}
return payload
}
payload["data"] = dataBody
}
return payload
}
// readFleetEventBody reads an SSE-style event body up to the first blank line.
// Uses core.ReadAll instead of bufio+io.EOF for AX compliance.
//
// body, err := readFleetEventBody(response.Body)
func readFleetEventBody(body any) (string, error) {
r := core.ReadAll(body)
if !r.OK {
if err, ok := r.Value.(error); ok {
return "", err
}
return "", core.E("readFleetEventBody", "failed to read body", nil)
}
content := r.Value.(string)
if core.Trim(content) == "" {
return "", nil
}
// SSE event: content up to the first blank line.
rawLines := make([]string, 0, 4)
for _, line := range core.Split(content, "\n") {
trimmed := core.Trim(line)
if trimmed == "" {
if len(rawLines) > 0 {
return core.Join("\n", rawLines...), nil
}
continue
}
rawLines = append(rawLines, trimmed)
}
if len(rawLines) > 0 {
return core.Join("\n", rawLines...), nil
}
return "", nil
}
func parseFleetEvent(values map[string]any) FleetEvent {
payload := map[string]any{}
for key, value := range values {
switch key {
case "type", "event", "agent_id", "task_id", "repo", "branch", "status", "received_at":
continue
default:
payload[key] = value
}
}
if len(payload) == 0 {
payload = nil
}
event := FleetEvent{
Type: stringValue(values["type"]),
Event: stringValue(values["event"]),
AgentID: stringValue(values["agent_id"]),
TaskID: intValue(values["task_id"]),
Repo: stringValue(values["repo"]),
Branch: stringValue(values["branch"]),
Status: stringValue(values["status"]),
ReceivedAt: stringValue(values["received_at"]),
Payload: payload,
}
if event.Event == "" {
event.Event = event.Type
}
if event.Type == "" {
event.Type = event.Event
}
return event
}
func fleetEventOutputFromTask(agentID string, task *FleetTask) FleetEventOutput {
payload := map[string]any{
"task_id": task.ID,
"repo": task.Repo,
"branch": task.Branch,
"task": task.Task,
"template": task.Template,
"agent_model": task.AgentModel,
"status": task.Status,
"source": "polling",
"fleet_node_id": task.FleetNodeID,
}
return FleetEventOutput{
Success: true,
Event: FleetEvent{
Type: "task.assigned",
Event: "task.assigned",
AgentID: agentID,
TaskID: task.ID,
Repo: task.Repo,
Branch: task.Branch,
Status: "assigned",
Payload: payload,
},
Raw: "polling fallback",
}
}
func parseCreditEntry(values map[string]any) CreditEntry {
return CreditEntry{
ID: intValue(values["id"]),
WorkspaceID: intValue(values["workspace_id"]),
FleetNodeID: intValue(values["fleet_node_id"]),
TaskType: stringValue(values["task_type"]),
Amount: intValue(values["amount"]),
BalanceAfter: intValue(values["balance_after"]),
Description: stringValue(values["description"]),
CreatedAt: stringValue(values["created_at"]),
}
}
func parseCreditBalance(values map[string]any) CreditBalance {
return CreditBalance{
AgentID: stringValue(values["agent_id"]),
Balance: intValue(values["balance"]),
Entries: intValue(values["entries"]),
}
}
func parseCreditsHistoryOutput(payload map[string]any) CreditsHistoryOutput {
entriesData := payloadDataSlice(payload, "entries", "history")
entries := make([]CreditEntry, 0, len(entriesData))
for _, values := range entriesData {
entries = append(entries, parseCreditEntry(values))
}
total := mapIntValue(payload, "total", "count")
if total == 0 {
total = mapIntValue(payloadDataMap(payload), "total", "count")
}
if total == 0 {
total = len(entries)
}
return CreditsHistoryOutput{
Total: total,
Entries: entries,
}
}
func parseSubscriptionCapabilities(values map[string]any) SubscriptionCapabilities {
capabilities := SubscriptionCapabilities{
Providers: boolMapValue(values["providers"]),
Available: listValue(values["available"]),
}
if len(capabilities.Available) == 0 && len(capabilities.Providers) > 0 {
for name, enabled := range capabilities.Providers {
if enabled {
capabilities.Available = append(capabilities.Available, name)
}
}
capabilities.Available = cleanStrings(capabilities.Available)
}
return capabilities
}
func appendQueryParam(path, key, value string) string {
value = core.Trim(value)
if value == "" {
return path
}
separator := "?"
if core.Contains(path, "?") {
separator = "&"
}
return core.Concat(path, separator, key, "=", value)
}
func appendQuerySlice(path, key string, values []string) string {
for _, value := range values {
path = appendQueryParam(path, key, value)
}
return path
}
func optionAnyMapValue(options core.Options, keys ...string) map[string]any {
for _, key := range keys {
result := options.Get(key)
if !result.OK {
continue
}
values := anyMapValue(result.Value)
if len(values) > 0 {
return values
}
}
return nil
}
func optionAnyMapSliceValue(options core.Options, keys ...string) []map[string]any {
for _, key := range keys {
result := options.Get(key)
if !result.OK {
continue
}
values := anyMapSliceValue(result.Value)
if len(values) > 0 {
return values
}
}
return nil
}
func anyMapValue(value any) map[string]any {
switch typed := value.(type) {
case map[string]any:
return typed
case map[string]string:
values := make(map[string]any, len(typed))
for key, item := range typed {
values[key] = item
}
return values
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return nil
}
if core.HasPrefix(trimmed, "{") {
var values map[string]any
if result := core.JSONUnmarshalString(trimmed, &values); result.OK {
return values
}
var stringValues map[string]string
if result := core.JSONUnmarshalString(trimmed, &stringValues); result.OK {
return anyMapValue(stringValues)
}
}
values := stringMapValue(trimmed)
if len(values) > 0 {
return anyMapValue(values)
}
}
return nil
}
func anyMapSliceValue(value any) []map[string]any {
switch typed := value.(type) {
case []map[string]any:
return typed
case []any:
values := make([]map[string]any, 0, len(typed))
for _, item := range typed {
if mapValue := anyMapValue(item); len(mapValue) > 0 {
values = append(values, mapValue)
}
}
return values
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return nil
}
if core.HasPrefix(trimmed, "[") {
var values []map[string]any
if result := core.JSONUnmarshalString(trimmed, &values); result.OK {
return values
}
var generic []any
if result := core.JSONUnmarshalString(trimmed, &generic); result.OK {
return anyMapSliceValue(generic)
}
}
}
return nil
}
func boolMapValue(value any) map[string]bool {
switch typed := value.(type) {
case map[string]bool:
return typed
case map[string]any:
values := make(map[string]bool, len(typed))
for key, item := range typed {
switch resolved := item.(type) {
case bool:
values[key] = resolved
case string:
values[key] = core.Lower(core.Trim(resolved)) == "true"
default:
values[key] = intValue(resolved) > 0
}
}
return values
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return nil
}
if core.HasPrefix(trimmed, "{") {
var values map[string]bool
if result := core.JSONUnmarshalString(trimmed, &values); result.OK {
return values
}
var generic map[string]any
if result := core.JSONUnmarshalString(trimmed, &generic); result.OK {
return boolMapValue(generic)
}
}
}
return nil
}
func listValue(value any) []string {
switch typed := value.(type) {
case map[string]any:
values := make([]string, 0, len(typed))
for key, item := range typed {
if item == true || core.Trim(stringValue(item)) != "" {
values = append(values, key)
}
}
return cleanStrings(values)
default:
return stringSliceValue(value)
}
}
func intValue(value any) int {
switch typed := value.(type) {
case int:
return typed
case int64:
return int(typed)
case float64:
return int(typed)
case string:
parsed := parseInt(typed)
if parsed != 0 || core.Trim(typed) == "0" {
return parsed
}
}
return 0
}
func floatValue(value any) float64 {
switch typed := value.(type) {
case float64:
return typed
case float32:
return float64(typed)
case int:
return float64(typed)
case int64:
return float64(typed)
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return 0
}
var parsed float64
if result := core.JSONUnmarshalString(core.Concat("{\"n\":", trimmed, "}"), &struct {
N *float64 `json:"n"`
}{N: &parsed}); result.OK {
return parsed
}
}
return 0
}