mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 21:57:29 +08:00
add autonomy quiet-hours policy and reflect logs with task persistence
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -26,6 +27,7 @@ type Options struct {
|
||||
DefaultNotifyChannel string
|
||||
DefaultNotifyChatID string
|
||||
NotifyCooldownSec int
|
||||
QuietHours string
|
||||
}
|
||||
|
||||
type taskState struct {
|
||||
@@ -168,6 +170,7 @@ func (e *Engine) tick() {
|
||||
st.Status = "running"
|
||||
st.LastRunAt = now
|
||||
st.LastAutonomyAt = now
|
||||
e.writeReflectLog("dispatch", st, "task dispatched to agent loop")
|
||||
dispatched++
|
||||
}
|
||||
e.persistStateLocked()
|
||||
@@ -231,6 +234,7 @@ func (e *Engine) dispatchTask(st *taskState) {
|
||||
}
|
||||
|
||||
func (e *Engine) sendCompletionNotification(st *taskState) {
|
||||
e.writeReflectLog("complete", st, "task marked completed")
|
||||
if !e.shouldNotify("done:" + st.ID) {
|
||||
return
|
||||
}
|
||||
@@ -242,6 +246,7 @@ func (e *Engine) sendCompletionNotification(st *taskState) {
|
||||
}
|
||||
|
||||
func (e *Engine) sendFailureNotification(st *taskState, reason string) {
|
||||
e.writeReflectLog("blocked", st, reason)
|
||||
if !e.shouldNotify("blocked:" + st.ID) {
|
||||
return
|
||||
}
|
||||
@@ -257,6 +262,9 @@ func (e *Engine) shouldNotify(key string) bool {
|
||||
return false
|
||||
}
|
||||
now := time.Now()
|
||||
if inQuietHours(now, e.opts.QuietHours) {
|
||||
return false
|
||||
}
|
||||
if last, ok := e.lastNotify[key]; ok {
|
||||
if now.Sub(last) < time.Duration(e.opts.NotifyCooldownSec)*time.Second {
|
||||
return false
|
||||
@@ -266,6 +274,55 @@ func (e *Engine) shouldNotify(key string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Engine) writeReflectLog(stage string, st *taskState, outcome string) {
|
||||
if strings.TrimSpace(e.opts.Workspace) == "" || st == nil {
|
||||
return
|
||||
}
|
||||
memDir := filepath.Join(e.opts.Workspace, "memory")
|
||||
_ = os.MkdirAll(memDir, 0755)
|
||||
path := filepath.Join(memDir, time.Now().Format("2006-01-02")+".md")
|
||||
line := fmt.Sprintf("- [%s] [autonomy][%s] task=%s status=%s outcome=%s\n", time.Now().Format("15:04"), stage, st.Content, st.Status, outcome)
|
||||
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
_, _ = f.WriteString(line)
|
||||
}
|
||||
|
||||
func inQuietHours(now time.Time, spec string) bool {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if spec == "" {
|
||||
return false
|
||||
}
|
||||
parts := strings.Split(spec, "-")
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
parseHM := func(v string) (int, bool) {
|
||||
hm := strings.Split(strings.TrimSpace(v), ":")
|
||||
if len(hm) != 2 {
|
||||
return 0, false
|
||||
}
|
||||
h, err1 := strconv.Atoi(hm[0])
|
||||
m, err2 := strconv.Atoi(hm[1])
|
||||
if err1 != nil || err2 != nil || h < 0 || h > 23 || m < 0 || m > 59 {
|
||||
return 0, false
|
||||
}
|
||||
return h*60 + m, true
|
||||
}
|
||||
start, ok1 := parseHM(parts[0])
|
||||
end, ok2 := parseHM(parts[1])
|
||||
if !ok1 || !ok2 {
|
||||
return false
|
||||
}
|
||||
nowMin := now.Hour()*60 + now.Minute()
|
||||
if start <= end {
|
||||
return nowMin >= start && nowMin <= end
|
||||
}
|
||||
return nowMin >= start || nowMin <= end
|
||||
}
|
||||
|
||||
func (e *Engine) persistStateLocked() {
|
||||
items := make([]TaskItem, 0, len(e.state))
|
||||
for _, st := range e.state {
|
||||
|
||||
@@ -45,15 +45,16 @@ type AgentDefaults struct {
|
||||
}
|
||||
|
||||
type AutonomyConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_ENABLED"`
|
||||
TickIntervalSec int `json:"tick_interval_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_TICK_INTERVAL_SEC"`
|
||||
MinRunIntervalSec int `json:"min_run_interval_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MIN_RUN_INTERVAL_SEC"`
|
||||
MaxPendingDurationSec int `json:"max_pending_duration_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_PENDING_DURATION_SEC"`
|
||||
MaxConsecutiveStalls int `json:"max_consecutive_stalls" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_CONSECUTIVE_STALLS"`
|
||||
MaxDispatchPerTick int `json:"max_dispatch_per_tick" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_DISPATCH_PER_TICK"`
|
||||
NotifyCooldownSec int `json:"notify_cooldown_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_NOTIFY_COOLDOWN_SEC"`
|
||||
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"`
|
||||
Enabled bool `json:"enabled" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_ENABLED"`
|
||||
TickIntervalSec int `json:"tick_interval_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_TICK_INTERVAL_SEC"`
|
||||
MinRunIntervalSec int `json:"min_run_interval_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MIN_RUN_INTERVAL_SEC"`
|
||||
MaxPendingDurationSec int `json:"max_pending_duration_sec" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_PENDING_DURATION_SEC"`
|
||||
MaxConsecutiveStalls int `json:"max_consecutive_stalls" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_CONSECUTIVE_STALLS"`
|
||||
MaxDispatchPerTick int `json:"max_dispatch_per_tick" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_DISPATCH_PER_TICK"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type AgentTextConfig struct {
|
||||
@@ -305,6 +306,7 @@ func DefaultConfig() *Config {
|
||||
MaxConsecutiveStalls: 3,
|
||||
MaxDispatchPerTick: 2,
|
||||
NotifyCooldownSec: 300,
|
||||
QuietHours: "23:00-08:00",
|
||||
NotifyChannel: "",
|
||||
NotifyChatID: "",
|
||||
},
|
||||
|
||||
@@ -102,6 +102,12 @@ func Validate(cfg *Config) []error {
|
||||
if aut.NotifyCooldownSec <= 0 {
|
||||
errs = append(errs, fmt.Errorf("agents.defaults.autonomy.notify_cooldown_sec must be > 0 when enabled=true"))
|
||||
}
|
||||
if qh := strings.TrimSpace(aut.QuietHours); qh != "" {
|
||||
parts := strings.Split(qh, "-")
|
||||
if len(parts) != 2 {
|
||||
errs = append(errs, fmt.Errorf("agents.defaults.autonomy.quiet_hours must be HH:MM-HH:MM"))
|
||||
}
|
||||
}
|
||||
}
|
||||
texts := cfg.Agents.Defaults.Texts
|
||||
if strings.TrimSpace(texts.NoResponseFallback) == "" {
|
||||
|
||||
Reference in New Issue
Block a user