From 36d72a0112bc8ebaeef1198b8cafc64c726fdbac Mon Sep 17 00:00:00 2001
From: Alix-007
Date: Fri, 26 Jun 2026 16:11:54 +0800
Subject: [PATCH] fix(gateway): guard startup info assertions
---
pkg/gateway/gateway.go | 63 +++++++++++++++++++++-----
pkg/gateway/gateway_test.go | 88 +++++++++++++++++++++++++++++++++++++
2 files changed, 140 insertions(+), 11 deletions(-)
diff --git a/pkg/gateway/gateway.go b/pkg/gateway/gateway.go
index c95f586d..ec519e45 100644
--- a/pkg/gateway/gateway.go
+++ b/pkg/gateway/gateway.go
@@ -205,18 +205,11 @@ func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) (runEr
publishGatewayEvent(agentLoop, runtimeevents.KindGatewayStart, startedAt, nil)
fmt.Println("\n📦 Agent Status:")
- startupInfo := agentLoop.GetStartupInfo()
- toolsInfo := startupInfo["tools"].(map[string]any)
- skillsInfo := startupInfo["skills"].(map[string]any)
- fmt.Printf(" • Tools: %d loaded\n", toolsInfo["count"])
- fmt.Printf(" • Skills: %d/%d available\n", skillsInfo["available"], skillsInfo["total"])
+ startupStatus := collectGatewayStartupStatus(agentLoop.GetStartupInfo())
+ fmt.Printf(" • Tools: %d loaded\n", startupStatus.toolsCount)
+ fmt.Printf(" • Skills: %d/%d available\n", startupStatus.skillsAvailable, startupStatus.skillsTotal)
- logger.InfoCF("agent", "Agent initialized",
- map[string]any{
- "tools_count": toolsInfo["count"],
- "skills_total": skillsInfo["total"],
- "skills_available": skillsInfo["available"],
- })
+ logger.InfoCF("agent", "Agent initialized", startupStatus.logFields)
runningServices, err := setupAndStartServices(cfg, agentLoop, msgBus, pidData.Token, listenResult)
if err != nil {
@@ -315,6 +308,54 @@ func preCheckConfig(cfg *config.Config) error {
return nil
}
+type gatewayStartupStatus struct {
+ toolsCount int
+ skillsAvailable int
+ skillsTotal int
+ logFields map[string]any
+}
+
+func collectGatewayStartupStatus(startupInfo map[string]any) gatewayStartupStatus {
+ status := gatewayStartupStatus{logFields: map[string]any{}}
+
+ if toolsInfo, ok := startupInfo["tools"].(map[string]any); ok {
+ if count, ok := startupInfoInt(toolsInfo["count"]); ok {
+ status.toolsCount = count
+ status.logFields["tools_count"] = count
+ }
+ }
+
+ if skillsInfo, ok := startupInfo["skills"].(map[string]any); ok {
+ if total, ok := startupInfoInt(skillsInfo["total"]); ok {
+ status.skillsTotal = total
+ status.logFields["skills_total"] = total
+ }
+ if available, ok := startupInfoInt(skillsInfo["available"]); ok {
+ status.skillsAvailable = available
+ status.logFields["skills_available"] = available
+ }
+ }
+
+ return status
+}
+
+func startupInfoInt(value any) (int, bool) {
+ switch v := value.(type) {
+ case int:
+ return v, true
+ case int32:
+ return int(v), true
+ case int64:
+ return int(v), true
+ case float32:
+ return int(v), true
+ case float64:
+ return int(v), true
+ default:
+ return 0, false
+ }
+}
+
func executeReload(
ctx context.Context,
agentLoop *agent.AgentLoop,
diff --git a/pkg/gateway/gateway_test.go b/pkg/gateway/gateway_test.go
index ab3833ba..2eb8a25d 100644
--- a/pkg/gateway/gateway_test.go
+++ b/pkg/gateway/gateway_test.go
@@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "reflect"
"strings"
"testing"
"time"
@@ -113,6 +114,93 @@ func TestGatewayRunStartupFailureHelper(t *testing.T) {
os.Exit(0)
}
+func TestCollectGatewayStartupStatusHandlesMalformedInfo(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ startupInfo map[string]any
+ wantToolsCount int
+ wantSkillsAvailable int
+ wantSkillsTotal int
+ wantLogFields map[string]any
+ }{
+ {
+ name: "missing info",
+ startupInfo: map[string]any{},
+ wantLogFields: map[string]any{},
+ },
+ {
+ name: "wrong map shapes",
+ startupInfo: map[string]any{
+ "tools": "unexpected",
+ "skills": []any{"unexpected"},
+ },
+ wantLogFields: map[string]any{},
+ },
+ {
+ name: "valid startup info",
+ startupInfo: map[string]any{
+ "tools": map[string]any{
+ "count": 3,
+ },
+ "skills": map[string]any{
+ "available": 2,
+ "total": 5,
+ },
+ },
+ wantToolsCount: 3,
+ wantSkillsAvailable: 2,
+ wantSkillsTotal: 5,
+ wantLogFields: map[string]any{
+ "tools_count": 3,
+ "skills_available": 2,
+ "skills_total": 5,
+ },
+ },
+ {
+ name: "json number startup info",
+ startupInfo: map[string]any{
+ "tools": map[string]any{
+ "count": float64(4),
+ },
+ "skills": map[string]any{
+ "available": float64(1),
+ "total": float64(6),
+ },
+ },
+ wantToolsCount: 4,
+ wantSkillsAvailable: 1,
+ wantSkillsTotal: 6,
+ wantLogFields: map[string]any{
+ "tools_count": 4,
+ "skills_available": 1,
+ "skills_total": 6,
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ got := collectGatewayStartupStatus(tt.startupInfo)
+ if got.toolsCount != tt.wantToolsCount {
+ t.Fatalf("toolsCount = %d, want %d", got.toolsCount, tt.wantToolsCount)
+ }
+ if got.skillsAvailable != tt.wantSkillsAvailable {
+ t.Fatalf("skillsAvailable = %d, want %d", got.skillsAvailable, tt.wantSkillsAvailable)
+ }
+ if got.skillsTotal != tt.wantSkillsTotal {
+ t.Fatalf("skillsTotal = %d, want %d", got.skillsTotal, tt.wantSkillsTotal)
+ }
+ if !reflect.DeepEqual(got.logFields, tt.wantLogFields) {
+ t.Fatalf("logFields = %#v, want %#v", got.logFields, tt.wantLogFields)
+ }
+ })
+ }
+}
+
func TestPublishGatewayEvent(t *testing.T) {
eventBus := runtimeevents.NewBus()
t.Cleanup(func() {