mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-14 07:00:23 +02:00
* feat(chat): add project-based conversation sidebar * feat(chat): refine Codex-style conversation UI * feat(chat): add Codex-style conversation workflow * feat(ui): 优化对话框与项目侧边栏交互 * fix(chat): 修复暗色输入框圆角填色 * fix(chat): 恢复输入区分层错位布局 * fix(hitl): isolate reviewer state per conversation * feat(hitl): 增加双入口审批与倒计时进度 * fix(hitl): 汇总项目审批并隔离对话状态 * fix(ui): 修复审批状态与无项目新任务 * fix(ui): 优化审批状态与对话切换性能 * fix(ui): 修复中断任务审批仍计时 * fix(ui): 修复多对话并发切换卡顿 * fix(hitl): 主动同步审批并关闭中断状态 * fix(ui): 固定项目审批汇总为绿色 * feat(ui): 同步系统模型与推理强度 * feat(ui): 完善项目侧栏预览与新建入口 * fix(ui): 防止无项目文件夹误展开 * fix(ui): 防止长历史对话滚动误触审批 * fix(ui): 修复对话操作并补充项目置顶 * fix(ui): 移除对话分组并调整项目置顶排序 * feat(ui): 优化迭代导航与审批交互 * fix(hitl): 将 write_file 加入内置免审批工具 * fix(chat): 支持回车发送与 Shift 回车换行 * fix(chat): 优化对话刷新与 Codex 风格交互 * fix(ui): 显示对话具体更新时间 * fix(ui): 优化对话刷新与项目加载 * fix(ui): 修复 Agent 审查文字裁切 * fix(chat): 修复刷新续流与多标签页同步 * fix(chat): 修复滚动跟随与中断任务终态 * fix(ui): 修复流式滚动跳动与工具状态恢复 * fix(ui): 修复刷新后流式输出停止粘底
90 lines
2.7 KiB
Go
90 lines
2.7 KiB
Go
package multiagent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const toolSearchToolName = "tool_search"
|
|
|
|
// HitlExemptMetaTools 为 HITL 内置免审批工具:包括编排/元工具,以及模型输出修复链路依赖的 write_file。
|
|
// tool_search 必须免审批,否则其 HITL 拒绝结果与 Eino toolsearch 中间件不兼容(会硬崩 ChatModel);
|
|
// write_file 必须免审批,否则长脚本或请求体无法先安全落盘,模型输出修复链路会被再次阻塞。
|
|
var HitlExemptMetaTools = []string{
|
|
toolSearchToolName,
|
|
"skill",
|
|
"task",
|
|
"write_todos",
|
|
"write_file",
|
|
"transfer_to_agent",
|
|
"exit",
|
|
"TaskCreate",
|
|
"TaskGet",
|
|
"TaskUpdate",
|
|
"TaskList",
|
|
"upsert_project_fact",
|
|
"get_project_fact",
|
|
}
|
|
|
|
// IsToolSearchTool reports whether name is the Eino dynamictool tool_search meta-tool.
|
|
func IsToolSearchTool(name string) bool {
|
|
return strings.EqualFold(strings.TrimSpace(name), toolSearchToolName)
|
|
}
|
|
|
|
// MergeHitlExemptMetaTools unions configured whitelist with built-in meta-tool exemptions.
|
|
func MergeHitlExemptMetaTools(configured []string) []string {
|
|
merged := make([]string, 0, len(configured)+len(HitlExemptMetaTools))
|
|
seen := make(map[string]struct{}, len(configured)+len(HitlExemptMetaTools))
|
|
add := func(name string) {
|
|
n := strings.ToLower(strings.TrimSpace(name))
|
|
if n == "" {
|
|
return
|
|
}
|
|
if _, ok := seen[n]; ok {
|
|
return
|
|
}
|
|
seen[n] = struct{}{}
|
|
merged = append(merged, strings.TrimSpace(name))
|
|
}
|
|
for _, t := range configured {
|
|
add(t)
|
|
}
|
|
for _, t := range HitlExemptMetaTools {
|
|
add(t)
|
|
}
|
|
return merged
|
|
}
|
|
|
|
type toolSearchHitlRejectPayload struct {
|
|
SelectedTools []string `json:"selectedTools"`
|
|
HitlRejected bool `json:"_hitlRejected"`
|
|
Reason string `json:"reason"`
|
|
}
|
|
|
|
// HitlRejectToolResult returns a tool result body safe for downstream consumers.
|
|
// tool_search must stay JSON-shaped so toolsearch.extractSelectedTools does not terminate the graph.
|
|
func HitlRejectToolResult(toolName, reason string) string {
|
|
reason = strings.TrimSpace(reason)
|
|
if !IsToolSearchTool(toolName) {
|
|
if reason == "" {
|
|
reason = "rejected by reviewer"
|
|
}
|
|
return fmt.Sprintf("[HITL Reject] Tool '%s' was rejected by reviewer. Reason: %s\nPlease adjust parameters/plan and continue without this call.",
|
|
strings.TrimSpace(toolName), reason)
|
|
}
|
|
payload := toolSearchHitlRejectPayload{
|
|
SelectedTools: []string{},
|
|
HitlRejected: true,
|
|
Reason: reason,
|
|
}
|
|
if payload.Reason == "" {
|
|
payload.Reason = "tool_search rejected by reviewer; no dynamic tools unlocked"
|
|
}
|
|
out, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return `{"selectedTools":[],"_hitlRejected":true,"reason":"tool_search rejected by reviewer"}`
|
|
}
|
|
return string(out)
|
|
}
|