mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-14 03:31:58 +08:00
fix(codeagent-wrapper): propagate SkipPermissions to parallel tasks (#113)
Parallel task execution was not inheriting the --skip-permissions flag, causing permission prompts to appear for parallel tasks while single tasks worked correctly. Changes: - Add SkipPermissions field to TaskSpec struct - Parse skip_permissions/skip-permissions in parallel task config - Inherit SkipPermissions from CLI args to parallel tasks - Pass SkipPermissions when creating task Config in executor Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
This commit is contained in:
@@ -46,6 +46,7 @@ type TaskSpec struct {
|
|||||||
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
||||||
Agent string `json:"agent,omitempty"`
|
Agent string `json:"agent,omitempty"`
|
||||||
PromptFile string `json:"prompt_file,omitempty"`
|
PromptFile string `json:"prompt_file,omitempty"`
|
||||||
|
SkipPermissions bool `json:"skip_permissions,omitempty"`
|
||||||
Mode string `json:"-"`
|
Mode string `json:"-"`
|
||||||
UseStdin bool `json:"-"`
|
UseStdin bool `json:"-"`
|
||||||
Context context.Context `json:"-"`
|
Context context.Context `json:"-"`
|
||||||
@@ -201,6 +202,12 @@ func parseParallelConfig(data []byte) (*ParallelConfig, error) {
|
|||||||
case "agent":
|
case "agent":
|
||||||
agentSpecified = true
|
agentSpecified = true
|
||||||
task.Agent = value
|
task.Agent = value
|
||||||
|
case "skip_permissions", "skip-permissions":
|
||||||
|
if value == "" {
|
||||||
|
task.SkipPermissions = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
task.SkipPermissions = parseBoolFlag(value, false)
|
||||||
case "dependencies":
|
case "dependencies":
|
||||||
for _, dep := range strings.Split(value, ",") {
|
for _, dep := range strings.Split(value, ",") {
|
||||||
dep = strings.TrimSpace(dep)
|
dep = strings.TrimSpace(dep)
|
||||||
|
|||||||
@@ -815,6 +815,7 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
|
|||||||
WorkDir: taskSpec.WorkDir,
|
WorkDir: taskSpec.WorkDir,
|
||||||
Model: taskSpec.Model,
|
Model: taskSpec.Model,
|
||||||
ReasoningEffort: taskSpec.ReasoningEffort,
|
ReasoningEffort: taskSpec.ReasoningEffort,
|
||||||
|
SkipPermissions: taskSpec.SkipPermissions,
|
||||||
Backend: defaultBackendName,
|
Backend: defaultBackendName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -625,6 +625,27 @@ func TestExecutorRunCodexTaskWithContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("claudeSkipPermissionsPropagatesFromTaskSpec", func(t *testing.T) {
|
||||||
|
t.Setenv("CODEAGENT_SKIP_PERMISSIONS", "false")
|
||||||
|
var gotArgs []string
|
||||||
|
newCommandRunner = func(ctx context.Context, name string, args ...string) commandRunner {
|
||||||
|
gotArgs = append([]string(nil), args...)
|
||||||
|
return &execFakeRunner{
|
||||||
|
stdout: newReasonReadCloser(`{"type":"item.completed","item":{"type":"agent_message","text":"ok"}}`),
|
||||||
|
process: &execFakeProcess{pid: 15},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = closeLogger()
|
||||||
|
res := runCodexTaskWithContext(context.Background(), TaskSpec{ID: "task-skip", Task: "payload", WorkDir: ".", SkipPermissions: true}, ClaudeBackend{}, nil, false, false, 1)
|
||||||
|
if res.ExitCode != 0 || res.Error != "" {
|
||||||
|
t.Fatalf("unexpected result: %+v", res)
|
||||||
|
}
|
||||||
|
if !slices.Contains(gotArgs, "--dangerously-skip-permissions") {
|
||||||
|
t.Fatalf("expected --dangerously-skip-permissions in args, got %v", gotArgs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("missingMessage", func(t *testing.T) {
|
t.Run("missingMessage", func(t *testing.T) {
|
||||||
newCommandRunner = func(ctx context.Context, name string, args ...string) commandRunner {
|
newCommandRunner = func(ctx context.Context, name string, args ...string) commandRunner {
|
||||||
return &execFakeRunner{
|
return &execFakeRunner{
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ func run() (exitCode int) {
|
|||||||
backendName := defaultBackendName
|
backendName := defaultBackendName
|
||||||
model := ""
|
model := ""
|
||||||
fullOutput := false
|
fullOutput := false
|
||||||
|
skipPermissions := envFlagEnabled("CODEAGENT_SKIP_PERMISSIONS")
|
||||||
var extras []string
|
var extras []string
|
||||||
|
|
||||||
for i := 0; i < len(args); i++ {
|
for i := 0; i < len(args); i++ {
|
||||||
@@ -214,13 +215,19 @@ func run() (exitCode int) {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
model = value
|
model = value
|
||||||
|
case arg == "--skip-permissions", arg == "--dangerously-skip-permissions":
|
||||||
|
skipPermissions = true
|
||||||
|
case strings.HasPrefix(arg, "--skip-permissions="):
|
||||||
|
skipPermissions = parseBoolFlag(strings.TrimPrefix(arg, "--skip-permissions="), skipPermissions)
|
||||||
|
case strings.HasPrefix(arg, "--dangerously-skip-permissions="):
|
||||||
|
skipPermissions = parseBoolFlag(strings.TrimPrefix(arg, "--dangerously-skip-permissions="), skipPermissions)
|
||||||
default:
|
default:
|
||||||
extras = append(extras, arg)
|
extras = append(extras, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(extras) > 0 {
|
if len(extras) > 0 {
|
||||||
fmt.Fprintln(os.Stderr, "ERROR: --parallel reads its task configuration from stdin; only --backend, --model and --full-output are allowed.")
|
fmt.Fprintln(os.Stderr, "ERROR: --parallel reads its task configuration from stdin; only --backend, --model, --full-output and --skip-permissions are allowed.")
|
||||||
fmt.Fprintln(os.Stderr, "Usage examples:")
|
fmt.Fprintln(os.Stderr, "Usage examples:")
|
||||||
fmt.Fprintf(os.Stderr, " %s --parallel < tasks.txt\n", name)
|
fmt.Fprintf(os.Stderr, " %s --parallel < tasks.txt\n", name)
|
||||||
fmt.Fprintf(os.Stderr, " echo '...' | %s --parallel\n", name)
|
fmt.Fprintf(os.Stderr, " echo '...' | %s --parallel\n", name)
|
||||||
@@ -257,6 +264,7 @@ func run() (exitCode int) {
|
|||||||
if strings.TrimSpace(cfg.Tasks[i].Model) == "" && model != "" {
|
if strings.TrimSpace(cfg.Tasks[i].Model) == "" && model != "" {
|
||||||
cfg.Tasks[i].Model = model
|
cfg.Tasks[i].Model = model
|
||||||
}
|
}
|
||||||
|
cfg.Tasks[i].SkipPermissions = cfg.Tasks[i].SkipPermissions || skipPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
timeoutSec := resolveTimeout()
|
timeoutSec := resolveTimeout()
|
||||||
@@ -436,6 +444,7 @@ func run() (exitCode int) {
|
|||||||
SessionID: cfg.SessionID,
|
SessionID: cfg.SessionID,
|
||||||
Model: cfg.Model,
|
Model: cfg.Model,
|
||||||
ReasoningEffort: cfg.ReasoningEffort,
|
ReasoningEffort: cfg.ReasoningEffort,
|
||||||
|
SkipPermissions: cfg.SkipPermissions,
|
||||||
UseStdin: useStdin,
|
UseStdin: useStdin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1587,6 +1587,26 @@ do something`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParallelParseConfig_SkipPermissions(t *testing.T) {
|
||||||
|
input := `---TASK---
|
||||||
|
id: task-1
|
||||||
|
skip_permissions: true
|
||||||
|
---CONTENT---
|
||||||
|
do something`
|
||||||
|
|
||||||
|
cfg, err := parseParallelConfig([]byte(input))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parseParallelConfig() unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.Tasks) != 1 {
|
||||||
|
t.Fatalf("expected 1 task, got %d", len(cfg.Tasks))
|
||||||
|
}
|
||||||
|
task := cfg.Tasks[0]
|
||||||
|
if !task.SkipPermissions {
|
||||||
|
t.Fatalf("SkipPermissions = %v, want true", task.SkipPermissions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParallelParseConfig_EmptySessionID(t *testing.T) {
|
func TestParallelParseConfig_EmptySessionID(t *testing.T) {
|
||||||
input := `---TASK---
|
input := `---TASK---
|
||||||
id: task-1
|
id: task-1
|
||||||
@@ -4014,6 +4034,30 @@ do two`)
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("parallelSkipPermissions", func(t *testing.T) {
|
||||||
|
defer resetTestHooks()
|
||||||
|
cleanupHook = func() {}
|
||||||
|
cleanupLogsFn = func() (CleanupStats, error) { return CleanupStats{}, nil }
|
||||||
|
t.Setenv("CODEAGENT_SKIP_PERMISSIONS", "false")
|
||||||
|
|
||||||
|
runCodexTaskFn = func(task TaskSpec, timeout int) TaskResult {
|
||||||
|
if !task.SkipPermissions {
|
||||||
|
return TaskResult{TaskID: task.ID, ExitCode: 1, Error: "SkipPermissions not propagated"}
|
||||||
|
}
|
||||||
|
return TaskResult{TaskID: task.ID, ExitCode: 0, Message: "ok"}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdinReader = strings.NewReader(`---TASK---
|
||||||
|
id: only
|
||||||
|
backend: claude
|
||||||
|
---CONTENT---
|
||||||
|
do one`)
|
||||||
|
os.Args = []string{"codeagent-wrapper", "--parallel", "--skip-permissions"}
|
||||||
|
if code := run(); code != 0 {
|
||||||
|
t.Fatalf("run exit = %d, want 0", code)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("parallelErrors", func(t *testing.T) {
|
t.Run("parallelErrors", func(t *testing.T) {
|
||||||
defer resetTestHooks()
|
defer resetTestHooks()
|
||||||
cleanupLogsFn = func() (CleanupStats, error) { return CleanupStats{}, nil }
|
cleanupLogsFn = func() (CleanupStats, error) { return CleanupStats{}, nil }
|
||||||
|
|||||||
Reference in New Issue
Block a user