package server import ( "context" "crypto/sha256" "encoding/hex" "fmt" "chat-agent-system/internal/bizdev" "chat-agent-system/internal/squad" "chat-agent-system/internal/taskrun" "chat-agent-system/internal/workspace" ) // FinalizeTaskRunProjection 将任务执行结果投影到 workflow_runs 与聊天助手消息(供回填/管理命令使用)。 func (s *Server) FinalizeTaskRunProjection(ctx context.Context, record *taskrun.RunRecord, result *bizdev.TaskRunResult) (string, error) { return s.finalizeTaskRun(ctx, record, result) } // finalizeTaskRun 是薄适配:产物哈希在事务外经 workspace.Capability 读取,终态命令交给 taskrun.Commands。 func (s *Server) finalizeTaskRun(ctx context.Context, record *taskrun.RunRecord, result *bizdev.TaskRunResult) (string, error) { if record == nil || result == nil { return "failed", fmt.Errorf("finalize: missing record or result") } if result.DocRecord != nil && result.DocRecord.ContentSHA256 == "" { if hexSum := s.hashDeliverable(ctx, record, result); hexSum != "" { result.DocRecord.ContentSHA256 = hexSum } } cmds := s.taskCommands() if s != nil { cmds.PW = s.pwSvc() cmds.Ctx = s.ctxMgr() } status, err := cmds.Finalize(ctx, record, result) if err != nil { return status, err } if status == "completed" && record.AssistantMsgID > 0 { summaryContent := result.CompletionSummary() s.ingestAssistantReply(ctx, record, summaryContent, status) } // Dialogue-context conflict cards are independent of task outcome; the // OnTerminal hook covers the cmds path, this covers the finalizer path. if s.cmds() == nil { s.projectContextConflictCards(ctx, record, result) } return status, nil } func (s *Server) taskCommands() *taskrun.Commands { if s != nil && s.cmds() != nil { return s.cmds() } cmds := &taskrun.Commands{ PW: s.pwSvc(), Env: "test", } if s != nil && s.cfg != nil { cmds.Env = s.cfg.Env cmds.StorageIdentity = "env:" + s.cfg.Env } if s != nil { cmds.Ctx = s.ctxMgr() } return cmds } func (s *Server) hashDeliverable(ctx context.Context, record *taskrun.RunRecord, result *bizdev.TaskRunResult) string { if s == nil || s.wsCap() == nil || record == nil { return "" } id := workspace.OpaqueID(result.WorkDir, record.WorkspaceID) if id == "" { return "" } env := "" if s.cfg != nil { env = s.cfg.Env } ref := workspace.WorkspaceRef{ ID: id, SchemaVersion: workspace.SchemaVersion, ProjectScope: record.ProjectID, RunID: record.RunID, Environment: env, } body, err := s.wsCap().ReadArtifact(ctx, ref, workspace.DeliverableKey) if err != nil || len(body) == 0 { return "" } sum := sha256.Sum256(body) return hex.EncodeToString(sum[:]) } func resolveTerminalStatus(result *bizdev.TaskRunResult) string { return result.TerminalStatus() } func buildTaskCompletionSummary(result *bizdev.TaskRunResult, terminalStatus string) string { _ = terminalStatus return result.CompletionSummary() } func squadResultToTaskRun(res *squad.SquadRunResult, taskKey string) *bizdev.TaskRunResult { if res == nil { return nil } return &bizdev.TaskRunResult{ TaskKey: res.TaskKey, ArtifactTitle: res.ArtifactTitle, MarkdownBody: res.MarkdownBody, ExtractedData: res.ExtractedData, Attachments: res.Attachments, WorkDir: res.WorkDir, BackendUsed: res.BackendUsed, Duration: res.Duration, Success: res.Success, ErrorReason: res.ErrorReason, QualityReport: res.QualityReport, Manifest: res.Manifest, AdvisorBrief: res.AdvisorBrief, } }