From e353844dfbf8dc33aa3ce7a88d692e1a46d11b8c Mon Sep 17 00:00:00 2001 From: yinwm Date: Fri, 13 Feb 2026 02:09:49 +0800 Subject: [PATCH] feat: re-enable cronTool service after refactor completion Re-enable cronTool service integration after completing the ToolResult refactor (US-016). Removed all temporary disable comments and restored full cron service lifecycle including start/stop operations. Additional improvements: - Add thread-safe access to onHeartbeatWithTools handler - Fix channel parsing to handle user IDs with special characters - Add error handling for state file loading failures --- cmd/picoclaw/main.go | 22 ++++++++-------------- pkg/heartbeat/service.go | 18 ++++++++++++------ pkg/state/state.go | 6 +++++- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/cmd/picoclaw/main.go b/cmd/picoclaw/main.go index a6ae6cd..d83597f 100644 --- a/cmd/picoclaw/main.go +++ b/cmd/picoclaw/main.go @@ -30,8 +30,7 @@ import ( "github.com/sipeed/picoclaw/pkg/migrate" "github.com/sipeed/picoclaw/pkg/providers" "github.com/sipeed/picoclaw/pkg/skills" - // TEMPORARILY DISABLED - cronTool is being refactored to use ToolResult (US-016) - toolsPkg "github.com/sipeed/picoclaw/pkg/tools" // nolint: unused + "github.com/sipeed/picoclaw/pkg/tools" "github.com/sipeed/picoclaw/pkg/voice" ) @@ -39,8 +38,6 @@ var ( version = "0.1.0" buildTime string goVersion string - // TEMPORARILY DISABLED - cronTool is being refactored to use ToolResult (US-016) - _ = toolsPkg.ErrorResult // nolint: unused ) const logo = "🦞" @@ -653,8 +650,7 @@ func gatewayCmd() { }) // Setup cron tool and service - // TEMPORARILY DISABLED - cronTool is being refactored to use ToolResult (US-016) - // cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath()) + cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath()) heartbeatService := heartbeat.NewHeartbeatService( cfg.WorkspacePath(), @@ -709,11 +705,10 @@ func gatewayCmd() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // TEMPORARILY DISABLED - cronTool is being refactored to use ToolResult (US-016) - // if err := cronService.Start(); err != nil { - // fmt.Printf("Error starting cron service: %v\n", err) - // } - // fmt.Println("✓ Cron service started") + if err := cronService.Start(); err != nil { + fmt.Printf("Error starting cron service: %v\n", err) + } + fmt.Println("✓ Cron service started") if err := heartbeatService.Start(); err != nil { fmt.Printf("Error starting heartbeat service: %v\n", err) @@ -733,8 +728,7 @@ func gatewayCmd() { fmt.Println("\nShutting down...") cancel() heartbeatService.Stop() - // TEMPORARILY DISABLED - cronTool is being refactored to use ToolResult (US-016) - // cronService.Stop() + cronService.Stop() agentLoop.Stop() channelManager.StopAll(ctx) fmt.Println("✓ Gateway stopped") @@ -1040,7 +1034,7 @@ func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace cronService := cron.NewCronService(cronStorePath, nil) // Create and register CronTool - cronTool := toolsPkg.NewCronTool(cronService, agentLoop, msgBus, workspace) + cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace) agentLoop.RegisterTool(cronTool) // Set the onJob handler diff --git a/pkg/heartbeat/service.go b/pkg/heartbeat/service.go index 33ba313..912b938 100644 --- a/pkg/heartbeat/service.go +++ b/pkg/heartbeat/service.go @@ -11,6 +11,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "sync" "time" @@ -203,13 +204,17 @@ func (hs *HeartbeatService) ExecuteHeartbeatWithTools(prompt string) { // executeHeartbeatWithTools is the internal implementation of tool-supporting heartbeat. func (hs *HeartbeatService) executeHeartbeatWithTools(prompt string) { - // Check if handler is configured - if hs.onHeartbeatWithTools == nil { + // Check if handler is configured (thread-safe read) + hs.mu.RLock() + handler := hs.onHeartbeatWithTools + hs.mu.RUnlock() + + if handler == nil { hs.logError("onHeartbeatWithTools handler not configured") return } - result := hs.onHeartbeatWithTools(prompt) + result := handler(prompt) if result == nil { hs.logInfo("Heartbeat handler returned nil result") @@ -343,12 +348,13 @@ func (hs *HeartbeatService) sendResponse(response string) { } // Parse channel format: "platform:user_id" (e.g., "telegram:123456") - var platform, userID string - n, err := fmt.Sscanf(lastChannel, "%[^:]:%s", &platform, &userID) - if err != nil || n != 2 { + // Use SplitN to handle user IDs that may contain special characters + parts := strings.SplitN(lastChannel, ":", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { hs.logError("Invalid last channel format: %s", lastChannel) return } + platform, userID := parts[0], parts[1] // Send to channel ctx := context.Background() diff --git a/pkg/state/state.go b/pkg/state/state.go index 4c54a71..5c2cd98 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -3,6 +3,7 @@ package state import ( "encoding/json" "fmt" + "log" "os" "path/filepath" "sync" @@ -45,7 +46,10 @@ func NewManager(workspace string) *Manager { } // Load existing state if available - sm.load() + if err := sm.load(); err != nil { + // Log warning but continue with empty state + log.Printf("[WARN] state: failed to load state file, starting fresh: %v", err) + } return sm }