From ca8bed4a5809b6051ba9ec556eab7890bc8aea33 Mon Sep 17 00:00:00 2001 From: shinchan-zhai Date: Mon, 11 May 2026 16:43:36 +0800 Subject: [PATCH] fix(agent): add TargetRef nil guards and ensureHistory for robustness - Add nil checks for session.TargetRef in all four execute*Action handlers (Trader/Exchange/Model/Strategy) to prevent panic on corrupted sessions; bulk-delete and query actions are excluded - Add ensureHistory() helper and call it in runPlannedAgentWithContextMode to prevent nil panic when history is not initialized Co-Authored-By: Claude Opus 4.6 --- agent/agent.go | 6 +++++ agent/planner_runtime.go | 1 + agent/skill_execution_handlers.go | 40 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/agent/agent.go b/agent/agent.go index 9de0834a..6f4223d0 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -80,6 +80,12 @@ func New(tm *manager.TraderManager, st *store.Store, cfg *Config, logger *slog.L func (a *Agent) SetAIClient(c mcp.AIClient) { a.aiClient = c } +func (a *Agent) ensureHistory() { + if a.history == nil { + a.history = newChatHistory(100) + } +} + func (a *Agent) log() *slog.Logger { if a != nil && a.logger != nil { return a.logger diff --git a/agent/planner_runtime.go b/agent/planner_runtime.go index bc4dfbcb..95839e28 100644 --- a/agent/planner_runtime.go +++ b/agent/planner_runtime.go @@ -2551,6 +2551,7 @@ func (a *Agent) runPlannedAgentWithContextMode(ctx context.Context, storeUserID answer, _, err := a.driveActiveSession(ctx, storeUserID, userID, lang, text, session, onEvent) return answer, err } + a.ensureHistory() a.history.Add(userID, "user", text) if onEvent != nil { onEvent(StreamEventPlanning, a.planningStatusText(lang)) diff --git a/agent/skill_execution_handlers.go b/agent/skill_execution_handlers.go index f1fdda68..bed12c7b 100644 --- a/agent/skill_execution_handlers.go +++ b/agent/skill_execution_handlers.go @@ -1479,6 +1479,12 @@ func (a *Agent) executeTraderManagementAction(storeUserID string, userID int64, } return formatReadFastPathResponse(lang, "list_traders", a.toolListTraders(storeUserID)) case "start", "stop", "delete": + if session.TargetRef == nil && !(session.Action == "delete" && fieldValue(session, "bulk_scope") == "all") { + if lang == "zh" { + return "请先指定要操作的交易员。" + } + return "Please specify which trader to operate on." + } if fieldValue(session, skillDAGStepField) == "" { setSkillDAGStep(&session, "await_confirmation") } @@ -1894,6 +1900,17 @@ func (a *Agent) executeBulkTraderDelete(storeUserID string, userID int64, lang, } func (a *Agent) executeExchangeManagementAction(storeUserID string, userID int64, lang, text string, session skillSession) string { + switch session.Action { + case "query", "query_list", "create": + // These actions don't need a target — fall through. + default: + if session.TargetRef == nil { + if lang == "zh" { + return "请先指定要操作的交易所配置。" + } + return "Please specify which exchange config to operate on." + } + } switch session.Action { case "query_detail": if detail, ok := a.describeExchange(storeUserID, lang, session.TargetRef); ok { @@ -2069,6 +2086,17 @@ func (a *Agent) executeExchangeManagementAction(storeUserID string, userID int64 } func (a *Agent) executeModelManagementAction(storeUserID string, userID int64, lang, text string, session skillSession) string { + switch session.Action { + case "query", "query_list", "create": + // These actions don't need a target — fall through. + default: + if session.TargetRef == nil { + if lang == "zh" { + return "请先指定要操作的模型。" + } + return "Please specify which model to operate on." + } + } switch session.Action { case "query_detail": if detail, ok := a.describeModel(storeUserID, lang, session.TargetRef); ok { @@ -2228,6 +2256,18 @@ func (a *Agent) executeModelManagementAction(storeUserID string, userID int64, l } func (a *Agent) executeStrategyManagementAction(storeUserID string, userID int64, lang, text string, session skillSession) string { + switch session.Action { + case "query", "query_list", "create": + // These actions don't need a target — fall through. + default: + isBulkDelete := session.Action == "delete" && fieldValue(session, "bulk_scope") == "all" + if session.TargetRef == nil && !isBulkDelete { + if lang == "zh" { + return "请先指定要操作的策略。" + } + return "Please specify which strategy to operate on." + } + } switch session.Action { case "query", "query_list": return formatReadFastPathResponse(lang, "get_strategies", a.toolGetStrategies(storeUserID))