mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 13:17:28 +08:00
fix start check
This commit is contained in:
@@ -857,6 +857,7 @@ func gatewayCmd() {
|
||||
}
|
||||
|
||||
go agentLoop.Run(ctx)
|
||||
go runGatewayStartupSelfCheck(ctx, agentLoop, cfg.WorkspacePath())
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
||||
@@ -965,6 +966,50 @@ func gatewayCmd() {
|
||||
}
|
||||
}
|
||||
|
||||
func runGatewayStartupSelfCheck(parent context.Context, agentLoop *agent.AgentLoop, workspace string) {
|
||||
if agentLoop == nil {
|
||||
return
|
||||
}
|
||||
|
||||
checkCtx, cancel := context.WithTimeout(parent, 10*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
prompt := buildGatewayStartupSelfCheckPrompt(workspace)
|
||||
report := agentLoop.RunStartupSelfCheckAllSessions(checkCtx, prompt, "gateway:startup-self-check")
|
||||
logger.InfoCF("gateway", "Startup self-check completed", map[string]interface{}{
|
||||
"sessions_total": report.TotalSessions,
|
||||
"sessions_compacted": report.CompactedSessions,
|
||||
"sessions_checked": report.CheckedSessions,
|
||||
"sessions_failed": report.FailedSessions,
|
||||
})
|
||||
}
|
||||
|
||||
func buildGatewayStartupSelfCheckPrompt(workspace string) string {
|
||||
now := time.Now().Format(time.RFC3339)
|
||||
notesPath := filepath.Join(workspace, "memory", "HEARTBEAT.md")
|
||||
notes := ""
|
||||
if data, err := os.ReadFile(notesPath); err == nil {
|
||||
notes = strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("网关刚刚启动,请立即执行一次自检。\n")
|
||||
sb.WriteString("目标:基于你自己的历史记录与记忆,判断是否有未完成任务需要继续执行,或是否需要立即采取其他行动。\n")
|
||||
sb.WriteString("要求:\n")
|
||||
sb.WriteString("1) 先给出结论(继续执行 / 暂无待续任务 / 其他行动)。\n")
|
||||
sb.WriteString("2) 如果需要继续,请直接开始推进,并在关键节点自然汇报。\n")
|
||||
sb.WriteString("3) 如果无需继续,也请给出下一步建议。\n")
|
||||
sb.WriteString("4) 将本次结论简要写入 memory/MEMORY.md 便于下次启动继承。\n")
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString("当前时间: ")
|
||||
sb.WriteString(now)
|
||||
if notes != "" {
|
||||
sb.WriteString("\n\n参考 HEARTBEAT.md:\n")
|
||||
sb.WriteString(notes)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func maybePromptAndEscalateRoot(command string) {
|
||||
if os.Getenv(envRootPrompted) == "1" {
|
||||
return
|
||||
|
||||
@@ -127,6 +127,13 @@ type stageReporter struct {
|
||||
onUpdate func(content string)
|
||||
}
|
||||
|
||||
type StartupSelfCheckReport struct {
|
||||
TotalSessions int
|
||||
CompactedSessions int
|
||||
CheckedSessions int
|
||||
FailedSessions int
|
||||
}
|
||||
|
||||
func (sr *stageReporter) Publish(stage int, total int, status string, detail string) {
|
||||
if sr == nil || sr.onUpdate == nil {
|
||||
return
|
||||
@@ -1536,6 +1543,72 @@ func (al *AgentLoop) GetStartupInfo() map[string]interface{} {
|
||||
return info
|
||||
}
|
||||
|
||||
func (al *AgentLoop) RunStartupSelfCheckAllSessions(ctx context.Context, prompt, fallbackSessionKey string) StartupSelfCheckReport {
|
||||
report := StartupSelfCheckReport{}
|
||||
if al == nil || al.sessions == nil {
|
||||
return report
|
||||
}
|
||||
|
||||
fallbackSessionKey = strings.TrimSpace(fallbackSessionKey)
|
||||
if fallbackSessionKey == "" {
|
||||
fallbackSessionKey = "gateway:startup-self-check"
|
||||
}
|
||||
|
||||
keys := al.sessions.ListSessionKeys()
|
||||
seen := make(map[string]struct{}, len(keys)+1)
|
||||
sessions := make([]string, 0, len(keys)+1)
|
||||
for _, key := range keys {
|
||||
key = strings.TrimSpace(key)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
sessions = append(sessions, key)
|
||||
}
|
||||
if _, ok := seen[fallbackSessionKey]; !ok {
|
||||
sessions = append(sessions, fallbackSessionKey)
|
||||
}
|
||||
report.TotalSessions = len(sessions)
|
||||
|
||||
for _, sessionKey := range sessions {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return report
|
||||
default:
|
||||
}
|
||||
|
||||
before := al.sessions.MessageCount(sessionKey)
|
||||
if err := al.persistSessionWithCompaction(ctx, sessionKey); err != nil {
|
||||
logger.WarnCF("agent", "Startup self-check pre-compaction failed", map[string]interface{}{
|
||||
"session_key": sessionKey,
|
||||
logger.FieldError: err.Error(),
|
||||
})
|
||||
}
|
||||
after := al.sessions.MessageCount(sessionKey)
|
||||
if after < before {
|
||||
report.CompactedSessions++
|
||||
}
|
||||
|
||||
runCtx, cancel := context.WithTimeout(ctx, 90*time.Second)
|
||||
_, err := al.ProcessDirect(runCtx, prompt, sessionKey)
|
||||
cancel()
|
||||
if err != nil {
|
||||
report.FailedSessions++
|
||||
logger.WarnCF("agent", "Startup self-check task failed", map[string]interface{}{
|
||||
"session_key": sessionKey,
|
||||
logger.FieldError: err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
report.CheckedSessions++
|
||||
}
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
// formatMessagesForLog formats messages for logging
|
||||
func formatMessagesForLog(messages []providers.Message) string {
|
||||
if len(messages) == 0 {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -229,6 +230,22 @@ func (sm *SessionManager) MessageCount(key string) int {
|
||||
return len(session.Messages)
|
||||
}
|
||||
|
||||
func (sm *SessionManager) ListSessionKeys() []string {
|
||||
sm.mu.RLock()
|
||||
defer sm.mu.RUnlock()
|
||||
|
||||
keys := make([]string, 0, len(sm.sessions))
|
||||
for k := range sm.sessions {
|
||||
k = strings.TrimSpace(k)
|
||||
if k == "" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (sm *SessionManager) CompactHistory(key, summary string, keepLast int) (int, int, error) {
|
||||
sm.mu.RLock()
|
||||
session, ok := sm.sessions[key]
|
||||
|
||||
Reference in New Issue
Block a user