mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-05 14:07:32 +08:00
add autonomy control-file switching and block-reason status analytics
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -44,9 +45,15 @@ func gatewayCmd() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
case "autonomy":
|
||||||
|
if err := gatewayAutonomyControlCmd(args[1:]); err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Unknown gateway command: %s\n", args[0])
|
fmt.Printf("Unknown gateway command: %s\n", args[0])
|
||||||
fmt.Println("Usage: clawgo gateway [run|start|stop|restart|status]")
|
fmt.Println("Usage: clawgo gateway [run|start|stop|restart|status|autonomy on|off|status]")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,6 +308,58 @@ func gatewayCmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gatewayAutonomyControlCmd(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("usage: clawgo gateway autonomy [on|off|status]")
|
||||||
|
}
|
||||||
|
cfg, err := loadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
memDir := filepath.Join(cfg.WorkspacePath(), "memory")
|
||||||
|
if err := os.MkdirAll(memDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pausePath := filepath.Join(memDir, "autonomy.pause")
|
||||||
|
ctrlPath := filepath.Join(memDir, "autonomy.control.json")
|
||||||
|
|
||||||
|
switch strings.ToLower(strings.TrimSpace(args[0])) {
|
||||||
|
case "on":
|
||||||
|
_ = os.Remove(pausePath)
|
||||||
|
if err := os.WriteFile(ctrlPath, []byte("{\n \"enabled\": true\n}\n"), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("✓ Autonomy enabled")
|
||||||
|
return nil
|
||||||
|
case "off":
|
||||||
|
if err := os.WriteFile(ctrlPath, []byte("{\n \"enabled\": false\n}\n"), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(pausePath, []byte(time.Now().UTC().Format(time.RFC3339)+"\n"), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("✓ Autonomy disabled (paused)")
|
||||||
|
return nil
|
||||||
|
case "status":
|
||||||
|
enabled := true
|
||||||
|
if data, err := os.ReadFile(ctrlPath); err == nil {
|
||||||
|
var c struct{ Enabled bool `json:"enabled"` }
|
||||||
|
if json.Unmarshal(data, &c) == nil {
|
||||||
|
enabled = c.Enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(pausePath); err == nil {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
fmt.Printf("Autonomy status: %v\n", enabled)
|
||||||
|
fmt.Printf("Control file: %s\n", ctrlPath)
|
||||||
|
fmt.Printf("Pause file: %s\n", pausePath)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("usage: clawgo gateway autonomy [on|off|status]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func summarizeAutonomyChanges(oldCfg, newCfg *config.Config) []string {
|
func summarizeAutonomyChanges(oldCfg, newCfg *config.Config) []string {
|
||||||
if oldCfg == nil || newCfg == nil {
|
if oldCfg == nil || newCfg == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ func statusCmd() {
|
|||||||
if nextRetry != "" {
|
if nextRetry != "" {
|
||||||
fmt.Printf("Autonomy Next Retry: %s\n", nextRetry)
|
fmt.Printf("Autonomy Next Retry: %s\n", nextRetry)
|
||||||
}
|
}
|
||||||
|
fmt.Printf("Autonomy Control: %s\n", autonomyControlState(workspace))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +165,25 @@ func printTemplateField(name, current, def string) {
|
|||||||
fmt.Printf(" %s: %s\n", name, state)
|
fmt.Printf(" %s: %s\n", name, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func autonomyControlState(workspace string) string {
|
||||||
|
memDir := filepath.Join(workspace, "memory")
|
||||||
|
pausePath := filepath.Join(memDir, "autonomy.pause")
|
||||||
|
if _, err := os.Stat(pausePath); err == nil {
|
||||||
|
return "paused (autonomy.pause)"
|
||||||
|
}
|
||||||
|
ctrlPath := filepath.Join(memDir, "autonomy.control.json")
|
||||||
|
if data, err := os.ReadFile(ctrlPath); err == nil {
|
||||||
|
var c struct{ Enabled bool `json:"enabled"` }
|
||||||
|
if json.Unmarshal(data, &c) == nil {
|
||||||
|
if c.Enabled {
|
||||||
|
return "enabled"
|
||||||
|
}
|
||||||
|
return "disabled (control file)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
|
||||||
func collectSessionKindCounts(sessionsDir string) (map[string]int, error) {
|
func collectSessionKindCounts(sessionsDir string) (map[string]int, error) {
|
||||||
indexPath := filepath.Join(sessionsDir, "sessions.json")
|
indexPath := filepath.Join(sessionsDir, "sessions.json")
|
||||||
data, err := os.ReadFile(indexPath)
|
data, err := os.ReadFile(indexPath)
|
||||||
|
|||||||
@@ -416,7 +416,8 @@ func (e *Engine) writeTriggerAudit(action string, st *taskState, errText string)
|
|||||||
if strings.TrimSpace(e.opts.Workspace) == "" || st == nil {
|
if strings.TrimSpace(e.opts.Workspace) == "" || st == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
path := filepath.Join(e.opts.Workspace, "memory", "trigger-audit.jsonl")
|
memDir := filepath.Join(e.opts.Workspace, "memory")
|
||||||
|
path := filepath.Join(memDir, "trigger-audit.jsonl")
|
||||||
_ = os.MkdirAll(filepath.Dir(path), 0755)
|
_ = os.MkdirAll(filepath.Dir(path), 0755)
|
||||||
row := map[string]interface{}{
|
row := map[string]interface{}{
|
||||||
"time": time.Now().UTC().Format(time.RFC3339),
|
"time": time.Now().UTC().Format(time.RFC3339),
|
||||||
@@ -434,6 +435,23 @@ func (e *Engine) writeTriggerAudit(action string, st *taskState, errText string)
|
|||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statsPath := filepath.Join(memDir, "trigger-stats.json")
|
||||||
|
stats := struct {
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
Counts map[string]int `json:"counts"`
|
||||||
|
}{Counts: map[string]int{}}
|
||||||
|
if raw, rErr := os.ReadFile(statsPath); rErr == nil {
|
||||||
|
_ = json.Unmarshal(raw, &stats)
|
||||||
|
if stats.Counts == nil {
|
||||||
|
stats.Counts = map[string]int{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats.Counts["autonomy"]++
|
||||||
|
stats.UpdatedAt = time.Now().UTC().Format(time.RFC3339)
|
||||||
|
if raw, mErr := json.MarshalIndent(stats, "", " "); mErr == nil {
|
||||||
|
_ = os.WriteFile(statsPath, raw, 0644)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) writeReflectLog(stage string, st *taskState, outcome string) {
|
func (e *Engine) writeReflectLog(stage string, st *taskState, outcome string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user