fix: LLM 工具循环 400、中断消息注入、ConPTY 终端支持

- agent: 工具轮请求尾部补 user 占位(zen 网关强制),tool 消息正确配对
- agent: 工具提醒/中断以 system 角色注入并带 [中断消息] 前缀,不进用户履历;系统提示词说明中断消息格式
- agentcli: 基于 ConPTY 的交互式终端(ptywin fork),terminal_create/read/write/resize/close/watch
- webui: server 输出通道适配器(保留 reasoning_content/disable_thinking)
- GUI: 沉浸式标题栏、icon 圆角重制、mascot 等打磨
This commit is contained in:
JianFeeeee
2026-08-14 00:48:40 +08:00
parent 816597caac
commit 147d0baaf9
43 changed files with 4670 additions and 1478 deletions

View File

@ -6,6 +6,7 @@ import (
"log"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
@ -30,6 +31,8 @@ type Plugin struct {
baseDir string // L0 写前留档根目录(<data>/file_baseline 的父目录),空则禁用
}
var isWindowsBuild = runtime.GOOS == "windows"
func New(name string) *Plugin {
return &Plugin{name: name}
}
@ -178,12 +181,41 @@ func (p *Plugin) resolvePath(userPath string) (string, error) {
return "", fmt.Errorf("resolve path: %w", err)
}
base := filepath.Clean(p.filesDir)
if base != "/" && !strings.HasPrefix(abs, base+string(filepath.Separator)) && abs != base {
if !pathWithinSandbox(abs, base) {
return "", fmt.Errorf("path outside sandbox: %s", userPath)
}
return abs, nil
}
// pathWithinSandbox 判断 abs 是否位于沙箱 base 之内。
// Windows 文件系统大小写不敏感,且卷根目录(如 C:\)应放行全盘路径。
func pathWithinSandbox(abs, base string) bool {
lower := func(s string) string {
if isWindowsBuild {
return strings.ToLower(s)
}
return s
}
abs = filepath.Clean(abs)
base = filepath.Clean(base)
if equalFoldPath(abs, base) {
return true
}
// 卷根沙箱C:\、D:\ 等)表示整机可访问
if isWindowsBuild && len(base) == 3 && base[1] == ':' && base[2] == '\\' {
return true
}
prefix := lower(base) + string(filepath.Separator)
return strings.HasPrefix(lower(abs), prefix)
}
func equalFoldPath(a, b string) bool {
if isWindowsBuild {
return strings.EqualFold(a, b)
}
return a == b
}
func (p *Plugin) handleRead(args map[string]interface{}) (interface{}, error) {
path, _ := args["path"].(string)
if path == "" {