mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-23 10:37:30 +08:00
add autonomy block-reason analytics and manual pause control file support
This commit is contained in:
@@ -129,9 +129,12 @@ func statusCmd() {
|
|||||||
fmt.Printf(" - %s\n", key)
|
fmt.Printf(" - %s\n", key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if summary, prio, 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"])
|
||||||
|
if reasons["active_user"] > 0 || reasons["manual_pause"] > 0 || reasons["max_consecutive_stalls"] > 0 {
|
||||||
|
fmt.Printf("Autonomy Block Reasons: active_user=%d manual_pause=%d max_stalls=%d\n", reasons["active_user"], reasons["manual_pause"], reasons["max_consecutive_stalls"])
|
||||||
|
}
|
||||||
if nextRetry != "" {
|
if nextRetry != "" {
|
||||||
fmt.Printf("Autonomy Next Retry: %s\n", nextRetry)
|
fmt.Printf("Autonomy Next Retry: %s\n", nextRetry)
|
||||||
}
|
}
|
||||||
@@ -249,25 +252,27 @@ func collectTriggerErrorCounts(path string) (map[string]int, error) {
|
|||||||
return counts, nil
|
return counts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectAutonomyTaskSummary(path string) (map[string]int, map[string]int, string, int, error) {
|
func collectAutonomyTaskSummary(path string) (map[string]int, map[string]int, map[string]int, string, int, error) {
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return map[string]int{"todo": 0, "doing": 0, "waiting": 0, "blocked": 0, "done": 0}, map[string]int{"high": 0, "normal": 0, "low": 0}, "", 0, nil
|
return map[string]int{"todo": 0, "doing": 0, "waiting": 0, "blocked": 0, "done": 0}, map[string]int{"high": 0, "normal": 0, "low": 0}, map[string]int{"active_user": 0, "manual_pause": 0, "max_consecutive_stalls": 0}, "", 0, nil
|
||||||
}
|
}
|
||||||
return nil, nil, "", 0, err
|
return nil, nil, nil, "", 0, err
|
||||||
}
|
}
|
||||||
var items []struct {
|
var items []struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Priority string `json:"priority"`
|
Priority string `json:"priority"`
|
||||||
RetryAfter string `json:"retry_after"`
|
BlockReason string `json:"block_reason"`
|
||||||
DedupeHits int `json:"dedupe_hits"`
|
RetryAfter string `json:"retry_after"`
|
||||||
|
DedupeHits int `json:"dedupe_hits"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, &items); err != nil {
|
if err := json.Unmarshal(data, &items); err != nil {
|
||||||
return nil, nil, "", 0, err
|
return nil, nil, nil, "", 0, err
|
||||||
}
|
}
|
||||||
summary := map[string]int{"todo": 0, "doing": 0, "waiting": 0, "blocked": 0, "done": 0}
|
summary := map[string]int{"todo": 0, "doing": 0, "waiting": 0, "blocked": 0, "done": 0}
|
||||||
priorities := map[string]int{"high": 0, "normal": 0, "low": 0}
|
priorities := map[string]int{"high": 0, "normal": 0, "low": 0}
|
||||||
|
reasons := map[string]int{"active_user": 0, "manual_pause": 0, "max_consecutive_stalls": 0}
|
||||||
nextRetry := ""
|
nextRetry := ""
|
||||||
nextRetryAt := time.Time{}
|
nextRetryAt := time.Time{}
|
||||||
totalDedupe := 0
|
totalDedupe := 0
|
||||||
@@ -277,6 +282,10 @@ func collectAutonomyTaskSummary(path string) (map[string]int, map[string]int, st
|
|||||||
summary[s]++
|
summary[s]++
|
||||||
}
|
}
|
||||||
totalDedupe += it.DedupeHits
|
totalDedupe += it.DedupeHits
|
||||||
|
r := strings.ToLower(strings.TrimSpace(it.BlockReason))
|
||||||
|
if _, ok := reasons[r]; ok {
|
||||||
|
reasons[r]++
|
||||||
|
}
|
||||||
p := strings.ToLower(strings.TrimSpace(it.Priority))
|
p := strings.ToLower(strings.TrimSpace(it.Priority))
|
||||||
if _, ok := priorities[p]; ok {
|
if _, ok := priorities[p]; ok {
|
||||||
priorities[p]++
|
priorities[p]++
|
||||||
@@ -292,7 +301,7 @@ func collectAutonomyTaskSummary(path string) (map[string]int, map[string]int, st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return summary, priorities, nextRetry, totalDedupe, nil
|
return summary, priorities, reasons, nextRetry, totalDedupe, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectRecentSubagentSessions(sessionsDir string, limit int) ([]string, error) {
|
func collectRecentSubagentSessions(sessionsDir string, limit int) ([]string, error) {
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ func (e *Engine) tick() {
|
|||||||
st.Status = "waiting"
|
st.Status = "waiting"
|
||||||
st.BlockReason = "manual_pause"
|
st.BlockReason = "manual_pause"
|
||||||
e.writeReflectLog("waiting", st, "paused by manual switch")
|
e.writeReflectLog("waiting", st, "paused by manual switch")
|
||||||
|
e.writeTriggerAudit("waiting", st, "manual_pause")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.persistStateLocked()
|
e.persistStateLocked()
|
||||||
@@ -137,7 +138,9 @@ func (e *Engine) tick() {
|
|||||||
for _, st := range e.state {
|
for _, st := range e.state {
|
||||||
if st.Status == "running" {
|
if st.Status == "running" {
|
||||||
st.Status = "waiting"
|
st.Status = "waiting"
|
||||||
|
st.BlockReason = "active_user"
|
||||||
e.writeReflectLog("waiting", st, "paused due to active user conversation")
|
e.writeReflectLog("waiting", st, "paused due to active user conversation")
|
||||||
|
e.writeTriggerAudit("waiting", st, "active_user")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.persistStateLocked()
|
e.persistStateLocked()
|
||||||
@@ -220,9 +223,11 @@ func (e *Engine) tick() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if st.Status == "waiting" {
|
if st.Status == "waiting" {
|
||||||
|
reason := st.BlockReason
|
||||||
st.Status = "idle"
|
st.Status = "idle"
|
||||||
st.BlockReason = ""
|
st.BlockReason = ""
|
||||||
e.writeReflectLog("resume", st, "user conversation idle, autonomy resumed")
|
e.writeReflectLog("resume", st, "autonomy resumed from waiting")
|
||||||
|
e.writeTriggerAudit("resume", st, reason)
|
||||||
}
|
}
|
||||||
if st.Status == "blocked" {
|
if st.Status == "blocked" {
|
||||||
if !st.RetryAfter.IsZero() && now.Before(st.RetryAfter) {
|
if !st.RetryAfter.IsZero() && now.Before(st.RetryAfter) {
|
||||||
@@ -578,13 +583,37 @@ func (e *Engine) pauseFilePath() string {
|
|||||||
return filepath.Join(e.opts.Workspace, "memory", "autonomy.pause")
|
return filepath.Join(e.opts.Workspace, "memory", "autonomy.pause")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) controlFilePath() string {
|
||||||
|
if strings.TrimSpace(e.opts.Workspace) == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return filepath.Join(e.opts.Workspace, "memory", "autonomy.control.json")
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Engine) hasManualPause() bool {
|
func (e *Engine) hasManualPause() bool {
|
||||||
p := e.pauseFilePath()
|
p := e.pauseFilePath()
|
||||||
if p == "" {
|
if p == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_, err := os.Stat(p)
|
_, err := os.Stat(p)
|
||||||
return err == nil
|
if err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ctrl := e.controlFilePath()
|
||||||
|
if ctrl == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
data, rErr := os.ReadFile(ctrl)
|
||||||
|
if rErr != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var c struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
if jErr := json.Unmarshal(data, &c); jErr != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !c.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) hasRecentUserActivity(now time.Time) bool {
|
func (e *Engine) hasRecentUserActivity(now time.Time) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user