fix(summoning): prevent identity markdown from corrupting display names (#356)

This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-03-23 10:17:47 +07:00
1 parent a9a40a3046
commit 40b01d989d
3 files changed
+128 -7

No files matched your search

-3
View File
@@ -38,9 +38,6 @@ var summoningFiles = []string{
// fileTagRe parses <file name="SOUL.md">content</file> from LLM output.
var fileTagRe = regexp.MustCompile(`(?s)<file\s+name="([^"]+)">\s*(.*?)\s*</file>`)
// identityNameRe extracts the Name field from IDENTITY.md format: - **Name:** value
var identityNameRe = regexp.MustCompile(`(?m)^-\s*\*\*Name:\*\*\s*(.+)$`)
// frontmatterTagRe parses <frontmatter>short expertise summary</frontmatter> from LLM output.
var frontmatterTagRe = regexp.MustCompile(`(?s)<frontmatter>\s*(.*?)\s*</frontmatter>`)
+69 -4
View File
@@ -4,17 +4,82 @@ import (
"strings"
)
var identityFieldPrefixes = []string{
"- **Name:**",
"- **Creature:**",
"- **Purpose:**",
"- **Vibe:**",
"- **Emoji:**",
"- **Avatar:**",
"Name:",
"Creature:",
"Purpose:",
"Vibe:",
"Emoji:",
"Avatar:",
}
// extractIdentityName extracts the Name field from IDENTITY.md content.
// Matches format: - **Name:** value
// Accepts only an inline Name value and ignores markdown field spillover.
func extractIdentityName(content string) string {
if content == "" {
return ""
}
m := identityNameRe.FindStringSubmatch(content)
if len(m) < 2 {
for _, rawLine := range strings.Split(content, "\n") {
line := strings.TrimSpace(rawLine)
switch {
case strings.HasPrefix(line, "- **Name:**"):
return normalizeIdentityName(strings.TrimSpace(strings.TrimPrefix(line, "- **Name:**")))
case strings.HasPrefix(line, "Name:"):
return normalizeIdentityName(strings.TrimSpace(strings.TrimPrefix(line, "Name:")))
}
}
return ""
}
func normalizeIdentityName(value string) string {
value = strings.TrimSpace(value)
if value == "" || looksLikeIdentityField(value) {
return ""
}
return strings.TrimSpace(m[1])
for {
next := trimMarkdownWrapper(value)
if next == value {
return value
}
value = strings.TrimSpace(next)
if value == "" || looksLikeIdentityField(value) {
return ""
}
}
}
func looksLikeIdentityField(value string) bool {
for _, prefix := range identityFieldPrefixes {
if strings.HasPrefix(value, prefix) {
return true
}
}
return false
}
func trimMarkdownWrapper(value string) string {
wrappers := [][2]string{
{"**", "**"},
{"__", "__"},
{"`", "`"},
{"*", "*"},
{"_", "_"},
}
for _, wrapper := range wrappers {
if strings.HasPrefix(value, wrapper[0]) && strings.HasSuffix(value, wrapper[1]) && len(value) > len(wrapper[0])+len(wrapper[1]) {
return strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(value, wrapper[0]), wrapper[1]))
}
}
return value
}
// suffixString returns the last n runes of s.
+59
View File
@@ -0,0 +1,59 @@
package http
import "testing"
func TestExtractIdentityName(t *testing.T) {
tests := []struct {
name string
content string
want string
}{
{
name: "extracts inline markdown name",
content: `# IDENTITY.md - Who Am I?
- **Name:** GoClaw
- **Creature:** AI assistant`,
want: "GoClaw",
},
{
name: "ignores next bullet when name is blank",
content: `# IDENTITY.md - Who Am I?
- **Name:**
- **Creature:** AI assistant`,
want: "",
},
{
name: "rejects inline spillover from another field",
content: `# IDENTITY.md - Who Am I?
- **Name:** - **Creature:** AI assistant
- **Purpose:** Help users`,
want: "",
},
{
name: "strips simple markdown wrappers from name",
content: `# IDENTITY.md - Who Am I?
- **Name:** **GoClaw**
- **Creature:** AI assistant`,
want: "GoClaw",
},
{
name: "supports plain name format",
content: `# Identity
Name: GoClaw
Emoji: 🤖`,
want: "GoClaw",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := extractIdentityName(tt.content); got != tt.want {
t.Fatalf("extractIdentityName() = %q, want %q", got, tt.want)
}
})
}
}