ci / vet-and-test (pull_request) Successful in 39s
Why: workflow scripts and run_tool_script should share one Python host, while a slow MCP server must not block the first prompt. Evidence: GOWORK=off go test ./internal/runtime/ ./internal/app/ ./internal/settings/ ./internal/tools/ ./internal/runmgr/ Impact: runtime.codeOrchestration defaults to false, so the tool stays hidden. Workflow script confirmation and its longer timeout cap stay as they were. Refs: feature/code-orchestration Co-authored-by: Cursor <cursoragent@cursor.com>
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"agent-native/runtime/codeorch"
|
|
core "agent-system-core"
|
|
)
|
|
|
|
// RegisterCodeTool 注册 run_tool_script。开关关闭时执行会被拒绝,清单侧另行隐藏。
|
|
func RegisterCodeTool(r *Registry) error {
|
|
return r.Register(core.ToolDesc{
|
|
Name: "run_tool_script",
|
|
Description: "用一段 Python 依次调用已有工具。禁止再调用解释器、本工具、派发和工作流。失败时会列出已经执行的调用",
|
|
Source: core.SourceBuiltin,
|
|
Risk: "medium",
|
|
Parameters: json.RawMessage(`{"type":"object","properties":{"script":{"type":"string"},"timeout_sec":{"type":"integer"}},"required":["script"]}`),
|
|
}, execCodeScript)
|
|
}
|
|
|
|
type toolCaller interface {
|
|
ExecuteTool(call core.ToolCallRequest) core.ToolCallResult
|
|
}
|
|
|
|
func execCodeScript(rc ToolExecCtx, args json.RawMessage) (string, error) {
|
|
if !rc.SettingBool("runtime.codeOrchestration", false) {
|
|
return "", fmt.Errorf("代码编排未开启")
|
|
}
|
|
caller, ok := rc.(toolCaller)
|
|
if !ok {
|
|
return "", fmt.Errorf("代码编排未接入工具执行")
|
|
}
|
|
var req struct {
|
|
Script string `json:"script"`
|
|
Timeout int `json:"timeout_sec"`
|
|
}
|
|
if err := json.Unmarshal(args, &req); err != nil {
|
|
return "", err
|
|
}
|
|
if strings.TrimSpace(req.Script) == "" {
|
|
return "", fmt.Errorf("script 不能为空")
|
|
}
|
|
root := ""
|
|
if s, ok := rc.(interface{ ConversationSandbox() string }); ok {
|
|
root = s.ConversationSandbox()
|
|
}
|
|
if root == "" {
|
|
root = rc.WorkspaceRoot()
|
|
}
|
|
if root == "" {
|
|
return "", fmt.Errorf("代码编排没有会话目录")
|
|
}
|
|
lim := codeorch.Limits{FullDir: root}
|
|
if req.Timeout > 0 {
|
|
lim.Timeout = time.Duration(req.Timeout) * time.Second
|
|
}
|
|
out := codeorch.Run(context.Background(), root, req.Script, lim, nil, func(_ context.Context, call codeorch.Call) (string, error) {
|
|
if call.Op != "tool.call" {
|
|
return "", fmt.Errorf("不能调用 %s", call.Op)
|
|
}
|
|
if codeorch.Forbidden(call.Name) {
|
|
return "", fmt.Errorf("不能调用 %s", call.Name)
|
|
}
|
|
res := caller.ExecuteTool(core.ToolCallRequest{Name: call.Name, Arguments: call.Args})
|
|
if res.Status == "error" {
|
|
return "", fmt.Errorf("%s", res.Output)
|
|
}
|
|
return res.Output, nil
|
|
})
|
|
if out.Err != nil {
|
|
return out.ModelText(), fmt.Errorf("%s", out.ModelText())
|
|
}
|
|
return out.ModelText(), nil
|
|
}
|