Merge remote-tracking branch 'origin/main' into feature/chat-timeline-preview
This commit is contained in:
Vendored
+1
-1
Submodule deps/agent-native updated: 292142ac1a...f9ab2ef698
Vendored
+1
-1
Submodule deps/agent-squad updated: 5454f7485e...9a402e468e
@@ -16,7 +16,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
agentllm "agent-native/runtime/llm"
|
||||
agenttools "agent-native/runtime/tools"
|
||||
squad "agent-squad/squad"
|
||||
"agent-system-backend/internal/bus"
|
||||
"agent-system-backend/internal/cardsvc"
|
||||
"agent-system-backend/internal/cmdbus"
|
||||
@@ -108,6 +110,33 @@ func Run(opt Options) error {
|
||||
body, err := promptSvc.AgentPrompt(store.OwnerDefault, "agent.squad."+kind)
|
||||
return body, err == nil
|
||||
}
|
||||
// 小队的 agent-native 内核工厂(组合根装配,逐次运行时从 settings 解析):
|
||||
// 模型走 settings.models.chat 同一路由;搜索/解释器复用产品同一套配置与白名单;
|
||||
// 技能根取数据目录 skills/(存在才注入)。主模型未配置时返回未启用(不阻塞产品)。
|
||||
squadadapter.AgentNativeOptions = func() (squad.AgentNativeConfig, bool) {
|
||||
effNow := setSvc.Effective(store.OwnerDefault, "")
|
||||
route := runtime.ResolveChatModel(core.AgentProfile{}, effNow)
|
||||
if route.Model == "" || route.Base == "" {
|
||||
return squad.AgentNativeConfig{}, false
|
||||
}
|
||||
cfg := squad.AgentNativeConfig{
|
||||
Client: &agentllm.Client{BaseURL: route.Base, APIKey: route.Key},
|
||||
DefaultModel: route.Model,
|
||||
MaxTurns: 12,
|
||||
Timeout: 10 * time.Minute,
|
||||
}
|
||||
if keys := splitKeyPool(effNow.GetString("tools.tavily.apiKeys", "")); len(keys) > 0 {
|
||||
cfg.Search = agenttools.NewTavilyProvider(effNow.GetString("tools.tavily.endpoint", ""), keys)
|
||||
}
|
||||
if effNow.GetBool("runtime.exec", true) {
|
||||
cfg.Exec = tools.InterpreterExecConfig()
|
||||
}
|
||||
skillsRoot := filepath.Join(opt.DataDir, "skills")
|
||||
if st2, err := os.Stat(skillsRoot); err == nil && st2.IsDir() {
|
||||
cfg.SkillsRoots = []string{skillsRoot}
|
||||
}
|
||||
return cfg, true
|
||||
}
|
||||
profSvc := profiles.New(st)
|
||||
if err := profSvc.EnsureDefault(); err != nil {
|
||||
return fmt.Errorf("default profile: %w", err)
|
||||
|
||||
@@ -68,10 +68,7 @@ func Orchestrate(rc Ctx, dataDir, goalText string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
backends := squad.NewBackendResolver()
|
||||
backends.Register("agent-system", func(role squad.RoleSpec) (exec.Backend, error) {
|
||||
return &stepBackend{rc: rc, role: role}, nil
|
||||
})
|
||||
backends := buildBackends(rc)
|
||||
|
||||
engine, err := squad.NewEngine(squad.Options{
|
||||
DataDir: runDir,
|
||||
@@ -262,6 +259,27 @@ func isHoldStatus(s string) bool {
|
||||
return s == "waiting_human" || s == "awaiting_confirmation" || s == "awaiting_clarification" || s == "quality_review"
|
||||
}
|
||||
|
||||
// AgentNativeOptions 可选组合根注入:返回配置且启用时,为每次小队运行注册
|
||||
// "agent-native" 内核后端工厂(模型/搜索/执行/技能根由组合根从 settings 解析)。
|
||||
var AgentNativeOptions func() (squad.AgentNativeConfig, bool)
|
||||
|
||||
// buildBackends 组装小队后端解析器:"agent-system"(SpawnSubagent 路径)恒注册;
|
||||
// 组合根注入 AgentNativeOptions 时额外注册 "agent-native" 内核工厂——角色显式
|
||||
// 声明 Backend:"agent-native" 才会走内核执行;默认后端仍是 agent-system,
|
||||
// 不因工厂注册而切换(对齐 parity 渐进启用条款)。
|
||||
func buildBackends(rc Ctx) *squad.BackendResolver {
|
||||
backends := squad.NewBackendResolver()
|
||||
backends.Register("agent-system", func(role squad.RoleSpec) (exec.Backend, error) {
|
||||
return &stepBackend{rc: rc, role: role}, nil
|
||||
})
|
||||
if AgentNativeOptions != nil {
|
||||
if cfg, ok := AgentNativeOptions(); ok {
|
||||
backends.RegisterAgentNativeFull(cfg)
|
||||
}
|
||||
}
|
||||
return backends
|
||||
}
|
||||
|
||||
// RoleInstructions 小队角色指令来源:app 装配时注入提示词库查找
|
||||
// (agent.squad.<kind>);nil 或未命中 = 使用内联回退文案(测试与骨架兼容)。
|
||||
var RoleInstructions func(kind string) (string, bool)
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"time"
|
||||
|
||||
core "agent-system-core"
|
||||
|
||||
agenttools "agent-native/runtime/tools"
|
||||
)
|
||||
|
||||
// RegisterInterpreterTools 注册解释器执行原语(run_shell / run_python)。
|
||||
@@ -232,3 +234,20 @@ func firstPos(vals ...int) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// InterpreterExecConfig 把产品解释器白名单换算为 agent-native 内核的
|
||||
// ExecConfig(squad 角色经 agent-native 后端执行时复用同一套约束与环境)。
|
||||
// 两个解释器都缺席时返回 nil(内核侧不注册执行工具,fail-closed)。
|
||||
func InterpreterExecConfig() *agenttools.ExecConfig {
|
||||
cfg := &agenttools.ExecConfig{Env: execEnv()}
|
||||
if p, err := exec.LookPath("bash"); err == nil {
|
||||
cfg.Bash = p
|
||||
}
|
||||
if p, err := exec.LookPath("python3"); err == nil {
|
||||
cfg.Python = p
|
||||
}
|
||||
if cfg.Bash == "" && cfg.Python == "" {
|
||||
return nil
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -138,3 +138,24 @@ func TestRunPythonWritesZipMagic(t *testing.T) {
|
||||
t.Fatalf("docx magic: %q", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpreterExecConfigForKernel(t *testing.T) {
|
||||
cfg := InterpreterExecConfig()
|
||||
if cfg == nil {
|
||||
t.Fatal("host has bash/python3; kernel exec config must resolve")
|
||||
}
|
||||
if cfg.Bash == "" && cfg.Python == "" {
|
||||
t.Fatal("at least one interpreter path must be set")
|
||||
}
|
||||
if !filepath.IsAbs(cfg.Bash) && cfg.Bash != "" {
|
||||
t.Fatalf("bash path must be absolute, got %q", cfg.Bash)
|
||||
}
|
||||
for _, kv := range cfg.Env {
|
||||
if strings.HasPrefix(kv, "PATH=") || strings.HasPrefix(kv, "HOME=") || strings.HasPrefix(kv, "LANG=") ||
|
||||
strings.HasPrefix(kv, "LC_") || strings.HasPrefix(kv, "TZ=") || strings.HasPrefix(kv, "TERM=") ||
|
||||
kv == "PYTHONDONTWRITEBYTECODE=1" {
|
||||
continue
|
||||
}
|
||||
t.Fatalf("env whitelist violated: %q", kv)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user