From 83ff81dd2cd787091c560350a6889773bf07bb38 Mon Sep 17 00:00:00 2001 From: dwrz Date: Tue, 6 Oct 2026 12:08:12 +0000 Subject: [PATCH] Add tool requires approval --- internal/config/config.go | 3 +++ internal/config/config_test.go | 44 ++++++++++++++++++++++++++++++++++ internal/tool/tool.go | 18 ++++++++------ internal/tool/tool_test.go | 12 ++++++---- 4 files changed, 66 insertions(+), 11 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index a2d8389..7a8d916 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -238,6 +238,9 @@ type ToolConfig struct { Parameters map[string]any `yaml:"parameters"` // Timeout limits execution time (e.g., "30s", "5m"). Zero means no limit. Timeout time.Duration `yaml:"timeout"` + // RequiresApproval specifies whether a tool must be approved by a user + // to execute. + RequiresApproval bool `yaml:"require_approval,omitempty"` } func (cfg ToolConfig) Validate() error { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 55fdb01..9ea4658 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -386,6 +386,50 @@ tools: cfg.Tools[0].Name, "echo", ) } + if cfg.Tools[0].RequiresApproval { + t.Error("Tools[0].RequiresApproval = true, want false") + } + }, + }, + { + name: "tool with require_approval", + config: ` +database: + path: "/tmp/odidere-test.db" + +default_model: + provider: testprovider + model: test-model + +providers: + - name: testprovider + url: http://localhost:8080 + +stt: + url: http://localhost:8178 + +tts: + url: http://localhost:8880 + voice: af_heart + +tools: + - name: echo + description: echoes input + command: echo + arguments: + - "{{.message}}" + require_approval: true +`, + check: func(t *testing.T, cfg *Config) { + if len(cfg.Tools) != 1 { + t.Errorf( + "len(Tools) = %d, want 1", + len(cfg.Tools), + ) + } + if !cfg.Tools[0].RequiresApproval { + t.Error("Tools[0].RequiresApproval = false, want true") + } }, }, { diff --git a/internal/tool/tool.go b/internal/tool/tool.go index add01ec..0f7c653 100644 --- a/internal/tool/tool.go +++ b/internal/tool/tool.go @@ -52,6 +52,9 @@ type Tool struct { // used to validate a tool call before its arguments are rendered. See // config.ToolConfig.Parameters. Parameters map[string]any + // RequiresApproval reports whether calls to this tool must be approved + // by a user before the subprocess launches. + RequiresApproval bool // timeout is the parsed execution duration limit. timeout time.Duration } @@ -72,13 +75,14 @@ func NewTool(cfg config.ToolConfig) (*Tool, error) { } return &Tool{ - Name: cfg.Name, - Description: cfg.Description, - Command: cfg.Command, - Arguments: cfg.Arguments, - Parameters: cfg.Parameters, - tmpl: templates, - timeout: cfg.Timeout, + Name: cfg.Name, + Description: cfg.Description, + Command: cfg.Command, + Arguments: cfg.Arguments, + Parameters: cfg.Parameters, + RequiresApproval: cfg.RequiresApproval, + tmpl: templates, + timeout: cfg.Timeout, }, nil } diff --git a/internal/tool/tool_test.go b/internal/tool/tool_test.go index 90bf99b..299480e 100644 --- a/internal/tool/tool_test.go +++ b/internal/tool/tool_test.go @@ -112,10 +112,11 @@ func TestTool_ParseArguments_ExecutionError(t *testing.T) { func TestNewTool(t *testing.T) { tool, err := NewTool(config.ToolConfig{ - Name: "test", - Description: "desc", - Command: "ls", - Timeout: 10 * time.Second, + Name: "test", + Description: "desc", + Command: "ls", + Timeout: 10 * time.Second, + RequiresApproval: true, }) if err != nil { t.Fatalf("NewTool() error = %v", err) @@ -123,6 +124,9 @@ func TestNewTool(t *testing.T) { if tool.Name != "test" { t.Errorf("Name = %q, want %q", tool.Name, "test") } + if !tool.RequiresApproval { + t.Error("RequiresApproval = false, want true") + } if tool.timeout != 10*time.Second { t.Errorf("timeout = %v, want 10s", tool.timeout) }