Files
MailUI4Agents/server/internal/models/address.go

169 lines
5.1 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 models
import (
"fmt"
"strings"
)
// Address 是三维寻址 name@path.session 的解析结果
type Address struct {
Name string `json:"name"` // Agent 实例名(或人类用户名)
Path string `json:"path"` // 工作区路径(可含 /,可为空)
Session string `json:"session"` // 会话别名;"new" = 新建;"" = 默认会话
Raw string `json:"raw"` // 原始字符串
}
// SessionMode 是 session 位的三种语义
type SessionMode int
const (
// SessionDefaultsession 位省略 → 投递到 name@path 的默认会话(不存在则建立)
SessionDefault SessionMode = iota
// SessionNewsession 位为 new → 强制新建一个会话
SessionNew
// SessionNamedsession 位为具体别名 → 必须已存在,否则无法送达
SessionNamed
)
// Mode 返回该地址 session 位的语义
func (a Address) Mode() SessionMode {
switch a.Session {
case "":
return SessionDefault
case "new":
return SessionNew
default:
return SessionNamed
}
}
// IsNewSession 表示该地址要求新建会话(仅 session == "new")。
// 注意session 位省略不等于 new那是「默认会话」见 Mode()。
func (a Address) IsNewSession() bool {
return a.Mode() == SessionNew
}
// IsDefaultSession 表示该地址省略了 session 位,走默认会话
func (a Address) IsDefaultSession() bool {
return a.Mode() == SessionDefault
}
func (a Address) String() string {
return a.Raw
}
// ParseAddress 解析 name@path.session 三维地址。
//
// 支持形态:
//
// deepseekharness@/program.updatefeature → name=deepseekharness path=/program session=updatefeature
// pi@root.new → name=pi path=root session=new新建
// builder@ModelRouter.fix-leak → name=builder path=ModelRouter session=fix-leak
// human@.new → name=human path="" session=new
// human → name=human path="" session=""(默认会话)
//
// 规则:
// - 第一个 @ 之前是 name必填
// - @ 之后按【最后一个 .】切成 path 与 session因此 path 内可以包含 . 与 /
// - 没有 . 时,整段视为 pathsession 为空(默认会话)
//
// session 位三态语义见 Address.Mode():省略=默认会话new=新建,其他=必须已存在。
func ParseAddress(s string) (Address, error) {
raw := strings.TrimSpace(s)
if raw == "" {
return Address{}, fmt.Errorf("empty address")
}
// 只有形如 "@name@path.session" 时才剥掉前导 @
// "@ModelRouter.new" 缺少 name应当报错而不是被当成名字。
trimmed := raw
if strings.HasPrefix(raw, "@") && strings.Contains(raw[1:], "@") {
trimmed = raw[1:]
}
at := strings.Index(trimmed, "@")
if at < 0 {
// 只有名字human / builder
name := strings.TrimSpace(trimmed)
if name == "" {
return Address{}, fmt.Errorf("missing agent name in %q", raw)
}
return Address{Name: name, Raw: raw}, nil
}
name := strings.TrimSpace(trimmed[:at])
if name == "" {
return Address{}, fmt.Errorf("missing agent name in %q", raw)
}
rest := trimmed[at+1:]
// 按最后一个 . 切 path / sessionpath 内允许 / 与 .
var path, session string
if dot := strings.LastIndex(rest, "."); dot >= 0 {
path = rest[:dot]
session = rest[dot+1:]
} else {
path = rest
}
return Address{
Name: name,
Path: strings.TrimSpace(path),
Session: strings.TrimSpace(session),
Raw: raw,
}, nil
}
// FormatAddress 把三段拼回可寻址的 name@path.session。
//
// **必须走这个函数而不是自己拼字符串**path 为空时(人类用户没有工作区)
// 朴素拼接得到 "admin.silent-harbor",而它没有 @ParseAddress 会把整串当成
// 名字session 位丢失,地址静默失效。空 path 也必须留下那个 @ 与 . ——
// "admin@.silent-harbor" 才解析成 name=admin path="" session=silent-harbor。
//
// session 传空则省略该位(默认会话语义)。
func FormatAddress(name, path, session string) string {
name = strings.TrimSpace(name)
path = strings.TrimSpace(path)
session = strings.TrimSpace(session)
if name == "" {
return ""
}
if session == "" {
if path == "" {
return name
}
return name + "@" + path
}
return name + "@" + path + "." + session
}
// WithSession 返回同一收件方在指定会话下的地址。
// 用于把 .new 换成刚建出来的会话别名 —— 参与方拿到的地址必须是能再次投递的那个。
func (a Address) WithSession(session string) string {
return FormatAddress(a.Name, a.Path, session)
}
// ParseAddressList 解析逗号/分号/空白分隔的多个地址(用于 CC
func ParseAddressList(s string) ([]Address, error) {
raw := strings.TrimSpace(s)
if raw == "" {
return nil, nil
}
fields := strings.FieldsFunc(raw, func(r rune) bool {
return r == ',' || r == ';' || r == '\n' || r == '\t' || r == ' '
})
out := make([]Address, 0, len(fields))
for _, f := range fields {
addr, err := ParseAddress(f)
if err != nil {
return nil, err
}
out = append(out, addr)
}
return out, nil
}