Files
Agent-System/internal/bizdev/task_result_status.go
T
weichen f8730791b9 fix(coordinator): 回填会话项目绑定,避免受控投影被跳过
聊天入口只把项目 ID 放在授权字段,协调器仍读空的 UserCtx.Project.ID,受控任务因此以 project_id=0 跳过账本投影,回复却称正文已保存。
2026-09-28 12:34:32 +00:00

158 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package bizdev
import (
"fmt"
"strings"
"chat-agent-system/internal/squad"
)
// Terminal task statuses projected to chat / workflow_runs.
const (
TaskStatusCompleted = "completed"
TaskStatusFailed = "failed"
TaskStatusQualityFailed = "quality_failed"
TaskStatusPendingAuthorConfirmation = "pending_author_confirmation"
)
// AwaitsAgentConfirmation reports high/critical revision candidate awaiting author decision.
// Distinct from DocGov change-proposal intercept (PendingProposal).
func (r *TaskRunResult) AwaitsAgentConfirmation() bool {
return r != nil && r.PendingAgentCandidateID > 0
}
// IsProposalIntercept reports claimed-document DocGov change-proposal interception.
func (r *TaskRunResult) IsProposalIntercept() bool {
return r != nil && r.PendingProposal != nil && r.RequiresAuthorApproval
}
// TerminalStatus maps TaskRunResult to a conversation/workflow terminal status.
// Pending agent confirmation is never "completed" until commit+projection.
// Quality failure stays quality_failed even when Success was cleared to block Materialize.
func (r *TaskRunResult) TerminalStatus() string {
if r == nil {
return TaskStatusFailed
}
if r.QualityReport != nil && !r.QualityReport.Passed {
return TaskStatusQualityFailed
}
if !r.Success {
return TaskStatusFailed
}
if r.AwaitsAgentConfirmation() {
return TaskStatusPendingAuthorConfirmation
}
return TaskStatusCompleted
}
// CompletionSummary builds the assistant-visible completion text for sync and async finalizers.
func (r *TaskRunResult) CompletionSummary() string {
if r == nil {
return "任务执行失败。"
}
title := r.ArtifactTitle
if title == "" && r.TaskKey != "" {
title = squad.TaskArtifactTitle(r.TaskKey)
}
if title == "" {
title = "交付文档"
}
status := r.TerminalStatus()
var b strings.Builder
switch status {
case TaskStatusPendingAuthorConfirmation:
fmt.Fprintf(&b, "《%s》文档候选已生成,待编制人确认。\n\n", title)
b.WriteString("当前状态:**待编制人确认**")
if r.PendingRiskLevel != "" {
fmt.Fprintf(&b, "(风险:%s)", r.PendingRiskLevel)
}
b.WriteString("\n正式正文仍为账本当前 head;候选正文仅保留在不可变快照中。请打开文档「待确认报告」确认提交或拒绝后,正式正文才会更新。")
if r.PendingConfirmationNote != "" {
fmt.Fprintf(&b, "\n%s", r.PendingConfirmationNote)
}
fmt.Fprintf(&b, "\n候选 ID:%d", r.PendingAgentCandidateID)
if r.PendingChangeReportID > 0 {
fmt.Fprintf(&b, " · 变更报告:%d", r.PendingChangeReportID)
}
case TaskStatusQualityFailed:
fmt.Fprintf(&b, "《%s》已生成。\n\n", title)
b.WriteString("当前状态:**质量检查未通过,待整改**")
if r.QualityReport != nil {
fmt.Fprintf(&b, "\n质量评分:%.0f/100", r.QualityReport.Score)
if len(r.QualityReport.Violations) > 0 {
fmt.Fprintf(&b, "\n主要问题:%s", strings.Join(r.QualityReport.Violations, ";"))
}
}
case TaskStatusCompleted:
fmt.Fprintf(&b, "《%s》已生成。\n\n", title)
b.WriteString("当前状态:**已完成**")
if r.QualityReport != nil && r.QualityReport.Passed {
fmt.Fprintf(&b, "\n质量评分:%.0f/100", r.QualityReport.Score)
}
if r.DocRecord == nil && controlledDeliverableRequiresProject(r.TaskKey) {
b.WriteString("\n交付物已生成于工作区,尚未写入受控文档账本。")
}
default:
fmt.Fprintf(&b, "《%s》未成功落盘。\n\n", title)
if r.ErrorReason != "" {
fmt.Fprintf(&b, "执行未成功:%s", r.ErrorReason)
} else {
b.WriteString("任务执行失败。")
}
}
if r.DocRecord != nil && r.DocRecord.DocID != "" {
fmt.Fprintf(&b, "\n\n📄 受控文档:`%s`", r.DocRecord.DocID)
}
return b.String()
}
// ArtifactMetadata returns chat metadata fields for a clickable document entry.
func (r *TaskRunResult) ArtifactMetadata() map[string]any {
if r == nil {
return nil
}
docID := ""
if r.DocRecord != nil {
docID = r.DocRecord.DocID
}
if docID == "" {
return nil
}
title := r.ArtifactTitle
if title == "" && r.TaskKey != "" {
title = squad.TaskArtifactTitle(r.TaskKey)
}
out := map[string]any{
"doc_id": docID,
"title": title,
"task_key": r.TaskKey,
}
if r.WorkDir != "" {
out["workspace_id"] = r.WorkDir
}
if r.DocRecord != nil && r.DocRecord.ContentSHA256 != "" {
out["sha256"] = r.DocRecord.ContentSHA256
}
if r.QualityReport != nil {
out["quality_passed"] = r.QualityReport.Passed
out["quality_score"] = r.QualityReport.Score
if len(r.QualityReport.Violations) > 0 {
out["quality_violations"] = r.QualityReport.Violations
}
}
if r.AwaitsAgentConfirmation() {
out["pending_agent_candidate_id"] = r.PendingAgentCandidateID
if r.PendingChangeReportID > 0 {
out["pending_change_report_id"] = r.PendingChangeReportID
}
if r.PendingRiskLevel != "" {
out["pending_risk_level"] = r.PendingRiskLevel
}
out["awaiting_author_confirmation"] = true
}
return out
}