Skip to content

Commit 8a68acd

Browse files
committed
chore: switch default permission to allow and improve deny error messages
Made-with: Cursor
1 parent 311e34c commit 8a68acd

6 files changed

Lines changed: 46 additions & 27 deletions

File tree

cmd/main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,7 @@ func runRunner() error {
175175
"workspace", cfg.WorkspaceRoot,
176176
)
177177

178-
// Create permission checker with default deny-all policy
179-
checker := permission.NewChecker(map[string]string{"*": "deny"})
178+
checker := permission.NewChecker(map[string]string{"*": "allow"})
180179

181180
// Create workspace
182181
wspace, err := workspace.New(cfg.WorkspaceRoot, checker)

permission/permission.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,25 @@ func (c *Checker) evaluateRules(command string) (Action, string) {
136136
return action, pattern
137137
}
138138

139-
// denyError creates an appropriate error message for denied commands.
139+
// denyError creates an error message that lists denied patterns so callers
140+
// (especially LLM agents) know which commands are blocked and stop retrying.
140141
func (c *Checker) denyError(matchedPattern, command string) error {
141-
if matchedPattern == "" {
142-
return fmt.Errorf("command denied (no matching allow rule): %s", command)
142+
denied := c.deniedPatterns()
143+
if len(denied) == 0 {
144+
return fmt.Errorf("command denied: %s", command)
143145
}
144-
return fmt.Errorf("command denied by rule '%s': %s", matchedPattern, command)
146+
return fmt.Errorf("command denied: %s. Blocked command patterns: [%s]",
147+
command, strings.Join(denied, ", "))
148+
}
149+
150+
func (c *Checker) deniedPatterns() []string {
151+
var patterns []string
152+
for _, r := range c.rules {
153+
if r.Action == ActionDeny {
154+
patterns = append(patterns, r.Pattern)
155+
}
156+
}
157+
return patterns
145158
}
146159

147160
// matchPattern checks if a command matches a glob pattern.
@@ -163,10 +176,10 @@ func (c *Checker) IsAllowed(command string) bool {
163176
return c.Check(command) == nil
164177
}
165178

166-
// DefaultRules returns a set of safe default rules.
179+
// DefaultRules returns a set of default rules that trust model capabilities.
167180
func DefaultRules() map[string]string {
168181
return map[string]string{
169-
"*": "deny", // Deny all by default
182+
"*": "allow",
170183
}
171184
}
172185

permission/permission_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,10 @@ func TestDefaultRules(t *testing.T) {
171171
rules := DefaultRules()
172172
checker := NewChecker(rules)
173173

174-
// All commands should be denied by default
175-
assert.False(t, checker.IsAllowed("ls"))
176-
assert.False(t, checker.IsAllowed("cat /etc/passwd"))
177-
assert.False(t, checker.IsAllowed("rm -rf /"))
174+
// All commands should be allowed by default
175+
assert.True(t, checker.IsAllowed("ls"))
176+
assert.True(t, checker.IsAllowed("cat /etc/passwd"))
177+
assert.True(t, checker.IsAllowed("rm -rf /"))
178178
}
179179

180180
func TestSafeReadOnlyRules(t *testing.T) {

workspace/large_output.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ const (
1818
// DefaultPreviewLines is the number of lines shown in the preview
1919
DefaultPreviewLines = 20
2020
// OutputsDir is the directory name for storing large outputs
21-
OutputsDir = ".work/outputs"
22-
// WorkDir is the base working directory
23-
WorkDir = ".work"
21+
OutputsDir = ".outputs"
2422
)
2523

2624
// LargeOutputConfig holds configuration for large output handling.
@@ -105,13 +103,13 @@ func (p *LargeOutputProcessor) Process(ctx context.Context, content string, pref
105103
}, nil
106104
}
107105

108-
// ShouldSkipForWorkDir checks if large output processing should be skipped
109-
// for commands operating on .work/ directory to avoid circular processing.
110-
func ShouldSkipForWorkDir(command string) bool {
106+
// ShouldSkipForOutputsDir checks if large output processing should be skipped
107+
// for commands operating on .outputs/ directory to avoid circular processing.
108+
func ShouldSkipForOutputsDir(command string) bool {
111109
readCommands := []string{"cat ", "head ", "tail ", "less ", "more ", "bat "}
112110
for _, cmd := range readCommands {
113111
if strings.Contains(command, cmd) {
114-
if strings.Contains(command, WorkDir+"/") || strings.Contains(command, WorkDir+"\\") {
112+
if strings.Contains(command, OutputsDir+"/") || strings.Contains(command, OutputsDir+"\\") {
115113
return true
116114
}
117115
}

workspace/workspace.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,8 @@ func (w *Workspace) Bash(ctx context.Context, args *protocol.BashArgs) (*protoco
363363
return result, err
364364
}
365365

366-
// Skip large output processing for .work/ directory reads
367-
if ShouldSkipForWorkDir(args.Command) {
366+
// Skip large output processing for .outputs/ directory reads
367+
if ShouldSkipForOutputsDir(args.Command) {
368368
result.TotalSize = int64(len(result.Stdout))
369369
return result, nil
370370
}

ws/client.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ const (
3131
pongWait = 60 * time.Second
3232

3333
// Maximum reconnect attempts
34-
maxReconnectAttempts = 10
34+
maxReconnectAttempts = 30
3535

36-
// Initial reconnect delay
36+
// Initial reconnect delay (used for first few attempts)
3737
initialReconnectDelay = 1 * time.Second
3838

39+
// Fast retry attempts before switching to exponential backoff
40+
fastRetryAttempts = 15
41+
3942
// Maximum reconnect delay
4043
maxReconnectDelay = 5 * time.Minute
4144
)
@@ -217,10 +220,16 @@ func (c *Client) RunWithReconnect(ctx context.Context) error {
217220
case <-time.After(delay):
218221
}
219222

220-
// Exponential backoff
221-
delay *= 2
222-
if delay > maxReconnectDelay {
223-
delay = maxReconnectDelay
223+
// Fast retry phase: use fixed delay for first few attempts
224+
// This helps quickly detect when server comes back online after restart
225+
if attempt < fastRetryAttempts {
226+
delay = initialReconnectDelay
227+
} else {
228+
// Exponential backoff after fast retry phase
229+
delay *= 2
230+
if delay > maxReconnectDelay {
231+
delay = maxReconnectDelay
232+
}
224233
}
225234
continue
226235
}

0 commit comments

Comments
 (0)