add autonomy waiting-resume debounce config and status visibility

This commit is contained in:
DBT
2026-02-24 03:51:14 +00:00
parent 357a826319
commit ee91d6e479
6 changed files with 39 additions and 24 deletions

View File

@@ -384,6 +384,9 @@ func summarizeAutonomyChanges(oldCfg, newCfg *config.Config) []string {
if o.UserIdleResumeSec != n.UserIdleResumeSec { if o.UserIdleResumeSec != n.UserIdleResumeSec {
changes = append(changes, "user_idle_resume_sec") changes = append(changes, "user_idle_resume_sec")
} }
if o.WaitingResumeDebounceSec != n.WaitingResumeDebounceSec {
changes = append(changes, "waiting_resume_debounce_sec")
}
if strings.TrimSpace(o.QuietHours) != strings.TrimSpace(n.QuietHours) { if strings.TrimSpace(o.QuietHours) != strings.TrimSpace(n.QuietHours) {
changes = append(changes, "quiet_hours") changes = append(changes, "quiet_hours")
} }
@@ -750,6 +753,7 @@ func buildAutonomyEngine(cfg *config.Config, msgBus *bus.MessageBus) *autonomy.E
NotifyCooldownSec: a.NotifyCooldownSec, NotifyCooldownSec: a.NotifyCooldownSec,
QuietHours: a.QuietHours, QuietHours: a.QuietHours,
UserIdleResumeSec: a.UserIdleResumeSec, UserIdleResumeSec: a.UserIdleResumeSec,
WaitingResumeDebounceSec: a.WaitingResumeDebounceSec,
Workspace: cfg.WorkspacePath(), Workspace: cfg.WorkspacePath(),
DefaultNotifyChannel: a.NotifyChannel, DefaultNotifyChannel: a.NotifyChannel,
DefaultNotifyChatID: a.NotifyChatID, DefaultNotifyChatID: a.NotifyChatID,

View File

@@ -132,6 +132,7 @@ func statusCmd() {
fmt.Printf(" - %s\n", key) fmt.Printf(" - %s\n", key)
} }
} }
fmt.Printf("Autonomy Config: idle_resume=%ds waiting_debounce=%ds\n", cfg.Agents.Defaults.Autonomy.UserIdleResumeSec, cfg.Agents.Defaults.Autonomy.WaitingResumeDebounceSec)
if summary, prio, reasons, nextRetry, dedupeHits, err := collectAutonomyTaskSummary(filepath.Join(workspace, "memory", "tasks.json")); err == nil { if summary, prio, reasons, nextRetry, dedupeHits, err := collectAutonomyTaskSummary(filepath.Join(workspace, "memory", "tasks.json")); err == nil {
fmt.Printf("Autonomy Tasks: todo=%d doing=%d waiting=%d blocked=%d done=%d dedupe_hits=%d\n", summary["todo"], summary["doing"], summary["waiting"], summary["blocked"], summary["done"], dedupeHits) fmt.Printf("Autonomy Tasks: todo=%d doing=%d waiting=%d blocked=%d done=%d dedupe_hits=%d\n", summary["todo"], summary["doing"], summary["waiting"], summary["blocked"], summary["done"], dedupeHits)
fmt.Printf("Autonomy Priority: high=%d normal=%d low=%d\n", prio["high"], prio["normal"], prio["low"]) fmt.Printf("Autonomy Priority: high=%d normal=%d low=%d\n", prio["high"], prio["normal"], prio["low"])

View File

@@ -23,6 +23,7 @@
"notify_cooldown_sec": 300, "notify_cooldown_sec": 300,
"quiet_hours": "23:00-08:00", "quiet_hours": "23:00-08:00",
"user_idle_resume_sec": 20, "user_idle_resume_sec": 20,
"waiting_resume_debounce_sec": 5,
"notify_channel": "", "notify_channel": "",
"notify_chat_id": "" "notify_chat_id": ""
}, },

View File

@@ -32,6 +32,7 @@ type Options struct {
NotifyCooldownSec int NotifyCooldownSec int
QuietHours string QuietHours string
UserIdleResumeSec int UserIdleResumeSec int
WaitingResumeDebounceSec int
} }
type taskState struct { type taskState struct {
@@ -82,6 +83,9 @@ func NewEngine(opts Options, msgBus *bus.MessageBus) *Engine {
if opts.UserIdleResumeSec <= 0 { if opts.UserIdleResumeSec <= 0 {
opts.UserIdleResumeSec = 20 opts.UserIdleResumeSec = 20
} }
if opts.WaitingResumeDebounceSec <= 0 {
opts.WaitingResumeDebounceSec = 5
}
return &Engine{ return &Engine{
opts: opts, opts: opts,
bus: msgBus, bus: msgBus,
@@ -227,7 +231,7 @@ func (e *Engine) tick() {
} }
if st.Status == "waiting" { if st.Status == "waiting" {
// Debounce waiting/resume flapping // Debounce waiting/resume flapping
if !st.WaitingSince.IsZero() && now.Sub(st.WaitingSince) < 5*time.Second { if !st.WaitingSince.IsZero() && now.Sub(st.WaitingSince) < time.Duration(e.opts.WaitingResumeDebounceSec)*time.Second {
continue continue
} }
reason := st.BlockReason reason := st.BlockReason

View File

@@ -54,6 +54,7 @@ type AutonomyConfig struct {
NotifyCooldownSec int `json:"notify_cooldown_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_COOLDOWN_SEC"` NotifyCooldownSec int `json:"notify_cooldown_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_COOLDOWN_SEC"`
QuietHours string `json:"quiet_hours" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_QUIET_HOURS"` QuietHours string `json:"quiet_hours" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_QUIET_HOURS"`
UserIdleResumeSec int `json:"user_idle_resume_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_USER_IDLE_RESUME_SEC"` UserIdleResumeSec int `json:"user_idle_resume_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_USER_IDLE_RESUME_SEC"`
WaitingResumeDebounceSec int `json:"waiting_resume_debounce_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_WAITING_RESUME_DEBOUNCE_SEC"`
NotifyChannel string `json:"notify_channel" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_CHANNEL"` 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"` NotifyChatID string `json:"notify_chat_id" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_CHAT_ID"`
} }
@@ -309,6 +310,7 @@ func DefaultConfig() *Config {
NotifyCooldownSec: 300, NotifyCooldownSec: 300,
QuietHours: "23:00-08:00", QuietHours: "23:00-08:00",
UserIdleResumeSec: 20, UserIdleResumeSec: 20,
WaitingResumeDebounceSec: 5,
NotifyChannel: "", NotifyChannel: "",
NotifyChatID: "", NotifyChatID: "",
}, },

View File

@@ -111,6 +111,9 @@ func Validate(cfg *Config) []error {
if aut.UserIdleResumeSec <= 0 { if aut.UserIdleResumeSec <= 0 {
errs = append(errs, fmt.Errorf("agents.defaults.autonomy.user_idle_resume_sec must be > 0 when enabled=true")) errs = append(errs, fmt.Errorf("agents.defaults.autonomy.user_idle_resume_sec must be > 0 when enabled=true"))
} }
if aut.WaitingResumeDebounceSec <= 0 {
errs = append(errs, fmt.Errorf("agents.defaults.autonomy.waiting_resume_debounce_sec must be > 0 when enabled=true"))
}
} }
texts := cfg.Agents.Defaults.Texts texts := cfg.Agents.Defaults.Texts
if strings.TrimSpace(texts.NoResponseFallback) == "" { if strings.TrimSpace(texts.NoResponseFallback) == "" {