Files
Agent-System-Backend/internal/runtime/hostbridge.go
T
weichen f1302ed5b3 Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements.

Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate.

Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable.

Refs: Versioned dependencies U1/U3; feature/dependency-workspace
2026-10-13 15:47:16 +00:00

186 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package runtime
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/adapters/native"
"git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/tools"
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
)
// kernelToolNames 是内核工作区自带的工具。同名宿主工具不注入,避免步骤因冲突失败。
var kernelToolNames = map[string]bool{
"read_file": true, "write_file": true, "edit_file": true, "multi_edit": true,
"list_dir": true, "grep": true, "glob": true, "load_skill": true,
"run_python": true, "run_command": true, "web_search": true, "tavily_search": true,
"fetch_url": true, "delegate_task": true,
}
type producedSink interface {
add(ref *core.ArtifactRef)
}
type artifactList struct {
items []*core.ArtifactRef
}
func (a *artifactList) add(ref *core.ArtifactRef) {
if ref != nil {
a.items = append(a.items, ref)
}
}
// bridgeHostTools 把画像允许的产品工具包成内核宿主工具。
// register_artifact 会先把内核步骤目录里的文件复制到会话工作区。
func bridgeHostTools(rc RunCtx, produced *artifactList) []tools.Tool {
if produced == nil {
produced = &artifactList{}
}
policy := rc.Profile().Tools
var out []tools.Tool
for _, desc := range rc.Tools() {
if desc.Hidden || kernelToolNames[desc.Name] || !policy.ToolAllowed(desc.Name) {
continue
}
out = append(out, &bridgedTool{rc: rc, desc: desc, produced: produced})
}
if rc.Settings().GetBool("ui.cardflow", false) {
for _, desc := range cardToolDescs() {
if policy.ToolAllowed(desc.Name) {
out = append(out, &bridgedTool{rc: rc, desc: desc, produced: produced, card: true})
}
}
}
return out
}
type bridgedTool struct {
rc RunCtx
desc core.ToolDesc
produced *artifactList
card bool
}
func (b *bridgedTool) Name() string { return b.desc.Name }
func (b *bridgedTool) Description() string { return b.desc.Description }
func (b *bridgedTool) Parameters() json.RawMessage { return b.desc.Parameters }
func (b *bridgedTool) Execute(ctx context.Context, args json.RawMessage) (tools.Result, error) {
if b.desc.Name == "register_artifact" {
stageStepFile(ctx, conversationSandbox(b.rc), args)
}
if b.card {
out, err := executeCardTool(b.rc, b.desc.Name, args)
if err != nil {
return tools.Result{Content: err.Error(), IsError: true}, nil
}
return tools.Result{Content: out}, nil
}
res := b.rc.ExecuteTool(core.ToolCallRequest{Name: b.desc.Name, Arguments: args})
if res.Artifact != nil {
b.produced.add(&core.ArtifactRef{ArtifactID: res.Artifact.ID, Name: res.Artifact.Name, Kind: res.Artifact.Kind})
}
if res.Status != "ok" {
return tools.Result{Content: res.Output, IsError: true}, nil
}
return tools.Result{Content: res.Output}, nil
}
func conversationSandbox(rc RunCtx) string {
s, ok := rc.(interface{ ConversationSandbox() string })
if !ok {
return ""
}
return s.ConversationSandbox()
}
func stageStepFile(ctx context.Context, sandbox string, args json.RawMessage) {
step := native.WorkDir(ctx)
if step == "" || sandbox == "" {
return
}
var a struct {
Path string `json:"path"`
}
if json.Unmarshal(args, &a) != nil || strings.TrimSpace(a.Path) == "" || strings.Contains(a.Path, "..") {
return
}
src := filepath.Join(step, filepath.FromSlash(a.Path))
dst := filepath.Join(sandbox, filepath.FromSlash(a.Path))
if _, err := os.Stat(dst); err == nil {
return
}
body, err := os.ReadFile(src)
if err != nil {
return
}
if os.MkdirAll(filepath.Dir(dst), 0o755) != nil {
return
}
_ = os.WriteFile(dst, body, 0o644)
}
func cardToolDescs() []core.ToolDesc {
return []core.ToolDesc{
{Name: toolCard, Description: "提交产物卡片", Parameters: json.RawMessage(`{"type":"object","properties":{"title":{"type":"string"},"summary":{"type":"string"},"body":{"type":"string"},"scope":{"type":"string"}},"required":["title","body"]}`)},
{Name: toolCardBegin, Description: "开始流式产物卡", Parameters: json.RawMessage(`{"type":"object","properties":{"title":{"type":"string"},"summary":{"type":"string"},"scope":{"type":"string"}},"required":["title"]}`)},
{Name: toolCardAppend, Description: "追加流式产物卡", Parameters: json.RawMessage(`{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]}`)},
{Name: toolCardCommit, Description: "收尾流式产物卡", Parameters: json.RawMessage(`{"type":"object","properties":{}}`)},
}
}
func executeCardTool(rc RunCtx, name string, args json.RawMessage) (string, error) {
switch name {
case toolCard:
var a struct{ Title, Summary, Body, Scope string }
if err := json.Unmarshal(args, &a); err != nil || strings.TrimSpace(a.Title) == "" || strings.TrimSpace(a.Body) == "" {
return "", fmt.Errorf("submit_card 参数错误")
}
card := core.ActivityCard{Kind: core.CardKindResult, Title: a.Title, Status: core.CardDone, Tone: "ok", Body: []core.UINode{{Type: "markdown", Markdown: a.Body}}}
if a.Scope == "project" {
card.ProjectID = core.ID(rc.Run().ProjectID)
}
created, err := rc.SubmitCard(card)
if err != nil {
return "", err
}
return "已作为卡片发出(" + created.Title + ")", nil
case toolCardBegin:
var a struct{ Title, Summary, Scope string }
if err := json.Unmarshal(args, &a); err != nil || strings.TrimSpace(a.Title) == "" {
return "", fmt.Errorf("card_begin 参数错误")
}
card := core.ActivityCard{Kind: core.CardKindResult, Title: a.Title, Status: core.CardActive, Tone: "ok"}
if a.Scope == "project" {
card.ProjectID = core.ID(rc.Run().ProjectID)
}
created, err := rc.BeginStreamCard(card)
if err != nil {
return "", err
}
return "流式卡已开始(" + created.Title + ")", nil
case toolCardAppend:
var a struct{ Text string }
if err := json.Unmarshal(args, &a); err != nil {
return "", err
}
if err := rc.AppendStreamCard(a.Text); err != nil {
return "", err
}
return "已追加", nil
case toolCardCommit:
card, err := rc.CommitStreamCard()
if err != nil {
return "", err
}
return "已作为卡片发出(" + card.Title + ")", nil
default:
return "", nil
}
}