mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-14 00:17:34 +08:00
task history retention: add 3-day configurable cleanup and notify allowlist enforcement
This commit is contained in:
@@ -950,6 +950,7 @@ func buildAutonomyEngine(cfg *config.Config, msgBus *bus.MessageBus) *autonomy.E
|
||||
QuietHours: a.QuietHours,
|
||||
UserIdleResumeSec: a.UserIdleResumeSec,
|
||||
MaxRoundsWithoutUser: a.MaxRoundsWithoutUser,
|
||||
TaskHistoryRetentionDays: a.TaskHistoryRetentionDays,
|
||||
WaitingResumeDebounceSec: a.WaitingResumeDebounceSec,
|
||||
AllowedTaskKeywords: a.AllowedTaskKeywords,
|
||||
ImportantKeywords: cfg.Agents.Defaults.Texts.AutonomyImportantKeywords,
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"notify_same_reason_cooldown_sec": 900,
|
||||
"quiet_hours": "23:00-08:00",
|
||||
"user_idle_resume_sec": 20,
|
||||
"max_rounds_without_user": 12,
|
||||
"task_history_retention_days": 3,
|
||||
"waiting_resume_debounce_sec": 5,
|
||||
"allowed_task_keywords": [],
|
||||
"notify_channel": "",
|
||||
"notify_chat_id": "",
|
||||
"notify_allow_chats": []
|
||||
|
||||
@@ -36,6 +36,7 @@ type Options struct {
|
||||
UserIdleResumeSec int
|
||||
WaitingResumeDebounceSec int
|
||||
MaxRoundsWithoutUser int
|
||||
TaskHistoryRetentionDays int
|
||||
AllowedTaskKeywords []string
|
||||
ImportantKeywords []string
|
||||
CompletionTemplate string
|
||||
@@ -73,6 +74,7 @@ type Engine struct {
|
||||
lockOwners map[string]string
|
||||
roundsWithoutUser int
|
||||
lastDailyReportDate string
|
||||
lastHistoryCleanupAt time.Time
|
||||
}
|
||||
|
||||
func NewEngine(opts Options, msgBus *bus.MessageBus) *Engine {
|
||||
@@ -106,6 +108,9 @@ func NewEngine(opts Options, msgBus *bus.MessageBus) *Engine {
|
||||
if opts.MaxRoundsWithoutUser < 0 {
|
||||
opts.MaxRoundsWithoutUser = 0
|
||||
}
|
||||
if opts.TaskHistoryRetentionDays <= 0 {
|
||||
opts.TaskHistoryRetentionDays = 3
|
||||
}
|
||||
return &Engine{
|
||||
opts: opts,
|
||||
bus: msgBus,
|
||||
@@ -363,6 +368,7 @@ func (e *Engine) tick() {
|
||||
}
|
||||
e.persistStateLocked()
|
||||
e.maybeWriteDailyReportLocked(now)
|
||||
e.maybeCleanupTaskHistoryLocked(now)
|
||||
}
|
||||
|
||||
func (e *Engine) tryAcquireLocksLocked(st *taskState) bool {
|
||||
@@ -919,6 +925,68 @@ func appendUniqueReport(path, content, date string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Engine) maybeCleanupTaskHistoryLocked(now time.Time) {
|
||||
if e.opts.TaskHistoryRetentionDays <= 0 {
|
||||
return
|
||||
}
|
||||
if !e.lastHistoryCleanupAt.IsZero() && now.Sub(e.lastHistoryCleanupAt) < time.Hour {
|
||||
return
|
||||
}
|
||||
workspace := e.opts.Workspace
|
||||
if workspace == "" {
|
||||
return
|
||||
}
|
||||
cutoff := now.AddDate(0, 0, -e.opts.TaskHistoryRetentionDays)
|
||||
|
||||
// Cleanup task-audit.jsonl by event time
|
||||
auditPath := filepath.Join(workspace, "memory", "task-audit.jsonl")
|
||||
if b, err := os.ReadFile(auditPath); err == nil {
|
||||
lines := strings.Split(string(b), "\n")
|
||||
kept := make([]string, 0, len(lines))
|
||||
for _, ln := range lines {
|
||||
if ln == "" {
|
||||
continue
|
||||
}
|
||||
var row map[string]interface{}
|
||||
if json.Unmarshal([]byte(ln), &row) != nil {
|
||||
continue
|
||||
}
|
||||
ts := fmt.Sprintf("%v", row["time"])
|
||||
tm, err := time.Parse(time.RFC3339, ts)
|
||||
if err != nil || tm.After(cutoff) {
|
||||
kept = append(kept, ln)
|
||||
}
|
||||
}
|
||||
_ = os.WriteFile(auditPath, []byte(strings.Join(kept, "\n")+"\n"), 0644)
|
||||
}
|
||||
|
||||
// Cleanup tasks.json old terminal states
|
||||
tasksPath := filepath.Join(workspace, "memory", "tasks.json")
|
||||
if b, err := os.ReadFile(tasksPath); err == nil {
|
||||
var items []TaskItem
|
||||
if json.Unmarshal(b, &items) == nil {
|
||||
kept := make([]TaskItem, 0, len(items))
|
||||
for _, it := range items {
|
||||
st := strings.ToLower(it.Status)
|
||||
terminal := st == "done" || st == "completed" || st == "suppressed" || st == "error"
|
||||
ts := strings.TrimSpace(it.UpdatedAt)
|
||||
if !terminal || ts == "" {
|
||||
kept = append(kept, it)
|
||||
continue
|
||||
}
|
||||
tm, err := time.Parse(time.RFC3339, ts)
|
||||
if err != nil || tm.After(cutoff) {
|
||||
kept = append(kept, it)
|
||||
}
|
||||
}
|
||||
if out, err := json.MarshalIndent(kept, "", " "); err == nil {
|
||||
_ = os.WriteFile(tasksPath, out, 0644)
|
||||
}
|
||||
}
|
||||
}
|
||||
e.lastHistoryCleanupAt = now
|
||||
}
|
||||
|
||||
func parseTodoAttributes(content string) (priority, dueAt, normalized string) {
|
||||
priority = "normal"
|
||||
normalized = content
|
||||
|
||||
@@ -56,6 +56,7 @@ type AutonomyConfig struct {
|
||||
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"`
|
||||
MaxRoundsWithoutUser int `json:"max_rounds_without_user" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_MAX_ROUNDS_WITHOUT_USER"`
|
||||
TaskHistoryRetentionDays int `json:"task_history_retention_days" env:"CLAWGO_AGENTS_DEFAULTS_AUTONOMY_TASK_HISTORY_RETENTION_DAYS"`
|
||||
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"`
|
||||
@@ -327,6 +328,7 @@ func DefaultConfig() *Config {
|
||||
QuietHours: "23:00-08:00",
|
||||
UserIdleResumeSec: 20,
|
||||
MaxRoundsWithoutUser: 12,
|
||||
TaskHistoryRetentionDays: 3,
|
||||
WaitingResumeDebounceSec: 5,
|
||||
AllowedTaskKeywords: []string{},
|
||||
NotifyChannel: "",
|
||||
|
||||
@@ -117,6 +117,9 @@ func Validate(cfg *Config) []error {
|
||||
if aut.WaitingResumeDebounceSec <= 0 {
|
||||
errs = append(errs, fmt.Errorf("agents.defaults.autonomy.waiting_resume_debounce_sec must be > 0 when enabled=true"))
|
||||
}
|
||||
if aut.TaskHistoryRetentionDays <= 0 {
|
||||
errs = append(errs, fmt.Errorf("agents.defaults.autonomy.task_history_retention_days must be > 0 when enabled=true"))
|
||||
}
|
||||
}
|
||||
texts := cfg.Agents.Defaults.Texts
|
||||
if strings.TrimSpace(texts.NoResponseFallback) == "" {
|
||||
|
||||
Reference in New Issue
Block a user