fix: resolve code review issues in tool-result-refactor

1. Remove duplicate ToolResult definition in heartbeat package
   - Import tools.ToolResult instead of local definition
   - Add nil check for handler before execution

2. Fix SpawnTool to return AsyncResult and implement AsyncTool
   - Add callback field and SetCallback method
   - Return AsyncResult instead of NewToolResult

3. Add context cancellation support to SubagentManager
   - Check ctx.Done() before and during task execution
   - Set task status to "cancelled" on cancellation
   - Call callback with result on completion

4. Fix data race window in CronTool.addJob
   - Use Lock instead of RLock for channel/chatID access
   - Ensure consistent snapshot during job creation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yinwm
2026-02-13 01:59:50 +08:00
parent e7e086155e
commit 474f3dbf90
5 changed files with 73 additions and 28 deletions

View File

@@ -5,6 +5,8 @@ import (
"path/filepath"
"testing"
"time"
"github.com/sipeed/picoclaw/pkg/tools"
)
func TestExecuteHeartbeatWithTools_Async(t *testing.T) {
@@ -23,7 +25,7 @@ func TestExecuteHeartbeatWithTools_Async(t *testing.T) {
// Track if async handler was called
asyncCalled := false
asyncResult := &ToolResult{
asyncResult := &tools.ToolResult{
ForLLM: "Background task started",
ForUser: "Task started in background",
Silent: false,
@@ -31,7 +33,7 @@ func TestExecuteHeartbeatWithTools_Async(t *testing.T) {
Async: true,
}
hs.SetOnHeartbeatWithTools(func(prompt string) *ToolResult {
hs.SetOnHeartbeatWithTools(func(prompt string) *tools.ToolResult {
asyncCalled = true
if prompt == "" {
t.Error("Expected non-empty prompt")
@@ -61,7 +63,7 @@ func TestExecuteHeartbeatWithTools_Error(t *testing.T) {
hs := NewHeartbeatService(tmpDir, nil, 30, true)
errorResult := &ToolResult{
errorResult := &tools.ToolResult{
ForLLM: "Heartbeat failed: connection error",
ForUser: "",
Silent: false,
@@ -69,7 +71,7 @@ func TestExecuteHeartbeatWithTools_Error(t *testing.T) {
Async: false,
}
hs.SetOnHeartbeatWithTools(func(prompt string) *ToolResult {
hs.SetOnHeartbeatWithTools(func(prompt string) *tools.ToolResult {
return errorResult
})
@@ -101,7 +103,7 @@ func TestExecuteHeartbeatWithTools_Sync(t *testing.T) {
hs := NewHeartbeatService(tmpDir, nil, 30, true)
syncResult := &ToolResult{
syncResult := &tools.ToolResult{
ForLLM: "Heartbeat completed successfully",
ForUser: "",
Silent: true,
@@ -109,7 +111,7 @@ func TestExecuteHeartbeatWithTools_Sync(t *testing.T) {
Async: false,
}
hs.SetOnHeartbeatWithTools(func(prompt string) *ToolResult {
hs.SetOnHeartbeatWithTools(func(prompt string) *tools.ToolResult {
return syncResult
})
@@ -185,7 +187,7 @@ func TestExecuteHeartbeatWithTools_NilResult(t *testing.T) {
hs := NewHeartbeatService(tmpDir, nil, 30, true)
hs.SetOnHeartbeatWithTools(func(prompt string) *ToolResult {
hs.SetOnHeartbeatWithTools(func(prompt string) *tools.ToolResult {
return nil
})