Files
Agent-Intent/intent/fold.go
weichenandCursor 05e74d8e32
ci / vet-and-test (pull_request) Successful in 18s
ci / docker-checkout-hostgateway (pull_request) Successful in 1s
Fold NFKC input and stop catalog metadata leaks
Why: fullwidth text missed case-folded terms, SpecByID shared maps, the catalog prompt printed handlers and private metadata, overlapping slots kept both values, and negation was easy to mistake for semantics.
Evidence: GOWORK=off go vet ./... && go test -count=1 -race ./... passed, including TestR2NFKCFoldsFullwidthButNotTraditional, TestR2SpecByIDDeepCopiesMetadata, TestR2CatalogHidesPrivateMetadataAndHandlerInMarkdown, TestR2EvalRulesDocumentsMaxSpecs, TestR2OverlappingSlotsAreAmbiguous, and TestR2NegationIsOnlyExcludesAny.
Impact: NFKC is opt-in and does not convert traditional Chinese; catalog markdown omits handlers and non-allowlisted metadata; overlapping slot spans are invalid.
Refs: I4, I7, I8, I10, I11, I12
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 12:56:55 +00:00

81 lines
2.0 KiB
Go

package intent
import (
"strings"
"golang.org/x/text/unicode/norm"
)
// foldMatchText 做识别用折叠。nfkc 只做兼容等价(全角 ASCII、组合字符),
// 不做繁简转换。caseSensitive 为真时保留大小写。
func foldMatchText(s string, nfkc, caseSensitive bool) string {
if nfkc {
s = norm.NFKC.String(s)
}
if !caseSensitive {
s = strings.ToLower(s)
}
return s
}
// foldInput 返回用于匹配的文本:第一段保留大小写(可选 NFKC),第二段是其小写。
func (s *Snapshot) foldInput(input string) (matchInput, folded string) {
if s == nil {
return input, strings.ToLower(input)
}
matchInput = foldMatchText(input, s.nfkc, true)
return matchInput, strings.ToLower(matchInput)
}
func cloneStrings(in []string) []string {
if in == nil {
return nil
}
return append([]string(nil), in...)
}
func cloneIntentSpec(in IntentSpec) IntentSpec {
out := in
out.Matcher.IncludesAny = cloneStrings(in.Matcher.IncludesAny)
out.Matcher.IncludesAll = cloneStrings(in.Matcher.IncludesAll)
out.Matcher.ExcludesAny = cloneStrings(in.Matcher.ExcludesAny)
out.Matcher.Patterns = cloneStrings(in.Matcher.Patterns)
if in.Slots != nil {
out.Slots = append([]SlotSpec(nil), in.Slots...)
for i := range out.Slots {
out.Slots[i].Enum = cloneStrings(in.Slots[i].Enum)
out.Slots[i].Normalize = cloneStrings(in.Slots[i].Normalize)
}
}
if in.Metadata != nil {
out.Metadata = make(map[string]string, len(in.Metadata))
for k, v := range in.Metadata {
out.Metadata[k] = v
}
}
return out
}
// catalogMetadataAllow 是目录与 Markdown 允许带出的 metadata 键。
// handler 仍留在 CatalogSpec 供执行器按名分派,但不写入 Markdown。
var catalogMetadataAllow = map[string]bool{
"blueprint": true,
"guidance": true,
}
func catalogMetadata(in map[string]string) map[string]string {
if len(in) == 0 {
return nil
}
out := map[string]string{}
for k, v := range in {
if catalogMetadataAllow[k] {
out[k] = v
}
}
if len(out) == 0 {
return nil
}
return out
}