autonomy notify: enforce notify allowlist from config and add guard test

This commit is contained in:
DBT
2026-02-28 13:01:54 +00:00
parent 19e7e550ca
commit 6705a2b4e8
5 changed files with 41 additions and 3 deletions

View File

@@ -958,5 +958,6 @@ func buildAutonomyEngine(cfg *config.Config, msgBus *bus.MessageBus) *autonomy.E
Workspace: cfg.WorkspacePath(),
DefaultNotifyChannel: a.NotifyChannel,
DefaultNotifyChatID: a.NotifyChatID,
NotifyAllowChats: a.NotifyAllowChats,
}, msgBus)
}

View File

@@ -26,7 +26,8 @@
"user_idle_resume_sec": 20,
"waiting_resume_debounce_sec": 5,
"notify_channel": "",
"notify_chat_id": ""
"notify_chat_id": "",
"notify_allow_chats": []
},
"texts": {
"no_response_fallback": "I've completed processing but have no response to give.",

View File

@@ -29,6 +29,7 @@ type Options struct {
Workspace string
DefaultNotifyChannel string
DefaultNotifyChatID string
NotifyAllowChats []string
NotifyCooldownSec int
NotifySameReasonCooldownSec int
QuietHours string
@@ -636,6 +637,18 @@ func (e *Engine) shouldNotify(key string, reason string) bool {
if strings.TrimSpace(e.opts.DefaultNotifyChannel) == "" || strings.TrimSpace(e.opts.DefaultNotifyChatID) == "" {
return false
}
if len(e.opts.NotifyAllowChats) > 0 {
allowed := false
for _, c := range e.opts.NotifyAllowChats {
if c == e.opts.DefaultNotifyChatID {
allowed = true
break
}
}
if !allowed {
return false
}
}
now := time.Now()
if inQuietHours(now, e.opts.QuietHours) {
return false

View File

@@ -0,0 +1,21 @@
package autonomy
import "testing"
func TestShouldNotify_RespectsNotifyAllowChats(t *testing.T) {
e := &Engine{opts: Options{
DefaultNotifyChannel: "telegram",
DefaultNotifyChatID: "chat-1",
NotifyAllowChats: []string{"chat-2", "chat-3"},
NotifyCooldownSec: 1,
NotifySameReasonCooldownSec: 1,
}}
if e.shouldNotify("k1", "") {
t.Fatalf("expected notify to be blocked when chat not in allowlist")
}
e.opts.NotifyAllowChats = []string{"chat-1"}
if !e.shouldNotify("k2", "") {
t.Fatalf("expected notify to pass when chat in allowlist")
}
}

View File

@@ -58,8 +58,9 @@ type AutonomyConfig struct {
MaxRoundsWithoutUser int `json:"max_rounds_without_user" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_ROUNDS_WITHOUT_USER"`
WaitingResumeDebounceSec int `json:"waiting_resume_debounce_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_WAITING_RESUME_DEBOUNCE_SEC"`
AllowedTaskKeywords []string `json:"allowed_task_keywords" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_ALLOWED_TASK_KEYWORDS"`
NotifyChannel string `json:"notify_channel" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_CHANNEL"`
NotifyChatID string `json:"notify_chat_id" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_CHAT_ID"`
NotifyChannel string `json:"notify_channel" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_CHANNEL"`
NotifyChatID string `json:"notify_chat_id" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_CHAT_ID"`
NotifyAllowChats []string `json:"notify_allow_chats" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_ALLOW_CHATS"`
}
type AgentTextConfig struct {
@@ -330,6 +331,7 @@ func DefaultConfig() *Config {
AllowedTaskKeywords: []string{},
NotifyChannel: "",
NotifyChatID: "",
NotifyAllowChats: []string{},
},
Texts: AgentTextConfig{
NoResponseFallback: "I've completed processing but have no response to give.",