package orchestration import ( "context" "fmt" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/orchestration/audit" ) // AuditMode 选择子代理上下文为 subagent_ask 上游 WS 路由裁剪成什么形状。它与 // audit.ContextAuditor 的表面(Full/Tail/Keyword)一一对应,使网关能按客户端所请求 // 的窗口精确取用。 type AuditMode string const ( // AuditFull 返回整段会话上下文。 AuditFull AuditMode = "full" // AuditTail 返回上下文最后 n 条消息。 AuditTail AuditMode = "tail" // AuditKeyword 返回首个关键词命中处附近的窗口。 AuditKeyword AuditMode = "keyword" ) // AuditResult 是审计一个子代理会话上下文的只读结果。Messages 承载被选中的窗口; // Line 与 Match 仅在关键词查询时填充。它是 ws.subagent_event 应答的线上载荷。 type AuditResult struct { AgentID string `json:"agentId"` Status Status `json:"status"` Goal string `json:"goal,omitempty"` Mode AuditMode `json:"mode"` Messages []model.Message `json:"messages,omitempty"` Line int `json:"line,omitempty"` Match string `json:"match,omitempty"` } // AuditContext 返回子代理 id 的会话上下文只读视图,供 subagent_ask WS 路由使用。 // mode 选择窗口:AuditFull 返回整段上下文,AuditTail 返回最后 n 条消息, // AuditKeyword 返回首个 query 命中处附近的窗口。未知 id 包装 ErrAgentNotFound, // 关键词查询为空则包装 audit.ErrQueryEmpty,两者都以 %w 包装,便于调用方用 // errors.Is 归类。 func (s *Scheduler) AuditContext(ctx context.Context, id string, mode AuditMode, n int, query string) (*AuditResult, error) { if err := ctx.Err(); err != nil { return nil, fmt.Errorf("orchestration: audit %q: %w", id, err) } s.mu.Lock() sub := s.agents[id] s.mu.Unlock() if sub == nil { return nil, fmt.Errorf("orchestration: audit %q: %w", id, ErrAgentNotFound) } a := audit.NewContextAuditor(sub.Context) res := &AuditResult{AgentID: sub.ID, Status: sub.Status(), Goal: sub.Goal, Mode: mode} switch mode { case AuditFull: res.Messages = a.Full() case AuditTail: res.Messages = a.Tail(n) case AuditKeyword: loc, err := a.Keyword(ctx, query) if err != nil { return nil, fmt.Errorf("orchestration: audit %q: %w", id, err) } if loc == nil { res.Messages = []model.Message{} break } res.Line = loc.Line res.Messages = loc.Surrounding res.Match = messageText(loc.Surrounding[len(loc.Surrounding)-1]) default: return nil, fmt.Errorf("orchestration: audit %q: unknown mode %q", id, mode) } return res, nil } // messageText 拼接一条消息的各个文本分段。它与 audit 子包自己的同名辅助函数保持一致, // 使结果载荷能给出人类可读的命中文本,而无需导出跨包的文本提取器。 func messageText(m model.Message) string { var text string for _, c := range m.Content { if c.Type == model.ContentTypeText && c.Text != "" { if text != "" { text += " " } text += c.Text } } return text }