Files
Agent-System/internal/server/task_finalize.go
T
weichenandCursor 0c6fb090ff fix(chat): show pending confirmation instead of completed for high-risk
Sync coordinator and async CompletionSummary use pending_author_confirmation
with artifact metadata for document entry; completed only after commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 09:51:15 +00:00

160 lines
4.5 KiB
Go

package server
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/squad"
"chat-agent-system/internal/taskrun"
)
// 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 将任务执行结果投影到 workflow_runs 与聊天助手消息。
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")
}
workspaceID := result.WorkDir
if workspaceID == "" {
workspaceID = record.WorkspaceID
}
terminalStatus := result.TerminalStatus()
summaryContent := result.CompletionSummary()
docID := ""
artifactTitle := result.ArtifactTitle
sha256Hex := ""
if result.DocRecord != nil {
docID = result.DocRecord.DocID
if result.DocRecord.ContentSHA256 != "" {
sha256Hex = result.DocRecord.ContentSHA256
}
}
if docID == "" && record.ProjectID > 0 && record.TaskKey != "" {
docID = fmt.Sprintf("doc_proj%d_%s", record.ProjectID, record.TaskKey)
}
if artifactTitle == "" && record.TaskKey != "" {
artifactTitle = squad.TaskArtifactTitle(record.TaskKey)
}
if sha256Hex == "" && workspaceID != "" {
if computed, err := hashOutputFile(workspaceID); err == nil {
sha256Hex = computed
}
}
meta := map[string]any{
"run_id": record.RunID,
"workspace_id": workspaceID,
"task_status": terminalStatus,
"task_key": record.TaskKey,
}
if record.ProjectID > 0 {
meta["project_id"] = record.ProjectID
}
if art := result.ArtifactMetadata(); art != nil {
if workspaceID != "" {
art["workspace_id"] = workspaceID
}
if sha256Hex != "" {
art["sha256"] = sha256Hex
}
if artifactTitle != "" {
art["title"] = artifactTitle
}
if record.TaskKey != "" {
art["task_key"] = record.TaskKey
}
if docID != "" {
art["doc_id"] = docID
}
meta["artifact"] = art
} else if docID != "" {
artifact := map[string]any{
"doc_id": docID,
"title": artifactTitle,
"task_key": record.TaskKey,
"workspace_id": workspaceID,
}
if sha256Hex != "" {
artifact["sha256"] = sha256Hex
}
if result.QualityReport != nil {
artifact["quality_passed"] = result.QualityReport.Passed
artifact["quality_score"] = result.QualityReport.Score
if len(result.QualityReport.Violations) > 0 {
artifact["quality_violations"] = result.QualityReport.Violations
}
}
meta["artifact"] = artifact
}
if s.pwSvc != nil && record.ProjectID > 0 && record.RunID != "" {
if err := s.pwSvc.UpdateWorkflowRun(ctx, record.RunID, terminalStatus, docID, artifactTitle, ""); err != nil {
return "finalizing_failed", fmt.Errorf("update workflow run: %w", err)
}
}
if s.svc != nil && s.svc.ContextManager() != nil && record.AssistantMsgID > 0 {
if err := s.svc.ContextManager().FinalizeAssistantTaskMessage(ctx, record.AssistantMsgID, summaryContent, meta, terminalStatus); err != nil {
return "finalizing_failed", fmt.Errorf("finalize assistant message: %w", err)
}
}
return terminalStatus, nil
}
func resolveTerminalStatus(result *bizdev.TaskRunResult) string {
return result.TerminalStatus()
}
func buildTaskCompletionSummary(result *bizdev.TaskRunResult, terminalStatus string) string {
_ = terminalStatus
return result.CompletionSummary()
}
func hashOutputFile(workDir string) (string, error) {
path := filepath.Join(workDir, "output.md")
data, err := os.ReadFile(path)
if err != nil {
return "", err
}
if len(data) == 0 {
return "", fmt.Errorf("output.md is empty")
}
sum := sha256.Sum256(data)
return hex.EncodeToString(sum[:]), nil
}
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,
}
}