I made custom extension (AI wrote it when I asked advice by reading your sources :D )
Problem is, AI does not understand tool is blocked, I guess it does not see error message:

//go:build ignore
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"kit/ext"
)
// Init sets up the read-only enforcement and adds safe tools.
func Init(api ext.API) {
api.OnToolCall(func(tc ext.ToolCallEvent, ctx ext.Context) *ext.ToolCallResult {
// 1. BAN BASH
if tc.ToolName == "bash" {
return &ext.ToolCallResult{
Block: true,
Reason: "The 'bash' tool is banned in read-only mode for security.",
}
}
// 2. RESTRICT FILE ACCESS (read, write, edit)
if tc.ToolName == "read" || tc.ToolName == "write" || tc.ToolName == "edit" {
var input struct {
Path string `json:"path"`
}
// Try to unmarshal the path from various potential JSON structures
// edit tool has 'path' at top level, write also.
if err := json.Unmarshal([]byte(tc.Input), &input); err != nil {
// If it fails (e.g., if input is not a simple object with 'path'),
// we might be looking at an edit tool in multi-mode or something else.
// But usually, the path is required and present.
}
if input.Path != "" {
absInputPath, err := filepath.Abs(input.Path)
if err == nil {
tempDir := os.TempDir() // Usually /tmp on Linux
rel, relErr := filepath.Rel(tempDir, absInputPath)
isInsideTmp := relErr == nil && !strings.HasPrefix(rel, "..")
// If it's write or edit, enforce tmp directory restriction
if tc.ToolName == "write" || tc.ToolName == "edit" {
if !isInsideTmp {
return &ext.ToolCallResult{
Block: true,
Reason: fmt.Sprintf("File access restricted. You can only write/edit files inside %s (requested: %s)", tempDir, input.Path),
}
}
}
}
}
}
return nil // Allow other tools (like ls, grep, find)
})
// 3. ADD SAFE ALTERNATIVE TO BASH/LS (e.g., a python-based or native list_dir)
api.RegisterTool(ext.ToolDef{
Name: "list_files",
Description: "List files in a directory safely without using bash.",
Parameters: `{"type":"object","properties":{"directory":{"type":"string","description":"Path to the directory"}},"required":["directory"]}`,
Execute: func(input string) (string, error) {
var args struct {
Directory string `json:"directory"`
}
if err := json.Unmarshal([]byte(input), &args); err != nil {
return "", fmt.Errorf("invalid input: %w", err)
}
entries, err := os.ReadDir(args.Directory)
if err != nil {
return "", fmt.Errorf("failed to list directory: %v", err)
}
var sb strings.Builder
sb.WriteString(fmt.Sprintf("Contents of %s:\n", args.Directory))
for _, e := range entries {
typeStr := ""
if e.IsDir() {
typeStr = "/"
}
sb.WriteString(fmt.Sprintf("- %s%s\n", typeStr, e.Name()))
}
return sb.String(), nil
},
})
}
1. Save attached extension.
2. Use attached extension.
3. Ask AI to execute bash.
Bug Description
I made custom extension (AI wrote it when I asked advice by reading your sources :D )

Problem is, AI does not understand tool is blocked, I guess it does not see error message:
Steps to Reproduce
Relevant Code / Configuration
Affected Component
No response
Kit Version
No response
Additional Context
No response
Checklist