// SPDX-License-Identifier: EUPL-1.2 package agentic import ( "context" core "dappco.re/go/core" ) // node := agentic.FleetNode{AgentID: "charon", Platform: "linux", Status: "online"} type FleetNode struct { ID int `json:"id"` AgentID string `json:"agent_id"` Platform string `json:"platform"` Models []string `json:"models,omitempty"` Capabilities []string `json:"capabilities,omitempty"` Status string `json:"status"` ComputeBudget map[string]any `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"` 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"` } // 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"` 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()), } 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 := payloadDataMap(result.Value.(map[string]any)) if len(data) == 0 { return core.Result{Value: output, OK: true} } output.Status = stringValue(data["status"]) output.LastPushAt = stringValue(data["last_push_at"]) output.LastPullAt = stringValue(data["last_pull_at"]) 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(payloadDataMap(result.Value.(map[string]any))), 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(payloadDataMap(result.Value.(map[string]any))), 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(payloadDataMap(result.Value.(map[string]any))), 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 } return core.Result{Value: parseFleetTask(payloadDataMap(result.Value.(map[string]any))), 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 := payloadDataMap(result.Value.(map[string]any)) 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(payloadDataMap(result.Value.(map[string]any))), 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(payloadDataMap(result.Value.(map[string]any))), 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(payloadDataMap(result.Value.(map[string]any))), 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(payloadDataMap(result.Value.(map[string]any))), 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: payloadDataMap(result.Value.(map[string]any)), 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: payloadDataMap(result.Value.(map[string]any)), 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 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) []map[string]any { return anyMapSliceValue(payload["data"]) } func parseFleetNode(values map[string]any) FleetNode { return FleetNode{ ID: intValue(values["id"]), AgentID: stringValue(values["agent_id"]), Platform: stringValue(values["platform"]), Models: listValue(values["models"]), Capabilities: listValue(values["capabilities"]), Status: stringValue(values["status"]), ComputeBudget: anyMapValue(values["compute_budget"]), CurrentTaskID: intValue(values["current_task_id"]), LastHeartbeatAt: stringValue(values["last_heartbeat_at"]), RegisteredAt: stringValue(values["registered_at"]), } } func parseFleetTask(values map[string]any) FleetTask { return FleetTask{ ID: intValue(values["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 := make([]FleetNode, 0, len(nodesData)) for _, values := range nodesData { nodes = append(nodes, parseFleetNode(values)) } total := intValue(payload["total"]) 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 parseCreditEntry(values map[string]any) CreditEntry { return CreditEntry{ ID: intValue(values["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 := make([]CreditEntry, 0, len(entriesData)) for _, values := range entriesData { entries = append(entries, parseCreditEntry(values)) } total := intValue(payload["total"]) if total == 0 { total = len(entries) } return CreditsHistoryOutput{ Total: total, Entries: entries, } } func parseSubscriptionCapabilities(values map[string]any) SubscriptionCapabilities { return SubscriptionCapabilities{ Providers: boolMapValue(values["providers"]), Available: listValue(values["available"]), } } 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 strings map[string]string if result := core.JSONUnmarshalString(trimmed, &strings); result.OK { return anyMapValue(strings) } } 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 }