Add a script host, deferred tools, and background MCP connect
ci / vet-and-test (pull_request) Successful in 49s
ci / docker-checkout-hostgateway (pull_request) Successful in 1s

Why: models need a capped way to call several tools from one script, and a slow MCP server must not block the first prompt.
Evidence: GOWORK=off go test ./runtime/codeorch/ ./runtime/tools/ ./runtime/mcp/ ./runtime/policy/ ./adapters/native/
Impact: run_tool_script stays unregistered until the caller turns it on. Deferred tools stay out of the model list. An unready MCP server connects in the background.
Refs: feature/code-orchestration
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-03 18:02:53 +00:00
co-authored by Cursor
parent b6d6708416
commit e4020d297c
16 changed files with 1223 additions and 64 deletions
+19 -2
View File
@@ -49,6 +49,8 @@ type ToolOptions struct {
HostTools []tools.Tool
Checkpoints *checkpoint.Log
CheckpointAnchor func() string
// CodeOrchestration 为真时注册 run_tool_script。默认关。
CodeOrchestration bool
}
// SessionOptions 是单次执行的会话预算与压缩。
@@ -73,6 +75,8 @@ type GateOptions struct {
Reminders func(turn int) []string
RepeatGuard bool
Hooks *hooks.Bus
// Ask 在 MCP 工具要求用户输入时调用。nil 则把问题作为错误返回。
Ask func(ctx context.Context, question string) (string, error)
}
// Options 构造后端所需的模型接入与执行参数(由组合根注入,无产品依赖)。
@@ -147,6 +151,9 @@ func (b *Backend) Execute(ctx context.Context, req exec.Request) (*exec.Result,
if b.opts.Stream == nil && (strings.TrimSpace(b.opts.APIBase) == "" || strings.TrimSpace(b.opts.APIKey) == "") {
return &exec.Result{Success: false, ErrorReason: "agent-native backend requires api base/key (or injected stream)"}, nil
}
if b.opts.Ask != nil {
ctx = mcp.WithAsk(ctx, b.opts.Ask)
}
model := strings.TrimSpace(b.opts.Model)
if strings.TrimSpace(req.Model) != "" {
model = strings.TrimSpace(req.Model)
@@ -204,9 +211,19 @@ func (b *Backend) Execute(ctx context.Context, req exec.Request) (*exec.Result,
}
tools.RegisterExecTools(reg, workDir, execCfg)
}
if b.opts.CodeOrchestration {
tools.RegisterCodeTool(reg)
}
reg.SetActivationFile(filepath.Join(workDir, ".agentx", "activated-tools.json"))
_ = reg.RestoreActivated()
if b.opts.MCP != nil {
if err := b.opts.MCP.RegisterInto(ctx, reg); err != nil {
return &exec.Result{Success: false, ErrorReason: err.Error()}, nil
switch {
case len(req.ToolAllowlist) > 0 || b.opts.MCP.Ready():
if err := b.opts.MCP.RegisterInto(ctx, reg); err != nil {
return &exec.Result{Success: false, ErrorReason: err.Error()}, nil
}
default:
b.opts.MCP.StartBackground(ctx, reg)
}
}
subagent.Register(reg, b.opts.Subagent)
+3
View File
@@ -212,6 +212,9 @@ func TestBackendMCPToolsWiredViaOptions(t *testing.T) {
t.Fatal(err)
}
defer mgr.Close()
if err := mgr.RegisterInto(context.Background(), tools.NewRegistry(t.TempDir())); err != nil {
t.Fatal(err)
}
stream, c := captureStream(
llm.Message{Role: llm.RoleAssistant, StopReason: llm.StopToolCalls, ToolCalls: []llm.ToolCall{{
ID: "c1", Name: "mcp__fake__echo", Arguments: `{"text":"via-backend"}`,