Safe Outputs
The safe-outputs: element of your workflow’s frontmatter declares that your agentic workflow should conclude with optional automated actions based on the agentic workflow’s output. This enables your workflow to write content that is then automatically processed to create GitHub issues, comments, pull requests, or add labels—all without giving the agentic portion of the workflow any write permissions.
Why Safe Outputs?
Section titled “Why Safe Outputs?”Safe outputs are a security feature. Your AI agent runs with minimal permissions (read-only access by default). When the agent wants to make changes to your repository—like creating an issue, adding a comment, or opening a pull request—it cannot do so directly. Instead, it “requests” that action by writing structured output to a file.
A separate, permission-controlled job then reviews and executes these requests. This architecture provides:
- Principle of least privilege: The AI never has write permissions during execution
- Defense against prompt injection: Malicious inputs cannot trick the AI into making unauthorized changes
- Auditability: All requested actions are logged and can be reviewed before execution
- Controlled blast radius: Each safe output type has strict limits (e.g., maximum number of issues to create)
How It Works:
- The agentic part of your workflow runs with minimal read-only permissions. It is given additional prompting to write its output to special known files
- The compiler automatically generates additional jobs that read this output and perform the requested actions
- Only these generated jobs receive the necessary write permissions—scoped precisely to what each safe output type requires
For example:
safe-outputs: create-issue:This declares that the workflow should create at most one new issue. The AI agent can request issue creation, but a separate job with issues: write permission actually creates it.
Available Safe Output Types
Section titled “Available Safe Output Types”| Output Type | Key | Description | Max | Cross-Repo |
|---|---|---|---|---|
| Create Issue | create-issue: | Create GitHub issues | 1 | ✅ |
| Close Issue | close-issue: | Close issues with comment | 1 | ✅ |
| Add Comment | add-comment: | Post comments on issues, PRs, or discussions | 1 | ✅ |
| Hide Comment | hide-comment: | Hide comments on issues, PRs, or discussions | 5 | ✅ |
| Update Issue | update-issue: | Update issue status, title, or body | 1 | ✅ |
| Update PR | update-pull-request: | Update PR title or body | 1 | ✅ |
| Link Sub-Issue | link-sub-issue: | Link issues as sub-issues | 1 | ✅ |
| Update Project | update-project: | Manage GitHub Projects boards and campaign labels | 10 | ❌ |
| Add Labels | add-labels: | Add labels to issues or PRs | 3 | ✅ |
| Add Reviewer | add-reviewer: | Add reviewers to pull requests | 3 | ✅ |
| Assign Milestone | assign-milestone: | Assign issues to milestones | 1 | ✅ |
| Create PR | create-pull-request: | Create pull requests with code changes | 1 | ✅ |
| Close PR | close-pull-request: | Close pull requests without merging | 10 | ✅ |
| PR Review Comments | create-pull-request-review-comment: | Create review comments on code lines | 10 | ✅ |
| Create Discussion | create-discussion: | Create GitHub discussions | 1 | ✅ |
| Close Discussion | close-discussion: | Close discussions with comment and resolution | 1 | ✅ |
| Create Agent Task | create-agent-task: | Create Copilot agent tasks | 1 | ✅ |
| Assign to Agent | assign-to-agent: | Assign Copilot agents to issues | 1 | ✅ |
| Assign to User | assign-to-user: | Assign users to issues | 1 | ✅ |
| Push to PR Branch | push-to-pull-request-branch: | Push changes to PR branch | 1 | ❌ |
| Update Release | update-release: | Update GitHub release descriptions | 1 | ✅ |
| Upload Assets | upload-assets: | Upload files to orphaned git branch | 10 | ❌ |
| Code Scanning Alerts | create-code-scanning-alert: | Generate SARIF security advisories | unlimited | ❌ |
| No-Op | noop: | Log completion message for transparency (auto-enabled) | 1 | ❌ |
| Missing Tool | missing-tool: | Report missing tools (auto-enabled) | unlimited | ❌ |
Custom safe output types: Custom Safe Output Jobs.
Custom Safe Output Jobs (jobs:)
Section titled “Custom Safe Output Jobs (jobs:)”The jobs: field creates custom post-processing jobs that execute after the main workflow. Custom jobs are registered as MCP tools for the agent to call.
safe-outputs: jobs: deploy-app: description: "Deploy application" runs-on: ubuntu-latest output: "Deployment completed!" permissions: contents: write inputs: environment: description: "Target environment" required: true type: choice options: ["staging", "production"] steps: - name: Deploy run: echo "Deploying to ${{ inputs.environment }}"Jobs support standard GitHub Actions properties (runs-on, permissions, env, if, timeout-minutes) and automatically access agent output via $GH_AW_AGENT_OUTPUT. See Custom Safe Output Jobs.
Issue Creation (create-issue:)
Section titled “Issue Creation (create-issue:)”Creates GitHub issues based on workflow output.
safe-outputs: create-issue: title-prefix: "[ai] " # prefix for titles labels: [automation, agentic] # labels to attach assignees: [user1, copilot] # assignees (use 'copilot' for bot) max: 5 # max issues (default: 1) expires: 7 # auto-close after 7 days target-repo: "owner/repo" # cross-repositoryAuto-Expiration
Section titled “Auto-Expiration”The expires field automatically closes issues after a specified time period. Supports both integer (days) and relative time formats:
- Integer:
expires: 7(7 days) - Days:
expires: 7dor7D(7 days) - Weeks:
expires: 2wor2W(14 days) - Months:
expires: 1mor1M(30 days, approximate) - Years:
expires: 1yor1Y(365 days, approximate)
When enabled, the compiler automatically generates an agentics-maintenance.yml workflow that runs daily to close expired items. Issues are closed as “completed” with an explanatory comment and workflow attribution.
Temporary IDs for Issue References
Section titled “Temporary IDs for Issue References”When creating multiple issues, use temporary IDs to reference parent issues before they’re created. The agent provides a temporary_id field with format aw_ followed by 12 hex characters.
Agent Output Format:
[ { "type": "create_issue", "title": "Parent Issue", "body": "This is the parent issue", "temporary_id": "aw_abc123def456" }, { "type": "create_issue", "title": "Sub Issue", "body": "References #aw_abc123def456", "parent": "aw_abc123def456" }]References like #aw_abc123def456 in issue bodies are automatically replaced with the actual issue number (e.g., #42) after the parent issue is created. The parent field creates a sub-issue relationship.
Close Issue (close-issue:)
Section titled “Close Issue (close-issue:)”Closes GitHub issues with an optional comment and state reason. Filters by labels and title prefix control which issues can be closed.
safe-outputs: close-issue: target: "triggering" # "triggering" (default), "*", or number required-labels: [automated] # only close with any of these labels required-title-prefix: "[bot]" # only close matching prefix max: 20 # max closures (default: 1) target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires issue event), "*" (any issue), or number (specific issue).
State Reasons: completed, not_planned, reopened (default: completed).
Comment Creation (add-comment:)
Section titled “Comment Creation (add-comment:)”Posts comments on issues, PRs, or discussions. Defaults to triggering item; configure target for specific items or "*" for any.
safe-outputs: add-comment: max: 3 # max comments (default: 1) target: "*" # "triggering" (default), "*", or number discussion: true # target discussions target-repo: "owner/repo" # cross-repository hide-older-comments: true # hide previous comments from same workflow allowed-reasons: [outdated] # restrict hiding reasons (optional)When combined with create-issue, create-discussion, or create-pull-request, comments automatically include a “Related Items” section.
Hide Older Comments
Section titled “Hide Older Comments”The hide-older-comments field automatically minimizes all previous comments from the same agentic workflow before creating a new comment. This is useful for workflows that provide status updates, where you want to keep the conversation clean by hiding outdated information.
Configuration:
safe-outputs: add-comment: hide-older-comments: true allowed-reasons: [OUTDATED, RESOLVED] # optional: restrict reasonsHow it works:
- Comments from the same workflow are identified by the workflow ID (automatically from
GITHUB_WORKFLOWenvironment variable) - All matching older comments are minimized/hidden with reason “outdated” (by default)
- The new comment is then created normally
- Works for both issue/PR comments and discussion comments
Allowed Reasons:
Use allowed-reasons to restrict which reasons can be used when hiding comments. Valid reasons are:
spam- Mark as spamabuse- Mark as abusiveoff_topic- Mark as off-topicoutdated- Mark as outdated (default)resolved- Mark as resolved
If allowed-reasons is not specified, all reasons are allowed. If specified, only the listed reasons can be used. If the default reason (outdated) is not in the allowed list, hiding will be skipped with a warning.
Requirements:
- Workflow ID is automatically obtained from the
GITHUB_WORKFLOWenvironment variable - Only comments with matching workflow ID will be hidden
- Requires write permissions (automatically granted to the safe-output job)
Example workflow:
---safe-outputs: add-comment: hide-older-comments: true allowed-reasons: [outdated, resolved]---
Current status: {{ statusMessage }}Hide Comment (hide-comment:)
Section titled “Hide Comment (hide-comment:)”Hides comments on issues, pull requests, or discussions. Comments are collapsed in the GitHub UI with a specified reason. This safe output is useful for content moderation workflows.
safe-outputs: hide-comment: max: 5 # max comments to hide (default: 5) target-repo: "owner/repo" # cross-repositoryRequirements:
- Agent must provide GraphQL node IDs (strings like
IC_kwDOABCD123456) for comments - REST API numeric comment IDs cannot be used (no conversion available)
- Agent can optionally specify a reason (spam, abuse, off_topic, outdated, resolved)
Agent Output Format:
{ "type": "hide_comment", "comment_id": "IC_kwDOABCD123456", "reason": "spam"}Permissions Required: contents: read, issues: write, pull-requests: write, discussions: write
Add Labels (add-labels:)
Section titled “Add Labels (add-labels:)”Adds labels to issues or PRs. Specify allowed to restrict to specific labels.
safe-outputs: add-labels: allowed: [bug, enhancement] # restrict to specific labels max: 3 # max labels (default: 3) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryAdd Reviewer (add-reviewer:)
Section titled “Add Reviewer (add-reviewer:)”Adds reviewers to pull requests. Specify reviewers to restrict to specific GitHub usernames.
safe-outputs: add-reviewer: reviewers: [user1, copilot] # restrict to specific reviewers max: 3 # max reviewers (default: 3) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires PR event), "*" (any PR), or number (specific PR).
Use reviewers: copilot to assign the Copilot PR reviewer bot. Requires a PAT stored as COPILOT_GITHUB_TOKEN or GH_AW_GITHUB_TOKEN (legacy).
Assign Milestone (assign-milestone:)
Section titled “Assign Milestone (assign-milestone:)”Assigns issues to milestones. Specify allowed to restrict to specific milestone titles.
safe-outputs: assign-milestone: allowed: [v1.0, v2.0] # restrict to specific milestone titles max: 1 # max assignments (default: 1) target-repo: "owner/repo" # cross-repositoryIssue Updates (update-issue:)
Section titled “Issue Updates (update-issue:)”Updates issue status, title, or body. Only explicitly enabled fields can be updated. Status must be “open” or “closed”.
safe-outputs: update-issue: status: # enable status updates title: # enable title updates body: # enable body updates max: 3 # max updates (default: 1) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryPull Request Updates (update-pull-request:)
Section titled “Pull Request Updates (update-pull-request:)”Updates PR title or body. Both fields are enabled by default. The operation field controls how body updates are applied: append (default), prepend, or replace.
safe-outputs: update-pull-request: title: true # enable title updates (default: true) body: true # enable body updates (default: true) max: 1 # max updates (default: 1) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryOperation Types:
append(default): Adds content to the end with separator and attributionprepend: Adds content to the start with separator and attributionreplace: Completely replaces existing body
Title updates always replace the existing title. Disable fields by setting to false.
Link Sub-Issue (link-sub-issue:)
Section titled “Link Sub-Issue (link-sub-issue:)”Links issues as sub-issues using GitHub’s parent-child issue relationships. Supports filtering by labels and title prefixes for both parent and sub issues.
safe-outputs: link-sub-issue: parent-required-labels: [epic] # parent must have these labels parent-title-prefix: "[Epic]" # parent must match prefix sub-required-labels: [task] # sub must have these labels sub-title-prefix: "[Task]" # sub must match prefix max: 1 # max links (default: 1) target-repo: "owner/repo" # cross-repositoryAgent output includes parent_issue_number and sub_issue_number. Validation ensures both issues exist and meet label/prefix requirements before linking.
Project Board Updates (update-project:)
Section titled “Project Board Updates (update-project:)”Manages GitHub Projects boards. Generated job runs with projects: write permissions, links the board to the repository, and maintains campaign metadata.
Important: GitHub Projects v2 requires a PAT or GitHub App token. The default GITHUB_TOKEN cannot access the Projects v2 GraphQL API. You must configure GH_AW_PROJECT_GITHUB_TOKEN or provide a custom token via safe-outputs.update-project.github-token.
By default, update-project is update-only: if the project board does not exist, the job fails with instructions to create the board manually.
safe-outputs: update-project: max: 20 # max project operations (default: 10) github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} # required: PAT with Projects accessAgent output must include a full GitHub project URL in the project field (e.g., https://github.com/orgs/myorg/projects/42 or https://github.com/users/username/projects/5). Project names or numbers alone are not accepted. Can also supply content_number, content_type, fields, and campaign_id. The job adds the issue or PR to the board, updates custom fields, applies campaign:<id> labels, and exposes project-id, project-number, project-url, campaign-id, and item-id outputs. Cross-repository targeting not supported.
To opt in to creating missing project boards, include create_if_missing: true in the update_project output. Your token must have sufficient permissions to create projects in the organization (classic PAT with project + repo scopes, or fine-grained PAT with Projects: Read+Write, or GitHub App with Projects permissions).
Pull Request Creation (create-pull-request:)
Section titled “Pull Request Creation (create-pull-request:)”Creates pull requests with code changes. Falls back to creating an issue if PR creation fails (e.g., organization settings block it).
safe-outputs: create-pull-request: title-prefix: "[ai] " # prefix for titles labels: [automation] # labels to attach reviewers: [user1, copilot] # reviewers (use 'copilot' for bot) draft: true # create as draft (default: true) expires: 14 # auto-close after 14 days (same-repo only) if-no-changes: "warn" # "warn" (default), "error", or "ignore" target-repo: "owner/repo" # cross-repositoryAuto-Expiration (Same-Repository Only)
Section titled “Auto-Expiration (Same-Repository Only)”The expires field automatically closes pull requests after a specified time period. Only works for same-repository PRs (when target-repo is not set). Supports the same time formats as issues: integers for days, or relative time strings (7d, 2w, 1m, 1y).
Close Pull Request (close-pull-request:)
Section titled “Close Pull Request (close-pull-request:)”Closes pull requests without merging, with an optional comment. Filters by labels and title prefix control which PRs can be closed.
safe-outputs: close-pull-request: target: "triggering" # "triggering" (default), "*", or number required-labels: [automated, stale] # only close with any of these labels required-title-prefix: "[bot]" # only close matching prefix max: 10 # max closures (default: 1) target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires PR event), "*" (any PR), or number (specific PR).
Useful for automated cleanup of stale bot PRs or closing PRs that don’t meet criteria. The comment explains why the PR was closed and includes workflow attribution.
PR Review Comments (create-pull-request-review-comment:)
Section titled “PR Review Comments (create-pull-request-review-comment:)”Creates review comments on specific code lines in PRs. Supports single-line and multi-line comments.
safe-outputs: create-pull-request-review-comment: max: 3 # max comments (default: 10) side: "RIGHT" # "LEFT" or "RIGHT" (default: "RIGHT") target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryCode Scanning Alerts (create-code-scanning-alert:)
Section titled “Code Scanning Alerts (create-code-scanning-alert:)”Creates security advisories in SARIF format and submits to GitHub Code Scanning. Supports severity: error, warning, info, note.
safe-outputs: create-code-scanning-alert: max: 50 # max findings (default: unlimited)Push to PR Branch (push-to-pull-request-branch:)
Section titled “Push to PR Branch (push-to-pull-request-branch:)”Pushes changes to a PR’s branch. Validates via title-prefix and labels to ensure only approved PRs receive changes.
safe-outputs: push-to-pull-request-branch: target: "*" # "triggering" (default), "*", or number title-prefix: "[bot] " # require title prefix labels: [automated] # require all labels if-no-changes: "warn" # "warn" (default), "error", or "ignore"When create-pull-request or push-to-pull-request-branch are enabled, file editing tools (Edit, Write, NotebookEdit) and git commands are added.
Release Updates (update-release:)
Section titled “Release Updates (update-release:)”Updates GitHub release descriptions: replace (complete replacement), append (add to end), or prepend (add to start).
safe-outputs: update-release: max: 1 # max releases (default: 1, max: 10) target-repo: "owner/repo" # cross-repository github-token: ${{ secrets.CUSTOM_TOKEN }} # custom tokenAgent output format: {"type": "update_release", "tag": "v1.0.0", "operation": "replace", "body": "..."}. The tag field is optional for release events (inferred from context). Workflow needs read access; only the generated job receives write permissions.
Asset Uploads (upload-assets:)
Section titled “Asset Uploads (upload-assets:)”Uploads generated files (screenshots, charts, reports, diagrams) to an orphaned git branch for persistent, version-controlled storage. Assets are uploaded without requiring elevated permissions during agent execution—a separate job with contents: write handles the actual commit and push.
How it works:
- Agent generates files during workflow execution (screenshots, data visualizations, etc.)
- Agent calls the
upload_assettool to register each file for upload - Files are uploaded to GitHub Actions artifacts
- After agent completes, a separate job downloads the artifacts and commits them to the specified branch
- Assets are accessible via predictable GitHub URLs
safe-outputs: upload-assets: branch: "assets/my-workflow" # branch name (default: `"assets/${{ github.workflow }}"`) max-size: 5120 # max file size in KB (default: 10240 = 10MB) allowed-exts: [.png, .jpg, .svg] # allowed extensions (default: [.png, .jpg, .jpeg]) max: 20 # max assets (default: 10)Branch Name Requirements:
When creating a new branch, it must start with the assets/ prefix for security. This restriction prevents accidental overwrites of important branches. If the branch already exists, any name is allowed.
- ✅
assets/screenshots- Valid for new branches - ✅
assets/my-workflow- Valid for new branches - ✅
assets/daily-reports- Valid for new branches - ✅
existing-custom-branch- Valid only if branch already exists - ❌
custom/branch-name- Invalid for new branches (missingassets/prefix)
To use a custom branch name without the assets/ prefix, create the branch manually first:
git checkout --orphan my-custom-branchgit rm -rf .git commit --allow-empty -m "Initialize assets branch"git push origin my-custom-branchAgent Output Format:
The agent calls the upload_asset tool with the file path. The tool validates the file, uploads it as an artifact, and records it for later processing:
{ "type": "upload_asset", "path": "/tmp/screenshot.png"}The upload_asset tool automatically:
- Calculates the SHA-256 hash for integrity verification
- Records the file size and validates against
max-sizelimit - Validates the file extension against
allowed-extslist - Generates the target filename and future URL
- Uploads the file to GitHub Actions artifacts
Accessing Uploaded Assets:
Assets are stored in an orphaned branch with no commit history. Each asset gets a predictable URL (replace {owner}, {repo}, {branch}, and {filename} with actual values):
https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{filename}For example, if your workflow uploads screenshot.png to branch assets/docs-tester in repository octocat/hello-world:
https://raw.githubusercontent.com/octocat/hello-world/assets/docs-tester/screenshot.pngThese URLs can be used in:
- Issue comments and descriptions
- Pull request bodies
- Discussion posts
- Documentation and README files
- Any markdown content
Security Features:
- File Path Validation: Only files within the workspace or
/tmpdirectory can be uploaded - Extension Allowlist: Only specified file extensions are permitted (defaults to image formats)
- Size Limits: Maximum file size prevents excessive storage usage
- SHA Verification: Files are verified using SHA-256 hashes before and after upload
- Branch Isolation: Uses orphaned branches (no commit history) to isolate assets from code
- Minimal Permissions: Agent runs with read-only access; only the upload job has write permissions
Common Use Cases:
- Visual Testing: Upload browser screenshots showing UI issues or test results
- Data Visualization: Store generated charts, graphs, and data plots
- Documentation: Generate and store architecture diagrams or API documentation
- Reports: Save PDF or HTML reports for analysis results
- Test Artifacts: Preserve test output, logs, or debug information
Example Workflows:
Multi-device screenshot testing:
---name: Visual Testingon: scheduletools: playwright:safe-outputs: upload-assets: branch: "assets/screenshots" allowed-exts: [.png] max: 50 create-issue:---
Test the documentation site on mobile, tablet, and desktop. Take screenshotsof any layout issues and upload them. Create an issue with the screenshotsembedded using their raw.githubusercontent.com URLs.Data visualization workflow:
---name: Weekly Analyticson: scheduletools: bash:safe-outputs: upload-assets: branch: "assets/charts" allowed-exts: [.png, .svg] max-size: 2048 add-comment:---
Generate charts showing repository metrics (PRs, issues, commits) for thepast week. Save charts to /tmp and upload them. Add a comment to issue #123with the charts embedded.Job Outputs:
The upload assets job provides outputs that can be used by subsequent jobs:
published_count: Number of assets successfully uploadedbranch_name: The branch name where assets were uploaded (normalized)
Permissions Required: contents: write (automatically granted to the upload job only)
Limitations:
- Cross-repository uploads not supported (assets must be in the same repository)
- Maximum file size is 50MB (configurable up to 51200 KB)
- Maximum 100 assets per workflow run (configurable)
- Branch names are normalized to valid git branch names (lowercase, special chars replaced with dashes)
No-Op Logging (noop:)
Section titled “No-Op Logging (noop:)”Enabled by default. Allows agents to produce completion messages when no actions are needed, preventing silent workflow completion.
safe-outputs: create-issue: # noop enabled automatically noop: false # explicitly disableAgent output: {"type": "noop", "message": "Analysis complete - no issues found"}. Messages appear in the workflow conclusion comment or step summary.
Missing Tool Reporting (missing-tool:)
Section titled “Missing Tool Reporting (missing-tool:)”Enabled by default. Automatically detects and reports tools lacking permissions or unavailable functionality.
safe-outputs: create-issue: # missing-tool enabled automatically missing-tool: false # explicitly disableDiscussion Creation (create-discussion:)
Section titled “Discussion Creation (create-discussion:)”Creates GitHub discussions. The category accepts a slug, name, or ID (defaults to first available category if omitted).
safe-outputs: create-discussion: title-prefix: "[ai] " # prefix for titles category: "general" # category slug, name, or ID expires: 3 # auto-close after 3 days max: 3 # max discussions (default: 1) target-repo: "owner/repo" # cross-repositoryAuto-Expiration
Section titled “Auto-Expiration”The expires field automatically closes discussions after a specified time period. Supports both integer (days) and relative time formats (7d, 2w, 1m, 1y). Discussions are closed as “OUTDATED” with an explanatory comment.
When expires is used in any workflow, the compiler automatically generates an agentics-maintenance.yml workflow that runs daily to process expired items.
Close Discussion (close-discussion:)
Section titled “Close Discussion (close-discussion:)”Closes GitHub discussions with optional comment and resolution reason. Filters by category, labels, and title prefix control which discussions can be closed.
safe-outputs: close-discussion: target: "triggering" # "triggering" (default), "*", or number required-category: "Ideas" # only close in category required-labels: [resolved] # only close with labels required-title-prefix: "[ai]" # only close matching prefix max: 1 # max closures (default: 1) target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires discussion event), "*" (any discussion), or number (specific discussion).
Resolution Reasons: RESOLVED, DUPLICATE, OUTDATED, ANSWERED.
Agent Task Creation (create-agent-task:)
Section titled “Agent Task Creation (create-agent-task:)”Creates GitHub Copilot agent tasks. Requires a PAT stored as COPILOT_GITHUB_TOKEN (recommended) or GH_AW_GITHUB_TOKEN (legacy). The default GITHUB_TOKEN lacks agent task permissions.
safe-outputs: create-agent-task: base: main # base branch (defaults to current) target-repo: "owner/repo" # cross-repositoryAssign to Agent (assign-to-agent:)
Section titled “Assign to Agent (assign-to-agent:)”Assigns the GitHub Copilot coding agent to issues. The generated job automatically receives the necessary workflow permissions, you only need to provide a token with agent assignment scope.
safe-outputs: assign-to-agent: name: "copilot" target-repo: "owner/repo" # for cross-repository onlyToken Requirements:
The default GITHUB_TOKEN lacks permission to assign agents. The replaceActorsForAssignable mutation requires elevated permissions. Create a fine-grained personal access token with these permissions and store it as the GH_AW_AGENT_TOKEN secret:
- Read access to metadata (granted by default)
- Write access to actions, contents, issues, and pull requests
Without this token, agent assignment will fail with a clear error message
safe-outputs: assign-to-agent:Alternatively, use a GitHub App installation token or override with github-token:
safe-outputs: app: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} assign-to-agent:Agent Output Format:
{ "type": "assign_to_agent", "issue_number": 123, "agent": "copilot"}Supported Agents:
copilot- GitHub Copilot coding agent (copilot-swe-agent)
Repository Settings:
Ensure Copilot is enabled for your repository. Check organization settings if bot assignments are restricted.
Reference: GitHub Copilot agent documentation
Assign to User (assign-to-user:)
Section titled “Assign to User (assign-to-user:)”Assigns GitHub users to issues. Specify allowed to restrict which users can be assigned.
safe-outputs: assign-to-user: allowed: [user1, user2] # restrict to specific users max: 3 # max assignments (default: 1) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires issue event), "*" (any issue), or number (specific issue).
Agent Output Format:
{ "type": "assign_to_user", "issue_number": 123, "assignees": ["octocat", "mona"]}Single user assignment is also supported:
{ "type": "assign_to_user", "issue_number": 123, "assignee": "octocat"}Cross-Repository Operations
Section titled “Cross-Repository Operations”Many safe outputs support target-repo for cross-repository operations. Requires a PAT (via github-token or GH_AW_GITHUB_TOKEN) with access to target repositories. The default GITHUB_TOKEN only has permissions for the current repository.
safe-outputs: github-token: ${{ secrets.CROSS_REPO_PAT }} create-issue: target-repo: "org/tracking-repo"Use specific repository names (no wildcards).
Automatically Added Tools
Section titled “Automatically Added Tools”When create-pull-request or push-to-pull-request-branch are configured, file editing tools (Edit, MultiEdit, Write, NotebookEdit) and git commands (checkout, branch, switch, add, rm, commit, merge) are automatically enabled.
Security and Sanitization
Section titled “Security and Sanitization”Agent output is automatically sanitized: XML escaped, HTTPS URIs only, domain allowlist (defaults to GitHub), content truncated at 0.5MB or 65k lines, control characters stripped.
safe-outputs: allowed-domains: # additional trusted domains - api.github.com # GitHub domains always includedGlobal Configuration Options
Section titled “Global Configuration Options”Custom GitHub Token (github-token:)
Section titled “Custom GitHub Token (github-token:)”Token precedence: GH_AW_GITHUB_TOKEN → GITHUB_TOKEN (default). Override globally or per safe output:
safe-outputs: github-token: ${{ secrets.CUSTOM_PAT }} # global create-issue: create-pull-request: github-token: ${{ secrets.PR_PAT }} # per-outputGitHub App Token (app:)
Section titled “GitHub App Token (app:)”Use GitHub App installation tokens instead of PATs for enhanced security. Tokens are minted on-demand at job start and auto-revoked at job end, even on failure.
safe-outputs: app: app-id: ${{ vars.APP_ID }} # required private-key: ${{ secrets.APP_PRIVATE_KEY }} # required owner: "my-org" # installation owner repositories: ["repo1", "repo2"] # scope to repositories create-issue:Benefits: On-demand minting, automatic revocation, fine-grained permissions, better attribution, clear audit trail.
Repository Scoping: Not specified (current repo only), empty with owner (all repos in installation), or specified (listed repos only).
Import Support: App config can be imported from shared workflows. Local config takes precedence.
Maximum Patch Size (max-patch-size:)
Section titled “Maximum Patch Size (max-patch-size:)”Limits git patch size for PR operations (1-10,240 KB, default: 1024 KB):
safe-outputs: max-patch-size: 512 # max patch size in KB create-pull-request:Assigning to Copilot
Section titled “Assigning to Copilot”Use assignees: copilot or reviewers: copilot to assign to the Copilot bot. Requires a PAT stored as COPILOT_GITHUB_TOKEN (recommended) or GH_AW_GITHUB_TOKEN (legacy). The default GITHUB_TOKEN lacks bot assignment permissions.
safe-outputs: create-issue: assignees: copilot create-pull-request: reviewers: copilotCustom Runner Image
Section titled “Custom Runner Image”Specify a custom runner image for safe output jobs (default: ubuntu-slim):
safe-outputs: runs-on: ubuntu-22.04 create-issue:Threat Detection
Section titled “Threat Detection”Automatically enabled. Analyzes agent output for prompt injection, secret leaks, and malicious patches before applying safe outputs. See Threat Detection Guide for details.
safe-outputs: create-pull-request: threat-detection: true # explicit enable (default)Agentic Campaign Workflows
Section titled “Agentic Campaign Workflows”Combine create-issue with update-project to launch coordinated initiatives. The project job returns a campaign identifier, applies campaign:<id> labels, and keeps boards synchronized. See Agentic Campaign Workflows.
Custom Messages (messages:)
Section titled “Custom Messages (messages:)”Customize notification messages and footers for safe output operations using template variables. Messages support full Markdown formatting including links, emphasis, and emoji.
Basic Configuration
Section titled “Basic Configuration”safe-outputs: messages: footer: "> 🤖 Generated by [{workflow_name}]({run_url})" run-started: "🚀 [{workflow_name}]({run_url}) is processing this {event_type}..." run-success: "✅ [{workflow_name}]({run_url}) completed successfully" run-failure: "❌ [{workflow_name}]({run_url}) encountered {status}" create-issue:Real-World Example
Section titled “Real-World Example”The archie.md workflow demonstrates custom messages with personality:
safe-outputs: add-comment: max: 1 messages: footer: "> 📊 *Diagram rendered by [{workflow_name}]({run_url})*" run-started: "📐 Archie here! [{workflow_name}]({run_url}) is sketching the architecture on this {event_type}..." run-success: "🎨 Blueprint complete! [{workflow_name}]({run_url}) has visualized the connections. The architecture speaks for itself! ✅" run-failure: "📐 Drafting interrupted! [{workflow_name}]({run_url}) {status}. The diagram remains incomplete..."This example shows:
- Emoji usage: Adding 📐, 🎨, 📊, ✅ for visual appeal
- Markdown links:
[{workflow_name}]({run_url})creates clickable workflow links - Italic text:
*Diagram rendered by...*for emphasis - Placeholder interpolation:
{event_type}and{status}for dynamic content
Available Templates
Section titled “Available Templates”| Template | Description | Use Case |
|---|---|---|
footer | Appended to AI-generated content | Attribution on issues, PRs, comments |
footer-install | Installation instructions | Add install command to footer |
staged-title | Preview title | Staged mode preview header |
staged-description | Preview description | Staged mode preview body |
run-started | Activation comment | Notify when workflow starts |
run-success | Success comment | Notify on successful completion |
run-failure | Failure comment | Notify when workflow fails |
Template Variables
Section titled “Template Variables”| Variable | Description | Available In |
|---|---|---|
{workflow_name} | Workflow name from frontmatter | All templates |
{run_url} | GitHub Actions run URL | All templates |
{triggering_number} | Issue, PR, or discussion number | All templates |
{workflow_source} | Repository path (owner/repo/path@ref) | footer, footer-install |
{workflow_source_url} | GitHub URL to workflow source | footer, footer-install |
{event_type} | Event type description (issue, pull request, discussion, etc.) | run-started |
{status} | Workflow status (failed, cancelled, timed out) | run-failure |
{operation} | Safe output operation name | staged-title, staged-description |
Markdown Formatting
Section titled “Markdown Formatting”Messages support standard GitHub Markdown:
safe-outputs: messages: # Bold and italic footer: "> **Generated** by *[{workflow_name}]({run_url})*"
# Blockquotes run-started: "> 🤖 Processing [{workflow_name}]({run_url})..."
# Links with placeholders run-success: "✅ [View run details]({run_url})"URL Generation
Section titled “URL Generation”Template variables generate valid URLs automatically:
{run_url}→https://github.com/owner/repo/actions/runs/123456789{workflow_source_url}→https://github.com/owner/repo/blob/main/.github/workflows/workflow.md
Combine with Markdown link syntax for clickable links:
footer: "> [View workflow source]({workflow_source_url})"Custom messages can be imported from shared workflows. Local messages override imported ones.
Related Documentation
Section titled “Related Documentation”- Threat Detection Guide - Complete threat detection documentation and examples
- Frontmatter - All configuration options for workflows
- Workflow Structure - Directory layout and organization
- Command Triggers - Special /my-bot triggers and context text
- CLI Commands - CLI commands for workflow management