Files
Agent-System-Backend/internal/tools/codeorch_tool.go
T
weichen adcd8fefd2 Fence U3 account writes and stabilize directory cursors
Why: Account disable could be followed by late Backend business writes, API key creation could implicitly create users, account bootstrap locks could invert, and directory cursors were process-local or leaked out-of-scope changes.

Evidence: Local go test ./... and isolated PostgreSQL package suites passed on the staged candidate; exact committed-head SELF replay and stage 6 human review remain pending.

Impact: Adds final account transactions and external-effect checks across HTTP, Host, Bus and run manager; tightens API key issuance and persists versioned cursor signing. Delegated RDM rename still needs the separately proposed real-operator contract, so A1 and stage 6 are not complete.

Refs: Documents U3 r1, approved A1-A4 amendment fb3a821, pending delegated-projection amendment 5783940, Dev feature/iam-u3-account-directory-recovery.
2026-10-14 07:33:54 +00:00

85 lines
2.5 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("代码编排没有会话目录")
}
if err := checkToolExternalEffect(rc); err != nil {
return "", err
}
lim := codeorch.Limits{FullDir: root}
if req.Timeout > 0 {
lim.Timeout = time.Duration(req.Timeout) * time.Second
}
out := codeorch.Run(execParentCtx(rc), root, req.Script, lim, nil, func(callCtx context.Context, call codeorch.Call) (string, error) {
if err := checkExternalEffect(callCtx, func() error { return checkToolExternalEffect(rc) }); err != nil {
return "", err
}
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
}