| 630 | } |
| 631 | |
| 632 | func (b *InteractiveWorkflowBuilder) generatePermissionsConfig() string { |
| 633 | // Compute read permissions needed by the AI agent for data access. |
| 634 | // Write permissions are NEVER set here — they are always handled automatically |
| 635 | // by the safe-outputs job via workflow.ComputePermissionsForSafeOutputs(). |
| 636 | perms := workflow.NewPermissions() |
| 637 | perms.Set(workflow.PermissionContents, workflow.PermissionRead) |
| 638 | |
| 639 | if slices.Contains(b.Tools, "github") { |
| 640 | // Default toolsets: context, repos, issues, pull_requests |
| 641 | // repos → contents: read (already set) |
| 642 | // issues → issues: read |
| 643 | // pull_requests → pull-requests: read |
| 644 | perms.Set(workflow.PermissionIssues, workflow.PermissionRead) |
| 645 | perms.Set(workflow.PermissionPullRequests, workflow.PermissionRead) |
| 646 | } |
| 647 | |
| 648 | // Include read permissions needed by the safe-outputs job (e.g. contents: read |
| 649 | // is already present; actions: read for autofix scanning alerts). |
| 650 | // Write permissions from ComputePermissionsForSafeOutputs are handled by the |
| 651 | // safe-outputs job automatically and must not appear in the main workflow block. |
| 652 | safeOutputsPerms := workflow.ComputePermissionsForSafeOutputs(workflow.SafeOutputsConfigFromKeys(b.SafeOutputs)) |
| 653 | for _, scope := range workflow.GetAllPermissionScopes() { |
| 654 | if level, exists := safeOutputsPerms.Get(scope); exists && level == workflow.PermissionRead { |
| 655 | perms.Set(scope, workflow.PermissionRead) |
| 656 | } |
| 657 | } |
| 658 | |
| 659 | return perms.RenderToYAML() + "\n" |
| 660 | } |
| 661 | |
| 662 | func (b *InteractiveWorkflowBuilder) generateNetworkConfig() string { |
| 663 | interactiveLog.Printf("Generating network config: network=%s", b.NetworkAccess) |