Skip to content

Commit 9d4b5a8

Browse files
author
OpenClaw-User
committed
Merge PR sipeed#1402
2 parents ddc2e5c + 2b8d241 commit 9d4b5a8

2 files changed

Lines changed: 103 additions & 2 deletions

File tree

pkg/tools/cron.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,14 @@ func (t *CronTool) ExecuteJob(ctx context.Context, job *cron.CronJob) string {
341341
return fmt.Sprintf("Error: %v", err)
342342
}
343343

344-
// Response is automatically sent via MessageBus by AgentLoop
345-
_ = response // Will be sent by AgentLoop
344+
if strings.TrimSpace(response) != "" {
345+
pubCtx, pubCancel := context.WithTimeout(context.Background(), 5*time.Second)
346+
defer pubCancel()
347+
t.msgBus.PublishOutbound(pubCtx, bus.OutboundMessage{
348+
Channel: channel,
349+
ChatID: chatID,
350+
Content: response,
351+
})
352+
}
346353
return "ok"
347354
}

pkg/tools/cron_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ import (
1111
"github.com/sipeed/picoclaw/pkg/cron"
1212
)
1313

14+
type mockCronExecutor struct {
15+
response string
16+
err error
17+
calls int
18+
lastMsg string
19+
lastSess string
20+
lastChan string
21+
lastChat string
22+
}
23+
24+
func (m *mockCronExecutor) ProcessDirectWithChannel(
25+
ctx context.Context, content, sessionKey, channel, chatID string,
26+
) (string, error) {
27+
m.calls++
28+
m.lastMsg = content
29+
m.lastSess = sessionKey
30+
m.lastChan = channel
31+
m.lastChat = chatID
32+
return m.response, m.err
33+
}
34+
1435
func newTestCronTool(t *testing.T) *CronTool {
1536
t.Helper()
1637
storePath := filepath.Join(t.TempDir(), "cron.json")
@@ -114,3 +135,76 @@ func TestCronTool_NonCommandJobAllowedFromRemoteChannel(t *testing.T) {
114135
t.Fatalf("expected non-command reminder to succeed from remote channel, got: %s", result.ForLLM)
115136
}
116137
}
138+
139+
func TestCronTool_ExecuteJob_DeliverFalsePublishesAgentResponse(t *testing.T) {
140+
storePath := filepath.Join(t.TempDir(), "cron.json")
141+
cronService := cron.NewCronService(storePath, nil)
142+
msgBus := bus.NewMessageBus()
143+
executor := &mockCronExecutor{response: "agent reply"}
144+
cfg := config.DefaultConfig()
145+
tool, err := NewCronTool(cronService, executor, msgBus, t.TempDir(), true, 0, cfg)
146+
if err != nil {
147+
t.Fatalf("NewCronTool() error: %v", err)
148+
}
149+
150+
job := &cron.CronJob{
151+
ID: "job-1",
152+
Payload: cron.CronPayload{
153+
Message: "summarize status",
154+
Deliver: false,
155+
Channel: "telegram",
156+
To: "chat-42",
157+
},
158+
}
159+
160+
if got := tool.ExecuteJob(context.Background(), job); got != "ok" {
161+
t.Fatalf("ExecuteJob() = %q, want ok", got)
162+
}
163+
if executor.calls != 1 {
164+
t.Fatalf("executor calls = %d, want 1", executor.calls)
165+
}
166+
if executor.lastSess != "cron-job-1" {
167+
t.Fatalf("sessionKey = %q, want %q", executor.lastSess, "cron-job-1")
168+
}
169+
170+
msg, ok := msgBus.SubscribeOutbound(context.Background())
171+
if !ok {
172+
t.Fatal("expected outbound message")
173+
}
174+
if msg.Content != "agent reply" {
175+
t.Fatalf("outbound content = %q, want %q", msg.Content, "agent reply")
176+
}
177+
if msg.Channel != "telegram" || msg.ChatID != "chat-42" {
178+
t.Fatalf("unexpected destination: %+v", msg)
179+
}
180+
}
181+
182+
func TestCronTool_ExecuteJob_DeliverFalseSkipsEmptyAgentResponse(t *testing.T) {
183+
storePath := filepath.Join(t.TempDir(), "cron.json")
184+
cronService := cron.NewCronService(storePath, nil)
185+
msgBus := bus.NewMessageBus()
186+
executor := &mockCronExecutor{response: " "}
187+
cfg := config.DefaultConfig()
188+
tool, err := NewCronTool(cronService, executor, msgBus, t.TempDir(), true, 0, cfg)
189+
if err != nil {
190+
t.Fatalf("NewCronTool() error: %v", err)
191+
}
192+
193+
job := &cron.CronJob{
194+
ID: "job-2",
195+
Payload: cron.CronPayload{
196+
Message: "noop",
197+
Deliver: false,
198+
},
199+
}
200+
201+
if got := tool.ExecuteJob(context.Background(), job); got != "ok" {
202+
t.Fatalf("ExecuteJob() = %q, want ok", got)
203+
}
204+
205+
ctx, cancel := context.WithCancel(context.Background())
206+
cancel()
207+
if _, ok := msgBus.SubscribeOutbound(ctx); ok {
208+
t.Fatal("did not expect outbound message for empty response")
209+
}
210+
}

0 commit comments

Comments
 (0)