Files
Agent-System-Backend/internal/runmgr/inputs.go
T
weichenandCursor 6533f0fa88 Add professional standards for every agent deliverable
Why: Generated files kept Word's default theme, US Letter pages,
hand-typed bullets and empty sections. Recipes only showed library
calls, the prompt had no craft standard, and seeded skills could not
be upgraded once written.

Evidence: go test ./... passed. The house-style suite covers three
themes across docx, pptx and xlsx, and rejects a document that repeats
the diagnosed layout failures. lint on the sample minutes exits 1.

Impact: Every run gets a resident quality standard. Office files use
house-style (switchable themes, templates, imitation) and must pass
lint. Skill seeding upgrades untouched builtins and keeps user edits.
Uploads land in inputs/, and the default style is a setting.

Refs: feature/agent-professional-deliverables
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 08:16:58 +00:00

94 lines
2.6 KiB
Go

package runmgr
import (
"fmt"
"os"
"path/filepath"
"strings"
core "agent-system-core"
)
// WorkspaceFile 是要落到会话工作区 inputs/ 的原件(上传文件或默认模板)。
type WorkspaceFile struct {
Name string
Content []byte
}
// StageWorkspaceInputs 把原件和可选默认模板写到 <root>/<owner>/<conversation>/inputs/。
// root 为空时不写(工作区未配置)。返回工作区相对路径。同一批里重名的文件加序号。
func StageWorkspaceInputs(root, owner, conversation string, files []WorkspaceFile, templateName string, template []byte) ([]string, error) {
if strings.TrimSpace(root) == "" {
return nil, nil
}
dir := filepath.Join(root, owner, conversation, "inputs")
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, err
}
var rels []string
used := map[string]bool{}
for _, file := range files {
name, ok := safeInputName(file.Name)
if !ok || len(file.Content) == 0 {
continue
}
name = uniqueInputName(name, used)
if err := os.WriteFile(filepath.Join(dir, name), file.Content, 0o644); err != nil {
return rels, err
}
used[name] = true
rels = append(rels, "inputs/"+name)
}
if len(template) > 0 {
ext := filepath.Ext(templateName)
base, ok := safeInputName(templateName)
if ok {
ext = filepath.Ext(base)
}
name := "_default-template" + ext
if err := os.WriteFile(filepath.Join(dir, name), template, 0o644); err != nil {
return rels, err
}
rels = append(rels, "inputs/"+name)
}
return rels, nil
}
func (m *Manager) stageInputs(conv core.Conversation, eff core.EffectiveSettings, files []WorkspaceFile) ([]string, error) {
var templateName string
var template []byte
if id := strings.TrimSpace(eff.GetString("deliverables.templateArtifactId", "")); id != "" && m.st != nil {
if art, err := m.st.GetArtifact(conv.Owner, id); err == nil {
if body, err := m.st.ArtifactContent(conv.Owner, id); err == nil && len(body) > 0 {
templateName, template = art.Name, body
}
}
}
return StageWorkspaceInputs(m.workspaceRoot, conv.Owner, conv.ID, files, templateName, template)
}
func safeInputName(name string) (string, bool) {
base := filepath.Base(strings.TrimSpace(name))
if base == "" || base == "." || base == ".." || strings.HasPrefix(base, ".") {
return "", false
}
if strings.ContainsAny(base, `/\`) {
return "", false
}
return base, true
}
func uniqueInputName(name string, used map[string]bool) string {
if !used[name] {
return name
}
ext := filepath.Ext(name)
stem := strings.TrimSuffix(name, ext)
for n := 2; ; n++ {
candidate := fmt.Sprintf("%s-%d%s", stem, n, ext)
if !used[candidate] {
return candidate
}
}
}