mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 00:16:38 +00:00
feat(tools): add exec path exemptions and tool arguments in events
- Add AllowPathExemptions to ExecTool for fine-grained deny bypass (skills-store) - Include tool call arguments in tool.result event payloads
This commit is contained in:
1 parent
9d0af657e5
commit
d839e034af
6 files changed
+43
-14
No files matched your search
@@ -387,6 +387,8 @@ func resolveProviderType(name string) string {
|
||||
return store.ProviderCohere
|
||||
case "perplexity":
|
||||
return store.ProviderPerplexity
|
||||
case "yescale":
|
||||
return store.ProviderYesScale
|
||||
default:
|
||||
return store.ProviderOpenAICompat
|
||||
}
|
||||
@@ -417,6 +419,8 @@ func resolveProviderAPIBase(providerName string) string {
|
||||
return "https://api.cohere.com/v2"
|
||||
case "perplexity":
|
||||
return "https://api.perplexity.ai"
|
||||
case "yescale":
|
||||
return "https://api.yescale.one/v1"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -760,9 +760,10 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
}
|
||||
|
||||
toolResultPayload := map[string]interface{}{
|
||||
"name": tc.Name,
|
||||
"id": tc.ID,
|
||||
"is_error": result.IsError,
|
||||
"name": tc.Name,
|
||||
"id": tc.ID,
|
||||
"is_error": result.IsError,
|
||||
"arguments": tc.Arguments,
|
||||
}
|
||||
if result.IsError && result.ForLLM != "" {
|
||||
toolResultPayload["content"] = result.ForLLM
|
||||
@@ -899,9 +900,10 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
}
|
||||
|
||||
parToolResultPayload := map[string]interface{}{
|
||||
"name": r.tc.Name,
|
||||
"id": r.tc.ID,
|
||||
"is_error": r.result.IsError,
|
||||
"name": r.tc.Name,
|
||||
"id": r.tc.ID,
|
||||
"is_error": r.result.IsError,
|
||||
"arguments": r.tc.Arguments,
|
||||
}
|
||||
if r.result.IsError && r.result.ForLLM != "" {
|
||||
parToolResultPayload["content"] = r.result.ForLLM
|
||||
|
||||
@@ -25,6 +25,7 @@ const (
|
||||
ProviderChatGPTOAuth = "chatgpt_oauth"
|
||||
ProviderClaudeCLI = "claude_cli"
|
||||
ProviderSuno = "suno"
|
||||
ProviderYesScale = "yescale"
|
||||
)
|
||||
|
||||
// ValidProviderTypes lists all accepted provider_type values.
|
||||
@@ -45,6 +46,7 @@ var ValidProviderTypes = map[string]bool{
|
||||
ProviderChatGPTOAuth: true,
|
||||
ProviderClaudeCLI: true,
|
||||
ProviderSuno: true,
|
||||
ProviderYesScale: true,
|
||||
}
|
||||
|
||||
// LLMProviderData represents an LLM provider configuration.
|
||||
|
||||
+27
-8
@@ -9,6 +9,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nextlevelbuilder/goclaw/internal/sandbox"
|
||||
@@ -129,13 +130,14 @@ var defaultDenyPatterns = []*regexp.Regexp{
|
||||
|
||||
// ExecTool executes shell commands, optionally inside a sandbox container.
|
||||
type ExecTool struct {
|
||||
workingDir string
|
||||
timeout time.Duration
|
||||
denyPatterns []*regexp.Regexp
|
||||
restrict bool
|
||||
sandboxMgr sandbox.Manager // nil = no sandbox, execute on host
|
||||
approvalMgr *ExecApprovalManager // nil = no approval needed
|
||||
agentID string // for approval request context
|
||||
workingDir string
|
||||
timeout time.Duration
|
||||
denyPatterns []*regexp.Regexp
|
||||
denyExemptions []string // substrings that exempt a command from deny (e.g. ".goclaw/skills-store/")
|
||||
restrict bool
|
||||
sandboxMgr sandbox.Manager // nil = no sandbox, execute on host
|
||||
approvalMgr *ExecApprovalManager // nil = no approval needed
|
||||
agentID string // for approval request context
|
||||
}
|
||||
|
||||
// NewExecTool creates an exec tool that runs commands directly on the host.
|
||||
@@ -171,6 +173,13 @@ func (t *ExecTool) DenyPaths(paths ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
// AllowPathExemptions adds substrings that exempt a command from deny pattern matches.
|
||||
// E.g. AllowPathExemptions(".goclaw/skills-store/") lets commands referencing
|
||||
// skills-store pass even though ".goclaw/" is denied.
|
||||
func (t *ExecTool) AllowPathExemptions(substrings ...string) {
|
||||
t.denyExemptions = append(t.denyExemptions, substrings...)
|
||||
}
|
||||
|
||||
// SetApprovalManager sets the exec approval manager for this tool.
|
||||
func (t *ExecTool) SetApprovalManager(mgr *ExecApprovalManager, agentID string) {
|
||||
t.approvalMgr = mgr
|
||||
@@ -205,7 +214,17 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]interface{}) *Re
|
||||
// Check for dangerous commands (applies to both host and sandbox)
|
||||
for _, pattern := range t.denyPatterns {
|
||||
if pattern.MatchString(command) {
|
||||
return ErrorResult(fmt.Sprintf("command denied by safety policy: matches pattern %s", pattern.String()))
|
||||
// Check if any exemption applies (e.g. skills-store within .goclaw)
|
||||
exempt := false
|
||||
for _, ex := range t.denyExemptions {
|
||||
if strings.Contains(command, ex) {
|
||||
exempt = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exempt {
|
||||
return ErrorResult(fmt.Sprintf("command denied by safety policy: matches pattern %s", pattern.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,5 +20,6 @@ export const PROVIDER_TYPES: ProviderTypeInfo[] = [
|
||||
{ value: "perplexity", label: "Perplexity", apiBase: "https://api.perplexity.ai", placeholder: "" },
|
||||
{ value: "dashscope", label: "DashScope (Qwen)", apiBase: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", placeholder: "" },
|
||||
{ value: "bailian", label: "Bailian Coding", apiBase: "https://coding-intl.dashscope.aliyuncs.com/v1", placeholder: "" },
|
||||
{ value: "yescale", label: "YesScale", apiBase: "https://api.yescale.one/v1", placeholder: "" },
|
||||
{ value: "claude_cli", label: "Claude CLI (Local)", apiBase: "", placeholder: "" },
|
||||
];
|
||||
@@ -27,6 +27,7 @@ const typeBadge: Record<string, { label: string; variant: "default" | "secondary
|
||||
cohere: { label: "Cohere", variant: "secondary" },
|
||||
perplexity: { label: "Perplexity", variant: "secondary" },
|
||||
chatgpt_oauth: { label: "ChatGPT (OAuth)", variant: "default" },
|
||||
yescale: { label: "YesScale", variant: "secondary" },
|
||||
};
|
||||
|
||||
export function ProvidersPage() {
|
||||
|
||||
Reference in new issue
Block a user