diff --git a/pkg/tools/execute/code.go b/pkg/tools/execute/code.go index 5695b55..bb5f5dc 100644 --- a/pkg/tools/execute/code.go +++ b/pkg/tools/execute/code.go @@ -139,9 +139,10 @@ func (lw *limitedWriter) Write(p []byte) (n int, err error) { // dispatchShell handles the shell tool: a single bash command. func dispatchShell(ctx context.Context, e *Server, args json.RawMessage) (string, error) { var a struct { - Cmd string `json:"cmd"` - Timeout int `json:"timeout"` - Sandbox string `json:"sandbox"` + Cmd string `json:"cmd"` + Timeout int `json:"timeout"` + Sandbox string `json:"sandbox"` + Elevated bool `json:"elevated"` } if err := json.Unmarshal(args, &a); err != nil { return "", fmt.Errorf("shell: bad args: %w", err) @@ -153,6 +154,12 @@ func dispatchShell(ctx context.Context, e *Server, args json.RawMessage) (string if timeout <= 0 { timeout = 30 } + if a.Elevated { + e.wdMu.RLock() + workDir := e.cwd + e.wdMu.RUnlock() + return e.executeElevated(ctx, a.Cmd, workDir, timeout) + } sandboxName := a.Sandbox if sandboxName == "" { sandboxName = "default" diff --git a/pkg/tools/execute/server.go b/pkg/tools/execute/server.go index 19b6f96..3b33bbc 100644 --- a/pkg/tools/execute/server.go +++ b/pkg/tools/execute/server.go @@ -149,9 +149,10 @@ timeout applies to each call (default: 30s). A non-zero exit is an error.`, "type": "object", "required": ["cmd"], "properties": { - "cmd": {"type": "string", "description": "Bash command to execute."}, - "timeout": {"type": "integer", "description": "Timeout in seconds (default: 30). Use 0 for no timeout."}, - "sandbox": {"type": "string", "description": "Sandbox profile name (default: default)."} + "cmd": {"type": "string", "description": "Bash command to execute."}, + "timeout": {"type": "integer", "description": "Timeout in seconds (default: 30). Use 0 for no timeout."}, + "sandbox": {"type": "string", "description": "Sandbox profile name (default: default)."}, + "elevated": {"type": "boolean", "description": "Run outside the sandbox via elevation broker."} } }`), }, @@ -273,7 +274,7 @@ func (e *Server) SetToolRegistry(r *Registry, sessionID string) { } // callPromotedTool executes a tool promoted via the registry by running the -// script file directly inside the sandbox. +// script file inside the sandbox, or elevated if the args request it. func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) { // Resolve script path. if strings.Contains(tool, "/") || strings.Contains(tool, "..") { @@ -281,8 +282,9 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra } path := filepath.Join(ToolsPath(), tool) - // Parse positional args into a shell command string. + // Parse args — extract positional args and optional elevated flag. var positional []string + elevated := false var argMap map[string]interface{} if err := json.Unmarshal(args, &argMap); err == nil { if argsArr, ok := argMap["args"]; ok { @@ -292,10 +294,14 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra } } } + if e, ok := argMap["elevated"]; ok { + if b, ok := e.(bool); ok { + elevated = b + } + } } // Build a shell command that runs the tool script with its args. - // Quote each arg for safe shell passing. quoted := make([]string, 0, len(positional)+1) quoted = append(quoted, path) for _, a := range positional { @@ -303,8 +309,16 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra } code := strings.Join(quoted, " ") - // Execute through the sandbox via executeWithStdin. - result, err := e.executeWithStdin(ctx, code, "bash", 30, "default", false, "") + var result string + var err error + if elevated { + e.wdMu.RLock() + workDir := e.cwd + e.wdMu.RUnlock() + result, err = e.executeElevated(ctx, code, workDir, 30) + } else { + result, err = e.executeWithStdin(ctx, code, "bash", 30, "default", false, "") + } if err != nil { return json.Marshal(map[string]interface{}{ "isError": true,