feat: AutoZero 通用 Agent 框架(从 AutoGo 五阶段拆分完成)
llm(三协议 Provider)/ session(JSONL 持久化)/ tools(Registry、围栏、 builtin、computer)/ cdp(browser)/ skills / mcp / agent(循环、两级压缩、 编排、审批门控)+ 底座 jsonl/textx/execx/diag/scratch/spill/imagecache。 产品装配代码(config 桥接、schedule 工具、memory/plan/retro/prompt) 留在 AutoGo,宿主接入方式见 MIGRATION.md。
This commit is contained in:
commit
d5dac5a6fc
140 files changed
+26497
No files matched your search
@@ -0,0 +1,89 @@
|
||||
package session
|
||||
|
||||
// 会话标题:sidecar 文件 <id>.title(与会话 jsonl 同目录)。
|
||||
// 标题本地派生自用户首条消息,零请求、零延迟;TUI 与 WebUI 共用本文件。
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SafeID 拒绝路径穿越,标题文件只允许落在会话目录内。
|
||||
func SafeID(id string) (string, bool) {
|
||||
if id == "" || strings.ContainsAny(id, `/\`) || strings.Contains(id, "..") {
|
||||
return "", false
|
||||
}
|
||||
return id, true
|
||||
}
|
||||
|
||||
// TitlePath 标题 sidecar 文件路径。
|
||||
func TitlePath(id string) string {
|
||||
return filepath.Join(Dir(), id+".title")
|
||||
}
|
||||
|
||||
// LoadTitle 读取标题;无标题返回 ""。
|
||||
func LoadTitle(id string) string {
|
||||
safe, ok := SafeID(id)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
b, err := os.ReadFile(TitlePath(safe))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(b))
|
||||
}
|
||||
|
||||
// SaveTitle 写入标题(去首尾空白,上限 60 字)。
|
||||
func SaveTitle(id, title string) error {
|
||||
safe, ok := SafeID(id)
|
||||
if !ok {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
title = TruncateRunes(strings.TrimSpace(title), 60)
|
||||
if title == "" {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
if err := os.MkdirAll(Dir(), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(TitlePath(safe), []byte(title), 0o644)
|
||||
}
|
||||
|
||||
// TruncateRunes 按 rune 截断。
|
||||
func TruncateRunes(s string, n int) string {
|
||||
r := []rune(s)
|
||||
if len(r) <= n {
|
||||
return s
|
||||
}
|
||||
return string(r[:n])
|
||||
}
|
||||
|
||||
// SanitizeTitle 清洗标题:去空白、去包围引号,折叠空白,截断 24 字。
|
||||
func SanitizeTitle(s string) string {
|
||||
s = strings.TrimSpace(s)
|
||||
s = strings.Trim(s, `"'「」『』【】<>`)
|
||||
s = strings.TrimSpace(s)
|
||||
s = strings.Join(strings.Fields(s), " ")
|
||||
return TruncateRunes(s, 24)
|
||||
}
|
||||
|
||||
// DeriveTitle 从用户首条消息本地派生会话标题:不发请求、零延迟、无失败路径。
|
||||
//
|
||||
// 此前为标题单独发一次模型调用,代价远大于收益:该模型上常达 ~27s,与主回合并发时
|
||||
// 极易超时(30s 上限),导致长回合的会话静默丢标题。而标题只是会话列表里的速记,
|
||||
// 用「用户诉求的首行」就足够辨认,没必要为一个标签花掉一次完整往返。
|
||||
func DeriveTitle(userMsg string) string {
|
||||
s := strings.TrimSpace(userMsg)
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
// 多行输入取首行——那通常是主诉求,后续多为补充细节或粘贴内容。
|
||||
if i := strings.IndexByte(s, '\n'); i >= 0 {
|
||||
s = s[:i]
|
||||
}
|
||||
// 去掉 markdown 装饰:标题符号、引用、列表符、代码块围栏。
|
||||
s = strings.TrimLeft(s, "#>*-`~ ")
|
||||
return SanitizeTitle(s)
|
||||
}
|
||||
Reference in new issue
Block a user