Skip to content

Commit a0f1495

Browse files
committed
feat: add set_reminder tool for scheduling inbox notifications
New set_reminder tool lets the agent schedule reminders that fire via inbox.Append() after a specified delay (Go duration syntax: 30m, 1h, etc.). - internal/tools/reminder.go: SetReminderTool implementation using time.AfterFunc - internal/tools/tool.go: add NewSetReminderTool() to Defaults() - internal/web/chat.go: add set_reminder to system prompt, mightNeedTools keywords (remind/timer/alarm/提醒/定时), and shouldRetryWithTools list Previously the agent refused reminder requests because no scheduling tool existed; now it can call set_reminder directly.
1 parent c190a53 commit a0f1495

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

internal/tools/reminder.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package tools
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"strings"
8+
"time"
9+
10+
"github.com/clawplaza/clawwork-cli/internal/inbox"
11+
)
12+
13+
// SetReminderTool schedules a one-shot reminder that fires via the inbox.
14+
// The reminder is in-memory: if the CLI exits before it fires it is lost.
15+
type setReminderTool struct{}
16+
17+
func NewSetReminderTool() Tool { return &setReminderTool{} }
18+
19+
func (t *setReminderTool) Def() ToolDef {
20+
return ToolDef{
21+
Name: "set_reminder",
22+
Description: "Schedule a one-shot reminder message that will appear in your inbox after a given delay. " +
23+
"Use this when the owner asks you to remind them of something in the future (e.g. \"remind me in 30 minutes to check the gig\"). " +
24+
"The reminder fires inside the running CLI process; it will be lost if the CLI is stopped before the delay elapses.",
25+
Parameters: ToolParameters{
26+
Type: "object",
27+
Properties: map[string]ToolProperty{
28+
"message": {
29+
Type: "string",
30+
Description: "The reminder text to deliver (what to remind the owner about).",
31+
},
32+
"delay": {
33+
Type: "string",
34+
Description: "How long to wait before firing. Uses Go duration syntax: " +
35+
"e.g. \"30m\" (30 minutes), \"1h\" (1 hour), \"2h30m\", \"90s\". Max 24h.",
36+
},
37+
},
38+
Required: []string{"message", "delay"},
39+
},
40+
}
41+
}
42+
43+
func (t *setReminderTool) Call(_ context.Context, argsJSON string) string {
44+
var args struct {
45+
Message string `json:"message"`
46+
Delay string `json:"delay"`
47+
}
48+
if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
49+
return "error: invalid arguments: " + err.Error()
50+
}
51+
args.Message = strings.TrimSpace(args.Message)
52+
args.Delay = strings.TrimSpace(args.Delay)
53+
if args.Message == "" {
54+
return "error: message is required"
55+
}
56+
if args.Delay == "" {
57+
return "error: delay is required"
58+
}
59+
60+
d, err := time.ParseDuration(args.Delay)
61+
if err != nil {
62+
return fmt.Sprintf("error: invalid delay %q — use Go duration syntax like \"30m\", \"1h\", \"2h30m\": %s", args.Delay, err)
63+
}
64+
if d <= 0 {
65+
return "error: delay must be positive"
66+
}
67+
if d > 24*time.Hour {
68+
return "error: delay cannot exceed 24h"
69+
}
70+
71+
msg := args.Message
72+
time.AfterFunc(d, func() {
73+
_ = inbox.Append("⏰ Reminder", msg, inbox.TagSystem)
74+
})
75+
76+
fireAt := time.Now().Add(d).Format("15:04:05")
77+
return fmt.Sprintf("Reminder set. Will appear in your inbox at %s (in %s).", fireAt, args.Delay)
78+
}

internal/tools/tool.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ type ChatToolProvider interface {
6565
// Defaults returns the base built-in tools (no market client).
6666
func Defaults() []Tool {
6767
return []Tool{
68-
NewShellExecTool(), // shell: curl/wget/git/grep/jq/etc.
69-
NewHTTPFetchTool(), // native HTTP GET/POST (no shell required)
70-
NewRunScriptTool(), // execute Python or JavaScript
71-
NewFilesystemTool(), // read/write/list/mkdir/move/delete/info
68+
NewShellExecTool(), // shell: curl/wget/git/grep/jq/etc.
69+
NewHTTPFetchTool(), // native HTTP GET/POST (no shell required)
70+
NewRunScriptTool(), // execute Python or JavaScript
71+
NewFilesystemTool(), // read/write/list/mkdir/move/delete/info
72+
NewSetReminderTool(), // schedule a reminder that fires via inbox
7273
}
7374
}

internal/web/chat.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,9 @@ func cleanReply(reply string) string {
639639
// mightNeedTools returns true if the message likely requires a tool call.
640640
// Simple conversational messages skip the agentic path to save ~300 tokens.
641641
var toolKeywords = []string{
642+
// reminders / scheduling
643+
"remind", "reminder", "timer", "alarm", "schedule", "notify", "alert", "in 30", "in 1h", "in an hour",
644+
"提醒", "定时", "闹钟", "计时", "通知",
642645
// market IDs, URLs, and intent
643646
"gig_", "sk_", "/gig/", "/skill/", "clawplaza.ai/gig", "clawplaza.ai/skill",
644647
"localhost:5173/gig", "localhost:5173/skill",
@@ -697,7 +700,7 @@ func shouldRetryWithTools(_ string, reply string) bool {
697700
"social_dm_send", "social_messages_list", "social_messages_read", "social_moments_read",
698701
"social_like_moment", "social_connections",
699702
"cw_balance", "cw_transfer", "cw_history",
700-
"report_issue",
703+
"report_issue", "set_reminder",
701704
}
702705
for _, t := range toolMentions {
703706
if strings.Contains(lower, t) {
@@ -804,7 +807,8 @@ func ChatSystemPrompt(soul string) string {
804807
sb.WriteString("- cw_balance: Check your CW balance (both activated and unactivated), staked amount, and transfer allowance.\n")
805808
sb.WriteString("- cw_transfer: Transfer CW to another agent (requires owner-granted allowance).\n")
806809
sb.WriteString("- cw_history: View your CW transaction history.\n")
807-
sb.WriteString("- report_issue: Submit a bug report, question, or suggestion to the platform.\n\n")
810+
sb.WriteString("- report_issue: Submit a bug report, question, or suggestion to the platform.\n")
811+
sb.WriteString("- set_reminder: Schedule a one-shot reminder that fires in your inbox after a delay (e.g. \"30m\", \"1h\"). Use when the owner asks to be reminded about something later.\n\n")
808812
sb.WriteString("Always use these tools directly. Never ask the owner to perform platform actions manually.\n")
809813
sb.WriteString("IMPORTANT: Your API key is already embedded in this CLI process and injected automatically into every tool call.\n")
810814
sb.WriteString("You MUST NEVER ask the owner for an API key, credentials, or tokens — you already have them. Just call the tool.\n\n")

0 commit comments

Comments
 (0)